@odoo/o-spreadsheet 18.1.0-alpha.4 → 18.1.0-alpha.5
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 +953 -496
- package/dist/o-spreadsheet.d.ts +4582 -4569
- package/dist/o-spreadsheet.esm.js +953 -496
- package/dist/o-spreadsheet.iife.js +953 -496
- package/dist/o-spreadsheet.iife.min.js +567 -540
- package/dist/o_spreadsheet.xml +29 -26
- 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.1.0-alpha.
|
|
6
|
-
* @date 2024-11-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.1.0-alpha.5
|
|
6
|
+
* @date 2024-11-22T14:22:50.899Z
|
|
7
|
+
* @hash e13bd67
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
(function (exports, owl) {
|
|
@@ -129,7 +129,6 @@
|
|
|
129
129
|
const HEADER_BORDER_COLOR = "#C0C0C0";
|
|
130
130
|
const CELL_BORDER_COLOR = "#E2E3E3";
|
|
131
131
|
const BACKGROUND_CHART_COLOR = "#FFFFFF";
|
|
132
|
-
const BORDER_CHART_COLOR = "#FFFFFF";
|
|
133
132
|
const DISABLED_TEXT_COLOR = "#CACACA";
|
|
134
133
|
const DEFAULT_COLOR_SCALE_MIDPOINT_COLOR = 0xb6d7a8;
|
|
135
134
|
const LINK_COLOR = "#017E84";
|
|
@@ -3235,7 +3234,6 @@
|
|
|
3235
3234
|
(function (ClipboardMIMEType) {
|
|
3236
3235
|
ClipboardMIMEType["PlainText"] = "text/plain";
|
|
3237
3236
|
ClipboardMIMEType["Html"] = "text/html";
|
|
3238
|
-
ClipboardMIMEType["OSpreadsheet"] = "web application/o-spreadsheet";
|
|
3239
3237
|
})(ClipboardMIMEType || (ClipboardMIMEType = {}));
|
|
3240
3238
|
|
|
3241
3239
|
function isSheetDependent(cmd) {
|
|
@@ -4345,7 +4343,9 @@
|
|
|
4345
4343
|
return reverseSearch ? numberOfValues - i - 1 : i;
|
|
4346
4344
|
}
|
|
4347
4345
|
}
|
|
4348
|
-
return reverseSearch
|
|
4346
|
+
return reverseSearch && closestMatchIndex !== -1
|
|
4347
|
+
? numberOfValues - closestMatchIndex - 1
|
|
4348
|
+
: closestMatchIndex;
|
|
4349
4349
|
}
|
|
4350
4350
|
/**
|
|
4351
4351
|
* Normalize a value.
|
|
@@ -4399,47 +4399,83 @@
|
|
|
4399
4399
|
return true;
|
|
4400
4400
|
}
|
|
4401
4401
|
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
case "yesterday":
|
|
4408
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
|
|
4409
|
-
case "tomorrow":
|
|
4410
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
|
|
4411
|
-
case "lastWeek":
|
|
4412
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
|
|
4413
|
-
case "lastMonth":
|
|
4414
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
|
|
4415
|
-
case "lastYear":
|
|
4416
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
|
|
4417
|
-
}
|
|
4402
|
+
/**
|
|
4403
|
+
* Add the `https` prefix to the url if it's missing
|
|
4404
|
+
*/
|
|
4405
|
+
function withHttps(url) {
|
|
4406
|
+
return !/^https?:\/\//i.test(url) ? `https://${url}` : url;
|
|
4418
4407
|
}
|
|
4419
|
-
|
|
4420
|
-
function
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4408
|
+
const urlRegistry = new Registry();
|
|
4409
|
+
function createWebLink(url, label) {
|
|
4410
|
+
url = withHttps(url);
|
|
4411
|
+
return {
|
|
4412
|
+
url,
|
|
4413
|
+
label: label || url,
|
|
4414
|
+
isExternal: true,
|
|
4415
|
+
isUrlEditable: true,
|
|
4416
|
+
};
|
|
4425
4417
|
}
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4418
|
+
urlRegistry.add("sheet_URL", {
|
|
4419
|
+
match: (url) => isSheetUrl(url),
|
|
4420
|
+
createLink: (url, label) => {
|
|
4421
|
+
return {
|
|
4422
|
+
label,
|
|
4423
|
+
url,
|
|
4424
|
+
isExternal: false,
|
|
4425
|
+
isUrlEditable: false,
|
|
4426
|
+
};
|
|
4427
|
+
},
|
|
4428
|
+
urlRepresentation(url, getters) {
|
|
4429
|
+
const sheetId = parseSheetUrl(url);
|
|
4430
|
+
return getters.tryGetSheetName(sheetId) || _t("Invalid sheet");
|
|
4431
|
+
},
|
|
4432
|
+
open(url, env) {
|
|
4433
|
+
const sheetId = parseSheetUrl(url);
|
|
4434
|
+
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
4435
|
+
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
4436
|
+
sheetIdTo: sheetId,
|
|
4437
|
+
});
|
|
4438
|
+
if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
|
|
4439
|
+
env.notifyUser({
|
|
4440
|
+
type: "warning",
|
|
4441
|
+
sticky: false,
|
|
4442
|
+
text: _t("Cannot open the link because the linked sheet is hidden."),
|
|
4443
|
+
});
|
|
4444
|
+
}
|
|
4445
|
+
},
|
|
4446
|
+
sequence: 0,
|
|
4447
|
+
});
|
|
4448
|
+
const WebUrlSpec = {
|
|
4449
|
+
createLink: createWebLink,
|
|
4450
|
+
match: (url) => isWebLink(url),
|
|
4451
|
+
open: (url) => window.open(url, "_blank"),
|
|
4452
|
+
urlRepresentation: (url) => url,
|
|
4453
|
+
sequence: 0,
|
|
4454
|
+
};
|
|
4455
|
+
function findMatchingSpec(url) {
|
|
4456
|
+
return (urlRegistry
|
|
4457
|
+
.getAll()
|
|
4458
|
+
.sort((a, b) => a.sequence - b.sequence)
|
|
4459
|
+
.find((urlType) => urlType.match(url)) || WebUrlSpec);
|
|
4429
4460
|
}
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4461
|
+
function urlRepresentation(link, getters) {
|
|
4462
|
+
return findMatchingSpec(link.url).urlRepresentation(link.url, getters);
|
|
4463
|
+
}
|
|
4464
|
+
function openLink(link, env) {
|
|
4465
|
+
findMatchingSpec(link.url).open(link.url, env);
|
|
4466
|
+
}
|
|
4467
|
+
function detectLink(value) {
|
|
4468
|
+
if (typeof value !== "string") {
|
|
4469
|
+
return undefined;
|
|
4470
|
+
}
|
|
4471
|
+
if (isMarkdownLink(value)) {
|
|
4472
|
+
const { label, url } = parseMarkdownLink(value);
|
|
4473
|
+
return findMatchingSpec(url).createLink(url, label);
|
|
4474
|
+
}
|
|
4475
|
+
else if (isWebLink(value)) {
|
|
4476
|
+
return createWebLink(value);
|
|
4477
|
+
}
|
|
4478
|
+
return undefined;
|
|
4443
4479
|
}
|
|
4444
4480
|
|
|
4445
4481
|
function tokenizeFormat(str) {
|
|
@@ -5501,6 +5537,193 @@
|
|
|
5501
5537
|
}
|
|
5502
5538
|
}
|
|
5503
5539
|
|
|
5540
|
+
function evaluateLiteral(literalCell, localeFormat) {
|
|
5541
|
+
const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
|
|
5542
|
+
const functionResult = { value, format: localeFormat.format };
|
|
5543
|
+
return createEvaluatedCell(functionResult, localeFormat.locale);
|
|
5544
|
+
}
|
|
5545
|
+
function parseLiteral(content, locale) {
|
|
5546
|
+
if (content.startsWith("=")) {
|
|
5547
|
+
throw new Error(`Cannot parse "${content}" because it's not a literal value. It's a formula`);
|
|
5548
|
+
}
|
|
5549
|
+
if (content === "") {
|
|
5550
|
+
return null;
|
|
5551
|
+
}
|
|
5552
|
+
if (isNumber(content, DEFAULT_LOCALE)) {
|
|
5553
|
+
return parseNumber(content, DEFAULT_LOCALE);
|
|
5554
|
+
}
|
|
5555
|
+
const internalDate = parseDateTime(content, locale);
|
|
5556
|
+
if (internalDate) {
|
|
5557
|
+
return internalDate.value;
|
|
5558
|
+
}
|
|
5559
|
+
if (isBoolean(content)) {
|
|
5560
|
+
return content.toUpperCase() === "TRUE";
|
|
5561
|
+
}
|
|
5562
|
+
return content;
|
|
5563
|
+
}
|
|
5564
|
+
function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
|
|
5565
|
+
const link = detectLink(functionResult.value);
|
|
5566
|
+
if (!link) {
|
|
5567
|
+
return _createEvaluatedCell(functionResult, locale, cell);
|
|
5568
|
+
}
|
|
5569
|
+
const value = parseLiteral(link.label, locale);
|
|
5570
|
+
const format = functionResult.format ||
|
|
5571
|
+
(typeof value === "number"
|
|
5572
|
+
? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
|
|
5573
|
+
: undefined);
|
|
5574
|
+
const linkPayload = {
|
|
5575
|
+
value,
|
|
5576
|
+
format,
|
|
5577
|
+
};
|
|
5578
|
+
return {
|
|
5579
|
+
..._createEvaluatedCell(linkPayload, locale, cell),
|
|
5580
|
+
link,
|
|
5581
|
+
};
|
|
5582
|
+
}
|
|
5583
|
+
function _createEvaluatedCell(functionResult, locale, cell) {
|
|
5584
|
+
let { value, format, message } = functionResult;
|
|
5585
|
+
format = cell?.format || format;
|
|
5586
|
+
const formattedValue = formatValue(value, { format, locale });
|
|
5587
|
+
if (isEvaluationError(value)) {
|
|
5588
|
+
return errorCell(value, message);
|
|
5589
|
+
}
|
|
5590
|
+
if (isTextFormat(format)) {
|
|
5591
|
+
// TO DO:
|
|
5592
|
+
// with the next line, the value of the cell is transformed depending on the format.
|
|
5593
|
+
// This shouldn't happen, by doing this, the formulas handling numbers are not able
|
|
5594
|
+
// to interpret the value as a number.
|
|
5595
|
+
return textCell(toString(value), format, formattedValue);
|
|
5596
|
+
}
|
|
5597
|
+
if (value === null) {
|
|
5598
|
+
return emptyCell(format);
|
|
5599
|
+
}
|
|
5600
|
+
if (typeof value === "number") {
|
|
5601
|
+
if (isDateTimeFormat(format || "")) {
|
|
5602
|
+
return dateTimeCell(value, format, formattedValue);
|
|
5603
|
+
}
|
|
5604
|
+
return numberCell(value, format, formattedValue);
|
|
5605
|
+
}
|
|
5606
|
+
if (typeof value === "boolean") {
|
|
5607
|
+
return booleanCell(value, format, formattedValue);
|
|
5608
|
+
}
|
|
5609
|
+
return textCell(value, format, formattedValue);
|
|
5610
|
+
}
|
|
5611
|
+
function textCell(value, format, formattedValue) {
|
|
5612
|
+
return {
|
|
5613
|
+
value,
|
|
5614
|
+
format,
|
|
5615
|
+
formattedValue,
|
|
5616
|
+
type: CellValueType.text,
|
|
5617
|
+
isAutoSummable: true,
|
|
5618
|
+
defaultAlign: "left",
|
|
5619
|
+
};
|
|
5620
|
+
}
|
|
5621
|
+
function numberCell(value, format, formattedValue) {
|
|
5622
|
+
return {
|
|
5623
|
+
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
5624
|
+
format,
|
|
5625
|
+
formattedValue,
|
|
5626
|
+
type: CellValueType.number,
|
|
5627
|
+
isAutoSummable: true,
|
|
5628
|
+
defaultAlign: "right",
|
|
5629
|
+
};
|
|
5630
|
+
}
|
|
5631
|
+
const emptyCell = memoize(function emptyCell(format) {
|
|
5632
|
+
return {
|
|
5633
|
+
value: null,
|
|
5634
|
+
format,
|
|
5635
|
+
formattedValue: "",
|
|
5636
|
+
type: CellValueType.empty,
|
|
5637
|
+
isAutoSummable: true,
|
|
5638
|
+
defaultAlign: "left",
|
|
5639
|
+
};
|
|
5640
|
+
});
|
|
5641
|
+
function dateTimeCell(value, format, formattedValue) {
|
|
5642
|
+
return {
|
|
5643
|
+
value,
|
|
5644
|
+
format,
|
|
5645
|
+
formattedValue,
|
|
5646
|
+
type: CellValueType.number,
|
|
5647
|
+
isAutoSummable: false,
|
|
5648
|
+
defaultAlign: "right",
|
|
5649
|
+
};
|
|
5650
|
+
}
|
|
5651
|
+
function booleanCell(value, format, formattedValue) {
|
|
5652
|
+
return {
|
|
5653
|
+
value,
|
|
5654
|
+
format,
|
|
5655
|
+
formattedValue,
|
|
5656
|
+
type: CellValueType.boolean,
|
|
5657
|
+
isAutoSummable: false,
|
|
5658
|
+
defaultAlign: "center",
|
|
5659
|
+
};
|
|
5660
|
+
}
|
|
5661
|
+
function errorCell(value, message) {
|
|
5662
|
+
return {
|
|
5663
|
+
value,
|
|
5664
|
+
formattedValue: value,
|
|
5665
|
+
message,
|
|
5666
|
+
type: CellValueType.error,
|
|
5667
|
+
isAutoSummable: false,
|
|
5668
|
+
defaultAlign: "center",
|
|
5669
|
+
};
|
|
5670
|
+
}
|
|
5671
|
+
|
|
5672
|
+
function toCriterionDateNumber(dateValue) {
|
|
5673
|
+
const today = DateTime.now();
|
|
5674
|
+
switch (dateValue) {
|
|
5675
|
+
case "today":
|
|
5676
|
+
return jsDateToNumber(today);
|
|
5677
|
+
case "yesterday":
|
|
5678
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
|
|
5679
|
+
case "tomorrow":
|
|
5680
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
|
|
5681
|
+
case "lastWeek":
|
|
5682
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
|
|
5683
|
+
case "lastMonth":
|
|
5684
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
|
|
5685
|
+
case "lastYear":
|
|
5686
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
/** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
|
|
5690
|
+
function getDateNumberCriterionValues(criterion, locale) {
|
|
5691
|
+
if ("dateValue" in criterion && criterion.dateValue !== "exactDate") {
|
|
5692
|
+
return [toCriterionDateNumber(criterion.dateValue)];
|
|
5693
|
+
}
|
|
5694
|
+
return criterion.values.map((value) => valueToDateNumber(value, locale));
|
|
5695
|
+
}
|
|
5696
|
+
/** Convert the criterion values to numbers. Return undefined values if they cannot be converted to numbers. */
|
|
5697
|
+
function getCriterionValuesAsNumber(criterion, locale) {
|
|
5698
|
+
return criterion.values.map((value) => tryToNumber(value, locale));
|
|
5699
|
+
}
|
|
5700
|
+
function getDateCriterionFormattedValues(values, locale) {
|
|
5701
|
+
return values.map((valueStr) => {
|
|
5702
|
+
if (valueStr.startsWith("=")) {
|
|
5703
|
+
return valueStr;
|
|
5704
|
+
}
|
|
5705
|
+
const value = parseLiteral(valueStr, locale);
|
|
5706
|
+
if (typeof value === "number") {
|
|
5707
|
+
return formatValue(value, { format: locale.dateFormat, locale });
|
|
5708
|
+
}
|
|
5709
|
+
return "";
|
|
5710
|
+
});
|
|
5711
|
+
}
|
|
5712
|
+
|
|
5713
|
+
const MAX_DELAY = 140;
|
|
5714
|
+
const MIN_DELAY = 20;
|
|
5715
|
+
const ACCELERATION = 0.035;
|
|
5716
|
+
/**
|
|
5717
|
+
* Decreasing exponential function used to determine the "speed" of edge-scrolling
|
|
5718
|
+
* as the timeout delay.
|
|
5719
|
+
*
|
|
5720
|
+
* Returns a timeout delay in milliseconds.
|
|
5721
|
+
*/
|
|
5722
|
+
function scrollDelay(value) {
|
|
5723
|
+
// decreasing exponential from MAX_DELAY to MIN_DELAY
|
|
5724
|
+
return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
|
|
5725
|
+
}
|
|
5726
|
+
|
|
5504
5727
|
class RangeImpl {
|
|
5505
5728
|
getSheetSize;
|
|
5506
5729
|
_zone;
|
|
@@ -6148,6 +6371,22 @@
|
|
|
6148
6371
|
const width = content[0].length, height = content.length;
|
|
6149
6372
|
return target.map((t) => splitZoneForPaste(t, width, height)).flat();
|
|
6150
6373
|
}
|
|
6374
|
+
function parseOSClipboardContent(content) {
|
|
6375
|
+
if (!content[ClipboardMIMEType.Html]) {
|
|
6376
|
+
return {
|
|
6377
|
+
text: content[ClipboardMIMEType.PlainText],
|
|
6378
|
+
};
|
|
6379
|
+
}
|
|
6380
|
+
const htmlDocument = new DOMParser().parseFromString(content[ClipboardMIMEType.Html], "text/html");
|
|
6381
|
+
const oSheetClipboardData = htmlDocument
|
|
6382
|
+
.querySelector("div")
|
|
6383
|
+
?.getAttribute("data-osheet-clipboard");
|
|
6384
|
+
const spreadsheetContent = oSheetClipboardData && JSON.parse(oSheetClipboardData);
|
|
6385
|
+
return {
|
|
6386
|
+
text: content[ClipboardMIMEType.PlainText],
|
|
6387
|
+
data: spreadsheetContent,
|
|
6388
|
+
};
|
|
6389
|
+
}
|
|
6151
6390
|
|
|
6152
6391
|
class ClipboardHandler {
|
|
6153
6392
|
getters;
|
|
@@ -6169,7 +6408,7 @@
|
|
|
6169
6408
|
getPasteTarget(sheetId, target, content, options) {
|
|
6170
6409
|
return { zones: [], sheetId };
|
|
6171
6410
|
}
|
|
6172
|
-
|
|
6411
|
+
convertTextToClipboardData(data) {
|
|
6173
6412
|
return;
|
|
6174
6413
|
}
|
|
6175
6414
|
}
|
|
@@ -8012,7 +8251,7 @@
|
|
|
8012
8251
|
this.dispatch("CLEAR_CELL", target);
|
|
8013
8252
|
}
|
|
8014
8253
|
}
|
|
8015
|
-
|
|
8254
|
+
convertTextToClipboardData(text) {
|
|
8016
8255
|
const locale = this.getters.getLocale();
|
|
8017
8256
|
const copiedData = {
|
|
8018
8257
|
cells: [],
|
|
@@ -8120,6 +8359,7 @@
|
|
|
8120
8359
|
|
|
8121
8360
|
class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
8122
8361
|
uuidGenerator = new UuidGenerator();
|
|
8362
|
+
queuedChanges = {};
|
|
8123
8363
|
copy(data) {
|
|
8124
8364
|
if (!data.zones.length) {
|
|
8125
8365
|
return;
|
|
@@ -8141,6 +8381,7 @@
|
|
|
8141
8381
|
return { cfRules };
|
|
8142
8382
|
}
|
|
8143
8383
|
paste(target, clippedContent, options) {
|
|
8384
|
+
this.queuedChanges = {};
|
|
8144
8385
|
if (options.pasteOption === "asValue") {
|
|
8145
8386
|
return;
|
|
8146
8387
|
}
|
|
@@ -8152,6 +8393,7 @@
|
|
|
8152
8393
|
else {
|
|
8153
8394
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
8154
8395
|
}
|
|
8396
|
+
this.executeQueuedChanges();
|
|
8155
8397
|
}
|
|
8156
8398
|
pasteFromCut(sheetId, target, content) {
|
|
8157
8399
|
const selection = target[0];
|
|
@@ -8191,34 +8433,56 @@
|
|
|
8191
8433
|
* Add or remove cells to a given conditional formatting rule.
|
|
8192
8434
|
*/
|
|
8193
8435
|
adaptCFRules(sheetId, cf, toAdd, toRemove) {
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
return;
|
|
8436
|
+
if (!this.queuedChanges[sheetId]) {
|
|
8437
|
+
this.queuedChanges[sheetId] = [];
|
|
8197
8438
|
}
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8439
|
+
const queuedChange = this.queuedChanges[sheetId].find((queued) => queued.cf.id === cf.id);
|
|
8440
|
+
if (!queuedChange) {
|
|
8441
|
+
this.queuedChanges[sheetId].push({ toAdd, toRemove, cf });
|
|
8442
|
+
}
|
|
8443
|
+
else {
|
|
8444
|
+
queuedChange.toAdd.push(...toAdd);
|
|
8445
|
+
queuedChange.toRemove.push(...toRemove);
|
|
8446
|
+
}
|
|
8447
|
+
}
|
|
8448
|
+
executeQueuedChanges() {
|
|
8449
|
+
for (const sheetId in this.queuedChanges) {
|
|
8450
|
+
for (const { toAdd, toRemove, cf } of this.queuedChanges[sheetId]) {
|
|
8451
|
+
const newRangesXc = this.getters.getAdaptedCfRanges(sheetId, cf, toAdd, toRemove);
|
|
8452
|
+
if (!newRangesXc) {
|
|
8453
|
+
continue;
|
|
8454
|
+
}
|
|
8455
|
+
if (newRangesXc.length === 0) {
|
|
8456
|
+
this.dispatch("REMOVE_CONDITIONAL_FORMAT", { id: cf.id, sheetId });
|
|
8457
|
+
continue;
|
|
8458
|
+
}
|
|
8459
|
+
this.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
8460
|
+
cf: {
|
|
8461
|
+
id: cf.id,
|
|
8462
|
+
rule: cf.rule,
|
|
8463
|
+
stopIfTrue: cf.stopIfTrue,
|
|
8464
|
+
},
|
|
8465
|
+
ranges: newRangesXc,
|
|
8466
|
+
sheetId,
|
|
8467
|
+
});
|
|
8468
|
+
}
|
|
8201
8469
|
}
|
|
8202
|
-
this.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
8203
|
-
cf: {
|
|
8204
|
-
id: cf.id,
|
|
8205
|
-
rule: cf.rule,
|
|
8206
|
-
stopIfTrue: cf.stopIfTrue,
|
|
8207
|
-
},
|
|
8208
|
-
ranges: newRangesXc,
|
|
8209
|
-
sheetId,
|
|
8210
|
-
});
|
|
8211
8470
|
}
|
|
8212
8471
|
getCFToCopyTo(targetSheetId, originCF) {
|
|
8213
|
-
|
|
8472
|
+
let targetCF = this.getters
|
|
8214
8473
|
.getConditionalFormats(targetSheetId)
|
|
8215
8474
|
.find((cf) => cf.stopIfTrue === originCF.stopIfTrue && deepEquals(cf.rule, originCF.rule));
|
|
8216
|
-
|
|
8475
|
+
const queuedCfs = this.queuedChanges[targetSheetId];
|
|
8476
|
+
if (!targetCF && queuedCfs) {
|
|
8477
|
+
targetCF = queuedCfs.find((queued) => queued.cf.stopIfTrue === originCF.stopIfTrue && deepEquals(queued.cf.rule, originCF.rule))?.cf;
|
|
8478
|
+
}
|
|
8479
|
+
return targetCF || { ...originCF, id: this.uuidGenerator.uuidv4(), ranges: [] };
|
|
8217
8480
|
}
|
|
8218
8481
|
}
|
|
8219
8482
|
|
|
8220
8483
|
class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
8221
8484
|
uuidGenerator = new UuidGenerator();
|
|
8485
|
+
queuedChanges = {};
|
|
8222
8486
|
copy(data) {
|
|
8223
8487
|
const { rowsIndexes, columnsIndexes } = data;
|
|
8224
8488
|
const sheetId = data.sheetId;
|
|
@@ -8235,6 +8499,7 @@
|
|
|
8235
8499
|
return { dvRules };
|
|
8236
8500
|
}
|
|
8237
8501
|
paste(target, clippedContent, options) {
|
|
8502
|
+
this.queuedChanges = {};
|
|
8238
8503
|
if (options.pasteOption) {
|
|
8239
8504
|
return;
|
|
8240
8505
|
}
|
|
@@ -8246,6 +8511,7 @@
|
|
|
8246
8511
|
else {
|
|
8247
8512
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
8248
8513
|
}
|
|
8514
|
+
this.executeQueuedChanges();
|
|
8249
8515
|
}
|
|
8250
8516
|
pasteFromCut(sheetId, target, content) {
|
|
8251
8517
|
const selection = target[0];
|
|
@@ -8292,29 +8558,55 @@
|
|
|
8292
8558
|
}
|
|
8293
8559
|
}
|
|
8294
8560
|
getDataValidationRuleToCopyTo(targetSheetId, originRule, newId = true) {
|
|
8295
|
-
|
|
8561
|
+
let targetRule = this.getters
|
|
8296
8562
|
.getDataValidationRules(targetSheetId)
|
|
8297
8563
|
.find((rule) => deepEquals(originRule.criterion, rule.criterion) &&
|
|
8298
8564
|
originRule.isBlocking === rule.isBlocking);
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8565
|
+
const queuedRules = this.queuedChanges[targetSheetId];
|
|
8566
|
+
if (!targetRule && queuedRules) {
|
|
8567
|
+
targetRule = queuedRules.find((queued) => deepEquals(originRule.criterion, queued.rule.criterion) &&
|
|
8568
|
+
originRule.isBlocking === queued.rule.isBlocking)?.rule;
|
|
8569
|
+
}
|
|
8570
|
+
return (targetRule || {
|
|
8571
|
+
...originRule,
|
|
8572
|
+
id: newId ? this.uuidGenerator.uuidv4() : originRule.id,
|
|
8573
|
+
ranges: [],
|
|
8574
|
+
});
|
|
8302
8575
|
}
|
|
8303
8576
|
/**
|
|
8304
8577
|
* Add or remove XCs to a given data validation rule.
|
|
8305
8578
|
*/
|
|
8306
8579
|
adaptDataValidationRule(sheetId, rule, toAdd, toRemove) {
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8580
|
+
if (!this.queuedChanges[sheetId]) {
|
|
8581
|
+
this.queuedChanges[sheetId] = [];
|
|
8582
|
+
}
|
|
8583
|
+
const queuedChange = this.queuedChanges[sheetId].find((queued) => queued.rule.id === rule.id);
|
|
8584
|
+
if (!queuedChange) {
|
|
8585
|
+
this.queuedChanges[sheetId].push({ toAdd, toRemove, rule });
|
|
8586
|
+
}
|
|
8587
|
+
else {
|
|
8588
|
+
queuedChange.toAdd.push(...toAdd);
|
|
8589
|
+
queuedChange.toRemove.push(...toRemove);
|
|
8590
|
+
}
|
|
8591
|
+
}
|
|
8592
|
+
executeQueuedChanges() {
|
|
8593
|
+
for (const sheetId in this.queuedChanges) {
|
|
8594
|
+
for (const { toAdd, toRemove, rule: dv } of this.queuedChanges[sheetId]) {
|
|
8595
|
+
// Remove the zones first in case the same position is in toAdd and toRemove
|
|
8596
|
+
const dvZones = dv.ranges.map((range) => range.zone);
|
|
8597
|
+
const withRemovedZones = recomputeZones(dvZones, toRemove);
|
|
8598
|
+
const newDvZones = recomputeZones([...withRemovedZones, ...toAdd], []);
|
|
8599
|
+
if (newDvZones.length === 0) {
|
|
8600
|
+
this.dispatch("REMOVE_DATA_VALIDATION_RULE", { sheetId, id: dv.id });
|
|
8601
|
+
continue;
|
|
8602
|
+
}
|
|
8603
|
+
this.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
8604
|
+
rule: dv,
|
|
8605
|
+
ranges: newDvZones.map((zone) => this.getters.getRangeDataFromZone(sheetId, zone)),
|
|
8606
|
+
sheetId,
|
|
8607
|
+
});
|
|
8608
|
+
}
|
|
8312
8609
|
}
|
|
8313
|
-
this.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
8314
|
-
rule,
|
|
8315
|
-
ranges: newDvZones.map((zone) => this.getters.getRangeDataFromZone(sheetId, zone)),
|
|
8316
|
-
sheetId,
|
|
8317
|
-
});
|
|
8318
8610
|
}
|
|
8319
8611
|
}
|
|
8320
8612
|
|
|
@@ -10285,6 +10577,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10285
10577
|
description,
|
|
10286
10578
|
type: types,
|
|
10287
10579
|
};
|
|
10580
|
+
const acceptErrors = types.includes("ANY") || types.includes("RANGE");
|
|
10581
|
+
if (acceptErrors) {
|
|
10582
|
+
result.acceptErrors = true;
|
|
10583
|
+
}
|
|
10288
10584
|
if (isOptional) {
|
|
10289
10585
|
result.optional = true;
|
|
10290
10586
|
}
|
|
@@ -12347,8 +12643,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12347
12643
|
const COUNT = {
|
|
12348
12644
|
description: _t("The number of numeric values in dataset."),
|
|
12349
12645
|
args: [
|
|
12350
|
-
arg("value1 (number, range<number>)", _t("The first value or range to consider when counting.")),
|
|
12351
|
-
arg("value2 (number, range<number>, repeating)", _t("Additional values or ranges to consider when counting.")),
|
|
12646
|
+
arg("value1 (number, any, range<number>)", _t("The first value or range to consider when counting.")),
|
|
12647
|
+
arg("value2 (number, any, range<number>, repeating)", _t("Additional values or ranges to consider when counting.")),
|
|
12352
12648
|
],
|
|
12353
12649
|
compute: function (...values) {
|
|
12354
12650
|
return countNumbers(values, this.locale);
|
|
@@ -12737,6 +13033,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12737
13033
|
},
|
|
12738
13034
|
isExported: true,
|
|
12739
13035
|
};
|
|
13036
|
+
// CORREL
|
|
12740
13037
|
// In GSheet, CORREL is just an alias to PEARSON
|
|
12741
13038
|
const CORREL = PEARSON;
|
|
12742
13039
|
// -----------------------------------------------------------------------------
|
|
@@ -13347,7 +13644,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
13347
13644
|
}
|
|
13348
13645
|
const databaseArgs = [
|
|
13349
13646
|
arg("database (range)", _t("The array or range containing the data to consider, structured in such a way that the first row contains the labels for each column's values.")),
|
|
13350
|
-
arg("field (
|
|
13647
|
+
arg("field (number, string)", _t("Indicates which column in database contains the values to be extracted and operated on.")),
|
|
13351
13648
|
arg("criteria (range)", _t("An array or range containing zero or more criteria to filter the database values by before operating.")),
|
|
13352
13649
|
];
|
|
13353
13650
|
// -----------------------------------------------------------------------------
|
|
@@ -17729,7 +18026,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
17729
18026
|
args: [
|
|
17730
18027
|
arg("condition1 (boolean)", _t("The first condition to be evaluated. This can be a boolean, a number, an array, or a reference to any of those.")),
|
|
17731
18028
|
arg("value1 (any)", _t("The returned value if condition1 is TRUE.")),
|
|
17732
|
-
arg("condition2 (boolean, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
|
|
18029
|
+
arg("condition2 (boolean, any, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
|
|
17733
18030
|
arg("value2 (any, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
|
|
17734
18031
|
],
|
|
17735
18032
|
compute: function (...values) {
|
|
@@ -17968,7 +18265,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
17968
18265
|
const HLOOKUP = {
|
|
17969
18266
|
description: _t("Horizontal lookup"),
|
|
17970
18267
|
args: [
|
|
17971
|
-
arg("search_key (
|
|
18268
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
17972
18269
|
arg("range (range)", _t("The range to consider for the search. The first row in the range is searched for the key specified in search_key.")),
|
|
17973
18270
|
arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
|
|
17974
18271
|
arg(`is_sorted (boolean, default=${DEFAULT_IS_SORTED})`, _t("Indicates whether the row to be searched (the first row of the specified range) is sorted, in which case the closest match for search_key will be returned.")),
|
|
@@ -17976,9 +18273,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
17976
18273
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
17977
18274
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
17978
18275
|
assert(() => 1 <= _index && _index <= range[0].length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
|
|
17979
|
-
if (searchKey && isEvaluationError(searchKey.value)) {
|
|
17980
|
-
return searchKey;
|
|
17981
|
-
}
|
|
17982
18276
|
const getValueFromRange = (range, index) => range[index][0].value;
|
|
17983
18277
|
const _isSorted = toBoolean(isSorted.value);
|
|
17984
18278
|
const colIndex = _isSorted
|
|
@@ -18075,7 +18369,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18075
18369
|
const LOOKUP = {
|
|
18076
18370
|
description: _t("Look up a value."),
|
|
18077
18371
|
args: [
|
|
18078
|
-
arg("search_key (
|
|
18372
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
18079
18373
|
arg("search_array (range)", _t("One method of using this function is to provide a single sorted row or column search_array to look through for the search_key with a second argument result_range. The other way is to combine these two arguments into one search_array where the first row or column is searched and a value is returned from the last row or column in the array. If search_key is not found, a non-exact match may be returned.")),
|
|
18080
18374
|
arg("result_range (range, optional)", _t("The range from which to return a result. The value returned corresponds to the location where search_key is found in search_range. This range must be only a single row or column and should not be used if using the search_result_array method.")),
|
|
18081
18375
|
],
|
|
@@ -18115,7 +18409,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18115
18409
|
const MATCH = {
|
|
18116
18410
|
description: _t("Position of item in range that matches value."),
|
|
18117
18411
|
args: [
|
|
18118
|
-
arg("search_key (
|
|
18412
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
18119
18413
|
arg("range (any, range)", _t("The one-dimensional array to be searched.")),
|
|
18120
18414
|
arg(`search_type (number, default=${DEFAULT_SEARCH_TYPE})`, _t("The search method. 1 (default) finds the largest value less than or equal to search_key when range is sorted in ascending order. 0 finds the exact value when range is unsorted. -1 finds the smallest value greater than or equal to search_key when range is sorted in descending order.")),
|
|
18121
18415
|
],
|
|
@@ -18190,7 +18484,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18190
18484
|
const VLOOKUP = {
|
|
18191
18485
|
description: _t("Vertical lookup."),
|
|
18192
18486
|
args: [
|
|
18193
|
-
arg("search_key (
|
|
18487
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
18194
18488
|
arg("range (any, range)", _t("The range to consider for the search. The first column in the range is searched for the key specified in search_key.")),
|
|
18195
18489
|
arg("index (number)", _t("The column index of the value to be returned, where the first column in range is numbered 1.")),
|
|
18196
18490
|
arg(`is_sorted (boolean, default=${DEFAULT_IS_SORTED})`, _t("Indicates whether the column to be searched (the first column of the specified range) is sorted, in which case the closest match for search_key will be returned.")),
|
|
@@ -18198,9 +18492,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18198
18492
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
18199
18493
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
18200
18494
|
assert(() => 1 <= _index && _index <= range.length, _t("[[FUNCTION_NAME]] evaluates to an out of bounds range."));
|
|
18201
|
-
if (searchKey && isEvaluationError(searchKey.value)) {
|
|
18202
|
-
return searchKey;
|
|
18203
|
-
}
|
|
18204
18495
|
const getValueFromRange = (range, index) => range[0][index].value;
|
|
18205
18496
|
const _isSorted = toBoolean(isSorted.value);
|
|
18206
18497
|
const rowIndex = _isSorted
|
|
@@ -18226,7 +18517,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18226
18517
|
const XLOOKUP = {
|
|
18227
18518
|
description: _t("Search a range for a match and return the corresponding item from a second range."),
|
|
18228
18519
|
args: [
|
|
18229
|
-
arg("search_key (
|
|
18520
|
+
arg("search_key (string,number,boolean)", _t("The value to search for.")),
|
|
18230
18521
|
arg("lookup_range (any, range)", _t("The range to consider for the search. Should be a single column or a single row.")),
|
|
18231
18522
|
arg("return_range (any, range)", _t("The range containing the return value. Should have the same dimensions as lookup_range.")),
|
|
18232
18523
|
arg("if_not_found (any, optional)", _t("If a valid match is not found, return this value.")),
|
|
@@ -18251,9 +18542,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18251
18542
|
assert(() => lookupDirection === "col"
|
|
18252
18543
|
? returnRange[0].length === lookupRange[0].length
|
|
18253
18544
|
: returnRange.length === lookupRange.length, _t("return_range should have the same dimensions as lookup_range."));
|
|
18254
|
-
if (searchKey && isEvaluationError(searchKey.value)) {
|
|
18255
|
-
return [[searchKey]];
|
|
18256
|
-
}
|
|
18257
18545
|
const getElement = lookupDirection === "col"
|
|
18258
18546
|
? (range, index) => range[0][index].value
|
|
18259
18547
|
: (range, index) => range[index][0].value;
|
|
@@ -18278,13 +18566,14 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18278
18566
|
//--------------------------------------------------------------------------
|
|
18279
18567
|
// Pivot functions
|
|
18280
18568
|
//--------------------------------------------------------------------------
|
|
18569
|
+
// PIVOT.VALUE
|
|
18281
18570
|
const PIVOT_VALUE = {
|
|
18282
18571
|
description: _t("Get the value from a pivot."),
|
|
18283
18572
|
args: [
|
|
18284
|
-
arg("pivot_id (string)", _t("ID of the pivot.")),
|
|
18573
|
+
arg("pivot_id (number,string)", _t("ID of the pivot.")),
|
|
18285
18574
|
arg("measure_name (string)", _t("Name of the measure.")),
|
|
18286
18575
|
arg("domain_field_name (string,optional,repeating)", _t("Field name.")),
|
|
18287
|
-
arg("domain_value (string,optional,repeating)", _t("Value.")),
|
|
18576
|
+
arg("domain_value (number,string,boolean,optional,repeating)", _t("Value.")),
|
|
18288
18577
|
],
|
|
18289
18578
|
compute: function (formulaId, measureName, ...domainArgs) {
|
|
18290
18579
|
const _pivotFormulaId = toString(formulaId);
|
|
@@ -18311,12 +18600,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18311
18600
|
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
18312
18601
|
},
|
|
18313
18602
|
};
|
|
18603
|
+
// PIVOT.HEADER
|
|
18314
18604
|
const PIVOT_HEADER = {
|
|
18315
18605
|
description: _t("Get the header of a pivot."),
|
|
18316
18606
|
args: [
|
|
18317
|
-
arg("pivot_id (string)", _t("ID of the pivot.")),
|
|
18607
|
+
arg("pivot_id (number,string)", _t("ID of the pivot.")),
|
|
18318
18608
|
arg("domain_field_name (string,optional,repeating)", _t("Field name.")),
|
|
18319
|
-
arg("domain_value (string,optional,repeating)", _t("Value.")),
|
|
18609
|
+
arg("domain_value (number,string,value,optional,repeating)", _t("Value.")),
|
|
18320
18610
|
],
|
|
18321
18611
|
compute: function (pivotId, ...domainArgs) {
|
|
18322
18612
|
const _pivotFormulaId = toString(pivotId);
|
|
@@ -18563,8 +18853,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18563
18853
|
const EQ = {
|
|
18564
18854
|
description: _t("Equal."),
|
|
18565
18855
|
args: [
|
|
18566
|
-
arg("value1 (
|
|
18567
|
-
arg("value2 (
|
|
18856
|
+
arg("value1 (string, number, boolean)", _t("The first value.")),
|
|
18857
|
+
arg("value2 (string, number, boolean)", _t("The value to test against value1 for equality.")),
|
|
18568
18858
|
],
|
|
18569
18859
|
compute: function (value1, value2) {
|
|
18570
18860
|
if (isEvaluationError(value1?.value)) {
|
|
@@ -18615,8 +18905,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18615
18905
|
const GT = {
|
|
18616
18906
|
description: _t("Strictly greater than."),
|
|
18617
18907
|
args: [
|
|
18618
|
-
arg("value1 (
|
|
18619
|
-
arg("value2 (
|
|
18908
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being greater than value2.")),
|
|
18909
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18620
18910
|
],
|
|
18621
18911
|
compute: function (value1, value2) {
|
|
18622
18912
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
@@ -18630,8 +18920,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18630
18920
|
const GTE = {
|
|
18631
18921
|
description: _t("Greater than or equal to."),
|
|
18632
18922
|
args: [
|
|
18633
|
-
arg("value1 (
|
|
18634
|
-
arg("value2 (
|
|
18923
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being greater than or equal to value2.")),
|
|
18924
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18635
18925
|
],
|
|
18636
18926
|
compute: function (value1, value2) {
|
|
18637
18927
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
@@ -18645,8 +18935,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18645
18935
|
const LT = {
|
|
18646
18936
|
description: _t("Less than."),
|
|
18647
18937
|
args: [
|
|
18648
|
-
arg("value1 (
|
|
18649
|
-
arg("value2 (
|
|
18938
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being less than value2.")),
|
|
18939
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18650
18940
|
],
|
|
18651
18941
|
compute: function (value1, value2) {
|
|
18652
18942
|
const result = GTE.compute.bind(this)(value1, value2);
|
|
@@ -18662,8 +18952,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18662
18952
|
const LTE = {
|
|
18663
18953
|
description: _t("Less than or equal to."),
|
|
18664
18954
|
args: [
|
|
18665
|
-
arg("value1 (
|
|
18666
|
-
arg("value2 (
|
|
18955
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being less than or equal to value2.")),
|
|
18956
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18667
18957
|
],
|
|
18668
18958
|
compute: function (value1, value2) {
|
|
18669
18959
|
const result = GT.compute.bind(this)(value1, value2);
|
|
@@ -18711,8 +19001,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18711
19001
|
const NE = {
|
|
18712
19002
|
description: _t("Not equal."),
|
|
18713
19003
|
args: [
|
|
18714
|
-
arg("value1 (
|
|
18715
|
-
arg("value2 (
|
|
19004
|
+
arg("value1 (string, number, boolean)", _t("The first value.")),
|
|
19005
|
+
arg("value2 (string, number, boolean)", _t("The value to test against value1 for inequality.")),
|
|
18716
19006
|
],
|
|
18717
19007
|
compute: function (value1, value2) {
|
|
18718
19008
|
const result = EQ.compute.bind(this)(value1, value2);
|
|
@@ -19630,6 +19920,16 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
19630
19920
|
});
|
|
19631
19921
|
}
|
|
19632
19922
|
function errorHandlingCompute(...args) {
|
|
19923
|
+
for (let i = 0; i < args.length; i++) {
|
|
19924
|
+
const arg = args[i];
|
|
19925
|
+
const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
|
|
19926
|
+
// Early exit if the argument is an error and the function does not accept errors
|
|
19927
|
+
// We only check scalar arguments, not matrix arguments for performance reasons.
|
|
19928
|
+
// Casting helpers are responsible for handling errors in matrix arguments.
|
|
19929
|
+
if (!argDefinition.acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) {
|
|
19930
|
+
return arg;
|
|
19931
|
+
}
|
|
19932
|
+
}
|
|
19633
19933
|
try {
|
|
19634
19934
|
return computeFunctionToObject.apply(this, args);
|
|
19635
19935
|
}
|
|
@@ -19638,6 +19938,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
19638
19938
|
}
|
|
19639
19939
|
}
|
|
19640
19940
|
function computeFunctionToObject(...args) {
|
|
19941
|
+
if (this.debug) {
|
|
19942
|
+
debugger;
|
|
19943
|
+
}
|
|
19641
19944
|
const result = descr.compute.apply(this, args);
|
|
19642
19945
|
if (!isMatrix(result)) {
|
|
19643
19946
|
if (typeof result === "object" && result !== null && "value" in result) {
|
|
@@ -20747,6 +21050,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
20747
21050
|
}
|
|
20748
21051
|
if (ast.debug) {
|
|
20749
21052
|
code.append("debugger;");
|
|
21053
|
+
code.append(`ctx["debug"] = true;`);
|
|
20750
21054
|
}
|
|
20751
21055
|
switch (ast.type) {
|
|
20752
21056
|
case "BOOLEAN":
|
|
@@ -21345,217 +21649,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
21345
21649
|
},
|
|
21346
21650
|
});
|
|
21347
21651
|
|
|
21348
|
-
/**
|
|
21349
|
-
* Add the `https` prefix to the url if it's missing
|
|
21350
|
-
*/
|
|
21351
|
-
function withHttps(url) {
|
|
21352
|
-
return !/^https?:\/\//i.test(url) ? `https://${url}` : url;
|
|
21353
|
-
}
|
|
21354
|
-
const urlRegistry = new Registry();
|
|
21355
|
-
function createWebLink(url, label) {
|
|
21356
|
-
url = withHttps(url);
|
|
21357
|
-
return {
|
|
21358
|
-
url,
|
|
21359
|
-
label: label || url,
|
|
21360
|
-
isExternal: true,
|
|
21361
|
-
isUrlEditable: true,
|
|
21362
|
-
};
|
|
21363
|
-
}
|
|
21364
|
-
urlRegistry.add("sheet_URL", {
|
|
21365
|
-
match: (url) => isSheetUrl(url),
|
|
21366
|
-
createLink: (url, label) => {
|
|
21367
|
-
return {
|
|
21368
|
-
label,
|
|
21369
|
-
url,
|
|
21370
|
-
isExternal: false,
|
|
21371
|
-
isUrlEditable: false,
|
|
21372
|
-
};
|
|
21373
|
-
},
|
|
21374
|
-
urlRepresentation(url, getters) {
|
|
21375
|
-
const sheetId = parseSheetUrl(url);
|
|
21376
|
-
return getters.tryGetSheetName(sheetId) || _t("Invalid sheet");
|
|
21377
|
-
},
|
|
21378
|
-
open(url, env) {
|
|
21379
|
-
const sheetId = parseSheetUrl(url);
|
|
21380
|
-
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
21381
|
-
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
21382
|
-
sheetIdTo: sheetId,
|
|
21383
|
-
});
|
|
21384
|
-
if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
|
|
21385
|
-
env.notifyUser({
|
|
21386
|
-
type: "warning",
|
|
21387
|
-
sticky: false,
|
|
21388
|
-
text: _t("Cannot open the link because the linked sheet is hidden."),
|
|
21389
|
-
});
|
|
21390
|
-
}
|
|
21391
|
-
},
|
|
21392
|
-
sequence: 0,
|
|
21393
|
-
});
|
|
21394
|
-
const WebUrlSpec = {
|
|
21395
|
-
createLink: createWebLink,
|
|
21396
|
-
match: (url) => isWebLink(url),
|
|
21397
|
-
open: (url) => window.open(url, "_blank"),
|
|
21398
|
-
urlRepresentation: (url) => url,
|
|
21399
|
-
sequence: 0,
|
|
21400
|
-
};
|
|
21401
|
-
function findMatchingSpec(url) {
|
|
21402
|
-
return (urlRegistry
|
|
21403
|
-
.getAll()
|
|
21404
|
-
.sort((a, b) => a.sequence - b.sequence)
|
|
21405
|
-
.find((urlType) => urlType.match(url)) || WebUrlSpec);
|
|
21406
|
-
}
|
|
21407
|
-
function urlRepresentation(link, getters) {
|
|
21408
|
-
return findMatchingSpec(link.url).urlRepresentation(link.url, getters);
|
|
21409
|
-
}
|
|
21410
|
-
function openLink(link, env) {
|
|
21411
|
-
findMatchingSpec(link.url).open(link.url, env);
|
|
21412
|
-
}
|
|
21413
|
-
function detectLink(value) {
|
|
21414
|
-
if (typeof value !== "string") {
|
|
21415
|
-
return undefined;
|
|
21416
|
-
}
|
|
21417
|
-
if (isMarkdownLink(value)) {
|
|
21418
|
-
const { label, url } = parseMarkdownLink(value);
|
|
21419
|
-
return findMatchingSpec(url).createLink(url, label);
|
|
21420
|
-
}
|
|
21421
|
-
else if (isWebLink(value)) {
|
|
21422
|
-
return createWebLink(value);
|
|
21423
|
-
}
|
|
21424
|
-
return undefined;
|
|
21425
|
-
}
|
|
21426
|
-
|
|
21427
|
-
function evaluateLiteral(literalCell, localeFormat) {
|
|
21428
|
-
const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
|
|
21429
|
-
const functionResult = { value, format: localeFormat.format };
|
|
21430
|
-
return createEvaluatedCell(functionResult, localeFormat.locale);
|
|
21431
|
-
}
|
|
21432
|
-
function parseLiteral(content, locale) {
|
|
21433
|
-
if (content.startsWith("=")) {
|
|
21434
|
-
throw new Error(`Cannot parse "${content}" because it's not a literal value. It's a formula`);
|
|
21435
|
-
}
|
|
21436
|
-
if (content === "") {
|
|
21437
|
-
return null;
|
|
21438
|
-
}
|
|
21439
|
-
if (isNumber(content, DEFAULT_LOCALE)) {
|
|
21440
|
-
return parseNumber(content, DEFAULT_LOCALE);
|
|
21441
|
-
}
|
|
21442
|
-
const internalDate = parseDateTime(content, locale);
|
|
21443
|
-
if (internalDate) {
|
|
21444
|
-
return internalDate.value;
|
|
21445
|
-
}
|
|
21446
|
-
if (isBoolean(content)) {
|
|
21447
|
-
return content.toUpperCase() === "TRUE";
|
|
21448
|
-
}
|
|
21449
|
-
return content;
|
|
21450
|
-
}
|
|
21451
|
-
function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
|
|
21452
|
-
const link = detectLink(functionResult.value);
|
|
21453
|
-
if (!link) {
|
|
21454
|
-
return _createEvaluatedCell(functionResult, locale, cell);
|
|
21455
|
-
}
|
|
21456
|
-
const value = parseLiteral(link.label, locale);
|
|
21457
|
-
const format = functionResult.format ||
|
|
21458
|
-
(typeof value === "number"
|
|
21459
|
-
? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
|
|
21460
|
-
: undefined);
|
|
21461
|
-
const linkPayload = {
|
|
21462
|
-
value,
|
|
21463
|
-
format,
|
|
21464
|
-
};
|
|
21465
|
-
return {
|
|
21466
|
-
..._createEvaluatedCell(linkPayload, locale, cell),
|
|
21467
|
-
link,
|
|
21468
|
-
};
|
|
21469
|
-
}
|
|
21470
|
-
function _createEvaluatedCell(functionResult, locale, cell) {
|
|
21471
|
-
let { value, format, message } = functionResult;
|
|
21472
|
-
format = cell?.format || format;
|
|
21473
|
-
const formattedValue = formatValue(value, { format, locale });
|
|
21474
|
-
if (isEvaluationError(value)) {
|
|
21475
|
-
return errorCell(value, message);
|
|
21476
|
-
}
|
|
21477
|
-
if (isTextFormat(format)) {
|
|
21478
|
-
// TO DO:
|
|
21479
|
-
// with the next line, the value of the cell is transformed depending on the format.
|
|
21480
|
-
// This shouldn't happen, by doing this, the formulas handling numbers are not able
|
|
21481
|
-
// to interpret the value as a number.
|
|
21482
|
-
return textCell(toString(value), format, formattedValue);
|
|
21483
|
-
}
|
|
21484
|
-
if (value === null) {
|
|
21485
|
-
return emptyCell(format);
|
|
21486
|
-
}
|
|
21487
|
-
if (typeof value === "number") {
|
|
21488
|
-
if (isDateTimeFormat(format || "")) {
|
|
21489
|
-
return dateTimeCell(value, format, formattedValue);
|
|
21490
|
-
}
|
|
21491
|
-
return numberCell(value, format, formattedValue);
|
|
21492
|
-
}
|
|
21493
|
-
if (typeof value === "boolean") {
|
|
21494
|
-
return booleanCell(value, format, formattedValue);
|
|
21495
|
-
}
|
|
21496
|
-
return textCell(value, format, formattedValue);
|
|
21497
|
-
}
|
|
21498
|
-
function textCell(value, format, formattedValue) {
|
|
21499
|
-
return {
|
|
21500
|
-
value,
|
|
21501
|
-
format,
|
|
21502
|
-
formattedValue,
|
|
21503
|
-
type: CellValueType.text,
|
|
21504
|
-
isAutoSummable: true,
|
|
21505
|
-
defaultAlign: "left",
|
|
21506
|
-
};
|
|
21507
|
-
}
|
|
21508
|
-
function numberCell(value, format, formattedValue) {
|
|
21509
|
-
return {
|
|
21510
|
-
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
21511
|
-
format,
|
|
21512
|
-
formattedValue,
|
|
21513
|
-
type: CellValueType.number,
|
|
21514
|
-
isAutoSummable: true,
|
|
21515
|
-
defaultAlign: "right",
|
|
21516
|
-
};
|
|
21517
|
-
}
|
|
21518
|
-
const emptyCell = memoize(function emptyCell(format) {
|
|
21519
|
-
return {
|
|
21520
|
-
value: null,
|
|
21521
|
-
format,
|
|
21522
|
-
formattedValue: "",
|
|
21523
|
-
type: CellValueType.empty,
|
|
21524
|
-
isAutoSummable: true,
|
|
21525
|
-
defaultAlign: "left",
|
|
21526
|
-
};
|
|
21527
|
-
});
|
|
21528
|
-
function dateTimeCell(value, format, formattedValue) {
|
|
21529
|
-
return {
|
|
21530
|
-
value,
|
|
21531
|
-
format,
|
|
21532
|
-
formattedValue,
|
|
21533
|
-
type: CellValueType.number,
|
|
21534
|
-
isAutoSummable: false,
|
|
21535
|
-
defaultAlign: "right",
|
|
21536
|
-
};
|
|
21537
|
-
}
|
|
21538
|
-
function booleanCell(value, format, formattedValue) {
|
|
21539
|
-
return {
|
|
21540
|
-
value,
|
|
21541
|
-
format,
|
|
21542
|
-
formattedValue,
|
|
21543
|
-
type: CellValueType.boolean,
|
|
21544
|
-
isAutoSummable: false,
|
|
21545
|
-
defaultAlign: "center",
|
|
21546
|
-
};
|
|
21547
|
-
}
|
|
21548
|
-
function errorCell(value, message) {
|
|
21549
|
-
return {
|
|
21550
|
-
value,
|
|
21551
|
-
formattedValue: value,
|
|
21552
|
-
message,
|
|
21553
|
-
type: CellValueType.error,
|
|
21554
|
-
isAutoSummable: false,
|
|
21555
|
-
defaultAlign: "center",
|
|
21556
|
-
};
|
|
21557
|
-
}
|
|
21558
|
-
|
|
21559
21652
|
/**
|
|
21560
21653
|
* An AutofillModifierImplementation is used to describe how to handle a
|
|
21561
21654
|
* AutofillModifier.
|
|
@@ -22920,6 +23013,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
22920
23013
|
WarningTypes["CfIconSetEmptyIconNotSupported"] = "IconSets with empty icons";
|
|
22921
23014
|
WarningTypes["BadlyFormattedHyperlink"] = "Badly formatted hyperlink";
|
|
22922
23015
|
WarningTypes["NumFmtIdNotSupported"] = "Number format";
|
|
23016
|
+
WarningTypes["TimeDataValidationNotSupported"] = "Time data validation rules";
|
|
23017
|
+
WarningTypes["TextLengthDataValidationNotSupported"] = "Text length data validation rules";
|
|
23018
|
+
WarningTypes["WholeNumberDataValidationNotSupported"] = "Whole number data validation rules";
|
|
23019
|
+
WarningTypes["NotEqualDateDataValidationNotSupported"] = "Not equal date data validation rules";
|
|
22923
23020
|
})(WarningTypes || (WarningTypes = {}));
|
|
22924
23021
|
class XLSXImportWarningManager {
|
|
22925
23022
|
_parsingWarnings = new Set();
|
|
@@ -23298,6 +23395,25 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23298
23395
|
webp: "image/webp",
|
|
23299
23396
|
jpg: "image/jpeg",
|
|
23300
23397
|
};
|
|
23398
|
+
const XLSX_DV_DECIMAL_OPERATOR_MAPPING = {
|
|
23399
|
+
between: "isBetween",
|
|
23400
|
+
notBetween: "isNotBetween",
|
|
23401
|
+
equal: "isEqual",
|
|
23402
|
+
notEqual: "isNotEqual",
|
|
23403
|
+
greaterThan: "isGreaterThan",
|
|
23404
|
+
greaterThanOrEqual: "isGreaterOrEqualTo",
|
|
23405
|
+
lessThan: "isLessThan",
|
|
23406
|
+
lessThanOrEqual: "isLessOrEqualTo",
|
|
23407
|
+
};
|
|
23408
|
+
const XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING = {
|
|
23409
|
+
between: "dateIsBetween",
|
|
23410
|
+
notBetween: "dateIsNotBetween",
|
|
23411
|
+
equal: "dateIs",
|
|
23412
|
+
greaterThan: "dateIsAfter",
|
|
23413
|
+
greaterThanOrEqual: "dateIsOnOrAfter",
|
|
23414
|
+
lessThan: "dateIsBefore",
|
|
23415
|
+
lessThanOrEqual: "dateIsOnOrBefore",
|
|
23416
|
+
};
|
|
23301
23417
|
|
|
23302
23418
|
/**
|
|
23303
23419
|
* Most of the functions could stay private, but are exported for testing purposes
|
|
@@ -24079,6 +24195,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24079
24195
|
}
|
|
24080
24196
|
return position / HEIGHT_FACTOR;
|
|
24081
24197
|
}
|
|
24198
|
+
/**
|
|
24199
|
+
* Convert the o-spreadsheet data validation decimal
|
|
24200
|
+
* criterion type to the corresponding excel operator.
|
|
24201
|
+
*/
|
|
24202
|
+
function convertDecimalCriterionTypeToExcelOperator(operator) {
|
|
24203
|
+
return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
|
|
24204
|
+
}
|
|
24205
|
+
/**
|
|
24206
|
+
* Convert the o-spreadsheet data validation date
|
|
24207
|
+
* criterion type to the corresponding excel operator.
|
|
24208
|
+
*/
|
|
24209
|
+
function convertDateCriterionTypeToExcelOperator(operator) {
|
|
24210
|
+
return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
|
|
24211
|
+
}
|
|
24082
24212
|
|
|
24083
24213
|
function convertFigures(sheetData) {
|
|
24084
24214
|
let id = 1;
|
|
@@ -24188,6 +24318,112 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24188
24318
|
};
|
|
24189
24319
|
}
|
|
24190
24320
|
|
|
24321
|
+
function convertDataValidationRules(xlsxDataValidations, warningManager) {
|
|
24322
|
+
const dvRules = [];
|
|
24323
|
+
let dvId = 1;
|
|
24324
|
+
for (const dv of xlsxDataValidations) {
|
|
24325
|
+
if (!dv) {
|
|
24326
|
+
continue;
|
|
24327
|
+
}
|
|
24328
|
+
switch (dv.type) {
|
|
24329
|
+
case "time":
|
|
24330
|
+
warningManager.generateNotSupportedWarning(WarningTypes.TimeDataValidationNotSupported);
|
|
24331
|
+
break;
|
|
24332
|
+
case "textLength":
|
|
24333
|
+
warningManager.generateNotSupportedWarning(WarningTypes.TextLengthDataValidationNotSupported);
|
|
24334
|
+
break;
|
|
24335
|
+
case "whole":
|
|
24336
|
+
warningManager.generateNotSupportedWarning(WarningTypes.WholeNumberDataValidationNotSupported);
|
|
24337
|
+
break;
|
|
24338
|
+
case "decimal":
|
|
24339
|
+
const decimalRule = convertDecimalRule(dvId++, dv);
|
|
24340
|
+
dvRules.push(decimalRule);
|
|
24341
|
+
break;
|
|
24342
|
+
case "list":
|
|
24343
|
+
const listRule = convertListrule(dvId++, dv);
|
|
24344
|
+
dvRules.push(listRule);
|
|
24345
|
+
break;
|
|
24346
|
+
case "date":
|
|
24347
|
+
if (dv.operator === "notEqual") {
|
|
24348
|
+
warningManager.generateNotSupportedWarning(WarningTypes.NotEqualDateDataValidationNotSupported);
|
|
24349
|
+
break;
|
|
24350
|
+
}
|
|
24351
|
+
const dateRule = convertDateRule(dvId++, dv);
|
|
24352
|
+
dvRules.push(dateRule);
|
|
24353
|
+
break;
|
|
24354
|
+
case "custom":
|
|
24355
|
+
const customRule = convertCustomRule(dvId++, dv);
|
|
24356
|
+
dvRules.push(customRule);
|
|
24357
|
+
break;
|
|
24358
|
+
}
|
|
24359
|
+
}
|
|
24360
|
+
return dvRules;
|
|
24361
|
+
}
|
|
24362
|
+
function convertDecimalRule(id, dv) {
|
|
24363
|
+
const values = [dv.formula1.toString()];
|
|
24364
|
+
if (dv.formula2) {
|
|
24365
|
+
values.push(dv.formula2.toString());
|
|
24366
|
+
}
|
|
24367
|
+
return {
|
|
24368
|
+
id: id.toString(),
|
|
24369
|
+
ranges: dv.sqref,
|
|
24370
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24371
|
+
criterion: {
|
|
24372
|
+
type: XLSX_DV_DECIMAL_OPERATOR_MAPPING[dv.operator],
|
|
24373
|
+
values,
|
|
24374
|
+
},
|
|
24375
|
+
};
|
|
24376
|
+
}
|
|
24377
|
+
function convertListrule(id, dv) {
|
|
24378
|
+
const formula1 = dv.formula1.toString();
|
|
24379
|
+
const isRangeRule = rangeReference.test(formula1);
|
|
24380
|
+
return {
|
|
24381
|
+
id: id.toString(),
|
|
24382
|
+
ranges: dv.sqref,
|
|
24383
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24384
|
+
criterion: {
|
|
24385
|
+
type: isRangeRule ? "isValueInRange" : "isValueInList",
|
|
24386
|
+
values: isRangeRule ? [formula1] : formula1.replaceAll('"', "").split(","),
|
|
24387
|
+
displayStyle: "arrow",
|
|
24388
|
+
},
|
|
24389
|
+
};
|
|
24390
|
+
}
|
|
24391
|
+
function convertDateRule(id, dv) {
|
|
24392
|
+
let criterion;
|
|
24393
|
+
const values = [dv.formula1.toString()];
|
|
24394
|
+
if (dv.formula2) {
|
|
24395
|
+
values.push(dv.formula2.toString());
|
|
24396
|
+
criterion = {
|
|
24397
|
+
type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
|
|
24398
|
+
values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
|
|
24399
|
+
};
|
|
24400
|
+
}
|
|
24401
|
+
else {
|
|
24402
|
+
criterion = {
|
|
24403
|
+
type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
|
|
24404
|
+
values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
|
|
24405
|
+
dateValue: "exactDate",
|
|
24406
|
+
};
|
|
24407
|
+
}
|
|
24408
|
+
return {
|
|
24409
|
+
id: id.toString(),
|
|
24410
|
+
ranges: dv.sqref,
|
|
24411
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24412
|
+
criterion: criterion,
|
|
24413
|
+
};
|
|
24414
|
+
}
|
|
24415
|
+
function convertCustomRule(id, dv) {
|
|
24416
|
+
return {
|
|
24417
|
+
id: id.toString(),
|
|
24418
|
+
ranges: dv.sqref,
|
|
24419
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24420
|
+
criterion: {
|
|
24421
|
+
type: "customFormula",
|
|
24422
|
+
values: [`=${dv.formula1.toString()}`],
|
|
24423
|
+
},
|
|
24424
|
+
};
|
|
24425
|
+
}
|
|
24426
|
+
|
|
24191
24427
|
/**
|
|
24192
24428
|
* Match external reference (ex. '[1]Sheet 3'!$B$4)
|
|
24193
24429
|
*
|
|
@@ -24308,6 +24544,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24308
24544
|
cols: convertCols(sheet, sheetDims[0], colHeaderGroups),
|
|
24309
24545
|
rows: convertRows(sheet, sheetDims[1], rowHeaderGroups),
|
|
24310
24546
|
conditionalFormats: convertConditionalFormats(sheet.cfs, data.dxfs, warningManager),
|
|
24547
|
+
dataValidationRules: convertDataValidationRules(sheet.dataValidations, warningManager),
|
|
24311
24548
|
figures: convertFigures(sheet),
|
|
24312
24549
|
isVisible: sheet.isVisible,
|
|
24313
24550
|
panes: sheetOptions
|
|
@@ -25587,6 +25824,41 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25587
25824
|
}
|
|
25588
25825
|
}
|
|
25589
25826
|
|
|
25827
|
+
class XlsxDataValidationExtractor extends XlsxBaseExtractor {
|
|
25828
|
+
theme;
|
|
25829
|
+
constructor(sheetFile, xlsxStructure, warningManager, theme) {
|
|
25830
|
+
super(sheetFile, xlsxStructure, warningManager);
|
|
25831
|
+
this.theme = theme;
|
|
25832
|
+
}
|
|
25833
|
+
extractDataValidations() {
|
|
25834
|
+
const dataValidations = this.mapOnElements({ parent: this.rootFile.file.xml, query: "worksheet > dataValidations > dataValidation" }, (dvElement) => {
|
|
25835
|
+
return {
|
|
25836
|
+
type: this.extractAttr(dvElement, "type", { required: true }).asString(),
|
|
25837
|
+
operator: this.extractAttr(dvElement, "operator", {
|
|
25838
|
+
default: "between",
|
|
25839
|
+
})?.asString(),
|
|
25840
|
+
sqref: this.extractAttr(dvElement, "sqref", { required: true }).asString().split(" "),
|
|
25841
|
+
errorStyle: this.extractAttr(dvElement, "errorStyle")?.asString(),
|
|
25842
|
+
formula1: this.extractDataValidationFormula(dvElement, 1)[0],
|
|
25843
|
+
formula2: this.extractDataValidationFormula(dvElement, 2)[0],
|
|
25844
|
+
showErrorMessage: this.extractAttr(dvElement, "showErrorMessage")?.asBool(),
|
|
25845
|
+
errorTitle: this.extractAttr(dvElement, "errorTitle")?.asString(),
|
|
25846
|
+
error: this.extractAttr(dvElement, "error")?.asString(),
|
|
25847
|
+
showInputMessage: this.extractAttr(dvElement, "showInputMessage")?.asBool(),
|
|
25848
|
+
promptTitle: this.extractAttr(dvElement, "promptTitle")?.asString(),
|
|
25849
|
+
prompt: this.extractAttr(dvElement, "prompt")?.asString(),
|
|
25850
|
+
allowBlank: this.extractAttr(dvElement, "allowBlank")?.asBool(),
|
|
25851
|
+
};
|
|
25852
|
+
});
|
|
25853
|
+
return dataValidations;
|
|
25854
|
+
}
|
|
25855
|
+
extractDataValidationFormula(dvElement, index) {
|
|
25856
|
+
return this.mapOnElements({ parent: dvElement, query: `formula${index}` }, (cfFormulaElements) => {
|
|
25857
|
+
return this.extractTextContent(cfFormulaElements, { required: true });
|
|
25858
|
+
});
|
|
25859
|
+
}
|
|
25860
|
+
}
|
|
25861
|
+
|
|
25590
25862
|
class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
25591
25863
|
extractChart() {
|
|
25592
25864
|
return this.mapOnElements({ parent: this.rootFile.file.xml, query: "c:chartSpace" }, (rootChartElement) => {
|
|
@@ -25954,6 +26226,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25954
26226
|
sharedFormulas: this.extractSharedFormulas(sheetElement),
|
|
25955
26227
|
merges: this.extractMerges(sheetElement),
|
|
25956
26228
|
cfs: this.extractConditionalFormats(),
|
|
26229
|
+
dataValidations: this.extractDataValidations(),
|
|
25957
26230
|
figures: this.extractFigures(sheetElement),
|
|
25958
26231
|
hyperlinks: this.extractHyperLinks(sheetElement),
|
|
25959
26232
|
tables: this.extractTables(sheetElement),
|
|
@@ -26025,6 +26298,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
26025
26298
|
extractConditionalFormats() {
|
|
26026
26299
|
return new XlsxCfExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractConditionalFormattings();
|
|
26027
26300
|
}
|
|
26301
|
+
extractDataValidations() {
|
|
26302
|
+
return new XlsxDataValidationExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractDataValidations();
|
|
26303
|
+
}
|
|
26028
26304
|
extractFigures(worksheet) {
|
|
26029
26305
|
const figures = this.mapOnElements({ parent: worksheet, query: "drawing" }, (drawingElement) => {
|
|
26030
26306
|
const drawingId = this.extractAttr(drawingElement, "r:id", { required: true })?.asString();
|
|
@@ -27332,6 +27608,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27332
27608
|
rows: {},
|
|
27333
27609
|
merges: [],
|
|
27334
27610
|
conditionalFormats: [],
|
|
27611
|
+
dataValidationRules: [],
|
|
27335
27612
|
figures: [],
|
|
27336
27613
|
tables: [],
|
|
27337
27614
|
isVisible: true,
|
|
@@ -27406,17 +27683,15 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27406
27683
|
});
|
|
27407
27684
|
}
|
|
27408
27685
|
catch (error) {
|
|
27409
|
-
const parsedSpreadsheetContent = clipboardContent
|
|
27410
|
-
|
|
27411
|
-
: {};
|
|
27412
|
-
if (parsedSpreadsheetContent.version && parsedSpreadsheetContent.version !== CURRENT_VERSION) {
|
|
27686
|
+
const parsedSpreadsheetContent = clipboardContent.data;
|
|
27687
|
+
if (parsedSpreadsheetContent?.version !== CURRENT_VERSION) {
|
|
27413
27688
|
env.raiseError(_t("An unexpected error occurred while pasting content.\
|
|
27414
27689
|
This is probably due to a spreadsheet version mismatch."));
|
|
27415
27690
|
}
|
|
27416
27691
|
result = env.model.dispatch("PASTE_FROM_OS_CLIPBOARD", {
|
|
27417
27692
|
target,
|
|
27418
27693
|
clipboardContent: {
|
|
27419
|
-
|
|
27694
|
+
text: clipboardContent.text,
|
|
27420
27695
|
},
|
|
27421
27696
|
pasteOption,
|
|
27422
27697
|
});
|
|
@@ -27541,6 +27816,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27541
27816
|
numberValue: _t("The value must be a number"),
|
|
27542
27817
|
dateValue: _t("The value must be a date"),
|
|
27543
27818
|
validRange: _t("The value must be a valid range"),
|
|
27819
|
+
validFormula: _t("The formula must be valid"),
|
|
27820
|
+
},
|
|
27821
|
+
Errors: {
|
|
27822
|
+
["InvalidRange" /* CommandResult.InvalidRange */]: _t("The range is invalid."),
|
|
27823
|
+
["InvalidDataValidationCriterionValue" /* CommandResult.InvalidDataValidationCriterionValue */]: _t("One or more of the provided criteria values are invalid. Please review and correct them."),
|
|
27824
|
+
["InvalidNumberOfCriterionValues" /* CommandResult.InvalidNumberOfCriterionValues */]: _t("One or more of the provided criteria values are missing."),
|
|
27825
|
+
Unexpected: _t("The rule is invalid for an unknown reason."),
|
|
27544
27826
|
},
|
|
27545
27827
|
};
|
|
27546
27828
|
const TableTerms = {
|
|
@@ -27751,7 +28033,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27751
28033
|
labels.length > dataSetsValues[0].data.length) {
|
|
27752
28034
|
labels.shift();
|
|
27753
28035
|
}
|
|
27754
|
-
({ labels, dataSetsValues } =
|
|
28036
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27755
28037
|
if (definition.aggregated) {
|
|
27756
28038
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
27757
28039
|
}
|
|
@@ -27806,7 +28088,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27806
28088
|
labels.length > dataSetsValues[0].data.length) {
|
|
27807
28089
|
labels.shift();
|
|
27808
28090
|
}
|
|
27809
|
-
({ labels, dataSetsValues } =
|
|
28091
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27810
28092
|
if (axisType === "time") {
|
|
27811
28093
|
({ labels, dataSetsValues } = fixEmptyLabelsForDateCharts(labels, dataSetsValues));
|
|
27812
28094
|
}
|
|
@@ -27823,7 +28105,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27823
28105
|
if (definition.cumulative) {
|
|
27824
28106
|
let accumulator = 0;
|
|
27825
28107
|
data = data.map((value) => {
|
|
27826
|
-
if (!isNaN(value)) {
|
|
28108
|
+
if (!isNaN(parseFloat(value))) {
|
|
27827
28109
|
accumulator += parseFloat(value);
|
|
27828
28110
|
return accumulator;
|
|
27829
28111
|
}
|
|
@@ -27856,11 +28138,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27856
28138
|
labels.length > dataSetsValues[0].data.length) {
|
|
27857
28139
|
labels.shift();
|
|
27858
28140
|
}
|
|
27859
|
-
({ labels, dataSetsValues } =
|
|
28141
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27860
28142
|
if (definition.aggregated) {
|
|
27861
28143
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
27862
28144
|
}
|
|
27863
|
-
({ dataSetsValues, labels } =
|
|
28145
|
+
({ dataSetsValues, labels } = keepOnlyPositiveValues(labels, dataSetsValues));
|
|
27864
28146
|
const dataSetFormat = getChartDatasetFormat(getters, dataSets, "left");
|
|
27865
28147
|
return {
|
|
27866
28148
|
dataSetsValues,
|
|
@@ -27878,7 +28160,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27878
28160
|
labels.length > dataSetsValues[0].data.length) {
|
|
27879
28161
|
labels.shift();
|
|
27880
28162
|
}
|
|
27881
|
-
({ labels, dataSetsValues } =
|
|
28163
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27882
28164
|
if (definition.aggregated) {
|
|
27883
28165
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
27884
28166
|
}
|
|
@@ -28057,26 +28339,16 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
28057
28339
|
}
|
|
28058
28340
|
return isInstalled;
|
|
28059
28341
|
}
|
|
28060
|
-
function
|
|
28061
|
-
const
|
|
28062
|
-
|
|
28063
|
-
|
|
28064
|
-
|
|
28065
|
-
|
|
28066
|
-
|
|
28067
|
-
|
|
28068
|
-
}
|
|
28069
|
-
|
|
28070
|
-
}, []);
|
|
28071
|
-
const filteredLabels = dataPointsIndexes.map((i) => labels[i] || "");
|
|
28072
|
-
const filteredDatasets = datasets.map((dataset) => ({
|
|
28073
|
-
...dataset,
|
|
28074
|
-
data: dataPointsIndexes.map((i) => {
|
|
28075
|
-
const dataPoint = dataset.data[i];
|
|
28076
|
-
return typeof dataPoint !== "number" || dataPoint >= 0 ? dataPoint : 0;
|
|
28077
|
-
}),
|
|
28078
|
-
}));
|
|
28079
|
-
return { labels: filteredLabels, dataSetsValues: filteredDatasets };
|
|
28342
|
+
function keepOnlyPositiveValues(labels, datasets) {
|
|
28343
|
+
const numberOfDataPoints = Math.max(labels.length, ...datasets.map((dataset) => dataset.data?.length || 0));
|
|
28344
|
+
const filteredIndexes = range(0, numberOfDataPoints).filter((i) => datasets.some((ds) => typeof ds.data[i] === "number" && ds.data[i] > 0));
|
|
28345
|
+
return {
|
|
28346
|
+
labels: filteredIndexes.map((i) => labels[i] || ""),
|
|
28347
|
+
dataSetsValues: datasets.map((ds) => ({
|
|
28348
|
+
...ds,
|
|
28349
|
+
data: filteredIndexes.map((i) => typeof ds.data[i] === "number" && ds.data[i] > 0 ? ds.data[i] : null),
|
|
28350
|
+
})),
|
|
28351
|
+
};
|
|
28080
28352
|
}
|
|
28081
28353
|
function fixEmptyLabelsForDateCharts(labels, dataSetsValues) {
|
|
28082
28354
|
if (labels.length === 0 || labels.every((label) => !label)) {
|
|
@@ -28109,18 +28381,23 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
28109
28381
|
}
|
|
28110
28382
|
return [];
|
|
28111
28383
|
}
|
|
28112
|
-
|
|
28384
|
+
/**
|
|
28385
|
+
* Filter the data points that:
|
|
28386
|
+
* - have neither a label nor a value
|
|
28387
|
+
* - have no label and a non-numeric value
|
|
28388
|
+
*/
|
|
28389
|
+
function filterInvalidDataPoints(labels, datasets) {
|
|
28113
28390
|
const numberOfDataPoints = Math.max(labels.length, ...datasets.map((dataset) => dataset.data?.length || 0));
|
|
28114
28391
|
const dataPointsIndexes = range(0, numberOfDataPoints).filter((dataPointIndex) => {
|
|
28115
28392
|
const label = labels[dataPointIndex];
|
|
28116
28393
|
const values = datasets.map((dataset) => dataset.data?.[dataPointIndex]);
|
|
28117
|
-
return label || values.some((value) => value ===
|
|
28394
|
+
return label || values.some((value) => typeof value === "number");
|
|
28118
28395
|
});
|
|
28119
28396
|
return {
|
|
28120
28397
|
labels: dataPointsIndexes.map((i) => labels[i] || ""),
|
|
28121
28398
|
dataSetsValues: datasets.map((dataset) => ({
|
|
28122
28399
|
...dataset,
|
|
28123
|
-
data: dataPointsIndexes.map((i) => dataset.data[i]),
|
|
28400
|
+
data: dataPointsIndexes.map((i) => typeof dataset.data[i] === "number" ? dataset.data[i] : null),
|
|
28124
28401
|
})),
|
|
28125
28402
|
};
|
|
28126
28403
|
}
|
|
@@ -28263,8 +28540,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
28263
28540
|
const dataset = {
|
|
28264
28541
|
label,
|
|
28265
28542
|
data,
|
|
28266
|
-
borderColor:
|
|
28267
|
-
borderWidth: 1,
|
|
28543
|
+
borderColor: definition.background || BACKGROUND_CHART_COLOR,
|
|
28544
|
+
borderWidth: definition.stacked ? 1 : 0,
|
|
28268
28545
|
backgroundColor,
|
|
28269
28546
|
yAxisID: definition.horizontal ? "y" : definition.dataSets?.[index].yAxisId || "y",
|
|
28270
28547
|
xAxisID: "x",
|
|
@@ -32936,13 +33213,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
32936
33213
|
const osClipboard = await env.clipboard.read();
|
|
32937
33214
|
switch (osClipboard.status) {
|
|
32938
33215
|
case "ok":
|
|
32939
|
-
const
|
|
32940
|
-
const
|
|
32941
|
-
const clipboardId = JSON.parse(osClipboardSpreadsheetContent).clipboardId ??
|
|
32942
|
-
htmlDocument.querySelector("div")?.getAttribute("data-clipboard-id");
|
|
33216
|
+
const clipboardContent = parseOSClipboardContent(osClipboard.content);
|
|
33217
|
+
const clipboardId = clipboardContent.data?.clipboardId;
|
|
32943
33218
|
const target = env.model.getters.getSelectedZones();
|
|
32944
33219
|
if (env.model.getters.getClipboardId() !== clipboardId) {
|
|
32945
|
-
interactivePasteFromOS(env, target,
|
|
33220
|
+
interactivePasteFromOS(env, target, clipboardContent, pasteOption);
|
|
32946
33221
|
}
|
|
32947
33222
|
else {
|
|
32948
33223
|
interactivePaste(env, target, pasteOption);
|
|
@@ -36162,7 +36437,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36162
36437
|
flex-grow: 0;
|
|
36163
36438
|
}
|
|
36164
36439
|
|
|
36165
|
-
|
|
36440
|
+
/* Make the character a bit bigger
|
|
36166
36441
|
compared to its neighbor INPUT box */
|
|
36167
36442
|
.o-remove-selection {
|
|
36168
36443
|
font-size: calc(100% + 4px);
|
|
@@ -36767,7 +37042,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36767
37042
|
css /* scss */ `
|
|
36768
37043
|
.o-color-picker {
|
|
36769
37044
|
padding: ${PICKER_PADDING}px 0;
|
|
36770
|
-
|
|
37045
|
+
/* FIXME: this is useless, overiden by the popover container */
|
|
36771
37046
|
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
|
|
36772
37047
|
background-color: white;
|
|
36773
37048
|
line-height: 1.2;
|
|
@@ -36910,7 +37185,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36910
37185
|
margin-right: 2px;
|
|
36911
37186
|
}
|
|
36912
37187
|
.o-wrong-color {
|
|
36913
|
-
|
|
37188
|
+
/* FIXME bootstrap class instead? */
|
|
36914
37189
|
outline-color: red;
|
|
36915
37190
|
border-color: red;
|
|
36916
37191
|
&:focus {
|
|
@@ -38954,10 +39229,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
38954
39229
|
}
|
|
38955
39230
|
|
|
38956
39231
|
.fa-stack {
|
|
38957
|
-
|
|
38958
|
-
width:
|
|
38959
|
-
height:
|
|
38960
|
-
line-height:
|
|
39232
|
+
/* reset stack size which is doubled by default */
|
|
39233
|
+
width: ${CLOSE_ICON_RADIUS * 2}px;
|
|
39234
|
+
height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
39235
|
+
line-height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
38961
39236
|
}
|
|
38962
39237
|
|
|
38963
39238
|
.force-open-assistant {
|
|
@@ -38974,7 +39249,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
38974
39249
|
margin: 1px 4px;
|
|
38975
39250
|
|
|
38976
39251
|
.o-semi-bold {
|
|
38977
|
-
|
|
39252
|
+
/* FIXME: to remove in favor of Bootstrap
|
|
38978
39253
|
* 'fw-semibold' when we upgrade to Bootstrap 5.2
|
|
38979
39254
|
*/
|
|
38980
39255
|
font-weight: 600 !important;
|
|
@@ -40210,7 +40485,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
40210
40485
|
.o-threshold-value {
|
|
40211
40486
|
flex-grow: 1;
|
|
40212
40487
|
flex-basis: 60%;
|
|
40213
|
-
min-width: 0px;
|
|
40488
|
+
min-width: 0px; /* input overflows in Firefox otherwise */
|
|
40214
40489
|
}
|
|
40215
40490
|
.o-threshold-value input:disabled {
|
|
40216
40491
|
background-color: #edebed;
|
|
@@ -41029,7 +41304,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41029
41304
|
name: _t("Date is"),
|
|
41030
41305
|
getPreview: (criterion, getters) => {
|
|
41031
41306
|
return criterion.dateValue === "exactDate"
|
|
41032
|
-
? _t("Date is %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41307
|
+
? _t("Date is %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41033
41308
|
: _t("Date is %s", DVTerms.DateIs[criterion.dateValue]);
|
|
41034
41309
|
},
|
|
41035
41310
|
});
|
|
@@ -41054,7 +41329,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41054
41329
|
name: _t("Date is before"),
|
|
41055
41330
|
getPreview: (criterion, getters) => {
|
|
41056
41331
|
return criterion.dateValue === "exactDate"
|
|
41057
|
-
? _t("Date is before %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41332
|
+
? _t("Date is before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41058
41333
|
: _t("Date is before %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41059
41334
|
},
|
|
41060
41335
|
});
|
|
@@ -41079,7 +41354,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41079
41354
|
name: _t("Date is on or before"),
|
|
41080
41355
|
getPreview: (criterion, getters) => {
|
|
41081
41356
|
return criterion.dateValue === "exactDate"
|
|
41082
|
-
? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41357
|
+
? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41083
41358
|
: _t("Date is on or before %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41084
41359
|
},
|
|
41085
41360
|
});
|
|
@@ -41104,7 +41379,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41104
41379
|
name: _t("Date is after"),
|
|
41105
41380
|
getPreview: (criterion, getters) => {
|
|
41106
41381
|
return criterion.dateValue === "exactDate"
|
|
41107
|
-
? _t("Date is after %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41382
|
+
? _t("Date is after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41108
41383
|
: _t("Date is after %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41109
41384
|
},
|
|
41110
41385
|
});
|
|
@@ -41129,7 +41404,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41129
41404
|
name: _t("Date is on or after"),
|
|
41130
41405
|
getPreview: (criterion, getters) => {
|
|
41131
41406
|
return criterion.dateValue === "exactDate"
|
|
41132
|
-
? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41407
|
+
? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41133
41408
|
: _t("Date is on or after %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41134
41409
|
},
|
|
41135
41410
|
});
|
|
@@ -41155,7 +41430,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41155
41430
|
numberOfValues: () => 2,
|
|
41156
41431
|
name: _t("Date is between"),
|
|
41157
41432
|
getPreview: (criterion, getters) => {
|
|
41158
|
-
const values = getDateCriterionFormattedValues(criterion, getters);
|
|
41433
|
+
const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
|
|
41159
41434
|
return _t("Date is between %s and %s", values[0], values[1]);
|
|
41160
41435
|
},
|
|
41161
41436
|
});
|
|
@@ -41181,7 +41456,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41181
41456
|
numberOfValues: () => 2,
|
|
41182
41457
|
name: _t("Date is not between"),
|
|
41183
41458
|
getPreview: (criterion, getters) => {
|
|
41184
|
-
const values = getDateCriterionFormattedValues(criterion, getters);
|
|
41459
|
+
const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
|
|
41185
41460
|
return _t("Date is not between %s and %s", values[0], values[1]);
|
|
41186
41461
|
},
|
|
41187
41462
|
});
|
|
@@ -41464,19 +41739,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41464
41739
|
const valueAsNumber = tryToNumber(value, DEFAULT_LOCALE);
|
|
41465
41740
|
return valueAsNumber !== undefined;
|
|
41466
41741
|
}
|
|
41467
|
-
function getDateCriterionFormattedValues(criterion, getters) {
|
|
41468
|
-
const locale = getters.getLocale();
|
|
41469
|
-
return criterion.values.map((valueStr) => {
|
|
41470
|
-
if (valueStr.startsWith("=")) {
|
|
41471
|
-
return valueStr;
|
|
41472
|
-
}
|
|
41473
|
-
const value = parseLiteral(valueStr, locale);
|
|
41474
|
-
if (typeof value === "number") {
|
|
41475
|
-
return formatValue(value, { format: locale.dateFormat, locale });
|
|
41476
|
-
}
|
|
41477
|
-
return "";
|
|
41478
|
-
});
|
|
41479
|
-
}
|
|
41480
41742
|
|
|
41481
41743
|
/** This component looks like a select input, but on click it opens a Menu with the items given as props instead of a dropdown */
|
|
41482
41744
|
class SelectMenu extends owl.Component {
|
|
@@ -41558,6 +41820,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41558
41820
|
focused: false,
|
|
41559
41821
|
onBlur: () => { },
|
|
41560
41822
|
};
|
|
41823
|
+
static components = { StandaloneComposer: StandaloneComposer };
|
|
41561
41824
|
inputRef = owl.useRef("input");
|
|
41562
41825
|
setup() {
|
|
41563
41826
|
owl.useEffect(() => {
|
|
@@ -41569,10 +41832,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41569
41832
|
state = owl.useState({
|
|
41570
41833
|
shouldDisplayError: !!this.props.value, // Don't display error if user inputted nothing yet
|
|
41571
41834
|
});
|
|
41572
|
-
onValueChanged(ev) {
|
|
41573
|
-
this.state.shouldDisplayError = true;
|
|
41574
|
-
this.props.onValueChanged(ev.target.value);
|
|
41575
|
-
}
|
|
41576
41835
|
get placeholder() {
|
|
41577
41836
|
const evaluator = dataValidationEvaluatorRegistry.get(this.props.criterionType);
|
|
41578
41837
|
if (evaluator.allowedValues === "onlyFormulas") {
|
|
@@ -41583,6 +41842,27 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41583
41842
|
}
|
|
41584
41843
|
return _t("Value or formula");
|
|
41585
41844
|
}
|
|
41845
|
+
get allowedValues() {
|
|
41846
|
+
const evaluator = dataValidationEvaluatorRegistry.get(this.props.criterionType);
|
|
41847
|
+
return evaluator.allowedValues ?? "any";
|
|
41848
|
+
}
|
|
41849
|
+
onInputValueChanged(ev) {
|
|
41850
|
+
this.state.shouldDisplayError = true;
|
|
41851
|
+
this.props.onValueChanged(ev.target.value);
|
|
41852
|
+
}
|
|
41853
|
+
onChangeComposerValue(str) {
|
|
41854
|
+
this.state.shouldDisplayError = true;
|
|
41855
|
+
this.props.onValueChanged(str);
|
|
41856
|
+
}
|
|
41857
|
+
getDataValidationRuleInputComposerProps() {
|
|
41858
|
+
return {
|
|
41859
|
+
onConfirm: (str) => this.onChangeComposerValue(str),
|
|
41860
|
+
composerContent: this.props.value,
|
|
41861
|
+
placeholder: this.placeholder,
|
|
41862
|
+
class: "o-sidePanel-composer",
|
|
41863
|
+
defaultRangeSheetId: this.env.model.getters.getActiveSheetId(),
|
|
41864
|
+
};
|
|
41865
|
+
}
|
|
41586
41866
|
get errorMessage() {
|
|
41587
41867
|
if (!this.state.shouldDisplayError) {
|
|
41588
41868
|
return undefined;
|
|
@@ -41918,13 +42198,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41918
42198
|
|
|
41919
42199
|
class DataValidationEditor extends owl.Component {
|
|
41920
42200
|
static template = "o-spreadsheet-DataValidationEditor";
|
|
41921
|
-
static components = { SelectionInput, SelectMenu, Section };
|
|
42201
|
+
static components = { SelectionInput, SelectMenu, Section, ValidationMessages };
|
|
41922
42202
|
static props = {
|
|
41923
42203
|
rule: { type: Object, optional: true },
|
|
41924
42204
|
onExit: Function,
|
|
41925
42205
|
onCloseSidePanel: { type: Function, optional: true },
|
|
41926
42206
|
};
|
|
41927
|
-
state = owl.useState({ rule: this.defaultDataValidationRule });
|
|
42207
|
+
state = owl.useState({ rule: this.defaultDataValidationRule, errors: [] });
|
|
41928
42208
|
setup() {
|
|
41929
42209
|
if (this.props.rule) {
|
|
41930
42210
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
@@ -41949,15 +42229,15 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41949
42229
|
this.state.rule.isBlocking = isBlocking === "true";
|
|
41950
42230
|
}
|
|
41951
42231
|
onSave() {
|
|
41952
|
-
if (
|
|
41953
|
-
|
|
42232
|
+
if (this.state.rule) {
|
|
42233
|
+
const result = this.env.model.dispatch("ADD_DATA_VALIDATION_RULE", this.dispatchPayload);
|
|
42234
|
+
if (!result.isSuccessful) {
|
|
42235
|
+
this.state.errors = result.reasons;
|
|
42236
|
+
}
|
|
42237
|
+
else {
|
|
42238
|
+
this.props.onExit();
|
|
42239
|
+
}
|
|
41954
42240
|
}
|
|
41955
|
-
this.env.model.dispatch("ADD_DATA_VALIDATION_RULE", this.dispatchPayload);
|
|
41956
|
-
this.props.onExit();
|
|
41957
|
-
}
|
|
41958
|
-
get canSave() {
|
|
41959
|
-
return this.env.model.canDispatch("ADD_DATA_VALIDATION_RULE", this.dispatchPayload)
|
|
41960
|
-
.isSuccessful;
|
|
41961
42241
|
}
|
|
41962
42242
|
get dispatchPayload() {
|
|
41963
42243
|
const rule = { ...this.state.rule, ranges: undefined };
|
|
@@ -41998,6 +42278,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41998
42278
|
get criterionComponent() {
|
|
41999
42279
|
return dataValidationPanelCriteriaRegistry.get(this.state.rule.criterion.type).component;
|
|
42000
42280
|
}
|
|
42281
|
+
get errorMessages() {
|
|
42282
|
+
return this.state.errors.map((error) => DVTerms.Errors[error] || DVTerms.Errors.Unexpected);
|
|
42283
|
+
}
|
|
42001
42284
|
}
|
|
42002
42285
|
|
|
42003
42286
|
css /* scss */ `
|
|
@@ -42009,7 +42292,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
42009
42292
|
border-bottom: 1px solid ${FIGURE_BORDER_COLOR};
|
|
42010
42293
|
|
|
42011
42294
|
.o-dv-container {
|
|
42012
|
-
min-width: 0;
|
|
42295
|
+
min-width: 0; /* otherwise flex won't shrink correctly */
|
|
42013
42296
|
}
|
|
42014
42297
|
|
|
42015
42298
|
.o-dv-preview-description {
|
|
@@ -45027,6 +45310,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45027
45310
|
PivotLayoutConfigurator,
|
|
45028
45311
|
Section,
|
|
45029
45312
|
};
|
|
45313
|
+
setup() {
|
|
45314
|
+
useHighlights(this);
|
|
45315
|
+
}
|
|
45030
45316
|
get sidePanelEditor() {
|
|
45031
45317
|
const pivot = this.env.model.getters.getPivotCoreDefinition(this.props.pivotId);
|
|
45032
45318
|
if (!pivot) {
|
|
@@ -45034,6 +45320,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45034
45320
|
}
|
|
45035
45321
|
return pivotSidePanelRegistry.get(pivot.type).editor;
|
|
45036
45322
|
}
|
|
45323
|
+
get highlights() {
|
|
45324
|
+
return getPivotHighlights(this.env.model.getters, this.props.pivotId);
|
|
45325
|
+
}
|
|
45037
45326
|
}
|
|
45038
45327
|
|
|
45039
45328
|
css /* scss */ `
|
|
@@ -45703,7 +45992,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45703
45992
|
|
|
45704
45993
|
css /* scss */ `
|
|
45705
45994
|
.o-table-style-popover {
|
|
45706
|
-
|
|
45995
|
+
/* 7 tables preview + padding by line */
|
|
45707
45996
|
width: calc((66px + 4px * 2) * 7 + 1.5rem * 2);
|
|
45708
45997
|
background: #fff;
|
|
45709
45998
|
font-size: 14px;
|
|
@@ -47255,7 +47544,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47255
47544
|
box-sizing: border-box !important;
|
|
47256
47545
|
accent-color: #808080;
|
|
47257
47546
|
margin: ${MARGIN}px;
|
|
47258
|
-
|
|
47547
|
+
/* required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
|
|
47259
47548
|
position: absolute;
|
|
47260
47549
|
}
|
|
47261
47550
|
`;
|
|
@@ -47602,7 +47891,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47602
47891
|
}
|
|
47603
47892
|
}
|
|
47604
47893
|
.o-figure-container {
|
|
47605
|
-
-webkit-user-select: none;
|
|
47894
|
+
-webkit-user-select: none; /* safari */
|
|
47606
47895
|
user-select: none;
|
|
47607
47896
|
}
|
|
47608
47897
|
`;
|
|
@@ -50675,25 +50964,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
50675
50964
|
if (!clipboardData) {
|
|
50676
50965
|
return;
|
|
50677
50966
|
}
|
|
50678
|
-
const
|
|
50679
|
-
|
|
50680
|
-
|
|
50681
|
-
|
|
50967
|
+
const osClipboard = {
|
|
50968
|
+
content: {
|
|
50969
|
+
[ClipboardMIMEType.PlainText]: clipboardData?.getData(ClipboardMIMEType.PlainText),
|
|
50970
|
+
[ClipboardMIMEType.Html]: clipboardData?.getData(ClipboardMIMEType.Html),
|
|
50971
|
+
},
|
|
50972
|
+
};
|
|
50682
50973
|
const target = this.env.model.getters.getSelectedZones();
|
|
50683
50974
|
const isCutOperation = this.env.model.getters.isCutOperation();
|
|
50684
|
-
const
|
|
50685
|
-
|
|
50975
|
+
const clipboardContent = parseOSClipboardContent(osClipboard.content);
|
|
50976
|
+
const clipboardId = clipboardContent.data?.clipboardId;
|
|
50686
50977
|
if (this.env.model.getters.getClipboardId() === clipboardId) {
|
|
50687
50978
|
interactivePaste(this.env, target);
|
|
50688
50979
|
}
|
|
50689
50980
|
else {
|
|
50690
|
-
const clipboardContent = {
|
|
50691
|
-
[ClipboardMIMEType.PlainText]: clipboardDataTextContent,
|
|
50692
|
-
[ClipboardMIMEType.Html]: clipboardDataHtmlContent,
|
|
50693
|
-
};
|
|
50694
|
-
if (osClipboardSpreadsheetContent !== "{}") {
|
|
50695
|
-
clipboardContent[ClipboardMIMEType.OSpreadsheet] = osClipboardSpreadsheetContent;
|
|
50696
|
-
}
|
|
50697
50981
|
interactivePasteFromOS(this.env, target, clipboardContent);
|
|
50698
50982
|
}
|
|
50699
50983
|
if (isCutOperation) {
|
|
@@ -52513,8 +52797,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52513
52797
|
if (replaceIndex > -1) {
|
|
52514
52798
|
currentRanges = rules[replaceIndex].ranges.map(toUnboundedZone);
|
|
52515
52799
|
}
|
|
52516
|
-
|
|
52517
|
-
|
|
52800
|
+
// Remove the zones first in case the same position is in toAdd and toRemove
|
|
52801
|
+
const withRemovedZones = recomputeZones(currentRanges, toRemove);
|
|
52802
|
+
return recomputeZones([...toAdd, ...withRemovedZones], []).map((zone) => this.getters.getRangeDataFromZone(sheetId, zone));
|
|
52518
52803
|
}
|
|
52519
52804
|
// ---------------------------------------------------------------------------
|
|
52520
52805
|
// Private
|
|
@@ -52825,7 +53110,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52825
53110
|
allowDispatch(cmd) {
|
|
52826
53111
|
switch (cmd.type) {
|
|
52827
53112
|
case "ADD_DATA_VALIDATION_RULE":
|
|
52828
|
-
return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
|
|
53113
|
+
return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkValidRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
|
|
52829
53114
|
case "REMOVE_DATA_VALIDATION_RULE":
|
|
52830
53115
|
if (!this.rules[cmd.sheetId].find((rule) => rule.id === cmd.id)) {
|
|
52831
53116
|
return "UnknownDataValidationRule" /* CommandResult.UnknownDataValidationRule */;
|
|
@@ -52984,6 +53269,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52984
53269
|
}
|
|
52985
53270
|
}
|
|
52986
53271
|
}
|
|
53272
|
+
exportForExcel(data) {
|
|
53273
|
+
if (!data.sheets) {
|
|
53274
|
+
return;
|
|
53275
|
+
}
|
|
53276
|
+
for (const sheet of data.sheets) {
|
|
53277
|
+
sheet.dataValidationRules = [];
|
|
53278
|
+
for (const rule of this.rules[sheet.id]) {
|
|
53279
|
+
sheet.dataValidationRules.push({
|
|
53280
|
+
...rule,
|
|
53281
|
+
ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useFixedReference: true })),
|
|
53282
|
+
});
|
|
53283
|
+
}
|
|
53284
|
+
}
|
|
53285
|
+
}
|
|
52987
53286
|
checkCriterionTypeIsValid(cmd) {
|
|
52988
53287
|
return dataValidationEvaluatorRegistry.contains(cmd.rule.criterion.type)
|
|
52989
53288
|
? "Success" /* CommandResult.Success */
|
|
@@ -53002,21 +53301,28 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53002
53301
|
checkCriterionValuesAreValid(cmd) {
|
|
53003
53302
|
const criterion = cmd.rule.criterion;
|
|
53004
53303
|
const evaluator = dataValidationEvaluatorRegistry.get(criterion.type);
|
|
53005
|
-
|
|
53006
|
-
if (value.startsWith("=")) {
|
|
53007
|
-
return evaluator.allowedValues === "onlyLiterals";
|
|
53008
|
-
}
|
|
53009
|
-
else if (evaluator.allowedValues === "onlyFormulas") {
|
|
53304
|
+
const isInvalid = (value) => {
|
|
53305
|
+
if (evaluator.allowedValues === "onlyFormulas" && !value.startsWith("=")) {
|
|
53010
53306
|
return true;
|
|
53011
53307
|
}
|
|
53012
|
-
|
|
53013
|
-
return
|
|
53308
|
+
if (value.startsWith("=")) {
|
|
53309
|
+
return evaluator.allowedValues === "onlyLiterals" || compile(value).isBadExpression;
|
|
53014
53310
|
}
|
|
53015
|
-
|
|
53311
|
+
return !evaluator.isCriterionValueValid(value);
|
|
53312
|
+
};
|
|
53313
|
+
if (criterion.values.some(isInvalid)) {
|
|
53016
53314
|
return "InvalidDataValidationCriterionValue" /* CommandResult.InvalidDataValidationCriterionValue */;
|
|
53017
53315
|
}
|
|
53018
53316
|
return "Success" /* CommandResult.Success */;
|
|
53019
53317
|
}
|
|
53318
|
+
checkValidRange(cmd) {
|
|
53319
|
+
const ranges = cmd.ranges.map((range) => this.getters.getRangeFromRangeData(range));
|
|
53320
|
+
const stringRanges = ranges.map((range) => this.getters.getRangeString(range, cmd.sheetId));
|
|
53321
|
+
if (stringRanges.some((xc) => !this.getters.isRangeValid(xc))) {
|
|
53322
|
+
return "InvalidRange" /* CommandResult.InvalidRange */;
|
|
53323
|
+
}
|
|
53324
|
+
return "Success" /* CommandResult.Success */;
|
|
53325
|
+
}
|
|
53020
53326
|
}
|
|
53021
53327
|
|
|
53022
53328
|
class FigurePlugin extends CorePlugin {
|
|
@@ -54751,6 +55057,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
54751
55057
|
formats: {},
|
|
54752
55058
|
borders: {},
|
|
54753
55059
|
conditionalFormats: [],
|
|
55060
|
+
dataValidationRules: [],
|
|
54754
55061
|
figures: [],
|
|
54755
55062
|
tables: [],
|
|
54756
55063
|
areGridLinesVisible: sheet.areGridLinesVisible === undefined ? true : sheet.areGridLinesVisible,
|
|
@@ -58120,6 +58427,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
58120
58427
|
}
|
|
58121
58428
|
for (let i = 0; i < positions.length; ++i) {
|
|
58122
58429
|
const position = positions[i];
|
|
58430
|
+
if (this.nextPositionsToUpdate.has(position)) {
|
|
58431
|
+
continue;
|
|
58432
|
+
}
|
|
58123
58433
|
const evaluatedCell = this.computeCell(position);
|
|
58124
58434
|
if (evaluatedCell !== EMPTY_CELL) {
|
|
58125
58435
|
this.evaluatedCells.set(position, evaluatedCell);
|
|
@@ -58127,6 +58437,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
58127
58437
|
}
|
|
58128
58438
|
onIterationEndEvaluationRegistry.getAll().forEach((callback) => callback(this.getters));
|
|
58129
58439
|
}
|
|
58440
|
+
if (currentIteration >= MAX_ITERATION) {
|
|
58441
|
+
console.warn("Maximum iteration reached while evaluating cells");
|
|
58442
|
+
}
|
|
58130
58443
|
}
|
|
58131
58444
|
computeCell(position) {
|
|
58132
58445
|
const evaluation = this.evaluatedCells.get(position);
|
|
@@ -58161,7 +58474,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
58161
58474
|
}
|
|
58162
58475
|
finally {
|
|
58163
58476
|
this.cellsBeingComputed.delete(cellId);
|
|
58164
|
-
this.nextPositionsToUpdate.delete(position);
|
|
58165
58477
|
}
|
|
58166
58478
|
}
|
|
58167
58479
|
computeAndSave(position) {
|
|
@@ -58197,6 +58509,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
58197
58509
|
invalidatePositionsDependingOnSpread(sheetId, resultZone) {
|
|
58198
58510
|
// the result matrix is split in 2 zones to exclude the array formula position
|
|
58199
58511
|
const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
|
|
58512
|
+
invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
|
|
58200
58513
|
this.nextPositionsToUpdate.addMany(invalidatedPositions);
|
|
58201
58514
|
}
|
|
58202
58515
|
assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
|
|
@@ -59320,25 +59633,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59320
59633
|
}
|
|
59321
59634
|
switch (cmd.type) {
|
|
59322
59635
|
case "ADD_DATA_VALIDATION_RULE":
|
|
59323
|
-
const ranges = cmd.ranges.map((range) => this.getters.getRangeFromRangeData(range));
|
|
59324
|
-
if (cmd.rule.criterion.type === "isBoolean") {
|
|
59325
|
-
this.setContentToBooleanCells({ ...cmd.rule, ranges });
|
|
59326
|
-
}
|
|
59327
|
-
delete this.validationResults[cmd.sheetId];
|
|
59328
|
-
break;
|
|
59329
59636
|
case "REMOVE_DATA_VALIDATION_RULE":
|
|
59330
59637
|
delete this.validationResults[cmd.sheetId];
|
|
59331
59638
|
break;
|
|
59332
59639
|
}
|
|
59333
59640
|
}
|
|
59334
|
-
setContentToBooleanCells(rule) {
|
|
59335
|
-
for (const position of getCellPositionsInRanges(rule.ranges)) {
|
|
59336
|
-
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
59337
|
-
if (evaluatedCell.type !== CellValueType.boolean) {
|
|
59338
|
-
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
59339
|
-
}
|
|
59340
|
-
}
|
|
59341
|
-
}
|
|
59342
59641
|
isDataValidationInvalid(cellPosition) {
|
|
59343
59642
|
return !this.getValidationResultForCell(cellPosition).isValid;
|
|
59344
59643
|
}
|
|
@@ -59354,9 +59653,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59354
59653
|
getDataValidationInvalidCriterionValueMessage(criterionType, value) {
|
|
59355
59654
|
const evaluator = dataValidationEvaluatorRegistry.get(criterionType);
|
|
59356
59655
|
if (value.startsWith("=")) {
|
|
59357
|
-
|
|
59358
|
-
|
|
59359
|
-
|
|
59656
|
+
if (evaluator.allowedValues === "onlyLiterals") {
|
|
59657
|
+
return _t("The value must not be a formula");
|
|
59658
|
+
}
|
|
59659
|
+
return this.isValidFormula(value) ? undefined : DVTerms.CriterionError.validFormula;
|
|
59360
59660
|
}
|
|
59361
59661
|
else if (evaluator.allowedValues === "onlyFormulas") {
|
|
59362
59662
|
return _t("The value must be a formula");
|
|
@@ -59382,6 +59682,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59382
59682
|
const error = this.getRuleErrorForCellValue(cellValue, cellPosition, rule);
|
|
59383
59683
|
return error ? { error, rule, isValid: false } : VALID_RESULT;
|
|
59384
59684
|
}
|
|
59685
|
+
isValidFormula(value) {
|
|
59686
|
+
return !compile(value).isBadExpression;
|
|
59687
|
+
}
|
|
59385
59688
|
getValidationResultForCell(cellPosition) {
|
|
59386
59689
|
const { col, row, sheetId } = cellPosition;
|
|
59387
59690
|
if (!this.validationResults[sheetId]) {
|
|
@@ -61817,7 +62120,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
61817
62120
|
* Notify the server that the user client left the collaborative session
|
|
61818
62121
|
*/
|
|
61819
62122
|
async leave(data) {
|
|
61820
|
-
if (Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
|
|
62123
|
+
if (data && Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
|
|
61821
62124
|
await this.snapshot(data());
|
|
61822
62125
|
}
|
|
61823
62126
|
delete this.clients[this.clientId];
|
|
@@ -63369,6 +63672,42 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
63369
63672
|
}
|
|
63370
63673
|
}
|
|
63371
63674
|
|
|
63675
|
+
class DataValidationInsertionPlugin extends UIPlugin {
|
|
63676
|
+
handle(cmd) {
|
|
63677
|
+
switch (cmd.type) {
|
|
63678
|
+
case "ADD_DATA_VALIDATION_RULE":
|
|
63679
|
+
if (cmd.rule.criterion.type === "isBoolean") {
|
|
63680
|
+
const ranges = cmd.ranges.map((range) => this.getters.getRangeFromRangeData(range));
|
|
63681
|
+
for (const position of getCellPositionsInRanges(ranges)) {
|
|
63682
|
+
const cell = this.getters.getCell(position);
|
|
63683
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
63684
|
+
if (!cell?.content) {
|
|
63685
|
+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
63686
|
+
// In this case, a cell has been updated in the core plugin but
|
|
63687
|
+
// not yet evaluated. This can occur after a paste operation.
|
|
63688
|
+
}
|
|
63689
|
+
else if (cell?.content && evaluatedCell.type === CellValueType.empty) {
|
|
63690
|
+
let value;
|
|
63691
|
+
if (cell.content.startsWith("=")) {
|
|
63692
|
+
const result = this.getters.evaluateFormula(position.sheetId, cell.content);
|
|
63693
|
+
value = (isMatrix(result) ? result[0][0] : result)?.toString();
|
|
63694
|
+
}
|
|
63695
|
+
else {
|
|
63696
|
+
value = cell.content;
|
|
63697
|
+
}
|
|
63698
|
+
if (!value || !isBoolean(value)) {
|
|
63699
|
+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
63700
|
+
}
|
|
63701
|
+
}
|
|
63702
|
+
else if (evaluatedCell.type !== CellValueType.boolean) {
|
|
63703
|
+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
63704
|
+
}
|
|
63705
|
+
}
|
|
63706
|
+
}
|
|
63707
|
+
}
|
|
63708
|
+
}
|
|
63709
|
+
}
|
|
63710
|
+
|
|
63372
63711
|
const genericRepeatsTransforms = [
|
|
63373
63712
|
repeatSheetDependantCommand,
|
|
63374
63713
|
repeatTargetDependantCommand,
|
|
@@ -64022,7 +64361,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64022
64361
|
const zones = this.getters.getSelectedZones();
|
|
64023
64362
|
return this.isCutAllowedOn(zones);
|
|
64024
64363
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
64025
|
-
const copiedData = this.
|
|
64364
|
+
const copiedData = this.convertTextToClipboardData(cmd.clipboardContent.text ?? "");
|
|
64026
64365
|
const pasteOption = cmd.pasteOption;
|
|
64027
64366
|
return this.isPasteAllowed(cmd.target, copiedData, { pasteOption, isCutOperation: false });
|
|
64028
64367
|
}
|
|
@@ -64075,12 +64414,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64075
64414
|
break;
|
|
64076
64415
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
64077
64416
|
this._isCutOperation = false;
|
|
64078
|
-
|
|
64079
|
-
|
|
64080
|
-
|
|
64081
|
-
else {
|
|
64082
|
-
this.copiedData = this.convertOSClipboardData(cmd.clipboardContent[ClipboardMIMEType.PlainText] ?? "");
|
|
64083
|
-
}
|
|
64417
|
+
this.copiedData =
|
|
64418
|
+
cmd.clipboardContent.data ||
|
|
64419
|
+
this.convertTextToClipboardData(cmd.clipboardContent.text ?? "");
|
|
64084
64420
|
const pasteOption = cmd.pasteOption;
|
|
64085
64421
|
this.paste(cmd.target, this.copiedData, {
|
|
64086
64422
|
pasteOption,
|
|
@@ -64211,11 +64547,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64211
64547
|
}
|
|
64212
64548
|
}
|
|
64213
64549
|
}
|
|
64214
|
-
|
|
64550
|
+
convertTextToClipboardData(clipboardData) {
|
|
64215
64551
|
const handlers = this.selectClipboardHandlers({ figureId: true }).concat(this.selectClipboardHandlers({}));
|
|
64216
64552
|
let copiedData = {};
|
|
64217
64553
|
for (const { handlerName, handler } of handlers) {
|
|
64218
|
-
const data = handler.
|
|
64554
|
+
const data = handler.convertTextToClipboardData(clipboardData);
|
|
64219
64555
|
copiedData[handlerName] = data;
|
|
64220
64556
|
const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
|
|
64221
64557
|
for (const key of minimalKeys) {
|
|
@@ -64384,21 +64720,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64384
64720
|
return {
|
|
64385
64721
|
[ClipboardMIMEType.PlainText]: this.getPlainTextContent(),
|
|
64386
64722
|
[ClipboardMIMEType.Html]: this.getHTMLContent(),
|
|
64387
|
-
[ClipboardMIMEType.OSpreadsheet]: this.getSerializedGridData(),
|
|
64388
64723
|
};
|
|
64389
64724
|
}
|
|
64390
|
-
|
|
64725
|
+
getSheetData() {
|
|
64391
64726
|
const data = {
|
|
64392
64727
|
version: CURRENT_VERSION,
|
|
64393
64728
|
clipboardId: this.clipboardId,
|
|
64394
64729
|
};
|
|
64395
64730
|
if (this.copiedData && "figureId" in this.copiedData) {
|
|
64396
|
-
return
|
|
64731
|
+
return data;
|
|
64397
64732
|
}
|
|
64398
|
-
return
|
|
64733
|
+
return {
|
|
64399
64734
|
...data,
|
|
64400
64735
|
...this.copiedData,
|
|
64401
|
-
}
|
|
64736
|
+
};
|
|
64402
64737
|
}
|
|
64403
64738
|
getPlainTextContent() {
|
|
64404
64739
|
if (!this.copiedData?.cells) {
|
|
@@ -64415,31 +64750,36 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64415
64750
|
.join("\n") || "\t");
|
|
64416
64751
|
}
|
|
64417
64752
|
getHTMLContent() {
|
|
64418
|
-
|
|
64419
|
-
|
|
64753
|
+
let innerHTML = "";
|
|
64754
|
+
const cells = this.copiedData?.cells;
|
|
64755
|
+
if (!cells) {
|
|
64756
|
+
innerHTML = "\t";
|
|
64420
64757
|
}
|
|
64421
|
-
|
|
64422
|
-
|
|
64423
|
-
return `<div data-clipboard-id="${this.clipboardId}">${this.getters.getCellText(cells[0][0].position)}</div>`;
|
|
64758
|
+
else if (cells.length === 1 && cells[0].length === 1) {
|
|
64759
|
+
innerHTML = `${this.getters.getCellText(cells[0][0].position)}`;
|
|
64424
64760
|
}
|
|
64425
|
-
if (!cells[0][0]) {
|
|
64761
|
+
else if (!cells[0][0]) {
|
|
64426
64762
|
return "";
|
|
64427
64763
|
}
|
|
64428
|
-
|
|
64429
|
-
|
|
64430
|
-
|
|
64431
|
-
|
|
64432
|
-
|
|
64433
|
-
|
|
64764
|
+
else {
|
|
64765
|
+
let htmlTable = `<table border="1" style="border-collapse:collapse">`;
|
|
64766
|
+
for (const row of cells) {
|
|
64767
|
+
htmlTable += "<tr>";
|
|
64768
|
+
for (const cell of row) {
|
|
64769
|
+
if (!cell) {
|
|
64770
|
+
continue;
|
|
64771
|
+
}
|
|
64772
|
+
const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
|
|
64773
|
+
const cellText = this.getters.getCellText(cell.position);
|
|
64774
|
+
htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
|
|
64434
64775
|
}
|
|
64435
|
-
|
|
64436
|
-
const cellText = this.getters.getCellText(cell.position);
|
|
64437
|
-
htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
|
|
64776
|
+
htmlTable += "</tr>";
|
|
64438
64777
|
}
|
|
64439
|
-
htmlTable += "</
|
|
64778
|
+
htmlTable += "</table>";
|
|
64779
|
+
innerHTML = htmlTable;
|
|
64440
64780
|
}
|
|
64441
|
-
|
|
64442
|
-
return
|
|
64781
|
+
const serializedData = JSON.stringify(this.getSheetData());
|
|
64782
|
+
return `<div data-osheet-clipboard='${xmlEscape(serializedData)}'>${innerHTML}</div>`;
|
|
64443
64783
|
}
|
|
64444
64784
|
isCutOperation() {
|
|
64445
64785
|
return this._isCutOperation ?? false;
|
|
@@ -66419,7 +66759,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
66419
66759
|
.add("history", HistoryPlugin)
|
|
66420
66760
|
.add("data_cleanup", DataCleanupPlugin)
|
|
66421
66761
|
.add("table_autofill", TableAutofillPlugin)
|
|
66422
|
-
.add("table_ui_resize", TableResizeUI)
|
|
66762
|
+
.add("table_ui_resize", TableResizeUI)
|
|
66763
|
+
.add("datavalidation_insert", DataValidationInsertionPlugin);
|
|
66423
66764
|
// Plugins which have a state, but which should not be shared in collaborative
|
|
66424
66765
|
const statefulUIPluginRegistry = new Registry()
|
|
66425
66766
|
.add("selection", GridSelectionPlugin)
|
|
@@ -67645,7 +67986,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
67645
67986
|
|
|
67646
67987
|
.o-header-group-main-pane {
|
|
67647
67988
|
&.o-group-rows {
|
|
67648
|
-
margin-top: -2px;
|
|
67989
|
+
margin-top: -2px; /* Counteract o-header-group-frozen-pane-border offset */
|
|
67649
67990
|
}
|
|
67650
67991
|
&.o-group-columns {
|
|
67651
67992
|
margin-left: -2px;
|
|
@@ -68678,10 +69019,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68678
69019
|
[ClipboardMIMEType.PlainText]: this.getBlob(content, ClipboardMIMEType.PlainText),
|
|
68679
69020
|
[ClipboardMIMEType.Html]: this.getBlob(content, ClipboardMIMEType.Html),
|
|
68680
69021
|
};
|
|
68681
|
-
const spreadsheetData = content[ClipboardMIMEType.OSpreadsheet];
|
|
68682
|
-
if (spreadsheetData) {
|
|
68683
|
-
clipboardItemData[ClipboardMIMEType.OSpreadsheet] = this.getBlob(content, ClipboardMIMEType.OSpreadsheet);
|
|
68684
|
-
}
|
|
68685
69022
|
return [new ClipboardItem(clipboardItemData)];
|
|
68686
69023
|
}
|
|
68687
69024
|
getBlob(clipboardContent, type) {
|
|
@@ -68723,7 +69060,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68723
69060
|
*:before,
|
|
68724
69061
|
*:after {
|
|
68725
69062
|
box-sizing: content-box;
|
|
68726
|
-
|
|
69063
|
+
/* rtl not supported ATM */
|
|
68727
69064
|
direction: ltr;
|
|
68728
69065
|
}
|
|
68729
69066
|
.o-separator {
|
|
@@ -71628,6 +71965,124 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
71628
71965
|
}
|
|
71629
71966
|
}
|
|
71630
71967
|
|
|
71968
|
+
function addDataValidationRules(dataValidationRules) {
|
|
71969
|
+
const dvRulesCount = dataValidationRules.length;
|
|
71970
|
+
if (dvRulesCount === 0) {
|
|
71971
|
+
return [];
|
|
71972
|
+
}
|
|
71973
|
+
const dvNodes = [new XMLString(`<dataValidations count="${dvRulesCount}">`)];
|
|
71974
|
+
for (const dvRule of dataValidationRules) {
|
|
71975
|
+
switch (dvRule.criterion.type) {
|
|
71976
|
+
case "dateIs":
|
|
71977
|
+
case "dateIsBefore":
|
|
71978
|
+
case "dateIsOnOrBefore":
|
|
71979
|
+
case "dateIsAfter":
|
|
71980
|
+
case "dateIsOnOrAfter":
|
|
71981
|
+
case "dateIsBetween":
|
|
71982
|
+
case "dateIsNotBetween":
|
|
71983
|
+
dvNodes.push(addDateRule(dvRule));
|
|
71984
|
+
break;
|
|
71985
|
+
case "isEqual":
|
|
71986
|
+
case "isNotEqual":
|
|
71987
|
+
case "isGreaterThan":
|
|
71988
|
+
case "isGreaterOrEqualTo":
|
|
71989
|
+
case "isLessThan":
|
|
71990
|
+
case "isLessOrEqualTo":
|
|
71991
|
+
case "isBetween":
|
|
71992
|
+
case "isNotBetween":
|
|
71993
|
+
dvNodes.push(addDecimalRule(dvRule));
|
|
71994
|
+
break;
|
|
71995
|
+
case "isValueInRange":
|
|
71996
|
+
case "isValueInList":
|
|
71997
|
+
dvNodes.push(addListRule(dvRule));
|
|
71998
|
+
break;
|
|
71999
|
+
case "customFormula":
|
|
72000
|
+
dvNodes.push(addCustomFormulaRule(dvRule));
|
|
72001
|
+
break;
|
|
72002
|
+
default:
|
|
72003
|
+
console.warn(`Data validation ${dvRule.criterion.type} is not supported in xlsx.`);
|
|
72004
|
+
break;
|
|
72005
|
+
}
|
|
72006
|
+
}
|
|
72007
|
+
dvNodes.push(new XMLString("</dataValidations>"));
|
|
72008
|
+
return dvNodes;
|
|
72009
|
+
}
|
|
72010
|
+
function addDateRule(dvRule) {
|
|
72011
|
+
const rule = dvRule.criterion;
|
|
72012
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
72013
|
+
const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
|
|
72014
|
+
const operator = convertDateCriterionTypeToExcelOperator(dvRule.criterion.type);
|
|
72015
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72016
|
+
attributes.push(["type", "date"], ["operator", operator]);
|
|
72017
|
+
if (formula2) {
|
|
72018
|
+
return escapeXml /*xml*/ `
|
|
72019
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72020
|
+
<formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
|
|
72021
|
+
<formula2>${toNumber(formula2, DEFAULT_LOCALE)}</formula2>
|
|
72022
|
+
</dataValidation>
|
|
72023
|
+
`;
|
|
72024
|
+
}
|
|
72025
|
+
return escapeXml /*xml*/ `
|
|
72026
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72027
|
+
<formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
|
|
72028
|
+
</dataValidation>
|
|
72029
|
+
`;
|
|
72030
|
+
}
|
|
72031
|
+
function addDecimalRule(dvRule) {
|
|
72032
|
+
const rule = dvRule.criterion;
|
|
72033
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
72034
|
+
const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
|
|
72035
|
+
const operator = convertDecimalCriterionTypeToExcelOperator(dvRule.criterion.type);
|
|
72036
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72037
|
+
attributes.push(["type", "decimal"], ["operator", operator]);
|
|
72038
|
+
if (formula2) {
|
|
72039
|
+
return escapeXml /*xml*/ `
|
|
72040
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72041
|
+
<formula1>${formula1}</formula1>
|
|
72042
|
+
<formula2>${formula2}</formula2>
|
|
72043
|
+
</dataValidation>
|
|
72044
|
+
`;
|
|
72045
|
+
}
|
|
72046
|
+
return escapeXml /*xml*/ `
|
|
72047
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72048
|
+
<formula1>${formula1}</formula1>
|
|
72049
|
+
</dataValidation>
|
|
72050
|
+
`;
|
|
72051
|
+
}
|
|
72052
|
+
function addListRule(dvRule) {
|
|
72053
|
+
const rule = dvRule.criterion;
|
|
72054
|
+
const formula1 = dvRule.criterion.type === "isValueInRange"
|
|
72055
|
+
? adaptFormulaToExcel(rule.values[0])
|
|
72056
|
+
: `"${rule.values.join(",")}"`;
|
|
72057
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72058
|
+
attributes.push(["type", "list"]);
|
|
72059
|
+
return escapeXml /*xml*/ `
|
|
72060
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72061
|
+
<formula1>${formula1}</formula1>
|
|
72062
|
+
</dataValidation>
|
|
72063
|
+
`;
|
|
72064
|
+
}
|
|
72065
|
+
function addCustomFormulaRule(dvRule) {
|
|
72066
|
+
const rule = dvRule.criterion;
|
|
72067
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
72068
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72069
|
+
attributes.push(["type", "custom"]);
|
|
72070
|
+
return escapeXml /*xml*/ `
|
|
72071
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72072
|
+
<formula1>${formula1}</formula1>
|
|
72073
|
+
</dataValidation>
|
|
72074
|
+
`;
|
|
72075
|
+
}
|
|
72076
|
+
function commonDataValidationAttributes(dvRule) {
|
|
72077
|
+
return [
|
|
72078
|
+
["allowBlank", "1"],
|
|
72079
|
+
["showInputMessage", "1"],
|
|
72080
|
+
["showErrorMessage", "1"],
|
|
72081
|
+
["errorStyle", !dvRule.isBlocking ? "warning" : ""],
|
|
72082
|
+
["sqref", dvRule.ranges.join(" ")],
|
|
72083
|
+
];
|
|
72084
|
+
}
|
|
72085
|
+
|
|
71631
72086
|
function createDrawing(drawingRelIds, sheet, figures) {
|
|
71632
72087
|
const namespaces = [
|
|
71633
72088
|
["xmlns:xdr", NAMESPACE.drawing],
|
|
@@ -72412,6 +72867,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
72412
72867
|
${addRows(construct, data, sheet)}
|
|
72413
72868
|
${addMerges(sheet.merges)}
|
|
72414
72869
|
${joinXmlNodes(addConditionalFormatting(construct.dxfs, sheet.conditionalFormats))}
|
|
72870
|
+
${joinXmlNodes(addDataValidationRules(sheet.dataValidationRules))}
|
|
72415
72871
|
${addHyperlinks(construct, data, sheetIndex)}
|
|
72416
72872
|
${drawingNode}
|
|
72417
72873
|
${tablesNode}
|
|
@@ -72735,7 +73191,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
72735
73191
|
this.session.join(this.config.client);
|
|
72736
73192
|
}
|
|
72737
73193
|
async leaveSession() {
|
|
72738
|
-
|
|
73194
|
+
const snapshot = this.getters.isReadonly() ? undefined : lazy(() => this.exportData());
|
|
73195
|
+
await this.session.leave(snapshot);
|
|
72739
73196
|
}
|
|
72740
73197
|
setupUiPlugin(Plugin) {
|
|
72741
73198
|
const plugin = new Plugin(this.uiPluginConfig);
|
|
@@ -73337,9 +73794,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
73337
73794
|
exports.tokenize = tokenize;
|
|
73338
73795
|
|
|
73339
73796
|
|
|
73340
|
-
__info__.version = "18.1.0-alpha.
|
|
73341
|
-
__info__.date = "2024-11-
|
|
73342
|
-
__info__.hash = "
|
|
73797
|
+
__info__.version = "18.1.0-alpha.5";
|
|
73798
|
+
__info__.date = "2024-11-22T14:22:50.899Z";
|
|
73799
|
+
__info__.hash = "e13bd67";
|
|
73343
73800
|
|
|
73344
73801
|
|
|
73345
73802
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|