@odoo/o-spreadsheet 18.1.0-alpha.4 → 18.1.0-alpha.6
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 +1006 -565
- package/dist/o-spreadsheet.d.ts +4790 -4660
- package/dist/o-spreadsheet.esm.js +1006 -565
- package/dist/o-spreadsheet.iife.js +1006 -565
- package/dist/o-spreadsheet.iife.min.js +563 -536
- package/dist/o_spreadsheet.xml +34 -27
- 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.6
|
|
6
|
+
* @date 2024-11-28T09:06:59.527Z
|
|
7
|
+
* @hash 875c901
|
|
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) {
|
|
@@ -3499,7 +3497,9 @@
|
|
|
3499
3497
|
CommandResult["MaxInvalidFormula"] = "MaxInvalidFormula";
|
|
3500
3498
|
CommandResult["ValueUpperInvalidFormula"] = "ValueUpperInvalidFormula";
|
|
3501
3499
|
CommandResult["ValueLowerInvalidFormula"] = "ValueLowerInvalidFormula";
|
|
3500
|
+
CommandResult["InvalidSortAnchor"] = "InvalidSortAnchor";
|
|
3502
3501
|
CommandResult["InvalidSortZone"] = "InvalidSortZone";
|
|
3502
|
+
CommandResult["SortZoneWithArrayFormulas"] = "SortZoneWithArrayFormulas";
|
|
3503
3503
|
CommandResult["WaitingSessionConfirmation"] = "WaitingSessionConfirmation";
|
|
3504
3504
|
CommandResult["MergeOverlap"] = "MergeOverlap";
|
|
3505
3505
|
CommandResult["TooManyHiddenElements"] = "TooManyHiddenElements";
|
|
@@ -3555,7 +3555,6 @@
|
|
|
3555
3555
|
CommandResult["ValueCellIsInvalidFormula"] = "ValueCellIsInvalidFormula";
|
|
3556
3556
|
CommandResult["InvalidDefinition"] = "InvalidDefinition";
|
|
3557
3557
|
CommandResult["InvalidColor"] = "InvalidColor";
|
|
3558
|
-
CommandResult["DataBarRangeValuesMismatch"] = "DataBarRangeValuesMismatch";
|
|
3559
3558
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
3560
3559
|
|
|
3561
3560
|
const DEFAULT_LOCALES = [
|
|
@@ -4345,7 +4344,9 @@
|
|
|
4345
4344
|
return reverseSearch ? numberOfValues - i - 1 : i;
|
|
4346
4345
|
}
|
|
4347
4346
|
}
|
|
4348
|
-
return reverseSearch
|
|
4347
|
+
return reverseSearch && closestMatchIndex !== -1
|
|
4348
|
+
? numberOfValues - closestMatchIndex - 1
|
|
4349
|
+
: closestMatchIndex;
|
|
4349
4350
|
}
|
|
4350
4351
|
/**
|
|
4351
4352
|
* Normalize a value.
|
|
@@ -4399,47 +4400,83 @@
|
|
|
4399
4400
|
return true;
|
|
4400
4401
|
}
|
|
4401
4402
|
|
|
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
|
-
}
|
|
4403
|
+
/**
|
|
4404
|
+
* Add the `https` prefix to the url if it's missing
|
|
4405
|
+
*/
|
|
4406
|
+
function withHttps(url) {
|
|
4407
|
+
return !/^https?:\/\//i.test(url) ? `https://${url}` : url;
|
|
4418
4408
|
}
|
|
4419
|
-
|
|
4420
|
-
function
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4409
|
+
const urlRegistry = new Registry();
|
|
4410
|
+
function createWebLink(url, label) {
|
|
4411
|
+
url = withHttps(url);
|
|
4412
|
+
return {
|
|
4413
|
+
url,
|
|
4414
|
+
label: label || url,
|
|
4415
|
+
isExternal: true,
|
|
4416
|
+
isUrlEditable: true,
|
|
4417
|
+
};
|
|
4425
4418
|
}
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4419
|
+
urlRegistry.add("sheet_URL", {
|
|
4420
|
+
match: (url) => isSheetUrl(url),
|
|
4421
|
+
createLink: (url, label) => {
|
|
4422
|
+
return {
|
|
4423
|
+
label,
|
|
4424
|
+
url,
|
|
4425
|
+
isExternal: false,
|
|
4426
|
+
isUrlEditable: false,
|
|
4427
|
+
};
|
|
4428
|
+
},
|
|
4429
|
+
urlRepresentation(url, getters) {
|
|
4430
|
+
const sheetId = parseSheetUrl(url);
|
|
4431
|
+
return getters.tryGetSheetName(sheetId) || _t("Invalid sheet");
|
|
4432
|
+
},
|
|
4433
|
+
open(url, env) {
|
|
4434
|
+
const sheetId = parseSheetUrl(url);
|
|
4435
|
+
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
4436
|
+
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
4437
|
+
sheetIdTo: sheetId,
|
|
4438
|
+
});
|
|
4439
|
+
if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
|
|
4440
|
+
env.notifyUser({
|
|
4441
|
+
type: "warning",
|
|
4442
|
+
sticky: false,
|
|
4443
|
+
text: _t("Cannot open the link because the linked sheet is hidden."),
|
|
4444
|
+
});
|
|
4445
|
+
}
|
|
4446
|
+
},
|
|
4447
|
+
sequence: 0,
|
|
4448
|
+
});
|
|
4449
|
+
const WebUrlSpec = {
|
|
4450
|
+
createLink: createWebLink,
|
|
4451
|
+
match: (url) => isWebLink(url),
|
|
4452
|
+
open: (url) => window.open(url, "_blank"),
|
|
4453
|
+
urlRepresentation: (url) => url,
|
|
4454
|
+
sequence: 0,
|
|
4455
|
+
};
|
|
4456
|
+
function findMatchingSpec(url) {
|
|
4457
|
+
return (urlRegistry
|
|
4458
|
+
.getAll()
|
|
4459
|
+
.sort((a, b) => a.sequence - b.sequence)
|
|
4460
|
+
.find((urlType) => urlType.match(url)) || WebUrlSpec);
|
|
4429
4461
|
}
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4462
|
+
function urlRepresentation(link, getters) {
|
|
4463
|
+
return findMatchingSpec(link.url).urlRepresentation(link.url, getters);
|
|
4464
|
+
}
|
|
4465
|
+
function openLink(link, env) {
|
|
4466
|
+
findMatchingSpec(link.url).open(link.url, env);
|
|
4467
|
+
}
|
|
4468
|
+
function detectLink(value) {
|
|
4469
|
+
if (typeof value !== "string") {
|
|
4470
|
+
return undefined;
|
|
4471
|
+
}
|
|
4472
|
+
if (isMarkdownLink(value)) {
|
|
4473
|
+
const { label, url } = parseMarkdownLink(value);
|
|
4474
|
+
return findMatchingSpec(url).createLink(url, label);
|
|
4475
|
+
}
|
|
4476
|
+
else if (isWebLink(value)) {
|
|
4477
|
+
return createWebLink(value);
|
|
4478
|
+
}
|
|
4479
|
+
return undefined;
|
|
4443
4480
|
}
|
|
4444
4481
|
|
|
4445
4482
|
function tokenizeFormat(str) {
|
|
@@ -5501,6 +5538,193 @@
|
|
|
5501
5538
|
}
|
|
5502
5539
|
}
|
|
5503
5540
|
|
|
5541
|
+
function evaluateLiteral(literalCell, localeFormat) {
|
|
5542
|
+
const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
|
|
5543
|
+
const functionResult = { value, format: localeFormat.format };
|
|
5544
|
+
return createEvaluatedCell(functionResult, localeFormat.locale);
|
|
5545
|
+
}
|
|
5546
|
+
function parseLiteral(content, locale) {
|
|
5547
|
+
if (content.startsWith("=")) {
|
|
5548
|
+
throw new Error(`Cannot parse "${content}" because it's not a literal value. It's a formula`);
|
|
5549
|
+
}
|
|
5550
|
+
if (content === "") {
|
|
5551
|
+
return null;
|
|
5552
|
+
}
|
|
5553
|
+
if (isNumber(content, DEFAULT_LOCALE)) {
|
|
5554
|
+
return parseNumber(content, DEFAULT_LOCALE);
|
|
5555
|
+
}
|
|
5556
|
+
const internalDate = parseDateTime(content, locale);
|
|
5557
|
+
if (internalDate) {
|
|
5558
|
+
return internalDate.value;
|
|
5559
|
+
}
|
|
5560
|
+
if (isBoolean(content)) {
|
|
5561
|
+
return content.toUpperCase() === "TRUE";
|
|
5562
|
+
}
|
|
5563
|
+
return content;
|
|
5564
|
+
}
|
|
5565
|
+
function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
|
|
5566
|
+
const link = detectLink(functionResult.value);
|
|
5567
|
+
if (!link) {
|
|
5568
|
+
return _createEvaluatedCell(functionResult, locale, cell);
|
|
5569
|
+
}
|
|
5570
|
+
const value = parseLiteral(link.label, locale);
|
|
5571
|
+
const format = functionResult.format ||
|
|
5572
|
+
(typeof value === "number"
|
|
5573
|
+
? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
|
|
5574
|
+
: undefined);
|
|
5575
|
+
const linkPayload = {
|
|
5576
|
+
value,
|
|
5577
|
+
format,
|
|
5578
|
+
};
|
|
5579
|
+
return {
|
|
5580
|
+
..._createEvaluatedCell(linkPayload, locale, cell),
|
|
5581
|
+
link,
|
|
5582
|
+
};
|
|
5583
|
+
}
|
|
5584
|
+
function _createEvaluatedCell(functionResult, locale, cell) {
|
|
5585
|
+
let { value, format, message } = functionResult;
|
|
5586
|
+
format = cell?.format || format;
|
|
5587
|
+
const formattedValue = formatValue(value, { format, locale });
|
|
5588
|
+
if (isEvaluationError(value)) {
|
|
5589
|
+
return errorCell(value, message);
|
|
5590
|
+
}
|
|
5591
|
+
if (isTextFormat(format)) {
|
|
5592
|
+
// TO DO:
|
|
5593
|
+
// with the next line, the value of the cell is transformed depending on the format.
|
|
5594
|
+
// This shouldn't happen, by doing this, the formulas handling numbers are not able
|
|
5595
|
+
// to interpret the value as a number.
|
|
5596
|
+
return textCell(toString(value), format, formattedValue);
|
|
5597
|
+
}
|
|
5598
|
+
if (value === null) {
|
|
5599
|
+
return emptyCell(format);
|
|
5600
|
+
}
|
|
5601
|
+
if (typeof value === "number") {
|
|
5602
|
+
if (isDateTimeFormat(format || "")) {
|
|
5603
|
+
return dateTimeCell(value, format, formattedValue);
|
|
5604
|
+
}
|
|
5605
|
+
return numberCell(value, format, formattedValue);
|
|
5606
|
+
}
|
|
5607
|
+
if (typeof value === "boolean") {
|
|
5608
|
+
return booleanCell(value, format, formattedValue);
|
|
5609
|
+
}
|
|
5610
|
+
return textCell(value, format, formattedValue);
|
|
5611
|
+
}
|
|
5612
|
+
function textCell(value, format, formattedValue) {
|
|
5613
|
+
return {
|
|
5614
|
+
value,
|
|
5615
|
+
format,
|
|
5616
|
+
formattedValue,
|
|
5617
|
+
type: CellValueType.text,
|
|
5618
|
+
isAutoSummable: true,
|
|
5619
|
+
defaultAlign: "left",
|
|
5620
|
+
};
|
|
5621
|
+
}
|
|
5622
|
+
function numberCell(value, format, formattedValue) {
|
|
5623
|
+
return {
|
|
5624
|
+
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
5625
|
+
format,
|
|
5626
|
+
formattedValue,
|
|
5627
|
+
type: CellValueType.number,
|
|
5628
|
+
isAutoSummable: true,
|
|
5629
|
+
defaultAlign: "right",
|
|
5630
|
+
};
|
|
5631
|
+
}
|
|
5632
|
+
const emptyCell = memoize(function emptyCell(format) {
|
|
5633
|
+
return {
|
|
5634
|
+
value: null,
|
|
5635
|
+
format,
|
|
5636
|
+
formattedValue: "",
|
|
5637
|
+
type: CellValueType.empty,
|
|
5638
|
+
isAutoSummable: true,
|
|
5639
|
+
defaultAlign: "left",
|
|
5640
|
+
};
|
|
5641
|
+
});
|
|
5642
|
+
function dateTimeCell(value, format, formattedValue) {
|
|
5643
|
+
return {
|
|
5644
|
+
value,
|
|
5645
|
+
format,
|
|
5646
|
+
formattedValue,
|
|
5647
|
+
type: CellValueType.number,
|
|
5648
|
+
isAutoSummable: false,
|
|
5649
|
+
defaultAlign: "right",
|
|
5650
|
+
};
|
|
5651
|
+
}
|
|
5652
|
+
function booleanCell(value, format, formattedValue) {
|
|
5653
|
+
return {
|
|
5654
|
+
value,
|
|
5655
|
+
format,
|
|
5656
|
+
formattedValue,
|
|
5657
|
+
type: CellValueType.boolean,
|
|
5658
|
+
isAutoSummable: false,
|
|
5659
|
+
defaultAlign: "center",
|
|
5660
|
+
};
|
|
5661
|
+
}
|
|
5662
|
+
function errorCell(value, message) {
|
|
5663
|
+
return {
|
|
5664
|
+
value,
|
|
5665
|
+
formattedValue: value,
|
|
5666
|
+
message,
|
|
5667
|
+
type: CellValueType.error,
|
|
5668
|
+
isAutoSummable: false,
|
|
5669
|
+
defaultAlign: "center",
|
|
5670
|
+
};
|
|
5671
|
+
}
|
|
5672
|
+
|
|
5673
|
+
function toCriterionDateNumber(dateValue) {
|
|
5674
|
+
const today = DateTime.now();
|
|
5675
|
+
switch (dateValue) {
|
|
5676
|
+
case "today":
|
|
5677
|
+
return jsDateToNumber(today);
|
|
5678
|
+
case "yesterday":
|
|
5679
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
|
|
5680
|
+
case "tomorrow":
|
|
5681
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
|
|
5682
|
+
case "lastWeek":
|
|
5683
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
|
|
5684
|
+
case "lastMonth":
|
|
5685
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
|
|
5686
|
+
case "lastYear":
|
|
5687
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
|
|
5688
|
+
}
|
|
5689
|
+
}
|
|
5690
|
+
/** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
|
|
5691
|
+
function getDateNumberCriterionValues(criterion, locale) {
|
|
5692
|
+
if ("dateValue" in criterion && criterion.dateValue !== "exactDate") {
|
|
5693
|
+
return [toCriterionDateNumber(criterion.dateValue)];
|
|
5694
|
+
}
|
|
5695
|
+
return criterion.values.map((value) => valueToDateNumber(value, locale));
|
|
5696
|
+
}
|
|
5697
|
+
/** Convert the criterion values to numbers. Return undefined values if they cannot be converted to numbers. */
|
|
5698
|
+
function getCriterionValuesAsNumber(criterion, locale) {
|
|
5699
|
+
return criterion.values.map((value) => tryToNumber(value, locale));
|
|
5700
|
+
}
|
|
5701
|
+
function getDateCriterionFormattedValues(values, locale) {
|
|
5702
|
+
return values.map((valueStr) => {
|
|
5703
|
+
if (valueStr.startsWith("=")) {
|
|
5704
|
+
return valueStr;
|
|
5705
|
+
}
|
|
5706
|
+
const value = parseLiteral(valueStr, locale);
|
|
5707
|
+
if (typeof value === "number") {
|
|
5708
|
+
return formatValue(value, { format: locale.dateFormat, locale });
|
|
5709
|
+
}
|
|
5710
|
+
return "";
|
|
5711
|
+
});
|
|
5712
|
+
}
|
|
5713
|
+
|
|
5714
|
+
const MAX_DELAY = 140;
|
|
5715
|
+
const MIN_DELAY = 20;
|
|
5716
|
+
const ACCELERATION = 0.035;
|
|
5717
|
+
/**
|
|
5718
|
+
* Decreasing exponential function used to determine the "speed" of edge-scrolling
|
|
5719
|
+
* as the timeout delay.
|
|
5720
|
+
*
|
|
5721
|
+
* Returns a timeout delay in milliseconds.
|
|
5722
|
+
*/
|
|
5723
|
+
function scrollDelay(value) {
|
|
5724
|
+
// decreasing exponential from MAX_DELAY to MIN_DELAY
|
|
5725
|
+
return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
|
|
5726
|
+
}
|
|
5727
|
+
|
|
5504
5728
|
class RangeImpl {
|
|
5505
5729
|
getSheetSize;
|
|
5506
5730
|
_zone;
|
|
@@ -6148,6 +6372,22 @@
|
|
|
6148
6372
|
const width = content[0].length, height = content.length;
|
|
6149
6373
|
return target.map((t) => splitZoneForPaste(t, width, height)).flat();
|
|
6150
6374
|
}
|
|
6375
|
+
function parseOSClipboardContent(content) {
|
|
6376
|
+
if (!content[ClipboardMIMEType.Html]) {
|
|
6377
|
+
return {
|
|
6378
|
+
text: content[ClipboardMIMEType.PlainText],
|
|
6379
|
+
};
|
|
6380
|
+
}
|
|
6381
|
+
const htmlDocument = new DOMParser().parseFromString(content[ClipboardMIMEType.Html], "text/html");
|
|
6382
|
+
const oSheetClipboardData = htmlDocument
|
|
6383
|
+
.querySelector("div")
|
|
6384
|
+
?.getAttribute("data-osheet-clipboard");
|
|
6385
|
+
const spreadsheetContent = oSheetClipboardData && JSON.parse(oSheetClipboardData);
|
|
6386
|
+
return {
|
|
6387
|
+
text: content[ClipboardMIMEType.PlainText],
|
|
6388
|
+
data: spreadsheetContent,
|
|
6389
|
+
};
|
|
6390
|
+
}
|
|
6151
6391
|
|
|
6152
6392
|
class ClipboardHandler {
|
|
6153
6393
|
getters;
|
|
@@ -6169,7 +6409,7 @@
|
|
|
6169
6409
|
getPasteTarget(sheetId, target, content, options) {
|
|
6170
6410
|
return { zones: [], sheetId };
|
|
6171
6411
|
}
|
|
6172
|
-
|
|
6412
|
+
convertTextToClipboardData(data) {
|
|
6173
6413
|
return;
|
|
6174
6414
|
}
|
|
6175
6415
|
}
|
|
@@ -8012,7 +8252,7 @@
|
|
|
8012
8252
|
this.dispatch("CLEAR_CELL", target);
|
|
8013
8253
|
}
|
|
8014
8254
|
}
|
|
8015
|
-
|
|
8255
|
+
convertTextToClipboardData(text) {
|
|
8016
8256
|
const locale = this.getters.getLocale();
|
|
8017
8257
|
const copiedData = {
|
|
8018
8258
|
cells: [],
|
|
@@ -8120,6 +8360,7 @@
|
|
|
8120
8360
|
|
|
8121
8361
|
class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
8122
8362
|
uuidGenerator = new UuidGenerator();
|
|
8363
|
+
queuedChanges = {};
|
|
8123
8364
|
copy(data) {
|
|
8124
8365
|
if (!data.zones.length) {
|
|
8125
8366
|
return;
|
|
@@ -8141,6 +8382,7 @@
|
|
|
8141
8382
|
return { cfRules };
|
|
8142
8383
|
}
|
|
8143
8384
|
paste(target, clippedContent, options) {
|
|
8385
|
+
this.queuedChanges = {};
|
|
8144
8386
|
if (options.pasteOption === "asValue") {
|
|
8145
8387
|
return;
|
|
8146
8388
|
}
|
|
@@ -8152,6 +8394,7 @@
|
|
|
8152
8394
|
else {
|
|
8153
8395
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
8154
8396
|
}
|
|
8397
|
+
this.executeQueuedChanges();
|
|
8155
8398
|
}
|
|
8156
8399
|
pasteFromCut(sheetId, target, content) {
|
|
8157
8400
|
const selection = target[0];
|
|
@@ -8191,34 +8434,56 @@
|
|
|
8191
8434
|
* Add or remove cells to a given conditional formatting rule.
|
|
8192
8435
|
*/
|
|
8193
8436
|
adaptCFRules(sheetId, cf, toAdd, toRemove) {
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
return;
|
|
8437
|
+
if (!this.queuedChanges[sheetId]) {
|
|
8438
|
+
this.queuedChanges[sheetId] = [];
|
|
8197
8439
|
}
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8440
|
+
const queuedChange = this.queuedChanges[sheetId].find((queued) => queued.cf.id === cf.id);
|
|
8441
|
+
if (!queuedChange) {
|
|
8442
|
+
this.queuedChanges[sheetId].push({ toAdd, toRemove, cf });
|
|
8443
|
+
}
|
|
8444
|
+
else {
|
|
8445
|
+
queuedChange.toAdd.push(...toAdd);
|
|
8446
|
+
queuedChange.toRemove.push(...toRemove);
|
|
8447
|
+
}
|
|
8448
|
+
}
|
|
8449
|
+
executeQueuedChanges() {
|
|
8450
|
+
for (const sheetId in this.queuedChanges) {
|
|
8451
|
+
for (const { toAdd, toRemove, cf } of this.queuedChanges[sheetId]) {
|
|
8452
|
+
const newRangesXc = this.getters.getAdaptedCfRanges(sheetId, cf, toAdd, toRemove);
|
|
8453
|
+
if (!newRangesXc) {
|
|
8454
|
+
continue;
|
|
8455
|
+
}
|
|
8456
|
+
if (newRangesXc.length === 0) {
|
|
8457
|
+
this.dispatch("REMOVE_CONDITIONAL_FORMAT", { id: cf.id, sheetId });
|
|
8458
|
+
continue;
|
|
8459
|
+
}
|
|
8460
|
+
this.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
8461
|
+
cf: {
|
|
8462
|
+
id: cf.id,
|
|
8463
|
+
rule: cf.rule,
|
|
8464
|
+
stopIfTrue: cf.stopIfTrue,
|
|
8465
|
+
},
|
|
8466
|
+
ranges: newRangesXc,
|
|
8467
|
+
sheetId,
|
|
8468
|
+
});
|
|
8469
|
+
}
|
|
8201
8470
|
}
|
|
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
8471
|
}
|
|
8212
8472
|
getCFToCopyTo(targetSheetId, originCF) {
|
|
8213
|
-
|
|
8473
|
+
let targetCF = this.getters
|
|
8214
8474
|
.getConditionalFormats(targetSheetId)
|
|
8215
8475
|
.find((cf) => cf.stopIfTrue === originCF.stopIfTrue && deepEquals(cf.rule, originCF.rule));
|
|
8216
|
-
|
|
8476
|
+
const queuedCfs = this.queuedChanges[targetSheetId];
|
|
8477
|
+
if (!targetCF && queuedCfs) {
|
|
8478
|
+
targetCF = queuedCfs.find((queued) => queued.cf.stopIfTrue === originCF.stopIfTrue && deepEquals(queued.cf.rule, originCF.rule))?.cf;
|
|
8479
|
+
}
|
|
8480
|
+
return targetCF || { ...originCF, id: this.uuidGenerator.uuidv4(), ranges: [] };
|
|
8217
8481
|
}
|
|
8218
8482
|
}
|
|
8219
8483
|
|
|
8220
8484
|
class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
8221
8485
|
uuidGenerator = new UuidGenerator();
|
|
8486
|
+
queuedChanges = {};
|
|
8222
8487
|
copy(data) {
|
|
8223
8488
|
const { rowsIndexes, columnsIndexes } = data;
|
|
8224
8489
|
const sheetId = data.sheetId;
|
|
@@ -8235,6 +8500,7 @@
|
|
|
8235
8500
|
return { dvRules };
|
|
8236
8501
|
}
|
|
8237
8502
|
paste(target, clippedContent, options) {
|
|
8503
|
+
this.queuedChanges = {};
|
|
8238
8504
|
if (options.pasteOption) {
|
|
8239
8505
|
return;
|
|
8240
8506
|
}
|
|
@@ -8246,6 +8512,7 @@
|
|
|
8246
8512
|
else {
|
|
8247
8513
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
8248
8514
|
}
|
|
8515
|
+
this.executeQueuedChanges();
|
|
8249
8516
|
}
|
|
8250
8517
|
pasteFromCut(sheetId, target, content) {
|
|
8251
8518
|
const selection = target[0];
|
|
@@ -8292,29 +8559,55 @@
|
|
|
8292
8559
|
}
|
|
8293
8560
|
}
|
|
8294
8561
|
getDataValidationRuleToCopyTo(targetSheetId, originRule, newId = true) {
|
|
8295
|
-
|
|
8562
|
+
let targetRule = this.getters
|
|
8296
8563
|
.getDataValidationRules(targetSheetId)
|
|
8297
8564
|
.find((rule) => deepEquals(originRule.criterion, rule.criterion) &&
|
|
8298
8565
|
originRule.isBlocking === rule.isBlocking);
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8566
|
+
const queuedRules = this.queuedChanges[targetSheetId];
|
|
8567
|
+
if (!targetRule && queuedRules) {
|
|
8568
|
+
targetRule = queuedRules.find((queued) => deepEquals(originRule.criterion, queued.rule.criterion) &&
|
|
8569
|
+
originRule.isBlocking === queued.rule.isBlocking)?.rule;
|
|
8570
|
+
}
|
|
8571
|
+
return (targetRule || {
|
|
8572
|
+
...originRule,
|
|
8573
|
+
id: newId ? this.uuidGenerator.uuidv4() : originRule.id,
|
|
8574
|
+
ranges: [],
|
|
8575
|
+
});
|
|
8302
8576
|
}
|
|
8303
8577
|
/**
|
|
8304
8578
|
* Add or remove XCs to a given data validation rule.
|
|
8305
8579
|
*/
|
|
8306
8580
|
adaptDataValidationRule(sheetId, rule, toAdd, toRemove) {
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8581
|
+
if (!this.queuedChanges[sheetId]) {
|
|
8582
|
+
this.queuedChanges[sheetId] = [];
|
|
8583
|
+
}
|
|
8584
|
+
const queuedChange = this.queuedChanges[sheetId].find((queued) => queued.rule.id === rule.id);
|
|
8585
|
+
if (!queuedChange) {
|
|
8586
|
+
this.queuedChanges[sheetId].push({ toAdd, toRemove, rule });
|
|
8587
|
+
}
|
|
8588
|
+
else {
|
|
8589
|
+
queuedChange.toAdd.push(...toAdd);
|
|
8590
|
+
queuedChange.toRemove.push(...toRemove);
|
|
8591
|
+
}
|
|
8592
|
+
}
|
|
8593
|
+
executeQueuedChanges() {
|
|
8594
|
+
for (const sheetId in this.queuedChanges) {
|
|
8595
|
+
for (const { toAdd, toRemove, rule: dv } of this.queuedChanges[sheetId]) {
|
|
8596
|
+
// Remove the zones first in case the same position is in toAdd and toRemove
|
|
8597
|
+
const dvZones = dv.ranges.map((range) => range.zone);
|
|
8598
|
+
const withRemovedZones = recomputeZones(dvZones, toRemove);
|
|
8599
|
+
const newDvZones = recomputeZones([...withRemovedZones, ...toAdd], []);
|
|
8600
|
+
if (newDvZones.length === 0) {
|
|
8601
|
+
this.dispatch("REMOVE_DATA_VALIDATION_RULE", { sheetId, id: dv.id });
|
|
8602
|
+
continue;
|
|
8603
|
+
}
|
|
8604
|
+
this.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
8605
|
+
rule: dv,
|
|
8606
|
+
ranges: newDvZones.map((zone) => this.getters.getRangeDataFromZone(sheetId, zone)),
|
|
8607
|
+
sheetId,
|
|
8608
|
+
});
|
|
8609
|
+
}
|
|
8312
8610
|
}
|
|
8313
|
-
this.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
8314
|
-
rule,
|
|
8315
|
-
ranges: newDvZones.map((zone) => this.getters.getRangeDataFromZone(sheetId, zone)),
|
|
8316
|
-
sheetId,
|
|
8317
|
-
});
|
|
8318
8611
|
}
|
|
8319
8612
|
}
|
|
8320
8613
|
|
|
@@ -10285,6 +10578,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10285
10578
|
description,
|
|
10286
10579
|
type: types,
|
|
10287
10580
|
};
|
|
10581
|
+
const acceptErrors = types.includes("ANY") || types.includes("RANGE");
|
|
10582
|
+
if (acceptErrors) {
|
|
10583
|
+
result.acceptErrors = true;
|
|
10584
|
+
}
|
|
10288
10585
|
if (isOptional) {
|
|
10289
10586
|
result.optional = true;
|
|
10290
10587
|
}
|
|
@@ -12347,8 +12644,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12347
12644
|
const COUNT = {
|
|
12348
12645
|
description: _t("The number of numeric values in dataset."),
|
|
12349
12646
|
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.")),
|
|
12647
|
+
arg("value1 (number, any, range<number>)", _t("The first value or range to consider when counting.")),
|
|
12648
|
+
arg("value2 (number, any, range<number>, repeating)", _t("Additional values or ranges to consider when counting.")),
|
|
12352
12649
|
],
|
|
12353
12650
|
compute: function (...values) {
|
|
12354
12651
|
return countNumbers(values, this.locale);
|
|
@@ -12737,6 +13034,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
12737
13034
|
},
|
|
12738
13035
|
isExported: true,
|
|
12739
13036
|
};
|
|
13037
|
+
// CORREL
|
|
12740
13038
|
// In GSheet, CORREL is just an alias to PEARSON
|
|
12741
13039
|
const CORREL = PEARSON;
|
|
12742
13040
|
// -----------------------------------------------------------------------------
|
|
@@ -13347,7 +13645,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
13347
13645
|
}
|
|
13348
13646
|
const databaseArgs = [
|
|
13349
13647
|
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 (
|
|
13648
|
+
arg("field (number, string)", _t("Indicates which column in database contains the values to be extracted and operated on.")),
|
|
13351
13649
|
arg("criteria (range)", _t("An array or range containing zero or more criteria to filter the database values by before operating.")),
|
|
13352
13650
|
];
|
|
13353
13651
|
// -----------------------------------------------------------------------------
|
|
@@ -14373,7 +14671,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
14373
14671
|
return cellsToSort.sort(cellsSortingCriterion(sortDirection));
|
|
14374
14672
|
}
|
|
14375
14673
|
function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
|
|
14376
|
-
let result = DispatchResult.Success;
|
|
14377
14674
|
//several columns => bypass the contiguity check
|
|
14378
14675
|
let multiColumns = zone.right > zone.left;
|
|
14379
14676
|
if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
|
|
@@ -14393,49 +14690,37 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
14393
14690
|
}
|
|
14394
14691
|
}
|
|
14395
14692
|
}
|
|
14396
|
-
const { col, row } = anchor;
|
|
14397
14693
|
if (multiColumns) {
|
|
14398
|
-
|
|
14694
|
+
interactiveSort(env, sheetId, anchor, zone, sortDirection);
|
|
14695
|
+
return;
|
|
14696
|
+
}
|
|
14697
|
+
const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
|
|
14698
|
+
if (isEqual(contiguousZone, zone)) {
|
|
14699
|
+
interactiveSort(env, sheetId, anchor, zone, sortDirection);
|
|
14399
14700
|
}
|
|
14400
14701
|
else {
|
|
14401
|
-
|
|
14402
|
-
const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
|
|
14403
|
-
if (isEqual(contiguousZone, zone)) {
|
|
14404
|
-
// merge as it is
|
|
14405
|
-
result = env.model.dispatch("SORT_CELLS", {
|
|
14406
|
-
sheetId,
|
|
14407
|
-
col,
|
|
14408
|
-
row,
|
|
14409
|
-
zone,
|
|
14410
|
-
sortDirection,
|
|
14411
|
-
});
|
|
14412
|
-
}
|
|
14413
|
-
else {
|
|
14414
|
-
env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => {
|
|
14415
|
-
zone = contiguousZone;
|
|
14416
|
-
result = env.model.dispatch("SORT_CELLS", {
|
|
14417
|
-
sheetId,
|
|
14418
|
-
col,
|
|
14419
|
-
row,
|
|
14420
|
-
zone,
|
|
14421
|
-
sortDirection,
|
|
14422
|
-
});
|
|
14423
|
-
}, () => {
|
|
14424
|
-
result = env.model.dispatch("SORT_CELLS", {
|
|
14425
|
-
sheetId,
|
|
14426
|
-
col,
|
|
14427
|
-
row,
|
|
14428
|
-
zone,
|
|
14429
|
-
sortDirection,
|
|
14430
|
-
});
|
|
14431
|
-
});
|
|
14432
|
-
}
|
|
14702
|
+
env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => interactiveSort(env, sheetId, anchor, contiguousZone, sortDirection), () => interactiveSort(env, sheetId, anchor, zone, sortDirection));
|
|
14433
14703
|
}
|
|
14704
|
+
}
|
|
14705
|
+
function interactiveSort(env, sheetId, anchor, zone, sortDirection, sortOptions) {
|
|
14706
|
+
const result = env.model.dispatch("SORT_CELLS", {
|
|
14707
|
+
sheetId,
|
|
14708
|
+
col: anchor.col,
|
|
14709
|
+
row: anchor.row,
|
|
14710
|
+
zone,
|
|
14711
|
+
sortDirection,
|
|
14712
|
+
sortOptions,
|
|
14713
|
+
});
|
|
14434
14714
|
if (result.isCancelledBecause("InvalidSortZone" /* CommandResult.InvalidSortZone */)) {
|
|
14435
14715
|
const { col, row } = anchor;
|
|
14436
14716
|
env.model.selection.selectZone({ cell: { col, row }, zone });
|
|
14437
14717
|
env.raiseError(_t("Cannot sort. To sort, select only cells or only merges that have the same size."));
|
|
14438
14718
|
}
|
|
14719
|
+
if (result.isCancelledBecause("SortZoneWithArrayFormulas" /* CommandResult.SortZoneWithArrayFormulas */)) {
|
|
14720
|
+
const { col, row } = anchor;
|
|
14721
|
+
env.model.selection.selectZone({ cell: { col, row }, zone });
|
|
14722
|
+
env.raiseError(_t("Cannot sort a zone with array formulas."));
|
|
14723
|
+
}
|
|
14439
14724
|
}
|
|
14440
14725
|
|
|
14441
14726
|
function sortMatrix(matrix, locale, ...criteria) {
|
|
@@ -17729,7 +18014,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
17729
18014
|
args: [
|
|
17730
18015
|
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
18016
|
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.")),
|
|
18017
|
+
arg("condition2 (boolean, any, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
|
|
17733
18018
|
arg("value2 (any, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
|
|
17734
18019
|
],
|
|
17735
18020
|
compute: function (...values) {
|
|
@@ -17968,7 +18253,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
17968
18253
|
const HLOOKUP = {
|
|
17969
18254
|
description: _t("Horizontal lookup"),
|
|
17970
18255
|
args: [
|
|
17971
|
-
arg("search_key (
|
|
18256
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
17972
18257
|
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
18258
|
arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
|
|
17974
18259
|
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 +18261,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
17976
18261
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
17977
18262
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
17978
18263
|
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
18264
|
const getValueFromRange = (range, index) => range[index][0].value;
|
|
17983
18265
|
const _isSorted = toBoolean(isSorted.value);
|
|
17984
18266
|
const colIndex = _isSorted
|
|
@@ -18075,7 +18357,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18075
18357
|
const LOOKUP = {
|
|
18076
18358
|
description: _t("Look up a value."),
|
|
18077
18359
|
args: [
|
|
18078
|
-
arg("search_key (
|
|
18360
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
18079
18361
|
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
18362
|
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
18363
|
],
|
|
@@ -18115,7 +18397,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18115
18397
|
const MATCH = {
|
|
18116
18398
|
description: _t("Position of item in range that matches value."),
|
|
18117
18399
|
args: [
|
|
18118
|
-
arg("search_key (
|
|
18400
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
18119
18401
|
arg("range (any, range)", _t("The one-dimensional array to be searched.")),
|
|
18120
18402
|
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
18403
|
],
|
|
@@ -18190,7 +18472,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18190
18472
|
const VLOOKUP = {
|
|
18191
18473
|
description: _t("Vertical lookup."),
|
|
18192
18474
|
args: [
|
|
18193
|
-
arg("search_key (
|
|
18475
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
18194
18476
|
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
18477
|
arg("index (number)", _t("The column index of the value to be returned, where the first column in range is numbered 1.")),
|
|
18196
18478
|
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 +18480,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18198
18480
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
18199
18481
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
18200
18482
|
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
18483
|
const getValueFromRange = (range, index) => range[0][index].value;
|
|
18205
18484
|
const _isSorted = toBoolean(isSorted.value);
|
|
18206
18485
|
const rowIndex = _isSorted
|
|
@@ -18226,7 +18505,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18226
18505
|
const XLOOKUP = {
|
|
18227
18506
|
description: _t("Search a range for a match and return the corresponding item from a second range."),
|
|
18228
18507
|
args: [
|
|
18229
|
-
arg("search_key (
|
|
18508
|
+
arg("search_key (string,number,boolean)", _t("The value to search for.")),
|
|
18230
18509
|
arg("lookup_range (any, range)", _t("The range to consider for the search. Should be a single column or a single row.")),
|
|
18231
18510
|
arg("return_range (any, range)", _t("The range containing the return value. Should have the same dimensions as lookup_range.")),
|
|
18232
18511
|
arg("if_not_found (any, optional)", _t("If a valid match is not found, return this value.")),
|
|
@@ -18251,9 +18530,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18251
18530
|
assert(() => lookupDirection === "col"
|
|
18252
18531
|
? returnRange[0].length === lookupRange[0].length
|
|
18253
18532
|
: 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
18533
|
const getElement = lookupDirection === "col"
|
|
18258
18534
|
? (range, index) => range[0][index].value
|
|
18259
18535
|
: (range, index) => range[index][0].value;
|
|
@@ -18278,13 +18554,14 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18278
18554
|
//--------------------------------------------------------------------------
|
|
18279
18555
|
// Pivot functions
|
|
18280
18556
|
//--------------------------------------------------------------------------
|
|
18557
|
+
// PIVOT.VALUE
|
|
18281
18558
|
const PIVOT_VALUE = {
|
|
18282
18559
|
description: _t("Get the value from a pivot."),
|
|
18283
18560
|
args: [
|
|
18284
|
-
arg("pivot_id (string)", _t("ID of the pivot.")),
|
|
18561
|
+
arg("pivot_id (number,string)", _t("ID of the pivot.")),
|
|
18285
18562
|
arg("measure_name (string)", _t("Name of the measure.")),
|
|
18286
18563
|
arg("domain_field_name (string,optional,repeating)", _t("Field name.")),
|
|
18287
|
-
arg("domain_value (string,optional,repeating)", _t("Value.")),
|
|
18564
|
+
arg("domain_value (number,string,boolean,optional,repeating)", _t("Value.")),
|
|
18288
18565
|
],
|
|
18289
18566
|
compute: function (formulaId, measureName, ...domainArgs) {
|
|
18290
18567
|
const _pivotFormulaId = toString(formulaId);
|
|
@@ -18311,12 +18588,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18311
18588
|
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
18312
18589
|
},
|
|
18313
18590
|
};
|
|
18591
|
+
// PIVOT.HEADER
|
|
18314
18592
|
const PIVOT_HEADER = {
|
|
18315
18593
|
description: _t("Get the header of a pivot."),
|
|
18316
18594
|
args: [
|
|
18317
|
-
arg("pivot_id (string)", _t("ID of the pivot.")),
|
|
18595
|
+
arg("pivot_id (number,string)", _t("ID of the pivot.")),
|
|
18318
18596
|
arg("domain_field_name (string,optional,repeating)", _t("Field name.")),
|
|
18319
|
-
arg("domain_value (string,optional,repeating)", _t("Value.")),
|
|
18597
|
+
arg("domain_value (number,string,value,optional,repeating)", _t("Value.")),
|
|
18320
18598
|
],
|
|
18321
18599
|
compute: function (pivotId, ...domainArgs) {
|
|
18322
18600
|
const _pivotFormulaId = toString(pivotId);
|
|
@@ -18563,8 +18841,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18563
18841
|
const EQ = {
|
|
18564
18842
|
description: _t("Equal."),
|
|
18565
18843
|
args: [
|
|
18566
|
-
arg("value1 (
|
|
18567
|
-
arg("value2 (
|
|
18844
|
+
arg("value1 (string, number, boolean)", _t("The first value.")),
|
|
18845
|
+
arg("value2 (string, number, boolean)", _t("The value to test against value1 for equality.")),
|
|
18568
18846
|
],
|
|
18569
18847
|
compute: function (value1, value2) {
|
|
18570
18848
|
if (isEvaluationError(value1?.value)) {
|
|
@@ -18615,8 +18893,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18615
18893
|
const GT = {
|
|
18616
18894
|
description: _t("Strictly greater than."),
|
|
18617
18895
|
args: [
|
|
18618
|
-
arg("value1 (
|
|
18619
|
-
arg("value2 (
|
|
18896
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being greater than value2.")),
|
|
18897
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18620
18898
|
],
|
|
18621
18899
|
compute: function (value1, value2) {
|
|
18622
18900
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
@@ -18630,8 +18908,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18630
18908
|
const GTE = {
|
|
18631
18909
|
description: _t("Greater than or equal to."),
|
|
18632
18910
|
args: [
|
|
18633
|
-
arg("value1 (
|
|
18634
|
-
arg("value2 (
|
|
18911
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being greater than or equal to value2.")),
|
|
18912
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18635
18913
|
],
|
|
18636
18914
|
compute: function (value1, value2) {
|
|
18637
18915
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
@@ -18645,8 +18923,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18645
18923
|
const LT = {
|
|
18646
18924
|
description: _t("Less than."),
|
|
18647
18925
|
args: [
|
|
18648
|
-
arg("value1 (
|
|
18649
|
-
arg("value2 (
|
|
18926
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being less than value2.")),
|
|
18927
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18650
18928
|
],
|
|
18651
18929
|
compute: function (value1, value2) {
|
|
18652
18930
|
const result = GTE.compute.bind(this)(value1, value2);
|
|
@@ -18662,8 +18940,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18662
18940
|
const LTE = {
|
|
18663
18941
|
description: _t("Less than or equal to."),
|
|
18664
18942
|
args: [
|
|
18665
|
-
arg("value1 (
|
|
18666
|
-
arg("value2 (
|
|
18943
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being less than or equal to value2.")),
|
|
18944
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18667
18945
|
],
|
|
18668
18946
|
compute: function (value1, value2) {
|
|
18669
18947
|
const result = GT.compute.bind(this)(value1, value2);
|
|
@@ -18711,8 +18989,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
18711
18989
|
const NE = {
|
|
18712
18990
|
description: _t("Not equal."),
|
|
18713
18991
|
args: [
|
|
18714
|
-
arg("value1 (
|
|
18715
|
-
arg("value2 (
|
|
18992
|
+
arg("value1 (string, number, boolean)", _t("The first value.")),
|
|
18993
|
+
arg("value2 (string, number, boolean)", _t("The value to test against value1 for inequality.")),
|
|
18716
18994
|
],
|
|
18717
18995
|
compute: function (value1, value2) {
|
|
18718
18996
|
const result = EQ.compute.bind(this)(value1, value2);
|
|
@@ -19630,6 +19908,16 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
19630
19908
|
});
|
|
19631
19909
|
}
|
|
19632
19910
|
function errorHandlingCompute(...args) {
|
|
19911
|
+
for (let i = 0; i < args.length; i++) {
|
|
19912
|
+
const arg = args[i];
|
|
19913
|
+
const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
|
|
19914
|
+
// Early exit if the argument is an error and the function does not accept errors
|
|
19915
|
+
// We only check scalar arguments, not matrix arguments for performance reasons.
|
|
19916
|
+
// Casting helpers are responsible for handling errors in matrix arguments.
|
|
19917
|
+
if (!argDefinition.acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) {
|
|
19918
|
+
return arg;
|
|
19919
|
+
}
|
|
19920
|
+
}
|
|
19633
19921
|
try {
|
|
19634
19922
|
return computeFunctionToObject.apply(this, args);
|
|
19635
19923
|
}
|
|
@@ -19638,6 +19926,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
19638
19926
|
}
|
|
19639
19927
|
}
|
|
19640
19928
|
function computeFunctionToObject(...args) {
|
|
19929
|
+
if (this.debug) {
|
|
19930
|
+
debugger;
|
|
19931
|
+
}
|
|
19641
19932
|
const result = descr.compute.apply(this, args);
|
|
19642
19933
|
if (!isMatrix(result)) {
|
|
19643
19934
|
if (typeof result === "object" && result !== null && "value" in result) {
|
|
@@ -20747,6 +21038,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
20747
21038
|
}
|
|
20748
21039
|
if (ast.debug) {
|
|
20749
21040
|
code.append("debugger;");
|
|
21041
|
+
code.append(`ctx["debug"] = true;`);
|
|
20750
21042
|
}
|
|
20751
21043
|
switch (ast.type) {
|
|
20752
21044
|
case "BOOLEAN":
|
|
@@ -21345,217 +21637,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
21345
21637
|
},
|
|
21346
21638
|
});
|
|
21347
21639
|
|
|
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
21640
|
/**
|
|
21560
21641
|
* An AutofillModifierImplementation is used to describe how to handle a
|
|
21561
21642
|
* AutofillModifier.
|
|
@@ -22920,6 +23001,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
22920
23001
|
WarningTypes["CfIconSetEmptyIconNotSupported"] = "IconSets with empty icons";
|
|
22921
23002
|
WarningTypes["BadlyFormattedHyperlink"] = "Badly formatted hyperlink";
|
|
22922
23003
|
WarningTypes["NumFmtIdNotSupported"] = "Number format";
|
|
23004
|
+
WarningTypes["TimeDataValidationNotSupported"] = "Time data validation rules";
|
|
23005
|
+
WarningTypes["TextLengthDataValidationNotSupported"] = "Text length data validation rules";
|
|
23006
|
+
WarningTypes["WholeNumberDataValidationNotSupported"] = "Whole number data validation rules";
|
|
23007
|
+
WarningTypes["NotEqualDateDataValidationNotSupported"] = "Not equal date data validation rules";
|
|
22923
23008
|
})(WarningTypes || (WarningTypes = {}));
|
|
22924
23009
|
class XLSXImportWarningManager {
|
|
22925
23010
|
_parsingWarnings = new Set();
|
|
@@ -23298,6 +23383,25 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23298
23383
|
webp: "image/webp",
|
|
23299
23384
|
jpg: "image/jpeg",
|
|
23300
23385
|
};
|
|
23386
|
+
const XLSX_DV_DECIMAL_OPERATOR_MAPPING = {
|
|
23387
|
+
between: "isBetween",
|
|
23388
|
+
notBetween: "isNotBetween",
|
|
23389
|
+
equal: "isEqual",
|
|
23390
|
+
notEqual: "isNotEqual",
|
|
23391
|
+
greaterThan: "isGreaterThan",
|
|
23392
|
+
greaterThanOrEqual: "isGreaterOrEqualTo",
|
|
23393
|
+
lessThan: "isLessThan",
|
|
23394
|
+
lessThanOrEqual: "isLessOrEqualTo",
|
|
23395
|
+
};
|
|
23396
|
+
const XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING = {
|
|
23397
|
+
between: "dateIsBetween",
|
|
23398
|
+
notBetween: "dateIsNotBetween",
|
|
23399
|
+
equal: "dateIs",
|
|
23400
|
+
greaterThan: "dateIsAfter",
|
|
23401
|
+
greaterThanOrEqual: "dateIsOnOrAfter",
|
|
23402
|
+
lessThan: "dateIsBefore",
|
|
23403
|
+
lessThanOrEqual: "dateIsOnOrBefore",
|
|
23404
|
+
};
|
|
23301
23405
|
|
|
23302
23406
|
/**
|
|
23303
23407
|
* Most of the functions could stay private, but are exported for testing purposes
|
|
@@ -24079,6 +24183,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24079
24183
|
}
|
|
24080
24184
|
return position / HEIGHT_FACTOR;
|
|
24081
24185
|
}
|
|
24186
|
+
/**
|
|
24187
|
+
* Convert the o-spreadsheet data validation decimal
|
|
24188
|
+
* criterion type to the corresponding excel operator.
|
|
24189
|
+
*/
|
|
24190
|
+
function convertDecimalCriterionTypeToExcelOperator(operator) {
|
|
24191
|
+
return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
|
|
24192
|
+
}
|
|
24193
|
+
/**
|
|
24194
|
+
* Convert the o-spreadsheet data validation date
|
|
24195
|
+
* criterion type to the corresponding excel operator.
|
|
24196
|
+
*/
|
|
24197
|
+
function convertDateCriterionTypeToExcelOperator(operator) {
|
|
24198
|
+
return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
|
|
24199
|
+
}
|
|
24082
24200
|
|
|
24083
24201
|
function convertFigures(sheetData) {
|
|
24084
24202
|
let id = 1;
|
|
@@ -24188,6 +24306,112 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24188
24306
|
};
|
|
24189
24307
|
}
|
|
24190
24308
|
|
|
24309
|
+
function convertDataValidationRules(xlsxDataValidations, warningManager) {
|
|
24310
|
+
const dvRules = [];
|
|
24311
|
+
let dvId = 1;
|
|
24312
|
+
for (const dv of xlsxDataValidations) {
|
|
24313
|
+
if (!dv) {
|
|
24314
|
+
continue;
|
|
24315
|
+
}
|
|
24316
|
+
switch (dv.type) {
|
|
24317
|
+
case "time":
|
|
24318
|
+
warningManager.generateNotSupportedWarning(WarningTypes.TimeDataValidationNotSupported);
|
|
24319
|
+
break;
|
|
24320
|
+
case "textLength":
|
|
24321
|
+
warningManager.generateNotSupportedWarning(WarningTypes.TextLengthDataValidationNotSupported);
|
|
24322
|
+
break;
|
|
24323
|
+
case "whole":
|
|
24324
|
+
warningManager.generateNotSupportedWarning(WarningTypes.WholeNumberDataValidationNotSupported);
|
|
24325
|
+
break;
|
|
24326
|
+
case "decimal":
|
|
24327
|
+
const decimalRule = convertDecimalRule(dvId++, dv);
|
|
24328
|
+
dvRules.push(decimalRule);
|
|
24329
|
+
break;
|
|
24330
|
+
case "list":
|
|
24331
|
+
const listRule = convertListrule(dvId++, dv);
|
|
24332
|
+
dvRules.push(listRule);
|
|
24333
|
+
break;
|
|
24334
|
+
case "date":
|
|
24335
|
+
if (dv.operator === "notEqual") {
|
|
24336
|
+
warningManager.generateNotSupportedWarning(WarningTypes.NotEqualDateDataValidationNotSupported);
|
|
24337
|
+
break;
|
|
24338
|
+
}
|
|
24339
|
+
const dateRule = convertDateRule(dvId++, dv);
|
|
24340
|
+
dvRules.push(dateRule);
|
|
24341
|
+
break;
|
|
24342
|
+
case "custom":
|
|
24343
|
+
const customRule = convertCustomRule(dvId++, dv);
|
|
24344
|
+
dvRules.push(customRule);
|
|
24345
|
+
break;
|
|
24346
|
+
}
|
|
24347
|
+
}
|
|
24348
|
+
return dvRules;
|
|
24349
|
+
}
|
|
24350
|
+
function convertDecimalRule(id, dv) {
|
|
24351
|
+
const values = [dv.formula1.toString()];
|
|
24352
|
+
if (dv.formula2) {
|
|
24353
|
+
values.push(dv.formula2.toString());
|
|
24354
|
+
}
|
|
24355
|
+
return {
|
|
24356
|
+
id: id.toString(),
|
|
24357
|
+
ranges: dv.sqref,
|
|
24358
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24359
|
+
criterion: {
|
|
24360
|
+
type: XLSX_DV_DECIMAL_OPERATOR_MAPPING[dv.operator],
|
|
24361
|
+
values,
|
|
24362
|
+
},
|
|
24363
|
+
};
|
|
24364
|
+
}
|
|
24365
|
+
function convertListrule(id, dv) {
|
|
24366
|
+
const formula1 = dv.formula1.toString();
|
|
24367
|
+
const isRangeRule = rangeReference.test(formula1);
|
|
24368
|
+
return {
|
|
24369
|
+
id: id.toString(),
|
|
24370
|
+
ranges: dv.sqref,
|
|
24371
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24372
|
+
criterion: {
|
|
24373
|
+
type: isRangeRule ? "isValueInRange" : "isValueInList",
|
|
24374
|
+
values: isRangeRule ? [formula1] : formula1.replaceAll('"', "").split(","),
|
|
24375
|
+
displayStyle: "arrow",
|
|
24376
|
+
},
|
|
24377
|
+
};
|
|
24378
|
+
}
|
|
24379
|
+
function convertDateRule(id, dv) {
|
|
24380
|
+
let criterion;
|
|
24381
|
+
const values = [dv.formula1.toString()];
|
|
24382
|
+
if (dv.formula2) {
|
|
24383
|
+
values.push(dv.formula2.toString());
|
|
24384
|
+
criterion = {
|
|
24385
|
+
type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
|
|
24386
|
+
values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
|
|
24387
|
+
};
|
|
24388
|
+
}
|
|
24389
|
+
else {
|
|
24390
|
+
criterion = {
|
|
24391
|
+
type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
|
|
24392
|
+
values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
|
|
24393
|
+
dateValue: "exactDate",
|
|
24394
|
+
};
|
|
24395
|
+
}
|
|
24396
|
+
return {
|
|
24397
|
+
id: id.toString(),
|
|
24398
|
+
ranges: dv.sqref,
|
|
24399
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24400
|
+
criterion: criterion,
|
|
24401
|
+
};
|
|
24402
|
+
}
|
|
24403
|
+
function convertCustomRule(id, dv) {
|
|
24404
|
+
return {
|
|
24405
|
+
id: id.toString(),
|
|
24406
|
+
ranges: dv.sqref,
|
|
24407
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24408
|
+
criterion: {
|
|
24409
|
+
type: "customFormula",
|
|
24410
|
+
values: [`=${dv.formula1.toString()}`],
|
|
24411
|
+
},
|
|
24412
|
+
};
|
|
24413
|
+
}
|
|
24414
|
+
|
|
24191
24415
|
/**
|
|
24192
24416
|
* Match external reference (ex. '[1]Sheet 3'!$B$4)
|
|
24193
24417
|
*
|
|
@@ -24308,6 +24532,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
24308
24532
|
cols: convertCols(sheet, sheetDims[0], colHeaderGroups),
|
|
24309
24533
|
rows: convertRows(sheet, sheetDims[1], rowHeaderGroups),
|
|
24310
24534
|
conditionalFormats: convertConditionalFormats(sheet.cfs, data.dxfs, warningManager),
|
|
24535
|
+
dataValidationRules: convertDataValidationRules(sheet.dataValidations, warningManager),
|
|
24311
24536
|
figures: convertFigures(sheet),
|
|
24312
24537
|
isVisible: sheet.isVisible,
|
|
24313
24538
|
panes: sheetOptions
|
|
@@ -25587,6 +25812,41 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25587
25812
|
}
|
|
25588
25813
|
}
|
|
25589
25814
|
|
|
25815
|
+
class XlsxDataValidationExtractor extends XlsxBaseExtractor {
|
|
25816
|
+
theme;
|
|
25817
|
+
constructor(sheetFile, xlsxStructure, warningManager, theme) {
|
|
25818
|
+
super(sheetFile, xlsxStructure, warningManager);
|
|
25819
|
+
this.theme = theme;
|
|
25820
|
+
}
|
|
25821
|
+
extractDataValidations() {
|
|
25822
|
+
const dataValidations = this.mapOnElements({ parent: this.rootFile.file.xml, query: "worksheet > dataValidations > dataValidation" }, (dvElement) => {
|
|
25823
|
+
return {
|
|
25824
|
+
type: this.extractAttr(dvElement, "type", { required: true }).asString(),
|
|
25825
|
+
operator: this.extractAttr(dvElement, "operator", {
|
|
25826
|
+
default: "between",
|
|
25827
|
+
})?.asString(),
|
|
25828
|
+
sqref: this.extractAttr(dvElement, "sqref", { required: true }).asString().split(" "),
|
|
25829
|
+
errorStyle: this.extractAttr(dvElement, "errorStyle")?.asString(),
|
|
25830
|
+
formula1: this.extractDataValidationFormula(dvElement, 1)[0],
|
|
25831
|
+
formula2: this.extractDataValidationFormula(dvElement, 2)[0],
|
|
25832
|
+
showErrorMessage: this.extractAttr(dvElement, "showErrorMessage")?.asBool(),
|
|
25833
|
+
errorTitle: this.extractAttr(dvElement, "errorTitle")?.asString(),
|
|
25834
|
+
error: this.extractAttr(dvElement, "error")?.asString(),
|
|
25835
|
+
showInputMessage: this.extractAttr(dvElement, "showInputMessage")?.asBool(),
|
|
25836
|
+
promptTitle: this.extractAttr(dvElement, "promptTitle")?.asString(),
|
|
25837
|
+
prompt: this.extractAttr(dvElement, "prompt")?.asString(),
|
|
25838
|
+
allowBlank: this.extractAttr(dvElement, "allowBlank")?.asBool(),
|
|
25839
|
+
};
|
|
25840
|
+
});
|
|
25841
|
+
return dataValidations;
|
|
25842
|
+
}
|
|
25843
|
+
extractDataValidationFormula(dvElement, index) {
|
|
25844
|
+
return this.mapOnElements({ parent: dvElement, query: `formula${index}` }, (cfFormulaElements) => {
|
|
25845
|
+
return this.extractTextContent(cfFormulaElements, { required: true });
|
|
25846
|
+
});
|
|
25847
|
+
}
|
|
25848
|
+
}
|
|
25849
|
+
|
|
25590
25850
|
class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
25591
25851
|
extractChart() {
|
|
25592
25852
|
return this.mapOnElements({ parent: this.rootFile.file.xml, query: "c:chartSpace" }, (rootChartElement) => {
|
|
@@ -25954,6 +26214,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
25954
26214
|
sharedFormulas: this.extractSharedFormulas(sheetElement),
|
|
25955
26215
|
merges: this.extractMerges(sheetElement),
|
|
25956
26216
|
cfs: this.extractConditionalFormats(),
|
|
26217
|
+
dataValidations: this.extractDataValidations(),
|
|
25957
26218
|
figures: this.extractFigures(sheetElement),
|
|
25958
26219
|
hyperlinks: this.extractHyperLinks(sheetElement),
|
|
25959
26220
|
tables: this.extractTables(sheetElement),
|
|
@@ -26025,6 +26286,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
26025
26286
|
extractConditionalFormats() {
|
|
26026
26287
|
return new XlsxCfExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractConditionalFormattings();
|
|
26027
26288
|
}
|
|
26289
|
+
extractDataValidations() {
|
|
26290
|
+
return new XlsxDataValidationExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractDataValidations();
|
|
26291
|
+
}
|
|
26028
26292
|
extractFigures(worksheet) {
|
|
26029
26293
|
const figures = this.mapOnElements({ parent: worksheet, query: "drawing" }, (drawingElement) => {
|
|
26030
26294
|
const drawingId = this.extractAttr(drawingElement, "r:id", { required: true })?.asString();
|
|
@@ -27332,6 +27596,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27332
27596
|
rows: {},
|
|
27333
27597
|
merges: [],
|
|
27334
27598
|
conditionalFormats: [],
|
|
27599
|
+
dataValidationRules: [],
|
|
27335
27600
|
figures: [],
|
|
27336
27601
|
tables: [],
|
|
27337
27602
|
isVisible: true,
|
|
@@ -27406,17 +27671,15 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27406
27671
|
});
|
|
27407
27672
|
}
|
|
27408
27673
|
catch (error) {
|
|
27409
|
-
const parsedSpreadsheetContent = clipboardContent
|
|
27410
|
-
|
|
27411
|
-
: {};
|
|
27412
|
-
if (parsedSpreadsheetContent.version && parsedSpreadsheetContent.version !== CURRENT_VERSION) {
|
|
27674
|
+
const parsedSpreadsheetContent = clipboardContent.data;
|
|
27675
|
+
if (parsedSpreadsheetContent?.version !== CURRENT_VERSION) {
|
|
27413
27676
|
env.raiseError(_t("An unexpected error occurred while pasting content.\
|
|
27414
27677
|
This is probably due to a spreadsheet version mismatch."));
|
|
27415
27678
|
}
|
|
27416
27679
|
result = env.model.dispatch("PASTE_FROM_OS_CLIPBOARD", {
|
|
27417
27680
|
target,
|
|
27418
27681
|
clipboardContent: {
|
|
27419
|
-
|
|
27682
|
+
text: clipboardContent.text,
|
|
27420
27683
|
},
|
|
27421
27684
|
pasteOption,
|
|
27422
27685
|
});
|
|
@@ -27445,7 +27708,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27445
27708
|
["ValueLowerInvalidFormula" /* CommandResult.ValueLowerInvalidFormula */]: _t("Invalid lower inflection point formula"),
|
|
27446
27709
|
["EmptyRange" /* CommandResult.EmptyRange */]: _t("A range needs to be defined"),
|
|
27447
27710
|
["ValueCellIsInvalidFormula" /* CommandResult.ValueCellIsInvalidFormula */]: _t("At least one of the provided values is an invalid formula"),
|
|
27448
|
-
["DataBarRangeValuesMismatch" /* CommandResult.DataBarRangeValuesMismatch */]: _t("All the ranges and the range values must have the same size"),
|
|
27449
27711
|
Unexpected: _t("The rule is invalid for an unknown reason"),
|
|
27450
27712
|
},
|
|
27451
27713
|
ColorScale: _t("Color scale"),
|
|
@@ -27541,6 +27803,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27541
27803
|
numberValue: _t("The value must be a number"),
|
|
27542
27804
|
dateValue: _t("The value must be a date"),
|
|
27543
27805
|
validRange: _t("The value must be a valid range"),
|
|
27806
|
+
validFormula: _t("The formula must be valid"),
|
|
27807
|
+
},
|
|
27808
|
+
Errors: {
|
|
27809
|
+
["InvalidRange" /* CommandResult.InvalidRange */]: _t("The range is invalid."),
|
|
27810
|
+
["InvalidDataValidationCriterionValue" /* CommandResult.InvalidDataValidationCriterionValue */]: _t("One or more of the provided criteria values are invalid. Please review and correct them."),
|
|
27811
|
+
["InvalidNumberOfCriterionValues" /* CommandResult.InvalidNumberOfCriterionValues */]: _t("One or more of the provided criteria values are missing."),
|
|
27812
|
+
Unexpected: _t("The rule is invalid for an unknown reason."),
|
|
27544
27813
|
},
|
|
27545
27814
|
};
|
|
27546
27815
|
const TableTerms = {
|
|
@@ -27751,7 +28020,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27751
28020
|
labels.length > dataSetsValues[0].data.length) {
|
|
27752
28021
|
labels.shift();
|
|
27753
28022
|
}
|
|
27754
|
-
({ labels, dataSetsValues } =
|
|
28023
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27755
28024
|
if (definition.aggregated) {
|
|
27756
28025
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
27757
28026
|
}
|
|
@@ -27806,7 +28075,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27806
28075
|
labels.length > dataSetsValues[0].data.length) {
|
|
27807
28076
|
labels.shift();
|
|
27808
28077
|
}
|
|
27809
|
-
({ labels, dataSetsValues } =
|
|
28078
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27810
28079
|
if (axisType === "time") {
|
|
27811
28080
|
({ labels, dataSetsValues } = fixEmptyLabelsForDateCharts(labels, dataSetsValues));
|
|
27812
28081
|
}
|
|
@@ -27823,7 +28092,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27823
28092
|
if (definition.cumulative) {
|
|
27824
28093
|
let accumulator = 0;
|
|
27825
28094
|
data = data.map((value) => {
|
|
27826
|
-
if (!isNaN(value)) {
|
|
28095
|
+
if (!isNaN(parseFloat(value))) {
|
|
27827
28096
|
accumulator += parseFloat(value);
|
|
27828
28097
|
return accumulator;
|
|
27829
28098
|
}
|
|
@@ -27856,11 +28125,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27856
28125
|
labels.length > dataSetsValues[0].data.length) {
|
|
27857
28126
|
labels.shift();
|
|
27858
28127
|
}
|
|
27859
|
-
({ labels, dataSetsValues } =
|
|
28128
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27860
28129
|
if (definition.aggregated) {
|
|
27861
28130
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
27862
28131
|
}
|
|
27863
|
-
({ dataSetsValues, labels } =
|
|
28132
|
+
({ dataSetsValues, labels } = keepOnlyPositiveValues(labels, dataSetsValues));
|
|
27864
28133
|
const dataSetFormat = getChartDatasetFormat(getters, dataSets, "left");
|
|
27865
28134
|
return {
|
|
27866
28135
|
dataSetsValues,
|
|
@@ -27878,7 +28147,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
27878
28147
|
labels.length > dataSetsValues[0].data.length) {
|
|
27879
28148
|
labels.shift();
|
|
27880
28149
|
}
|
|
27881
|
-
({ labels, dataSetsValues } =
|
|
28150
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27882
28151
|
if (definition.aggregated) {
|
|
27883
28152
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
27884
28153
|
}
|
|
@@ -28057,26 +28326,16 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
28057
28326
|
}
|
|
28058
28327
|
return isInstalled;
|
|
28059
28328
|
}
|
|
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 };
|
|
28329
|
+
function keepOnlyPositiveValues(labels, datasets) {
|
|
28330
|
+
const numberOfDataPoints = Math.max(labels.length, ...datasets.map((dataset) => dataset.data?.length || 0));
|
|
28331
|
+
const filteredIndexes = range(0, numberOfDataPoints).filter((i) => datasets.some((ds) => typeof ds.data[i] === "number" && ds.data[i] > 0));
|
|
28332
|
+
return {
|
|
28333
|
+
labels: filteredIndexes.map((i) => labels[i] || ""),
|
|
28334
|
+
dataSetsValues: datasets.map((ds) => ({
|
|
28335
|
+
...ds,
|
|
28336
|
+
data: filteredIndexes.map((i) => typeof ds.data[i] === "number" && ds.data[i] > 0 ? ds.data[i] : null),
|
|
28337
|
+
})),
|
|
28338
|
+
};
|
|
28080
28339
|
}
|
|
28081
28340
|
function fixEmptyLabelsForDateCharts(labels, dataSetsValues) {
|
|
28082
28341
|
if (labels.length === 0 || labels.every((label) => !label)) {
|
|
@@ -28109,18 +28368,23 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
28109
28368
|
}
|
|
28110
28369
|
return [];
|
|
28111
28370
|
}
|
|
28112
|
-
|
|
28371
|
+
/**
|
|
28372
|
+
* Filter the data points that:
|
|
28373
|
+
* - have neither a label nor a value
|
|
28374
|
+
* - have no label and a non-numeric value
|
|
28375
|
+
*/
|
|
28376
|
+
function filterInvalidDataPoints(labels, datasets) {
|
|
28113
28377
|
const numberOfDataPoints = Math.max(labels.length, ...datasets.map((dataset) => dataset.data?.length || 0));
|
|
28114
28378
|
const dataPointsIndexes = range(0, numberOfDataPoints).filter((dataPointIndex) => {
|
|
28115
28379
|
const label = labels[dataPointIndex];
|
|
28116
28380
|
const values = datasets.map((dataset) => dataset.data?.[dataPointIndex]);
|
|
28117
|
-
return label || values.some((value) => value ===
|
|
28381
|
+
return label || values.some((value) => typeof value === "number");
|
|
28118
28382
|
});
|
|
28119
28383
|
return {
|
|
28120
28384
|
labels: dataPointsIndexes.map((i) => labels[i] || ""),
|
|
28121
28385
|
dataSetsValues: datasets.map((dataset) => ({
|
|
28122
28386
|
...dataset,
|
|
28123
|
-
data: dataPointsIndexes.map((i) => dataset.data[i]),
|
|
28387
|
+
data: dataPointsIndexes.map((i) => typeof dataset.data[i] === "number" ? dataset.data[i] : null),
|
|
28124
28388
|
})),
|
|
28125
28389
|
};
|
|
28126
28390
|
}
|
|
@@ -28263,8 +28527,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
28263
28527
|
const dataset = {
|
|
28264
28528
|
label,
|
|
28265
28529
|
data,
|
|
28266
|
-
borderColor:
|
|
28267
|
-
borderWidth: 1,
|
|
28530
|
+
borderColor: definition.background || BACKGROUND_CHART_COLOR,
|
|
28531
|
+
borderWidth: definition.stacked ? 1 : 0,
|
|
28268
28532
|
backgroundColor,
|
|
28269
28533
|
yAxisID: definition.horizontal ? "y" : definition.dataSets?.[index].yAxisId || "y",
|
|
28270
28534
|
xAxisID: "x",
|
|
@@ -28817,20 +29081,27 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
28817
29081
|
return scales;
|
|
28818
29082
|
}
|
|
28819
29083
|
function getPyramidChartScales(definition, args) {
|
|
29084
|
+
const { dataSetsValues } = args;
|
|
28820
29085
|
const scales = getBarChartScales(definition, args);
|
|
28821
29086
|
const scalesXCallback = scales.x.ticks.callback;
|
|
28822
29087
|
scales.x.ticks.callback = (value) => scalesXCallback(Math.abs(value));
|
|
29088
|
+
const maxValue = Math.max(...dataSetsValues.map((dataSet) => Math.max(...dataSet.data.map(Math.abs))));
|
|
29089
|
+
scales.x.suggestedMin = -maxValue;
|
|
29090
|
+
scales.x.suggestedMax = maxValue;
|
|
28823
29091
|
return scales;
|
|
28824
29092
|
}
|
|
28825
29093
|
function getRadarChartScales(definition, args) {
|
|
28826
|
-
const { locale, axisFormats } = args;
|
|
29094
|
+
const { locale, axisFormats, dataSetsValues } = args;
|
|
29095
|
+
const minValue = Math.min(...dataSetsValues.map((ds) => Math.min(...ds.data.filter((x) => !isNaN(x)))));
|
|
28827
29096
|
return {
|
|
28828
29097
|
r: {
|
|
29098
|
+
beginAtZero: true,
|
|
28829
29099
|
ticks: {
|
|
28830
29100
|
callback: formatTickValue({ format: axisFormats?.r, locale }),
|
|
28831
29101
|
backdropColor: definition.background || "#FFFFFF",
|
|
28832
29102
|
},
|
|
28833
29103
|
pointLabels: { color: chartFontColor(definition.background) },
|
|
29104
|
+
suggestedMin: minValue < 0 ? minValue - 1 : 0,
|
|
28834
29105
|
},
|
|
28835
29106
|
};
|
|
28836
29107
|
}
|
|
@@ -31931,14 +32202,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
31931
32202
|
}
|
|
31932
32203
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
31933
32204
|
const contentZone = { ...tableZone, top: tableZone.top + 1 };
|
|
31934
|
-
|
|
31935
|
-
|
|
31936
|
-
|
|
31937
|
-
row: contentZone.top,
|
|
31938
|
-
zone: contentZone,
|
|
31939
|
-
sortDirection,
|
|
31940
|
-
sortOptions: { emptyCellAsZero: true, sortHeaders: true },
|
|
31941
|
-
});
|
|
32205
|
+
const sortAnchor = { col: filterPosition.col, row: contentZone.top };
|
|
32206
|
+
const sortOptions = { emptyCellAsZero: true, sortHeaders: true };
|
|
32207
|
+
interactiveSort(this.env, sheetId, sortAnchor, contentZone, sortDirection, sortOptions);
|
|
31942
32208
|
this.props.onClosed?.();
|
|
31943
32209
|
}
|
|
31944
32210
|
}
|
|
@@ -32936,13 +33202,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
32936
33202
|
const osClipboard = await env.clipboard.read();
|
|
32937
33203
|
switch (osClipboard.status) {
|
|
32938
33204
|
case "ok":
|
|
32939
|
-
const
|
|
32940
|
-
const
|
|
32941
|
-
const clipboardId = JSON.parse(osClipboardSpreadsheetContent).clipboardId ??
|
|
32942
|
-
htmlDocument.querySelector("div")?.getAttribute("data-clipboard-id");
|
|
33205
|
+
const clipboardContent = parseOSClipboardContent(osClipboard.content);
|
|
33206
|
+
const clipboardId = clipboardContent.data?.clipboardId;
|
|
32943
33207
|
const target = env.model.getters.getSelectedZones();
|
|
32944
33208
|
if (env.model.getters.getClipboardId() !== clipboardId) {
|
|
32945
|
-
interactivePasteFromOS(env, target,
|
|
33209
|
+
interactivePasteFromOS(env, target, clipboardContent, pasteOption);
|
|
32946
33210
|
}
|
|
32947
33211
|
else {
|
|
32948
33212
|
interactivePaste(env, target, pasteOption);
|
|
@@ -33956,6 +34220,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
33956
34220
|
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
33957
34221
|
return (pivotId && env.model.getters.isExistingPivot(pivotId)) || false;
|
|
33958
34222
|
},
|
|
34223
|
+
isReadonlyAllowed: true,
|
|
33959
34224
|
icon: "o-spreadsheet-Icon.PIVOT",
|
|
33960
34225
|
};
|
|
33961
34226
|
const FIX_FORMULAS = {
|
|
@@ -35673,6 +35938,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
35673
35938
|
id: `item_pivot_${env.model.getters.getPivotFormulaId(pivotId)}`,
|
|
35674
35939
|
name: env.model.getters.getPivotDisplayName(pivotId),
|
|
35675
35940
|
sequence: sequence + index,
|
|
35941
|
+
isReadonlyAllowed: true,
|
|
35676
35942
|
execute: (env) => env.openSidePanel("PivotSidePanel", { pivotId }),
|
|
35677
35943
|
onStartHover: (env) => env.getStore(HighlightStore).register(highlightProvider),
|
|
35678
35944
|
onStopHover: (env) => env.getStore(HighlightStore).unRegister(highlightProvider),
|
|
@@ -36162,7 +36428,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36162
36428
|
flex-grow: 0;
|
|
36163
36429
|
}
|
|
36164
36430
|
|
|
36165
|
-
|
|
36431
|
+
/* Make the character a bit bigger
|
|
36166
36432
|
compared to its neighbor INPUT box */
|
|
36167
36433
|
.o-remove-selection {
|
|
36168
36434
|
font-size: calc(100% + 4px);
|
|
@@ -36767,7 +37033,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36767
37033
|
css /* scss */ `
|
|
36768
37034
|
.o-color-picker {
|
|
36769
37035
|
padding: ${PICKER_PADDING}px 0;
|
|
36770
|
-
|
|
37036
|
+
/* FIXME: this is useless, overiden by the popover container */
|
|
36771
37037
|
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
|
|
36772
37038
|
background-color: white;
|
|
36773
37039
|
line-height: 1.2;
|
|
@@ -36910,7 +37176,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36910
37176
|
margin-right: 2px;
|
|
36911
37177
|
}
|
|
36912
37178
|
.o-wrong-color {
|
|
36913
|
-
|
|
37179
|
+
/* FIXME bootstrap class instead? */
|
|
36914
37180
|
outline-color: red;
|
|
36915
37181
|
border-color: red;
|
|
36916
37182
|
&:focus {
|
|
@@ -38954,10 +39220,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
38954
39220
|
}
|
|
38955
39221
|
|
|
38956
39222
|
.fa-stack {
|
|
38957
|
-
|
|
38958
|
-
width:
|
|
38959
|
-
height:
|
|
38960
|
-
line-height:
|
|
39223
|
+
/* reset stack size which is doubled by default */
|
|
39224
|
+
width: ${CLOSE_ICON_RADIUS * 2}px;
|
|
39225
|
+
height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
39226
|
+
line-height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
38961
39227
|
}
|
|
38962
39228
|
|
|
38963
39229
|
.force-open-assistant {
|
|
@@ -38974,7 +39240,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
38974
39240
|
margin: 1px 4px;
|
|
38975
39241
|
|
|
38976
39242
|
.o-semi-bold {
|
|
38977
|
-
|
|
39243
|
+
/* FIXME: to remove in favor of Bootstrap
|
|
38978
39244
|
* 'fw-semibold' when we upgrade to Bootstrap 5.2
|
|
38979
39245
|
*/
|
|
38980
39246
|
font-weight: 600 !important;
|
|
@@ -40210,7 +40476,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
40210
40476
|
.o-threshold-value {
|
|
40211
40477
|
flex-grow: 1;
|
|
40212
40478
|
flex-basis: 60%;
|
|
40213
|
-
min-width: 0px;
|
|
40479
|
+
min-width: 0px; /* input overflows in Firefox otherwise */
|
|
40214
40480
|
}
|
|
40215
40481
|
.o-threshold-value input:disabled {
|
|
40216
40482
|
background-color: #edebed;
|
|
@@ -41029,7 +41295,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41029
41295
|
name: _t("Date is"),
|
|
41030
41296
|
getPreview: (criterion, getters) => {
|
|
41031
41297
|
return criterion.dateValue === "exactDate"
|
|
41032
|
-
? _t("Date is %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41298
|
+
? _t("Date is %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41033
41299
|
: _t("Date is %s", DVTerms.DateIs[criterion.dateValue]);
|
|
41034
41300
|
},
|
|
41035
41301
|
});
|
|
@@ -41054,7 +41320,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41054
41320
|
name: _t("Date is before"),
|
|
41055
41321
|
getPreview: (criterion, getters) => {
|
|
41056
41322
|
return criterion.dateValue === "exactDate"
|
|
41057
|
-
? _t("Date is before %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41323
|
+
? _t("Date is before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41058
41324
|
: _t("Date is before %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41059
41325
|
},
|
|
41060
41326
|
});
|
|
@@ -41079,7 +41345,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41079
41345
|
name: _t("Date is on or before"),
|
|
41080
41346
|
getPreview: (criterion, getters) => {
|
|
41081
41347
|
return criterion.dateValue === "exactDate"
|
|
41082
|
-
? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41348
|
+
? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41083
41349
|
: _t("Date is on or before %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41084
41350
|
},
|
|
41085
41351
|
});
|
|
@@ -41104,7 +41370,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41104
41370
|
name: _t("Date is after"),
|
|
41105
41371
|
getPreview: (criterion, getters) => {
|
|
41106
41372
|
return criterion.dateValue === "exactDate"
|
|
41107
|
-
? _t("Date is after %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41373
|
+
? _t("Date is after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41108
41374
|
: _t("Date is after %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41109
41375
|
},
|
|
41110
41376
|
});
|
|
@@ -41129,7 +41395,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41129
41395
|
name: _t("Date is on or after"),
|
|
41130
41396
|
getPreview: (criterion, getters) => {
|
|
41131
41397
|
return criterion.dateValue === "exactDate"
|
|
41132
|
-
? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41398
|
+
? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41133
41399
|
: _t("Date is on or after %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41134
41400
|
},
|
|
41135
41401
|
});
|
|
@@ -41155,7 +41421,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41155
41421
|
numberOfValues: () => 2,
|
|
41156
41422
|
name: _t("Date is between"),
|
|
41157
41423
|
getPreview: (criterion, getters) => {
|
|
41158
|
-
const values = getDateCriterionFormattedValues(criterion, getters);
|
|
41424
|
+
const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
|
|
41159
41425
|
return _t("Date is between %s and %s", values[0], values[1]);
|
|
41160
41426
|
},
|
|
41161
41427
|
});
|
|
@@ -41181,7 +41447,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41181
41447
|
numberOfValues: () => 2,
|
|
41182
41448
|
name: _t("Date is not between"),
|
|
41183
41449
|
getPreview: (criterion, getters) => {
|
|
41184
|
-
const values = getDateCriterionFormattedValues(criterion, getters);
|
|
41450
|
+
const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
|
|
41185
41451
|
return _t("Date is not between %s and %s", values[0], values[1]);
|
|
41186
41452
|
},
|
|
41187
41453
|
});
|
|
@@ -41464,19 +41730,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41464
41730
|
const valueAsNumber = tryToNumber(value, DEFAULT_LOCALE);
|
|
41465
41731
|
return valueAsNumber !== undefined;
|
|
41466
41732
|
}
|
|
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
41733
|
|
|
41481
41734
|
/** 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
41735
|
class SelectMenu extends owl.Component {
|
|
@@ -41558,6 +41811,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41558
41811
|
focused: false,
|
|
41559
41812
|
onBlur: () => { },
|
|
41560
41813
|
};
|
|
41814
|
+
static components = { StandaloneComposer: StandaloneComposer };
|
|
41561
41815
|
inputRef = owl.useRef("input");
|
|
41562
41816
|
setup() {
|
|
41563
41817
|
owl.useEffect(() => {
|
|
@@ -41569,10 +41823,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41569
41823
|
state = owl.useState({
|
|
41570
41824
|
shouldDisplayError: !!this.props.value, // Don't display error if user inputted nothing yet
|
|
41571
41825
|
});
|
|
41572
|
-
onValueChanged(ev) {
|
|
41573
|
-
this.state.shouldDisplayError = true;
|
|
41574
|
-
this.props.onValueChanged(ev.target.value);
|
|
41575
|
-
}
|
|
41576
41826
|
get placeholder() {
|
|
41577
41827
|
const evaluator = dataValidationEvaluatorRegistry.get(this.props.criterionType);
|
|
41578
41828
|
if (evaluator.allowedValues === "onlyFormulas") {
|
|
@@ -41583,6 +41833,27 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41583
41833
|
}
|
|
41584
41834
|
return _t("Value or formula");
|
|
41585
41835
|
}
|
|
41836
|
+
get allowedValues() {
|
|
41837
|
+
const evaluator = dataValidationEvaluatorRegistry.get(this.props.criterionType);
|
|
41838
|
+
return evaluator.allowedValues ?? "any";
|
|
41839
|
+
}
|
|
41840
|
+
onInputValueChanged(ev) {
|
|
41841
|
+
this.state.shouldDisplayError = true;
|
|
41842
|
+
this.props.onValueChanged(ev.target.value);
|
|
41843
|
+
}
|
|
41844
|
+
onChangeComposerValue(str) {
|
|
41845
|
+
this.state.shouldDisplayError = true;
|
|
41846
|
+
this.props.onValueChanged(str);
|
|
41847
|
+
}
|
|
41848
|
+
getDataValidationRuleInputComposerProps() {
|
|
41849
|
+
return {
|
|
41850
|
+
onConfirm: (str) => this.onChangeComposerValue(str),
|
|
41851
|
+
composerContent: this.props.value,
|
|
41852
|
+
placeholder: this.placeholder,
|
|
41853
|
+
class: "o-sidePanel-composer",
|
|
41854
|
+
defaultRangeSheetId: this.env.model.getters.getActiveSheetId(),
|
|
41855
|
+
};
|
|
41856
|
+
}
|
|
41586
41857
|
get errorMessage() {
|
|
41587
41858
|
if (!this.state.shouldDisplayError) {
|
|
41588
41859
|
return undefined;
|
|
@@ -41918,13 +42189,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41918
42189
|
|
|
41919
42190
|
class DataValidationEditor extends owl.Component {
|
|
41920
42191
|
static template = "o-spreadsheet-DataValidationEditor";
|
|
41921
|
-
static components = { SelectionInput, SelectMenu, Section };
|
|
42192
|
+
static components = { SelectionInput, SelectMenu, Section, ValidationMessages };
|
|
41922
42193
|
static props = {
|
|
41923
42194
|
rule: { type: Object, optional: true },
|
|
41924
42195
|
onExit: Function,
|
|
41925
42196
|
onCloseSidePanel: { type: Function, optional: true },
|
|
41926
42197
|
};
|
|
41927
|
-
state = owl.useState({ rule: this.defaultDataValidationRule });
|
|
42198
|
+
state = owl.useState({ rule: this.defaultDataValidationRule, errors: [] });
|
|
41928
42199
|
setup() {
|
|
41929
42200
|
if (this.props.rule) {
|
|
41930
42201
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
@@ -41949,15 +42220,15 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41949
42220
|
this.state.rule.isBlocking = isBlocking === "true";
|
|
41950
42221
|
}
|
|
41951
42222
|
onSave() {
|
|
41952
|
-
if (
|
|
41953
|
-
|
|
42223
|
+
if (this.state.rule) {
|
|
42224
|
+
const result = this.env.model.dispatch("ADD_DATA_VALIDATION_RULE", this.dispatchPayload);
|
|
42225
|
+
if (!result.isSuccessful) {
|
|
42226
|
+
this.state.errors = result.reasons;
|
|
42227
|
+
}
|
|
42228
|
+
else {
|
|
42229
|
+
this.props.onExit();
|
|
42230
|
+
}
|
|
41954
42231
|
}
|
|
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
42232
|
}
|
|
41962
42233
|
get dispatchPayload() {
|
|
41963
42234
|
const rule = { ...this.state.rule, ranges: undefined };
|
|
@@ -41998,6 +42269,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
41998
42269
|
get criterionComponent() {
|
|
41999
42270
|
return dataValidationPanelCriteriaRegistry.get(this.state.rule.criterion.type).component;
|
|
42000
42271
|
}
|
|
42272
|
+
get errorMessages() {
|
|
42273
|
+
return this.state.errors.map((error) => DVTerms.Errors[error] || DVTerms.Errors.Unexpected);
|
|
42274
|
+
}
|
|
42001
42275
|
}
|
|
42002
42276
|
|
|
42003
42277
|
css /* scss */ `
|
|
@@ -42009,7 +42283,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
42009
42283
|
border-bottom: 1px solid ${FIGURE_BORDER_COLOR};
|
|
42010
42284
|
|
|
42011
42285
|
.o-dv-container {
|
|
42012
|
-
min-width: 0;
|
|
42286
|
+
min-width: 0; /* otherwise flex won't shrink correctly */
|
|
42013
42287
|
}
|
|
42014
42288
|
|
|
42015
42289
|
.o-dv-preview-description {
|
|
@@ -45027,6 +45301,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45027
45301
|
PivotLayoutConfigurator,
|
|
45028
45302
|
Section,
|
|
45029
45303
|
};
|
|
45304
|
+
setup() {
|
|
45305
|
+
useHighlights(this);
|
|
45306
|
+
}
|
|
45030
45307
|
get sidePanelEditor() {
|
|
45031
45308
|
const pivot = this.env.model.getters.getPivotCoreDefinition(this.props.pivotId);
|
|
45032
45309
|
if (!pivot) {
|
|
@@ -45034,6 +45311,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45034
45311
|
}
|
|
45035
45312
|
return pivotSidePanelRegistry.get(pivot.type).editor;
|
|
45036
45313
|
}
|
|
45314
|
+
get highlights() {
|
|
45315
|
+
return getPivotHighlights(this.env.model.getters, this.props.pivotId);
|
|
45316
|
+
}
|
|
45037
45317
|
}
|
|
45038
45318
|
|
|
45039
45319
|
css /* scss */ `
|
|
@@ -45703,7 +45983,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45703
45983
|
|
|
45704
45984
|
css /* scss */ `
|
|
45705
45985
|
.o-table-style-popover {
|
|
45706
|
-
|
|
45986
|
+
/* 7 tables preview + padding by line */
|
|
45707
45987
|
width: calc((66px + 4px * 2) * 7 + 1.5rem * 2);
|
|
45708
45988
|
background: #fff;
|
|
45709
45989
|
font-size: 14px;
|
|
@@ -47255,7 +47535,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47255
47535
|
box-sizing: border-box !important;
|
|
47256
47536
|
accent-color: #808080;
|
|
47257
47537
|
margin: ${MARGIN}px;
|
|
47258
|
-
|
|
47538
|
+
/* required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
|
|
47259
47539
|
position: absolute;
|
|
47260
47540
|
}
|
|
47261
47541
|
`;
|
|
@@ -47602,7 +47882,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47602
47882
|
}
|
|
47603
47883
|
}
|
|
47604
47884
|
.o-figure-container {
|
|
47605
|
-
-webkit-user-select: none;
|
|
47885
|
+
-webkit-user-select: none; /* safari */
|
|
47606
47886
|
user-select: none;
|
|
47607
47887
|
}
|
|
47608
47888
|
`;
|
|
@@ -50675,25 +50955,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
50675
50955
|
if (!clipboardData) {
|
|
50676
50956
|
return;
|
|
50677
50957
|
}
|
|
50678
|
-
const
|
|
50679
|
-
|
|
50680
|
-
|
|
50681
|
-
|
|
50958
|
+
const osClipboard = {
|
|
50959
|
+
content: {
|
|
50960
|
+
[ClipboardMIMEType.PlainText]: clipboardData?.getData(ClipboardMIMEType.PlainText),
|
|
50961
|
+
[ClipboardMIMEType.Html]: clipboardData?.getData(ClipboardMIMEType.Html),
|
|
50962
|
+
},
|
|
50963
|
+
};
|
|
50682
50964
|
const target = this.env.model.getters.getSelectedZones();
|
|
50683
50965
|
const isCutOperation = this.env.model.getters.isCutOperation();
|
|
50684
|
-
const
|
|
50685
|
-
|
|
50966
|
+
const clipboardContent = parseOSClipboardContent(osClipboard.content);
|
|
50967
|
+
const clipboardId = clipboardContent.data?.clipboardId;
|
|
50686
50968
|
if (this.env.model.getters.getClipboardId() === clipboardId) {
|
|
50687
50969
|
interactivePaste(this.env, target);
|
|
50688
50970
|
}
|
|
50689
50971
|
else {
|
|
50690
|
-
const clipboardContent = {
|
|
50691
|
-
[ClipboardMIMEType.PlainText]: clipboardDataTextContent,
|
|
50692
|
-
[ClipboardMIMEType.Html]: clipboardDataHtmlContent,
|
|
50693
|
-
};
|
|
50694
|
-
if (osClipboardSpreadsheetContent !== "{}") {
|
|
50695
|
-
clipboardContent[ClipboardMIMEType.OSpreadsheet] = osClipboardSpreadsheetContent;
|
|
50696
|
-
}
|
|
50697
50972
|
interactivePasteFromOS(this.env, target, clipboardContent);
|
|
50698
50973
|
}
|
|
50699
50974
|
if (isCutOperation) {
|
|
@@ -52513,8 +52788,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52513
52788
|
if (replaceIndex > -1) {
|
|
52514
52789
|
currentRanges = rules[replaceIndex].ranges.map(toUnboundedZone);
|
|
52515
52790
|
}
|
|
52516
|
-
|
|
52517
|
-
|
|
52791
|
+
// Remove the zones first in case the same position is in toAdd and toRemove
|
|
52792
|
+
const withRemovedZones = recomputeZones(currentRanges, toRemove);
|
|
52793
|
+
return recomputeZones([...toAdd, ...withRemovedZones], []).map((zone) => this.getters.getRangeDataFromZone(sheetId, zone));
|
|
52518
52794
|
}
|
|
52519
52795
|
// ---------------------------------------------------------------------------
|
|
52520
52796
|
// Private
|
|
@@ -52618,8 +52894,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52618
52894
|
case "IconSetRule": {
|
|
52619
52895
|
return this.checkValidations(rule, this.chainValidations(this.checkInflectionPoints(this.checkNaN), this.checkLowerBiggerThanUpper), this.chainValidations(this.checkInflectionPoints(this.checkFormulaCompilation)));
|
|
52620
52896
|
}
|
|
52621
|
-
case "DataBarRule":
|
|
52622
|
-
return this.checkDataBarRangeValues(rule, cmd.ranges, cmd.sheetId);
|
|
52623
52897
|
}
|
|
52624
52898
|
return "Success" /* CommandResult.Success */;
|
|
52625
52899
|
}
|
|
@@ -52750,18 +53024,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52750
53024
|
}
|
|
52751
53025
|
return "Success" /* CommandResult.Success */;
|
|
52752
53026
|
}
|
|
52753
|
-
checkDataBarRangeValues(rule, ranges, sheetId) {
|
|
52754
|
-
if (rule.rangeValues) {
|
|
52755
|
-
const { numberOfCols, numberOfRows } = zoneToDimension(this.getters.getRangeFromSheetXC(sheetId, rule.rangeValues).zone);
|
|
52756
|
-
for (const range of ranges) {
|
|
52757
|
-
const dimensions = zoneToDimension(this.getters.getRangeFromRangeData(range).zone);
|
|
52758
|
-
if (numberOfCols !== dimensions.numberOfCols || numberOfRows !== dimensions.numberOfRows) {
|
|
52759
|
-
return "DataBarRangeValuesMismatch" /* CommandResult.DataBarRangeValuesMismatch */;
|
|
52760
|
-
}
|
|
52761
|
-
}
|
|
52762
|
-
}
|
|
52763
|
-
return "Success" /* CommandResult.Success */;
|
|
52764
|
-
}
|
|
52765
53027
|
removeConditionalFormatting(id, sheet) {
|
|
52766
53028
|
const cfIndex = this.cfRules[sheet].findIndex((s) => s.id === id);
|
|
52767
53029
|
if (cfIndex !== -1) {
|
|
@@ -52825,7 +53087,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52825
53087
|
allowDispatch(cmd) {
|
|
52826
53088
|
switch (cmd.type) {
|
|
52827
53089
|
case "ADD_DATA_VALIDATION_RULE":
|
|
52828
|
-
return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
|
|
53090
|
+
return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkValidRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
|
|
52829
53091
|
case "REMOVE_DATA_VALIDATION_RULE":
|
|
52830
53092
|
if (!this.rules[cmd.sheetId].find((rule) => rule.id === cmd.id)) {
|
|
52831
53093
|
return "UnknownDataValidationRule" /* CommandResult.UnknownDataValidationRule */;
|
|
@@ -52984,6 +53246,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
52984
53246
|
}
|
|
52985
53247
|
}
|
|
52986
53248
|
}
|
|
53249
|
+
exportForExcel(data) {
|
|
53250
|
+
if (!data.sheets) {
|
|
53251
|
+
return;
|
|
53252
|
+
}
|
|
53253
|
+
for (const sheet of data.sheets) {
|
|
53254
|
+
sheet.dataValidationRules = [];
|
|
53255
|
+
for (const rule of this.rules[sheet.id]) {
|
|
53256
|
+
sheet.dataValidationRules.push({
|
|
53257
|
+
...rule,
|
|
53258
|
+
ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useFixedReference: true })),
|
|
53259
|
+
});
|
|
53260
|
+
}
|
|
53261
|
+
}
|
|
53262
|
+
}
|
|
52987
53263
|
checkCriterionTypeIsValid(cmd) {
|
|
52988
53264
|
return dataValidationEvaluatorRegistry.contains(cmd.rule.criterion.type)
|
|
52989
53265
|
? "Success" /* CommandResult.Success */
|
|
@@ -53002,21 +53278,28 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
53002
53278
|
checkCriterionValuesAreValid(cmd) {
|
|
53003
53279
|
const criterion = cmd.rule.criterion;
|
|
53004
53280
|
const evaluator = dataValidationEvaluatorRegistry.get(criterion.type);
|
|
53005
|
-
|
|
53006
|
-
if (value.startsWith("=")) {
|
|
53007
|
-
return evaluator.allowedValues === "onlyLiterals";
|
|
53008
|
-
}
|
|
53009
|
-
else if (evaluator.allowedValues === "onlyFormulas") {
|
|
53281
|
+
const isInvalid = (value) => {
|
|
53282
|
+
if (evaluator.allowedValues === "onlyFormulas" && !value.startsWith("=")) {
|
|
53010
53283
|
return true;
|
|
53011
53284
|
}
|
|
53012
|
-
|
|
53013
|
-
return
|
|
53285
|
+
if (value.startsWith("=")) {
|
|
53286
|
+
return evaluator.allowedValues === "onlyLiterals" || compile(value).isBadExpression;
|
|
53014
53287
|
}
|
|
53015
|
-
|
|
53288
|
+
return !evaluator.isCriterionValueValid(value);
|
|
53289
|
+
};
|
|
53290
|
+
if (criterion.values.some(isInvalid)) {
|
|
53016
53291
|
return "InvalidDataValidationCriterionValue" /* CommandResult.InvalidDataValidationCriterionValue */;
|
|
53017
53292
|
}
|
|
53018
53293
|
return "Success" /* CommandResult.Success */;
|
|
53019
53294
|
}
|
|
53295
|
+
checkValidRange(cmd) {
|
|
53296
|
+
const ranges = cmd.ranges.map((range) => this.getters.getRangeFromRangeData(range));
|
|
53297
|
+
const stringRanges = ranges.map((range) => this.getters.getRangeString(range, cmd.sheetId));
|
|
53298
|
+
if (stringRanges.some((xc) => !this.getters.isRangeValid(xc))) {
|
|
53299
|
+
return "InvalidRange" /* CommandResult.InvalidRange */;
|
|
53300
|
+
}
|
|
53301
|
+
return "Success" /* CommandResult.Success */;
|
|
53302
|
+
}
|
|
53020
53303
|
}
|
|
53021
53304
|
|
|
53022
53305
|
class FigurePlugin extends CorePlugin {
|
|
@@ -54751,6 +55034,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
54751
55034
|
formats: {},
|
|
54752
55035
|
borders: {},
|
|
54753
55036
|
conditionalFormats: [],
|
|
55037
|
+
dataValidationRules: [],
|
|
54754
55038
|
figures: [],
|
|
54755
55039
|
tables: [],
|
|
54756
55040
|
areGridLinesVisible: sheet.areGridLinesVisible === undefined ? true : sheet.areGridLinesVisible,
|
|
@@ -58120,6 +58404,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
58120
58404
|
}
|
|
58121
58405
|
for (let i = 0; i < positions.length; ++i) {
|
|
58122
58406
|
const position = positions[i];
|
|
58407
|
+
if (this.nextPositionsToUpdate.has(position)) {
|
|
58408
|
+
continue;
|
|
58409
|
+
}
|
|
58123
58410
|
const evaluatedCell = this.computeCell(position);
|
|
58124
58411
|
if (evaluatedCell !== EMPTY_CELL) {
|
|
58125
58412
|
this.evaluatedCells.set(position, evaluatedCell);
|
|
@@ -58127,6 +58414,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
58127
58414
|
}
|
|
58128
58415
|
onIterationEndEvaluationRegistry.getAll().forEach((callback) => callback(this.getters));
|
|
58129
58416
|
}
|
|
58417
|
+
if (currentIteration >= MAX_ITERATION) {
|
|
58418
|
+
console.warn("Maximum iteration reached while evaluating cells");
|
|
58419
|
+
}
|
|
58130
58420
|
}
|
|
58131
58421
|
computeCell(position) {
|
|
58132
58422
|
const evaluation = this.evaluatedCells.get(position);
|
|
@@ -58161,7 +58451,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
58161
58451
|
}
|
|
58162
58452
|
finally {
|
|
58163
58453
|
this.cellsBeingComputed.delete(cellId);
|
|
58164
|
-
this.nextPositionsToUpdate.delete(position);
|
|
58165
58454
|
}
|
|
58166
58455
|
}
|
|
58167
58456
|
computeAndSave(position) {
|
|
@@ -58197,6 +58486,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
58197
58486
|
invalidatePositionsDependingOnSpread(sheetId, resultZone) {
|
|
58198
58487
|
// the result matrix is split in 2 zones to exclude the array formula position
|
|
58199
58488
|
const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
|
|
58489
|
+
invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
|
|
58200
58490
|
this.nextPositionsToUpdate.addMany(invalidatedPositions);
|
|
58201
58491
|
}
|
|
58202
58492
|
assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
|
|
@@ -59138,8 +59428,12 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59138
59428
|
const zoneOfValues = rangeValues.zone;
|
|
59139
59429
|
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
59140
59430
|
for (let col = zone.left; col <= zone.right; col++) {
|
|
59141
|
-
const
|
|
59142
|
-
|
|
59431
|
+
const targetCol = col - zone.left + zoneOfValues.left;
|
|
59432
|
+
const targetRow = row - zone.top + zoneOfValues.top;
|
|
59433
|
+
const cell = this.getters.getEvaluatedCell({ sheetId, col: targetCol, row: targetRow });
|
|
59434
|
+
if (!isInside(targetCol, targetRow, zoneOfValues) ||
|
|
59435
|
+
cell.type !== CellValueType.number ||
|
|
59436
|
+
cell.value <= 0) {
|
|
59143
59437
|
// values negatives or 0 are ignored
|
|
59144
59438
|
continue;
|
|
59145
59439
|
}
|
|
@@ -59152,11 +59446,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59152
59446
|
}
|
|
59153
59447
|
}
|
|
59154
59448
|
}
|
|
59155
|
-
getEvaluatedCellInZone(sheetId, zone, col, row, targetZone) {
|
|
59156
|
-
const targetCol = col - zone.left + targetZone.left;
|
|
59157
|
-
const targetRow = row - zone.top + targetZone.top;
|
|
59158
|
-
return this.getters.getEvaluatedCell({ sheetId, col: targetCol, row: targetRow });
|
|
59159
|
-
}
|
|
59160
59449
|
/** Compute the color scale for the given range and CF rule, and apply in in the given computedStyle object */
|
|
59161
59450
|
applyColorScale(sheetId, range, rule, computedStyle) {
|
|
59162
59451
|
const minValue = this.parsePoint(sheetId, range, rule.minimum, "min");
|
|
@@ -59320,25 +59609,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59320
59609
|
}
|
|
59321
59610
|
switch (cmd.type) {
|
|
59322
59611
|
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
59612
|
case "REMOVE_DATA_VALIDATION_RULE":
|
|
59330
59613
|
delete this.validationResults[cmd.sheetId];
|
|
59331
59614
|
break;
|
|
59332
59615
|
}
|
|
59333
59616
|
}
|
|
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
59617
|
isDataValidationInvalid(cellPosition) {
|
|
59343
59618
|
return !this.getValidationResultForCell(cellPosition).isValid;
|
|
59344
59619
|
}
|
|
@@ -59354,9 +59629,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59354
59629
|
getDataValidationInvalidCriterionValueMessage(criterionType, value) {
|
|
59355
59630
|
const evaluator = dataValidationEvaluatorRegistry.get(criterionType);
|
|
59356
59631
|
if (value.startsWith("=")) {
|
|
59357
|
-
|
|
59358
|
-
|
|
59359
|
-
|
|
59632
|
+
if (evaluator.allowedValues === "onlyLiterals") {
|
|
59633
|
+
return _t("The value must not be a formula");
|
|
59634
|
+
}
|
|
59635
|
+
return this.isValidFormula(value) ? undefined : DVTerms.CriterionError.validFormula;
|
|
59360
59636
|
}
|
|
59361
59637
|
else if (evaluator.allowedValues === "onlyFormulas") {
|
|
59362
59638
|
return _t("The value must be a formula");
|
|
@@ -59382,6 +59658,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59382
59658
|
const error = this.getRuleErrorForCellValue(cellValue, cellPosition, rule);
|
|
59383
59659
|
return error ? { error, rule, isValid: false } : VALID_RESULT;
|
|
59384
59660
|
}
|
|
59661
|
+
isValidFormula(value) {
|
|
59662
|
+
return !compile(value).isBadExpression;
|
|
59663
|
+
}
|
|
59385
59664
|
getValidationResultForCell(cellPosition) {
|
|
59386
59665
|
const { col, row, sheetId } = cellPosition;
|
|
59387
59666
|
if (!this.validationResults[sheetId]) {
|
|
@@ -61817,7 +62096,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
61817
62096
|
* Notify the server that the user client left the collaborative session
|
|
61818
62097
|
*/
|
|
61819
62098
|
async leave(data) {
|
|
61820
|
-
if (Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
|
|
62099
|
+
if (data && Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
|
|
61821
62100
|
await this.snapshot(data());
|
|
61822
62101
|
}
|
|
61823
62102
|
delete this.clients[this.clientId];
|
|
@@ -62744,9 +63023,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
62744
63023
|
switch (cmd.type) {
|
|
62745
63024
|
case "SORT_CELLS":
|
|
62746
63025
|
if (!isInside(cmd.col, cmd.row, cmd.zone)) {
|
|
62747
|
-
|
|
63026
|
+
return "InvalidSortAnchor" /* CommandResult.InvalidSortAnchor */;
|
|
62748
63027
|
}
|
|
62749
|
-
return this.checkValidations(cmd, this.checkMerge, this.checkMergeSizes);
|
|
63028
|
+
return this.checkValidations(cmd, this.checkMerge, this.checkMergeSizes, this.checkArrayFormulaInSortZone);
|
|
62750
63029
|
}
|
|
62751
63030
|
return "Success" /* CommandResult.Success */;
|
|
62752
63031
|
}
|
|
@@ -62787,6 +63066,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
62787
63066
|
}
|
|
62788
63067
|
return "Success" /* CommandResult.Success */;
|
|
62789
63068
|
}
|
|
63069
|
+
checkArrayFormulaInSortZone({ sheetId, zone }) {
|
|
63070
|
+
const arrayFormulaInZone = positions(zone).some(({ col, row }) => this.getters.getArrayFormulaSpreadingOn({ sheetId, col, row }));
|
|
63071
|
+
return arrayFormulaInZone ? "SortZoneWithArrayFormulas" /* CommandResult.SortZoneWithArrayFormulas */ : "Success" /* CommandResult.Success */;
|
|
63072
|
+
}
|
|
62790
63073
|
/**
|
|
62791
63074
|
* This function evaluates if the top row of a provided zone can be considered as a `header`
|
|
62792
63075
|
* by checking the following criteria:
|
|
@@ -63369,6 +63652,42 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
63369
63652
|
}
|
|
63370
63653
|
}
|
|
63371
63654
|
|
|
63655
|
+
class DataValidationInsertionPlugin extends UIPlugin {
|
|
63656
|
+
handle(cmd) {
|
|
63657
|
+
switch (cmd.type) {
|
|
63658
|
+
case "ADD_DATA_VALIDATION_RULE":
|
|
63659
|
+
if (cmd.rule.criterion.type === "isBoolean") {
|
|
63660
|
+
const ranges = cmd.ranges.map((range) => this.getters.getRangeFromRangeData(range));
|
|
63661
|
+
for (const position of getCellPositionsInRanges(ranges)) {
|
|
63662
|
+
const cell = this.getters.getCell(position);
|
|
63663
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
63664
|
+
if (!cell?.content) {
|
|
63665
|
+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
63666
|
+
// In this case, a cell has been updated in the core plugin but
|
|
63667
|
+
// not yet evaluated. This can occur after a paste operation.
|
|
63668
|
+
}
|
|
63669
|
+
else if (cell?.content && evaluatedCell.type === CellValueType.empty) {
|
|
63670
|
+
let value;
|
|
63671
|
+
if (cell.content.startsWith("=")) {
|
|
63672
|
+
const result = this.getters.evaluateFormula(position.sheetId, cell.content);
|
|
63673
|
+
value = (isMatrix(result) ? result[0][0] : result)?.toString();
|
|
63674
|
+
}
|
|
63675
|
+
else {
|
|
63676
|
+
value = cell.content;
|
|
63677
|
+
}
|
|
63678
|
+
if (!value || !isBoolean(value)) {
|
|
63679
|
+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
63680
|
+
}
|
|
63681
|
+
}
|
|
63682
|
+
else if (evaluatedCell.type !== CellValueType.boolean) {
|
|
63683
|
+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
63684
|
+
}
|
|
63685
|
+
}
|
|
63686
|
+
}
|
|
63687
|
+
}
|
|
63688
|
+
}
|
|
63689
|
+
}
|
|
63690
|
+
|
|
63372
63691
|
const genericRepeatsTransforms = [
|
|
63373
63692
|
repeatSheetDependantCommand,
|
|
63374
63693
|
repeatTargetDependantCommand,
|
|
@@ -64022,7 +64341,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64022
64341
|
const zones = this.getters.getSelectedZones();
|
|
64023
64342
|
return this.isCutAllowedOn(zones);
|
|
64024
64343
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
64025
|
-
const copiedData = this.
|
|
64344
|
+
const copiedData = this.convertTextToClipboardData(cmd.clipboardContent.text ?? "");
|
|
64026
64345
|
const pasteOption = cmd.pasteOption;
|
|
64027
64346
|
return this.isPasteAllowed(cmd.target, copiedData, { pasteOption, isCutOperation: false });
|
|
64028
64347
|
}
|
|
@@ -64075,12 +64394,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64075
64394
|
break;
|
|
64076
64395
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
64077
64396
|
this._isCutOperation = false;
|
|
64078
|
-
|
|
64079
|
-
|
|
64080
|
-
|
|
64081
|
-
else {
|
|
64082
|
-
this.copiedData = this.convertOSClipboardData(cmd.clipboardContent[ClipboardMIMEType.PlainText] ?? "");
|
|
64083
|
-
}
|
|
64397
|
+
this.copiedData =
|
|
64398
|
+
cmd.clipboardContent.data ||
|
|
64399
|
+
this.convertTextToClipboardData(cmd.clipboardContent.text ?? "");
|
|
64084
64400
|
const pasteOption = cmd.pasteOption;
|
|
64085
64401
|
this.paste(cmd.target, this.copiedData, {
|
|
64086
64402
|
pasteOption,
|
|
@@ -64211,11 +64527,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64211
64527
|
}
|
|
64212
64528
|
}
|
|
64213
64529
|
}
|
|
64214
|
-
|
|
64530
|
+
convertTextToClipboardData(clipboardData) {
|
|
64215
64531
|
const handlers = this.selectClipboardHandlers({ figureId: true }).concat(this.selectClipboardHandlers({}));
|
|
64216
64532
|
let copiedData = {};
|
|
64217
64533
|
for (const { handlerName, handler } of handlers) {
|
|
64218
|
-
const data = handler.
|
|
64534
|
+
const data = handler.convertTextToClipboardData(clipboardData);
|
|
64219
64535
|
copiedData[handlerName] = data;
|
|
64220
64536
|
const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
|
|
64221
64537
|
for (const key of minimalKeys) {
|
|
@@ -64384,21 +64700,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64384
64700
|
return {
|
|
64385
64701
|
[ClipboardMIMEType.PlainText]: this.getPlainTextContent(),
|
|
64386
64702
|
[ClipboardMIMEType.Html]: this.getHTMLContent(),
|
|
64387
|
-
[ClipboardMIMEType.OSpreadsheet]: this.getSerializedGridData(),
|
|
64388
64703
|
};
|
|
64389
64704
|
}
|
|
64390
|
-
|
|
64705
|
+
getSheetData() {
|
|
64391
64706
|
const data = {
|
|
64392
64707
|
version: CURRENT_VERSION,
|
|
64393
64708
|
clipboardId: this.clipboardId,
|
|
64394
64709
|
};
|
|
64395
64710
|
if (this.copiedData && "figureId" in this.copiedData) {
|
|
64396
|
-
return
|
|
64711
|
+
return data;
|
|
64397
64712
|
}
|
|
64398
|
-
return
|
|
64713
|
+
return {
|
|
64399
64714
|
...data,
|
|
64400
64715
|
...this.copiedData,
|
|
64401
|
-
}
|
|
64716
|
+
};
|
|
64402
64717
|
}
|
|
64403
64718
|
getPlainTextContent() {
|
|
64404
64719
|
if (!this.copiedData?.cells) {
|
|
@@ -64415,31 +64730,36 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
64415
64730
|
.join("\n") || "\t");
|
|
64416
64731
|
}
|
|
64417
64732
|
getHTMLContent() {
|
|
64418
|
-
|
|
64419
|
-
|
|
64733
|
+
let innerHTML = "";
|
|
64734
|
+
const cells = this.copiedData?.cells;
|
|
64735
|
+
if (!cells) {
|
|
64736
|
+
innerHTML = "\t";
|
|
64420
64737
|
}
|
|
64421
|
-
|
|
64422
|
-
|
|
64423
|
-
return `<div data-clipboard-id="${this.clipboardId}">${this.getters.getCellText(cells[0][0].position)}</div>`;
|
|
64738
|
+
else if (cells.length === 1 && cells[0].length === 1) {
|
|
64739
|
+
innerHTML = `${this.getters.getCellText(cells[0][0].position)}`;
|
|
64424
64740
|
}
|
|
64425
|
-
if (!cells[0][0]) {
|
|
64741
|
+
else if (!cells[0][0]) {
|
|
64426
64742
|
return "";
|
|
64427
64743
|
}
|
|
64428
|
-
|
|
64429
|
-
|
|
64430
|
-
|
|
64431
|
-
|
|
64432
|
-
|
|
64433
|
-
|
|
64744
|
+
else {
|
|
64745
|
+
let htmlTable = `<table border="1" style="border-collapse:collapse">`;
|
|
64746
|
+
for (const row of cells) {
|
|
64747
|
+
htmlTable += "<tr>";
|
|
64748
|
+
for (const cell of row) {
|
|
64749
|
+
if (!cell) {
|
|
64750
|
+
continue;
|
|
64751
|
+
}
|
|
64752
|
+
const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
|
|
64753
|
+
const cellText = this.getters.getCellText(cell.position);
|
|
64754
|
+
htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
|
|
64434
64755
|
}
|
|
64435
|
-
|
|
64436
|
-
const cellText = this.getters.getCellText(cell.position);
|
|
64437
|
-
htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
|
|
64756
|
+
htmlTable += "</tr>";
|
|
64438
64757
|
}
|
|
64439
|
-
htmlTable += "</
|
|
64758
|
+
htmlTable += "</table>";
|
|
64759
|
+
innerHTML = htmlTable;
|
|
64440
64760
|
}
|
|
64441
|
-
|
|
64442
|
-
return
|
|
64761
|
+
const serializedData = JSON.stringify(this.getSheetData());
|
|
64762
|
+
return `<div data-osheet-clipboard='${xmlEscape(serializedData)}'>${innerHTML}</div>`;
|
|
64443
64763
|
}
|
|
64444
64764
|
isCutOperation() {
|
|
64445
64765
|
return this._isCutOperation ?? false;
|
|
@@ -66419,7 +66739,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
66419
66739
|
.add("history", HistoryPlugin)
|
|
66420
66740
|
.add("data_cleanup", DataCleanupPlugin)
|
|
66421
66741
|
.add("table_autofill", TableAutofillPlugin)
|
|
66422
|
-
.add("table_ui_resize", TableResizeUI)
|
|
66742
|
+
.add("table_ui_resize", TableResizeUI)
|
|
66743
|
+
.add("datavalidation_insert", DataValidationInsertionPlugin);
|
|
66423
66744
|
// Plugins which have a state, but which should not be shared in collaborative
|
|
66424
66745
|
const statefulUIPluginRegistry = new Registry()
|
|
66425
66746
|
.add("selection", GridSelectionPlugin)
|
|
@@ -67645,7 +67966,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
67645
67966
|
|
|
67646
67967
|
.o-header-group-main-pane {
|
|
67647
67968
|
&.o-group-rows {
|
|
67648
|
-
margin-top: -2px;
|
|
67969
|
+
margin-top: -2px; /* Counteract o-header-group-frozen-pane-border offset */
|
|
67649
67970
|
}
|
|
67650
67971
|
&.o-group-columns {
|
|
67651
67972
|
margin-left: -2px;
|
|
@@ -68678,10 +68999,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68678
68999
|
[ClipboardMIMEType.PlainText]: this.getBlob(content, ClipboardMIMEType.PlainText),
|
|
68679
69000
|
[ClipboardMIMEType.Html]: this.getBlob(content, ClipboardMIMEType.Html),
|
|
68680
69001
|
};
|
|
68681
|
-
const spreadsheetData = content[ClipboardMIMEType.OSpreadsheet];
|
|
68682
|
-
if (spreadsheetData) {
|
|
68683
|
-
clipboardItemData[ClipboardMIMEType.OSpreadsheet] = this.getBlob(content, ClipboardMIMEType.OSpreadsheet);
|
|
68684
|
-
}
|
|
68685
69002
|
return [new ClipboardItem(clipboardItemData)];
|
|
68686
69003
|
}
|
|
68687
69004
|
getBlob(clipboardContent, type) {
|
|
@@ -68723,7 +69040,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
68723
69040
|
*:before,
|
|
68724
69041
|
*:after {
|
|
68725
69042
|
box-sizing: content-box;
|
|
68726
|
-
|
|
69043
|
+
/* rtl not supported ATM */
|
|
68727
69044
|
direction: ltr;
|
|
68728
69045
|
}
|
|
68729
69046
|
.o-separator {
|
|
@@ -71628,6 +71945,124 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
71628
71945
|
}
|
|
71629
71946
|
}
|
|
71630
71947
|
|
|
71948
|
+
function addDataValidationRules(dataValidationRules) {
|
|
71949
|
+
const dvRulesCount = dataValidationRules.length;
|
|
71950
|
+
if (dvRulesCount === 0) {
|
|
71951
|
+
return [];
|
|
71952
|
+
}
|
|
71953
|
+
const dvNodes = [new XMLString(`<dataValidations count="${dvRulesCount}">`)];
|
|
71954
|
+
for (const dvRule of dataValidationRules) {
|
|
71955
|
+
switch (dvRule.criterion.type) {
|
|
71956
|
+
case "dateIs":
|
|
71957
|
+
case "dateIsBefore":
|
|
71958
|
+
case "dateIsOnOrBefore":
|
|
71959
|
+
case "dateIsAfter":
|
|
71960
|
+
case "dateIsOnOrAfter":
|
|
71961
|
+
case "dateIsBetween":
|
|
71962
|
+
case "dateIsNotBetween":
|
|
71963
|
+
dvNodes.push(addDateRule(dvRule));
|
|
71964
|
+
break;
|
|
71965
|
+
case "isEqual":
|
|
71966
|
+
case "isNotEqual":
|
|
71967
|
+
case "isGreaterThan":
|
|
71968
|
+
case "isGreaterOrEqualTo":
|
|
71969
|
+
case "isLessThan":
|
|
71970
|
+
case "isLessOrEqualTo":
|
|
71971
|
+
case "isBetween":
|
|
71972
|
+
case "isNotBetween":
|
|
71973
|
+
dvNodes.push(addDecimalRule(dvRule));
|
|
71974
|
+
break;
|
|
71975
|
+
case "isValueInRange":
|
|
71976
|
+
case "isValueInList":
|
|
71977
|
+
dvNodes.push(addListRule(dvRule));
|
|
71978
|
+
break;
|
|
71979
|
+
case "customFormula":
|
|
71980
|
+
dvNodes.push(addCustomFormulaRule(dvRule));
|
|
71981
|
+
break;
|
|
71982
|
+
default:
|
|
71983
|
+
console.warn(`Data validation ${dvRule.criterion.type} is not supported in xlsx.`);
|
|
71984
|
+
break;
|
|
71985
|
+
}
|
|
71986
|
+
}
|
|
71987
|
+
dvNodes.push(new XMLString("</dataValidations>"));
|
|
71988
|
+
return dvNodes;
|
|
71989
|
+
}
|
|
71990
|
+
function addDateRule(dvRule) {
|
|
71991
|
+
const rule = dvRule.criterion;
|
|
71992
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
71993
|
+
const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
|
|
71994
|
+
const operator = convertDateCriterionTypeToExcelOperator(dvRule.criterion.type);
|
|
71995
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
71996
|
+
attributes.push(["type", "date"], ["operator", operator]);
|
|
71997
|
+
if (formula2) {
|
|
71998
|
+
return escapeXml /*xml*/ `
|
|
71999
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72000
|
+
<formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
|
|
72001
|
+
<formula2>${toNumber(formula2, DEFAULT_LOCALE)}</formula2>
|
|
72002
|
+
</dataValidation>
|
|
72003
|
+
`;
|
|
72004
|
+
}
|
|
72005
|
+
return escapeXml /*xml*/ `
|
|
72006
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72007
|
+
<formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
|
|
72008
|
+
</dataValidation>
|
|
72009
|
+
`;
|
|
72010
|
+
}
|
|
72011
|
+
function addDecimalRule(dvRule) {
|
|
72012
|
+
const rule = dvRule.criterion;
|
|
72013
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
72014
|
+
const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
|
|
72015
|
+
const operator = convertDecimalCriterionTypeToExcelOperator(dvRule.criterion.type);
|
|
72016
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72017
|
+
attributes.push(["type", "decimal"], ["operator", operator]);
|
|
72018
|
+
if (formula2) {
|
|
72019
|
+
return escapeXml /*xml*/ `
|
|
72020
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72021
|
+
<formula1>${formula1}</formula1>
|
|
72022
|
+
<formula2>${formula2}</formula2>
|
|
72023
|
+
</dataValidation>
|
|
72024
|
+
`;
|
|
72025
|
+
}
|
|
72026
|
+
return escapeXml /*xml*/ `
|
|
72027
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72028
|
+
<formula1>${formula1}</formula1>
|
|
72029
|
+
</dataValidation>
|
|
72030
|
+
`;
|
|
72031
|
+
}
|
|
72032
|
+
function addListRule(dvRule) {
|
|
72033
|
+
const rule = dvRule.criterion;
|
|
72034
|
+
const formula1 = dvRule.criterion.type === "isValueInRange"
|
|
72035
|
+
? adaptFormulaToExcel(rule.values[0])
|
|
72036
|
+
: `"${rule.values.join(",")}"`;
|
|
72037
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72038
|
+
attributes.push(["type", "list"]);
|
|
72039
|
+
return escapeXml /*xml*/ `
|
|
72040
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72041
|
+
<formula1>${formula1}</formula1>
|
|
72042
|
+
</dataValidation>
|
|
72043
|
+
`;
|
|
72044
|
+
}
|
|
72045
|
+
function addCustomFormulaRule(dvRule) {
|
|
72046
|
+
const rule = dvRule.criterion;
|
|
72047
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
72048
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72049
|
+
attributes.push(["type", "custom"]);
|
|
72050
|
+
return escapeXml /*xml*/ `
|
|
72051
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72052
|
+
<formula1>${formula1}</formula1>
|
|
72053
|
+
</dataValidation>
|
|
72054
|
+
`;
|
|
72055
|
+
}
|
|
72056
|
+
function commonDataValidationAttributes(dvRule) {
|
|
72057
|
+
return [
|
|
72058
|
+
["allowBlank", "1"],
|
|
72059
|
+
["showInputMessage", "1"],
|
|
72060
|
+
["showErrorMessage", "1"],
|
|
72061
|
+
["errorStyle", !dvRule.isBlocking ? "warning" : ""],
|
|
72062
|
+
["sqref", dvRule.ranges.join(" ")],
|
|
72063
|
+
];
|
|
72064
|
+
}
|
|
72065
|
+
|
|
71631
72066
|
function createDrawing(drawingRelIds, sheet, figures) {
|
|
71632
72067
|
const namespaces = [
|
|
71633
72068
|
["xmlns:xdr", NAMESPACE.drawing],
|
|
@@ -72412,6 +72847,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
72412
72847
|
${addRows(construct, data, sheet)}
|
|
72413
72848
|
${addMerges(sheet.merges)}
|
|
72414
72849
|
${joinXmlNodes(addConditionalFormatting(construct.dxfs, sheet.conditionalFormats))}
|
|
72850
|
+
${joinXmlNodes(addDataValidationRules(sheet.dataValidationRules))}
|
|
72415
72851
|
${addHyperlinks(construct, data, sheetIndex)}
|
|
72416
72852
|
${drawingNode}
|
|
72417
72853
|
${tablesNode}
|
|
@@ -72735,7 +73171,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
72735
73171
|
this.session.join(this.config.client);
|
|
72736
73172
|
}
|
|
72737
73173
|
async leaveSession() {
|
|
72738
|
-
|
|
73174
|
+
const snapshot = this.getters.isReadonly() ? undefined : lazy(() => this.exportData());
|
|
73175
|
+
await this.session.leave(snapshot);
|
|
72739
73176
|
}
|
|
72740
73177
|
setupUiPlugin(Plugin) {
|
|
72741
73178
|
const plugin = new Plugin(this.uiPluginConfig);
|
|
@@ -73234,6 +73671,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
73234
73671
|
GaugeChartDesignPanel,
|
|
73235
73672
|
ScorecardChartConfigPanel,
|
|
73236
73673
|
ScorecardChartDesignPanel,
|
|
73674
|
+
RadarChartDesignPanel,
|
|
73675
|
+
WaterfallChartDesignPanel,
|
|
73676
|
+
ComboChartDesignPanel,
|
|
73237
73677
|
ChartTypePicker,
|
|
73238
73678
|
FigureComponent,
|
|
73239
73679
|
Menu,
|
|
@@ -73287,6 +73727,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
73287
73727
|
DEFAULT_LOCALE,
|
|
73288
73728
|
HIGHLIGHT_COLOR,
|
|
73289
73729
|
PIVOT_TABLE_CONFIG,
|
|
73730
|
+
ChartTerms,
|
|
73290
73731
|
};
|
|
73291
73732
|
const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
|
|
73292
73733
|
|
|
@@ -73337,9 +73778,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
73337
73778
|
exports.tokenize = tokenize;
|
|
73338
73779
|
|
|
73339
73780
|
|
|
73340
|
-
__info__.version = "18.1.0-alpha.
|
|
73341
|
-
__info__.date = "2024-11-
|
|
73342
|
-
__info__.hash = "
|
|
73781
|
+
__info__.version = "18.1.0-alpha.6";
|
|
73782
|
+
__info__.date = "2024-11-28T09:06:59.527Z";
|
|
73783
|
+
__info__.hash = "875c901";
|
|
73343
73784
|
|
|
73344
73785
|
|
|
73345
73786
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|