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