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