@odoo/o-spreadsheet 18.1.0-alpha.4 → 18.1.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o-spreadsheet.cjs.js +953 -496
- package/dist/o-spreadsheet.d.ts +4582 -4569
- package/dist/o-spreadsheet.esm.js +953 -496
- package/dist/o-spreadsheet.iife.js +953 -496
- package/dist/o-spreadsheet.iife.min.js +567 -540
- package/dist/o_spreadsheet.xml +29 -26
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 18.1.0-alpha.
|
|
6
|
-
* @date 2024-11-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.1.0-alpha.5
|
|
6
|
+
* @date 2024-11-22T14:22:50.899Z
|
|
7
|
+
* @hash e13bd67
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
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) {
|
|
@@ -4344,7 +4342,9 @@ function linearSearch(data, target, mode, numberOfValues, getValueInData, revers
|
|
|
4344
4342
|
return reverseSearch ? numberOfValues - i - 1 : i;
|
|
4345
4343
|
}
|
|
4346
4344
|
}
|
|
4347
|
-
return reverseSearch
|
|
4345
|
+
return reverseSearch && closestMatchIndex !== -1
|
|
4346
|
+
? numberOfValues - closestMatchIndex - 1
|
|
4347
|
+
: closestMatchIndex;
|
|
4348
4348
|
}
|
|
4349
4349
|
/**
|
|
4350
4350
|
* Normalize a value.
|
|
@@ -4398,47 +4398,83 @@ function isDataNonEmpty(data) {
|
|
|
4398
4398
|
return true;
|
|
4399
4399
|
}
|
|
4400
4400
|
|
|
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
|
-
}
|
|
4401
|
+
/**
|
|
4402
|
+
* Add the `https` prefix to the url if it's missing
|
|
4403
|
+
*/
|
|
4404
|
+
function withHttps(url) {
|
|
4405
|
+
return !/^https?:\/\//i.test(url) ? `https://${url}` : url;
|
|
4417
4406
|
}
|
|
4418
|
-
|
|
4419
|
-
function
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4407
|
+
const urlRegistry = new Registry();
|
|
4408
|
+
function createWebLink(url, label) {
|
|
4409
|
+
url = withHttps(url);
|
|
4410
|
+
return {
|
|
4411
|
+
url,
|
|
4412
|
+
label: label || url,
|
|
4413
|
+
isExternal: true,
|
|
4414
|
+
isUrlEditable: true,
|
|
4415
|
+
};
|
|
4424
4416
|
}
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4417
|
+
urlRegistry.add("sheet_URL", {
|
|
4418
|
+
match: (url) => isSheetUrl(url),
|
|
4419
|
+
createLink: (url, label) => {
|
|
4420
|
+
return {
|
|
4421
|
+
label,
|
|
4422
|
+
url,
|
|
4423
|
+
isExternal: false,
|
|
4424
|
+
isUrlEditable: false,
|
|
4425
|
+
};
|
|
4426
|
+
},
|
|
4427
|
+
urlRepresentation(url, getters) {
|
|
4428
|
+
const sheetId = parseSheetUrl(url);
|
|
4429
|
+
return getters.tryGetSheetName(sheetId) || _t("Invalid sheet");
|
|
4430
|
+
},
|
|
4431
|
+
open(url, env) {
|
|
4432
|
+
const sheetId = parseSheetUrl(url);
|
|
4433
|
+
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
4434
|
+
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
4435
|
+
sheetIdTo: sheetId,
|
|
4436
|
+
});
|
|
4437
|
+
if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
|
|
4438
|
+
env.notifyUser({
|
|
4439
|
+
type: "warning",
|
|
4440
|
+
sticky: false,
|
|
4441
|
+
text: _t("Cannot open the link because the linked sheet is hidden."),
|
|
4442
|
+
});
|
|
4443
|
+
}
|
|
4444
|
+
},
|
|
4445
|
+
sequence: 0,
|
|
4446
|
+
});
|
|
4447
|
+
const WebUrlSpec = {
|
|
4448
|
+
createLink: createWebLink,
|
|
4449
|
+
match: (url) => isWebLink(url),
|
|
4450
|
+
open: (url) => window.open(url, "_blank"),
|
|
4451
|
+
urlRepresentation: (url) => url,
|
|
4452
|
+
sequence: 0,
|
|
4453
|
+
};
|
|
4454
|
+
function findMatchingSpec(url) {
|
|
4455
|
+
return (urlRegistry
|
|
4456
|
+
.getAll()
|
|
4457
|
+
.sort((a, b) => a.sequence - b.sequence)
|
|
4458
|
+
.find((urlType) => urlType.match(url)) || WebUrlSpec);
|
|
4428
4459
|
}
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4460
|
+
function urlRepresentation(link, getters) {
|
|
4461
|
+
return findMatchingSpec(link.url).urlRepresentation(link.url, getters);
|
|
4462
|
+
}
|
|
4463
|
+
function openLink(link, env) {
|
|
4464
|
+
findMatchingSpec(link.url).open(link.url, env);
|
|
4465
|
+
}
|
|
4466
|
+
function detectLink(value) {
|
|
4467
|
+
if (typeof value !== "string") {
|
|
4468
|
+
return undefined;
|
|
4469
|
+
}
|
|
4470
|
+
if (isMarkdownLink(value)) {
|
|
4471
|
+
const { label, url } = parseMarkdownLink(value);
|
|
4472
|
+
return findMatchingSpec(url).createLink(url, label);
|
|
4473
|
+
}
|
|
4474
|
+
else if (isWebLink(value)) {
|
|
4475
|
+
return createWebLink(value);
|
|
4476
|
+
}
|
|
4477
|
+
return undefined;
|
|
4442
4478
|
}
|
|
4443
4479
|
|
|
4444
4480
|
function tokenizeFormat(str) {
|
|
@@ -5500,6 +5536,193 @@ function isTextFormat(format) {
|
|
|
5500
5536
|
}
|
|
5501
5537
|
}
|
|
5502
5538
|
|
|
5539
|
+
function evaluateLiteral(literalCell, localeFormat) {
|
|
5540
|
+
const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
|
|
5541
|
+
const functionResult = { value, format: localeFormat.format };
|
|
5542
|
+
return createEvaluatedCell(functionResult, localeFormat.locale);
|
|
5543
|
+
}
|
|
5544
|
+
function parseLiteral(content, locale) {
|
|
5545
|
+
if (content.startsWith("=")) {
|
|
5546
|
+
throw new Error(`Cannot parse "${content}" because it's not a literal value. It's a formula`);
|
|
5547
|
+
}
|
|
5548
|
+
if (content === "") {
|
|
5549
|
+
return null;
|
|
5550
|
+
}
|
|
5551
|
+
if (isNumber(content, DEFAULT_LOCALE)) {
|
|
5552
|
+
return parseNumber(content, DEFAULT_LOCALE);
|
|
5553
|
+
}
|
|
5554
|
+
const internalDate = parseDateTime(content, locale);
|
|
5555
|
+
if (internalDate) {
|
|
5556
|
+
return internalDate.value;
|
|
5557
|
+
}
|
|
5558
|
+
if (isBoolean(content)) {
|
|
5559
|
+
return content.toUpperCase() === "TRUE";
|
|
5560
|
+
}
|
|
5561
|
+
return content;
|
|
5562
|
+
}
|
|
5563
|
+
function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
|
|
5564
|
+
const link = detectLink(functionResult.value);
|
|
5565
|
+
if (!link) {
|
|
5566
|
+
return _createEvaluatedCell(functionResult, locale, cell);
|
|
5567
|
+
}
|
|
5568
|
+
const value = parseLiteral(link.label, locale);
|
|
5569
|
+
const format = functionResult.format ||
|
|
5570
|
+
(typeof value === "number"
|
|
5571
|
+
? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
|
|
5572
|
+
: undefined);
|
|
5573
|
+
const linkPayload = {
|
|
5574
|
+
value,
|
|
5575
|
+
format,
|
|
5576
|
+
};
|
|
5577
|
+
return {
|
|
5578
|
+
..._createEvaluatedCell(linkPayload, locale, cell),
|
|
5579
|
+
link,
|
|
5580
|
+
};
|
|
5581
|
+
}
|
|
5582
|
+
function _createEvaluatedCell(functionResult, locale, cell) {
|
|
5583
|
+
let { value, format, message } = functionResult;
|
|
5584
|
+
format = cell?.format || format;
|
|
5585
|
+
const formattedValue = formatValue(value, { format, locale });
|
|
5586
|
+
if (isEvaluationError(value)) {
|
|
5587
|
+
return errorCell(value, message);
|
|
5588
|
+
}
|
|
5589
|
+
if (isTextFormat(format)) {
|
|
5590
|
+
// TO DO:
|
|
5591
|
+
// with the next line, the value of the cell is transformed depending on the format.
|
|
5592
|
+
// This shouldn't happen, by doing this, the formulas handling numbers are not able
|
|
5593
|
+
// to interpret the value as a number.
|
|
5594
|
+
return textCell(toString(value), format, formattedValue);
|
|
5595
|
+
}
|
|
5596
|
+
if (value === null) {
|
|
5597
|
+
return emptyCell(format);
|
|
5598
|
+
}
|
|
5599
|
+
if (typeof value === "number") {
|
|
5600
|
+
if (isDateTimeFormat(format || "")) {
|
|
5601
|
+
return dateTimeCell(value, format, formattedValue);
|
|
5602
|
+
}
|
|
5603
|
+
return numberCell(value, format, formattedValue);
|
|
5604
|
+
}
|
|
5605
|
+
if (typeof value === "boolean") {
|
|
5606
|
+
return booleanCell(value, format, formattedValue);
|
|
5607
|
+
}
|
|
5608
|
+
return textCell(value, format, formattedValue);
|
|
5609
|
+
}
|
|
5610
|
+
function textCell(value, format, formattedValue) {
|
|
5611
|
+
return {
|
|
5612
|
+
value,
|
|
5613
|
+
format,
|
|
5614
|
+
formattedValue,
|
|
5615
|
+
type: CellValueType.text,
|
|
5616
|
+
isAutoSummable: true,
|
|
5617
|
+
defaultAlign: "left",
|
|
5618
|
+
};
|
|
5619
|
+
}
|
|
5620
|
+
function numberCell(value, format, formattedValue) {
|
|
5621
|
+
return {
|
|
5622
|
+
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
5623
|
+
format,
|
|
5624
|
+
formattedValue,
|
|
5625
|
+
type: CellValueType.number,
|
|
5626
|
+
isAutoSummable: true,
|
|
5627
|
+
defaultAlign: "right",
|
|
5628
|
+
};
|
|
5629
|
+
}
|
|
5630
|
+
const emptyCell = memoize(function emptyCell(format) {
|
|
5631
|
+
return {
|
|
5632
|
+
value: null,
|
|
5633
|
+
format,
|
|
5634
|
+
formattedValue: "",
|
|
5635
|
+
type: CellValueType.empty,
|
|
5636
|
+
isAutoSummable: true,
|
|
5637
|
+
defaultAlign: "left",
|
|
5638
|
+
};
|
|
5639
|
+
});
|
|
5640
|
+
function dateTimeCell(value, format, formattedValue) {
|
|
5641
|
+
return {
|
|
5642
|
+
value,
|
|
5643
|
+
format,
|
|
5644
|
+
formattedValue,
|
|
5645
|
+
type: CellValueType.number,
|
|
5646
|
+
isAutoSummable: false,
|
|
5647
|
+
defaultAlign: "right",
|
|
5648
|
+
};
|
|
5649
|
+
}
|
|
5650
|
+
function booleanCell(value, format, formattedValue) {
|
|
5651
|
+
return {
|
|
5652
|
+
value,
|
|
5653
|
+
format,
|
|
5654
|
+
formattedValue,
|
|
5655
|
+
type: CellValueType.boolean,
|
|
5656
|
+
isAutoSummable: false,
|
|
5657
|
+
defaultAlign: "center",
|
|
5658
|
+
};
|
|
5659
|
+
}
|
|
5660
|
+
function errorCell(value, message) {
|
|
5661
|
+
return {
|
|
5662
|
+
value,
|
|
5663
|
+
formattedValue: value,
|
|
5664
|
+
message,
|
|
5665
|
+
type: CellValueType.error,
|
|
5666
|
+
isAutoSummable: false,
|
|
5667
|
+
defaultAlign: "center",
|
|
5668
|
+
};
|
|
5669
|
+
}
|
|
5670
|
+
|
|
5671
|
+
function toCriterionDateNumber(dateValue) {
|
|
5672
|
+
const today = DateTime.now();
|
|
5673
|
+
switch (dateValue) {
|
|
5674
|
+
case "today":
|
|
5675
|
+
return jsDateToNumber(today);
|
|
5676
|
+
case "yesterday":
|
|
5677
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
|
|
5678
|
+
case "tomorrow":
|
|
5679
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
|
|
5680
|
+
case "lastWeek":
|
|
5681
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
|
|
5682
|
+
case "lastMonth":
|
|
5683
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
|
|
5684
|
+
case "lastYear":
|
|
5685
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
|
|
5686
|
+
}
|
|
5687
|
+
}
|
|
5688
|
+
/** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
|
|
5689
|
+
function getDateNumberCriterionValues(criterion, locale) {
|
|
5690
|
+
if ("dateValue" in criterion && criterion.dateValue !== "exactDate") {
|
|
5691
|
+
return [toCriterionDateNumber(criterion.dateValue)];
|
|
5692
|
+
}
|
|
5693
|
+
return criterion.values.map((value) => valueToDateNumber(value, locale));
|
|
5694
|
+
}
|
|
5695
|
+
/** Convert the criterion values to numbers. Return undefined values if they cannot be converted to numbers. */
|
|
5696
|
+
function getCriterionValuesAsNumber(criterion, locale) {
|
|
5697
|
+
return criterion.values.map((value) => tryToNumber(value, locale));
|
|
5698
|
+
}
|
|
5699
|
+
function getDateCriterionFormattedValues(values, locale) {
|
|
5700
|
+
return values.map((valueStr) => {
|
|
5701
|
+
if (valueStr.startsWith("=")) {
|
|
5702
|
+
return valueStr;
|
|
5703
|
+
}
|
|
5704
|
+
const value = parseLiteral(valueStr, locale);
|
|
5705
|
+
if (typeof value === "number") {
|
|
5706
|
+
return formatValue(value, { format: locale.dateFormat, locale });
|
|
5707
|
+
}
|
|
5708
|
+
return "";
|
|
5709
|
+
});
|
|
5710
|
+
}
|
|
5711
|
+
|
|
5712
|
+
const MAX_DELAY = 140;
|
|
5713
|
+
const MIN_DELAY = 20;
|
|
5714
|
+
const ACCELERATION = 0.035;
|
|
5715
|
+
/**
|
|
5716
|
+
* Decreasing exponential function used to determine the "speed" of edge-scrolling
|
|
5717
|
+
* as the timeout delay.
|
|
5718
|
+
*
|
|
5719
|
+
* Returns a timeout delay in milliseconds.
|
|
5720
|
+
*/
|
|
5721
|
+
function scrollDelay(value) {
|
|
5722
|
+
// decreasing exponential from MAX_DELAY to MIN_DELAY
|
|
5723
|
+
return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
|
|
5724
|
+
}
|
|
5725
|
+
|
|
5503
5726
|
class RangeImpl {
|
|
5504
5727
|
getSheetSize;
|
|
5505
5728
|
_zone;
|
|
@@ -6147,6 +6370,22 @@ function getPasteZones(target, content) {
|
|
|
6147
6370
|
const width = content[0].length, height = content.length;
|
|
6148
6371
|
return target.map((t) => splitZoneForPaste(t, width, height)).flat();
|
|
6149
6372
|
}
|
|
6373
|
+
function parseOSClipboardContent(content) {
|
|
6374
|
+
if (!content[ClipboardMIMEType.Html]) {
|
|
6375
|
+
return {
|
|
6376
|
+
text: content[ClipboardMIMEType.PlainText],
|
|
6377
|
+
};
|
|
6378
|
+
}
|
|
6379
|
+
const htmlDocument = new DOMParser().parseFromString(content[ClipboardMIMEType.Html], "text/html");
|
|
6380
|
+
const oSheetClipboardData = htmlDocument
|
|
6381
|
+
.querySelector("div")
|
|
6382
|
+
?.getAttribute("data-osheet-clipboard");
|
|
6383
|
+
const spreadsheetContent = oSheetClipboardData && JSON.parse(oSheetClipboardData);
|
|
6384
|
+
return {
|
|
6385
|
+
text: content[ClipboardMIMEType.PlainText],
|
|
6386
|
+
data: spreadsheetContent,
|
|
6387
|
+
};
|
|
6388
|
+
}
|
|
6150
6389
|
|
|
6151
6390
|
class ClipboardHandler {
|
|
6152
6391
|
getters;
|
|
@@ -6168,7 +6407,7 @@ class ClipboardHandler {
|
|
|
6168
6407
|
getPasteTarget(sheetId, target, content, options) {
|
|
6169
6408
|
return { zones: [], sheetId };
|
|
6170
6409
|
}
|
|
6171
|
-
|
|
6410
|
+
convertTextToClipboardData(data) {
|
|
6172
6411
|
return;
|
|
6173
6412
|
}
|
|
6174
6413
|
}
|
|
@@ -8011,7 +8250,7 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
8011
8250
|
this.dispatch("CLEAR_CELL", target);
|
|
8012
8251
|
}
|
|
8013
8252
|
}
|
|
8014
|
-
|
|
8253
|
+
convertTextToClipboardData(text) {
|
|
8015
8254
|
const locale = this.getters.getLocale();
|
|
8016
8255
|
const copiedData = {
|
|
8017
8256
|
cells: [],
|
|
@@ -8119,6 +8358,7 @@ class ChartClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
8119
8358
|
|
|
8120
8359
|
class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
8121
8360
|
uuidGenerator = new UuidGenerator();
|
|
8361
|
+
queuedChanges = {};
|
|
8122
8362
|
copy(data) {
|
|
8123
8363
|
if (!data.zones.length) {
|
|
8124
8364
|
return;
|
|
@@ -8140,6 +8380,7 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
8140
8380
|
return { cfRules };
|
|
8141
8381
|
}
|
|
8142
8382
|
paste(target, clippedContent, options) {
|
|
8383
|
+
this.queuedChanges = {};
|
|
8143
8384
|
if (options.pasteOption === "asValue") {
|
|
8144
8385
|
return;
|
|
8145
8386
|
}
|
|
@@ -8151,6 +8392,7 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
8151
8392
|
else {
|
|
8152
8393
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
8153
8394
|
}
|
|
8395
|
+
this.executeQueuedChanges();
|
|
8154
8396
|
}
|
|
8155
8397
|
pasteFromCut(sheetId, target, content) {
|
|
8156
8398
|
const selection = target[0];
|
|
@@ -8190,34 +8432,56 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
8190
8432
|
* Add or remove cells to a given conditional formatting rule.
|
|
8191
8433
|
*/
|
|
8192
8434
|
adaptCFRules(sheetId, cf, toAdd, toRemove) {
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
return;
|
|
8435
|
+
if (!this.queuedChanges[sheetId]) {
|
|
8436
|
+
this.queuedChanges[sheetId] = [];
|
|
8196
8437
|
}
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8438
|
+
const queuedChange = this.queuedChanges[sheetId].find((queued) => queued.cf.id === cf.id);
|
|
8439
|
+
if (!queuedChange) {
|
|
8440
|
+
this.queuedChanges[sheetId].push({ toAdd, toRemove, cf });
|
|
8441
|
+
}
|
|
8442
|
+
else {
|
|
8443
|
+
queuedChange.toAdd.push(...toAdd);
|
|
8444
|
+
queuedChange.toRemove.push(...toRemove);
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
executeQueuedChanges() {
|
|
8448
|
+
for (const sheetId in this.queuedChanges) {
|
|
8449
|
+
for (const { toAdd, toRemove, cf } of this.queuedChanges[sheetId]) {
|
|
8450
|
+
const newRangesXc = this.getters.getAdaptedCfRanges(sheetId, cf, toAdd, toRemove);
|
|
8451
|
+
if (!newRangesXc) {
|
|
8452
|
+
continue;
|
|
8453
|
+
}
|
|
8454
|
+
if (newRangesXc.length === 0) {
|
|
8455
|
+
this.dispatch("REMOVE_CONDITIONAL_FORMAT", { id: cf.id, sheetId });
|
|
8456
|
+
continue;
|
|
8457
|
+
}
|
|
8458
|
+
this.dispatch("ADD_CONDITIONAL_FORMAT", {
|
|
8459
|
+
cf: {
|
|
8460
|
+
id: cf.id,
|
|
8461
|
+
rule: cf.rule,
|
|
8462
|
+
stopIfTrue: cf.stopIfTrue,
|
|
8463
|
+
},
|
|
8464
|
+
ranges: newRangesXc,
|
|
8465
|
+
sheetId,
|
|
8466
|
+
});
|
|
8467
|
+
}
|
|
8200
8468
|
}
|
|
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
8469
|
}
|
|
8211
8470
|
getCFToCopyTo(targetSheetId, originCF) {
|
|
8212
|
-
|
|
8471
|
+
let targetCF = this.getters
|
|
8213
8472
|
.getConditionalFormats(targetSheetId)
|
|
8214
8473
|
.find((cf) => cf.stopIfTrue === originCF.stopIfTrue && deepEquals(cf.rule, originCF.rule));
|
|
8215
|
-
|
|
8474
|
+
const queuedCfs = this.queuedChanges[targetSheetId];
|
|
8475
|
+
if (!targetCF && queuedCfs) {
|
|
8476
|
+
targetCF = queuedCfs.find((queued) => queued.cf.stopIfTrue === originCF.stopIfTrue && deepEquals(queued.cf.rule, originCF.rule))?.cf;
|
|
8477
|
+
}
|
|
8478
|
+
return targetCF || { ...originCF, id: this.uuidGenerator.uuidv4(), ranges: [] };
|
|
8216
8479
|
}
|
|
8217
8480
|
}
|
|
8218
8481
|
|
|
8219
8482
|
class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
8220
8483
|
uuidGenerator = new UuidGenerator();
|
|
8484
|
+
queuedChanges = {};
|
|
8221
8485
|
copy(data) {
|
|
8222
8486
|
const { rowsIndexes, columnsIndexes } = data;
|
|
8223
8487
|
const sheetId = data.sheetId;
|
|
@@ -8234,6 +8498,7 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
8234
8498
|
return { dvRules };
|
|
8235
8499
|
}
|
|
8236
8500
|
paste(target, clippedContent, options) {
|
|
8501
|
+
this.queuedChanges = {};
|
|
8237
8502
|
if (options.pasteOption) {
|
|
8238
8503
|
return;
|
|
8239
8504
|
}
|
|
@@ -8245,6 +8510,7 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
8245
8510
|
else {
|
|
8246
8511
|
this.pasteFromCut(sheetId, zones, clippedContent);
|
|
8247
8512
|
}
|
|
8513
|
+
this.executeQueuedChanges();
|
|
8248
8514
|
}
|
|
8249
8515
|
pasteFromCut(sheetId, target, content) {
|
|
8250
8516
|
const selection = target[0];
|
|
@@ -8291,29 +8557,55 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
8291
8557
|
}
|
|
8292
8558
|
}
|
|
8293
8559
|
getDataValidationRuleToCopyTo(targetSheetId, originRule, newId = true) {
|
|
8294
|
-
|
|
8560
|
+
let targetRule = this.getters
|
|
8295
8561
|
.getDataValidationRules(targetSheetId)
|
|
8296
8562
|
.find((rule) => deepEquals(originRule.criterion, rule.criterion) &&
|
|
8297
8563
|
originRule.isBlocking === rule.isBlocking);
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8564
|
+
const queuedRules = this.queuedChanges[targetSheetId];
|
|
8565
|
+
if (!targetRule && queuedRules) {
|
|
8566
|
+
targetRule = queuedRules.find((queued) => deepEquals(originRule.criterion, queued.rule.criterion) &&
|
|
8567
|
+
originRule.isBlocking === queued.rule.isBlocking)?.rule;
|
|
8568
|
+
}
|
|
8569
|
+
return (targetRule || {
|
|
8570
|
+
...originRule,
|
|
8571
|
+
id: newId ? this.uuidGenerator.uuidv4() : originRule.id,
|
|
8572
|
+
ranges: [],
|
|
8573
|
+
});
|
|
8301
8574
|
}
|
|
8302
8575
|
/**
|
|
8303
8576
|
* Add or remove XCs to a given data validation rule.
|
|
8304
8577
|
*/
|
|
8305
8578
|
adaptDataValidationRule(sheetId, rule, toAdd, toRemove) {
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8579
|
+
if (!this.queuedChanges[sheetId]) {
|
|
8580
|
+
this.queuedChanges[sheetId] = [];
|
|
8581
|
+
}
|
|
8582
|
+
const queuedChange = this.queuedChanges[sheetId].find((queued) => queued.rule.id === rule.id);
|
|
8583
|
+
if (!queuedChange) {
|
|
8584
|
+
this.queuedChanges[sheetId].push({ toAdd, toRemove, rule });
|
|
8585
|
+
}
|
|
8586
|
+
else {
|
|
8587
|
+
queuedChange.toAdd.push(...toAdd);
|
|
8588
|
+
queuedChange.toRemove.push(...toRemove);
|
|
8589
|
+
}
|
|
8590
|
+
}
|
|
8591
|
+
executeQueuedChanges() {
|
|
8592
|
+
for (const sheetId in this.queuedChanges) {
|
|
8593
|
+
for (const { toAdd, toRemove, rule: dv } of this.queuedChanges[sheetId]) {
|
|
8594
|
+
// Remove the zones first in case the same position is in toAdd and toRemove
|
|
8595
|
+
const dvZones = dv.ranges.map((range) => range.zone);
|
|
8596
|
+
const withRemovedZones = recomputeZones(dvZones, toRemove);
|
|
8597
|
+
const newDvZones = recomputeZones([...withRemovedZones, ...toAdd], []);
|
|
8598
|
+
if (newDvZones.length === 0) {
|
|
8599
|
+
this.dispatch("REMOVE_DATA_VALIDATION_RULE", { sheetId, id: dv.id });
|
|
8600
|
+
continue;
|
|
8601
|
+
}
|
|
8602
|
+
this.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
8603
|
+
rule: dv,
|
|
8604
|
+
ranges: newDvZones.map((zone) => this.getters.getRangeDataFromZone(sheetId, zone)),
|
|
8605
|
+
sheetId,
|
|
8606
|
+
});
|
|
8607
|
+
}
|
|
8311
8608
|
}
|
|
8312
|
-
this.dispatch("ADD_DATA_VALIDATION_RULE", {
|
|
8313
|
-
rule,
|
|
8314
|
-
ranges: newDvZones.map((zone) => this.getters.getRangeDataFromZone(sheetId, zone)),
|
|
8315
|
-
sheetId,
|
|
8316
|
-
});
|
|
8317
8609
|
}
|
|
8318
8610
|
}
|
|
8319
8611
|
|
|
@@ -10284,6 +10576,10 @@ function makeArg(str, description) {
|
|
|
10284
10576
|
description,
|
|
10285
10577
|
type: types,
|
|
10286
10578
|
};
|
|
10579
|
+
const acceptErrors = types.includes("ANY") || types.includes("RANGE");
|
|
10580
|
+
if (acceptErrors) {
|
|
10581
|
+
result.acceptErrors = true;
|
|
10582
|
+
}
|
|
10287
10583
|
if (isOptional) {
|
|
10288
10584
|
result.optional = true;
|
|
10289
10585
|
}
|
|
@@ -12346,8 +12642,8 @@ const AVERAGEIFS = {
|
|
|
12346
12642
|
const COUNT = {
|
|
12347
12643
|
description: _t("The number of numeric values in dataset."),
|
|
12348
12644
|
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.")),
|
|
12645
|
+
arg("value1 (number, any, range<number>)", _t("The first value or range to consider when counting.")),
|
|
12646
|
+
arg("value2 (number, any, range<number>, repeating)", _t("Additional values or ranges to consider when counting.")),
|
|
12351
12647
|
],
|
|
12352
12648
|
compute: function (...values) {
|
|
12353
12649
|
return countNumbers(values, this.locale);
|
|
@@ -12736,6 +13032,7 @@ const PEARSON = {
|
|
|
12736
13032
|
},
|
|
12737
13033
|
isExported: true,
|
|
12738
13034
|
};
|
|
13035
|
+
// CORREL
|
|
12739
13036
|
// In GSheet, CORREL is just an alias to PEARSON
|
|
12740
13037
|
const CORREL = PEARSON;
|
|
12741
13038
|
// -----------------------------------------------------------------------------
|
|
@@ -13346,7 +13643,7 @@ function getMatchingCells(database, field, criteria, locale) {
|
|
|
13346
13643
|
}
|
|
13347
13644
|
const databaseArgs = [
|
|
13348
13645
|
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 (
|
|
13646
|
+
arg("field (number, string)", _t("Indicates which column in database contains the values to be extracted and operated on.")),
|
|
13350
13647
|
arg("criteria (range)", _t("An array or range containing zero or more criteria to filter the database values by before operating.")),
|
|
13351
13648
|
];
|
|
13352
13649
|
// -----------------------------------------------------------------------------
|
|
@@ -17728,7 +18025,7 @@ const IFS = {
|
|
|
17728
18025
|
args: [
|
|
17729
18026
|
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
18027
|
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.")),
|
|
18028
|
+
arg("condition2 (boolean, any, repeating)", _t("Additional conditions to be evaluated if the previous ones are FALSE.")),
|
|
17732
18029
|
arg("value2 (any, repeating)", _t("Additional values to be returned if their corresponding conditions are TRUE.")),
|
|
17733
18030
|
],
|
|
17734
18031
|
compute: function (...values) {
|
|
@@ -17967,7 +18264,7 @@ const COLUMNS = {
|
|
|
17967
18264
|
const HLOOKUP = {
|
|
17968
18265
|
description: _t("Horizontal lookup"),
|
|
17969
18266
|
args: [
|
|
17970
|
-
arg("search_key (
|
|
18267
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
17971
18268
|
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
18269
|
arg("index (number)", _t("The row index of the value to be returned, where the first row in range is numbered 1.")),
|
|
17973
18270
|
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 +18272,6 @@ const HLOOKUP = {
|
|
|
17975
18272
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
17976
18273
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
17977
18274
|
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
18275
|
const getValueFromRange = (range, index) => range[index][0].value;
|
|
17982
18276
|
const _isSorted = toBoolean(isSorted.value);
|
|
17983
18277
|
const colIndex = _isSorted
|
|
@@ -18074,7 +18368,7 @@ const INDIRECT = {
|
|
|
18074
18368
|
const LOOKUP = {
|
|
18075
18369
|
description: _t("Look up a value."),
|
|
18076
18370
|
args: [
|
|
18077
|
-
arg("search_key (
|
|
18371
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
18078
18372
|
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
18373
|
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
18374
|
],
|
|
@@ -18114,7 +18408,7 @@ const DEFAULT_SEARCH_TYPE = 1;
|
|
|
18114
18408
|
const MATCH = {
|
|
18115
18409
|
description: _t("Position of item in range that matches value."),
|
|
18116
18410
|
args: [
|
|
18117
|
-
arg("search_key (
|
|
18411
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
18118
18412
|
arg("range (any, range)", _t("The one-dimensional array to be searched.")),
|
|
18119
18413
|
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
18414
|
],
|
|
@@ -18189,7 +18483,7 @@ const ROWS = {
|
|
|
18189
18483
|
const VLOOKUP = {
|
|
18190
18484
|
description: _t("Vertical lookup."),
|
|
18191
18485
|
args: [
|
|
18192
|
-
arg("search_key (
|
|
18486
|
+
arg("search_key (string, number, boolean)", _t("The value to search for. For example, 42, 'Cats', or I24.")),
|
|
18193
18487
|
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
18488
|
arg("index (number)", _t("The column index of the value to be returned, where the first column in range is numbered 1.")),
|
|
18195
18489
|
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 +18491,6 @@ const VLOOKUP = {
|
|
|
18197
18491
|
compute: function (searchKey, range, index, isSorted = { value: DEFAULT_IS_SORTED }) {
|
|
18198
18492
|
const _index = Math.trunc(toNumber(index?.value, this.locale));
|
|
18199
18493
|
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
18494
|
const getValueFromRange = (range, index) => range[0][index].value;
|
|
18204
18495
|
const _isSorted = toBoolean(isSorted.value);
|
|
18205
18496
|
const rowIndex = _isSorted
|
|
@@ -18225,7 +18516,7 @@ const MATCH_MODE = {
|
|
|
18225
18516
|
const XLOOKUP = {
|
|
18226
18517
|
description: _t("Search a range for a match and return the corresponding item from a second range."),
|
|
18227
18518
|
args: [
|
|
18228
|
-
arg("search_key (
|
|
18519
|
+
arg("search_key (string,number,boolean)", _t("The value to search for.")),
|
|
18229
18520
|
arg("lookup_range (any, range)", _t("The range to consider for the search. Should be a single column or a single row.")),
|
|
18230
18521
|
arg("return_range (any, range)", _t("The range containing the return value. Should have the same dimensions as lookup_range.")),
|
|
18231
18522
|
arg("if_not_found (any, optional)", _t("If a valid match is not found, return this value.")),
|
|
@@ -18250,9 +18541,6 @@ const XLOOKUP = {
|
|
|
18250
18541
|
assert(() => lookupDirection === "col"
|
|
18251
18542
|
? returnRange[0].length === lookupRange[0].length
|
|
18252
18543
|
: 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
18544
|
const getElement = lookupDirection === "col"
|
|
18257
18545
|
? (range, index) => range[0][index].value
|
|
18258
18546
|
: (range, index) => range[index][0].value;
|
|
@@ -18277,13 +18565,14 @@ const XLOOKUP = {
|
|
|
18277
18565
|
//--------------------------------------------------------------------------
|
|
18278
18566
|
// Pivot functions
|
|
18279
18567
|
//--------------------------------------------------------------------------
|
|
18568
|
+
// PIVOT.VALUE
|
|
18280
18569
|
const PIVOT_VALUE = {
|
|
18281
18570
|
description: _t("Get the value from a pivot."),
|
|
18282
18571
|
args: [
|
|
18283
|
-
arg("pivot_id (string)", _t("ID of the pivot.")),
|
|
18572
|
+
arg("pivot_id (number,string)", _t("ID of the pivot.")),
|
|
18284
18573
|
arg("measure_name (string)", _t("Name of the measure.")),
|
|
18285
18574
|
arg("domain_field_name (string,optional,repeating)", _t("Field name.")),
|
|
18286
|
-
arg("domain_value (string,optional,repeating)", _t("Value.")),
|
|
18575
|
+
arg("domain_value (number,string,boolean,optional,repeating)", _t("Value.")),
|
|
18287
18576
|
],
|
|
18288
18577
|
compute: function (formulaId, measureName, ...domainArgs) {
|
|
18289
18578
|
const _pivotFormulaId = toString(formulaId);
|
|
@@ -18310,12 +18599,13 @@ const PIVOT_VALUE = {
|
|
|
18310
18599
|
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
18311
18600
|
},
|
|
18312
18601
|
};
|
|
18602
|
+
// PIVOT.HEADER
|
|
18313
18603
|
const PIVOT_HEADER = {
|
|
18314
18604
|
description: _t("Get the header of a pivot."),
|
|
18315
18605
|
args: [
|
|
18316
|
-
arg("pivot_id (string)", _t("ID of the pivot.")),
|
|
18606
|
+
arg("pivot_id (number,string)", _t("ID of the pivot.")),
|
|
18317
18607
|
arg("domain_field_name (string,optional,repeating)", _t("Field name.")),
|
|
18318
|
-
arg("domain_value (string,optional,repeating)", _t("Value.")),
|
|
18608
|
+
arg("domain_value (number,string,value,optional,repeating)", _t("Value.")),
|
|
18319
18609
|
],
|
|
18320
18610
|
compute: function (pivotId, ...domainArgs) {
|
|
18321
18611
|
const _pivotFormulaId = toString(pivotId);
|
|
@@ -18562,8 +18852,8 @@ const getNeutral = { number: 0, string: "", boolean: false };
|
|
|
18562
18852
|
const EQ = {
|
|
18563
18853
|
description: _t("Equal."),
|
|
18564
18854
|
args: [
|
|
18565
|
-
arg("value1 (
|
|
18566
|
-
arg("value2 (
|
|
18855
|
+
arg("value1 (string, number, boolean)", _t("The first value.")),
|
|
18856
|
+
arg("value2 (string, number, boolean)", _t("The value to test against value1 for equality.")),
|
|
18567
18857
|
],
|
|
18568
18858
|
compute: function (value1, value2) {
|
|
18569
18859
|
if (isEvaluationError(value1?.value)) {
|
|
@@ -18614,8 +18904,8 @@ function applyRelationalOperator(value1, value2, cb) {
|
|
|
18614
18904
|
const GT = {
|
|
18615
18905
|
description: _t("Strictly greater than."),
|
|
18616
18906
|
args: [
|
|
18617
|
-
arg("value1 (
|
|
18618
|
-
arg("value2 (
|
|
18907
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being greater than value2.")),
|
|
18908
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18619
18909
|
],
|
|
18620
18910
|
compute: function (value1, value2) {
|
|
18621
18911
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
@@ -18629,8 +18919,8 @@ const GT = {
|
|
|
18629
18919
|
const GTE = {
|
|
18630
18920
|
description: _t("Greater than or equal to."),
|
|
18631
18921
|
args: [
|
|
18632
|
-
arg("value1 (
|
|
18633
|
-
arg("value2 (
|
|
18922
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being greater than or equal to value2.")),
|
|
18923
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18634
18924
|
],
|
|
18635
18925
|
compute: function (value1, value2) {
|
|
18636
18926
|
return applyRelationalOperator(value1, value2, (v1, v2) => {
|
|
@@ -18644,8 +18934,8 @@ const GTE = {
|
|
|
18644
18934
|
const LT = {
|
|
18645
18935
|
description: _t("Less than."),
|
|
18646
18936
|
args: [
|
|
18647
|
-
arg("value1 (
|
|
18648
|
-
arg("value2 (
|
|
18937
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being less than value2.")),
|
|
18938
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18649
18939
|
],
|
|
18650
18940
|
compute: function (value1, value2) {
|
|
18651
18941
|
const result = GTE.compute.bind(this)(value1, value2);
|
|
@@ -18661,8 +18951,8 @@ const LT = {
|
|
|
18661
18951
|
const LTE = {
|
|
18662
18952
|
description: _t("Less than or equal to."),
|
|
18663
18953
|
args: [
|
|
18664
|
-
arg("value1 (
|
|
18665
|
-
arg("value2 (
|
|
18954
|
+
arg("value1 (number, string, boolean)", _t("The value to test as being less than or equal to value2.")),
|
|
18955
|
+
arg("value2 (number, string, boolean)", _t("The second value.")),
|
|
18666
18956
|
],
|
|
18667
18957
|
compute: function (value1, value2) {
|
|
18668
18958
|
const result = GT.compute.bind(this)(value1, value2);
|
|
@@ -18710,8 +19000,8 @@ const MULTIPLY = {
|
|
|
18710
19000
|
const NE = {
|
|
18711
19001
|
description: _t("Not equal."),
|
|
18712
19002
|
args: [
|
|
18713
|
-
arg("value1 (
|
|
18714
|
-
arg("value2 (
|
|
19003
|
+
arg("value1 (string, number, boolean)", _t("The first value.")),
|
|
19004
|
+
arg("value2 (string, number, boolean)", _t("The value to test against value1 for inequality.")),
|
|
18715
19005
|
],
|
|
18716
19006
|
compute: function (value1, value2) {
|
|
18717
19007
|
const result = EQ.compute.bind(this)(value1, value2);
|
|
@@ -19629,6 +19919,16 @@ function createComputeFunction(descr, functionName) {
|
|
|
19629
19919
|
});
|
|
19630
19920
|
}
|
|
19631
19921
|
function errorHandlingCompute(...args) {
|
|
19922
|
+
for (let i = 0; i < args.length; i++) {
|
|
19923
|
+
const arg = args[i];
|
|
19924
|
+
const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
|
|
19925
|
+
// Early exit if the argument is an error and the function does not accept errors
|
|
19926
|
+
// We only check scalar arguments, not matrix arguments for performance reasons.
|
|
19927
|
+
// Casting helpers are responsible for handling errors in matrix arguments.
|
|
19928
|
+
if (!argDefinition.acceptErrors && !isMatrix(arg) && isEvaluationError(arg?.value)) {
|
|
19929
|
+
return arg;
|
|
19930
|
+
}
|
|
19931
|
+
}
|
|
19632
19932
|
try {
|
|
19633
19933
|
return computeFunctionToObject.apply(this, args);
|
|
19634
19934
|
}
|
|
@@ -19637,6 +19937,9 @@ function createComputeFunction(descr, functionName) {
|
|
|
19637
19937
|
}
|
|
19638
19938
|
}
|
|
19639
19939
|
function computeFunctionToObject(...args) {
|
|
19940
|
+
if (this.debug) {
|
|
19941
|
+
debugger;
|
|
19942
|
+
}
|
|
19640
19943
|
const result = descr.compute.apply(this, args);
|
|
19641
19944
|
if (!isMatrix(result)) {
|
|
19642
19945
|
if (typeof result === "object" && result !== null && "value" in result) {
|
|
@@ -20746,6 +21049,7 @@ function compileTokensOrThrow(tokens) {
|
|
|
20746
21049
|
}
|
|
20747
21050
|
if (ast.debug) {
|
|
20748
21051
|
code.append("debugger;");
|
|
21052
|
+
code.append(`ctx["debug"] = true;`);
|
|
20749
21053
|
}
|
|
20750
21054
|
switch (ast.type) {
|
|
20751
21055
|
case "BOOLEAN":
|
|
@@ -21344,217 +21648,6 @@ autoCompleteProviders.add("sheet_names", {
|
|
|
21344
21648
|
},
|
|
21345
21649
|
});
|
|
21346
21650
|
|
|
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
21651
|
/**
|
|
21559
21652
|
* An AutofillModifierImplementation is used to describe how to handle a
|
|
21560
21653
|
* AutofillModifier.
|
|
@@ -22919,6 +23012,10 @@ var WarningTypes;
|
|
|
22919
23012
|
WarningTypes["CfIconSetEmptyIconNotSupported"] = "IconSets with empty icons";
|
|
22920
23013
|
WarningTypes["BadlyFormattedHyperlink"] = "Badly formatted hyperlink";
|
|
22921
23014
|
WarningTypes["NumFmtIdNotSupported"] = "Number format";
|
|
23015
|
+
WarningTypes["TimeDataValidationNotSupported"] = "Time data validation rules";
|
|
23016
|
+
WarningTypes["TextLengthDataValidationNotSupported"] = "Text length data validation rules";
|
|
23017
|
+
WarningTypes["WholeNumberDataValidationNotSupported"] = "Whole number data validation rules";
|
|
23018
|
+
WarningTypes["NotEqualDateDataValidationNotSupported"] = "Not equal date data validation rules";
|
|
22922
23019
|
})(WarningTypes || (WarningTypes = {}));
|
|
22923
23020
|
class XLSXImportWarningManager {
|
|
22924
23021
|
_parsingWarnings = new Set();
|
|
@@ -23297,6 +23394,25 @@ const IMAGE_EXTENSION_TO_MIMETYPE_MAPPING = {
|
|
|
23297
23394
|
webp: "image/webp",
|
|
23298
23395
|
jpg: "image/jpeg",
|
|
23299
23396
|
};
|
|
23397
|
+
const XLSX_DV_DECIMAL_OPERATOR_MAPPING = {
|
|
23398
|
+
between: "isBetween",
|
|
23399
|
+
notBetween: "isNotBetween",
|
|
23400
|
+
equal: "isEqual",
|
|
23401
|
+
notEqual: "isNotEqual",
|
|
23402
|
+
greaterThan: "isGreaterThan",
|
|
23403
|
+
greaterThanOrEqual: "isGreaterOrEqualTo",
|
|
23404
|
+
lessThan: "isLessThan",
|
|
23405
|
+
lessThanOrEqual: "isLessOrEqualTo",
|
|
23406
|
+
};
|
|
23407
|
+
const XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING = {
|
|
23408
|
+
between: "dateIsBetween",
|
|
23409
|
+
notBetween: "dateIsNotBetween",
|
|
23410
|
+
equal: "dateIs",
|
|
23411
|
+
greaterThan: "dateIsAfter",
|
|
23412
|
+
greaterThanOrEqual: "dateIsOnOrAfter",
|
|
23413
|
+
lessThan: "dateIsBefore",
|
|
23414
|
+
lessThanOrEqual: "dateIsOnOrBefore",
|
|
23415
|
+
};
|
|
23300
23416
|
|
|
23301
23417
|
/**
|
|
23302
23418
|
* Most of the functions could stay private, but are exported for testing purposes
|
|
@@ -24078,6 +24194,20 @@ function getRowPosition(rowIndex, sheetData) {
|
|
|
24078
24194
|
}
|
|
24079
24195
|
return position / HEIGHT_FACTOR;
|
|
24080
24196
|
}
|
|
24197
|
+
/**
|
|
24198
|
+
* Convert the o-spreadsheet data validation decimal
|
|
24199
|
+
* criterion type to the corresponding excel operator.
|
|
24200
|
+
*/
|
|
24201
|
+
function convertDecimalCriterionTypeToExcelOperator(operator) {
|
|
24202
|
+
return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
|
|
24203
|
+
}
|
|
24204
|
+
/**
|
|
24205
|
+
* Convert the o-spreadsheet data validation date
|
|
24206
|
+
* criterion type to the corresponding excel operator.
|
|
24207
|
+
*/
|
|
24208
|
+
function convertDateCriterionTypeToExcelOperator(operator) {
|
|
24209
|
+
return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
|
|
24210
|
+
}
|
|
24081
24211
|
|
|
24082
24212
|
function convertFigures(sheetData) {
|
|
24083
24213
|
let id = 1;
|
|
@@ -24187,6 +24317,112 @@ function getPositionFromAnchor(anchor, sheetData) {
|
|
|
24187
24317
|
};
|
|
24188
24318
|
}
|
|
24189
24319
|
|
|
24320
|
+
function convertDataValidationRules(xlsxDataValidations, warningManager) {
|
|
24321
|
+
const dvRules = [];
|
|
24322
|
+
let dvId = 1;
|
|
24323
|
+
for (const dv of xlsxDataValidations) {
|
|
24324
|
+
if (!dv) {
|
|
24325
|
+
continue;
|
|
24326
|
+
}
|
|
24327
|
+
switch (dv.type) {
|
|
24328
|
+
case "time":
|
|
24329
|
+
warningManager.generateNotSupportedWarning(WarningTypes.TimeDataValidationNotSupported);
|
|
24330
|
+
break;
|
|
24331
|
+
case "textLength":
|
|
24332
|
+
warningManager.generateNotSupportedWarning(WarningTypes.TextLengthDataValidationNotSupported);
|
|
24333
|
+
break;
|
|
24334
|
+
case "whole":
|
|
24335
|
+
warningManager.generateNotSupportedWarning(WarningTypes.WholeNumberDataValidationNotSupported);
|
|
24336
|
+
break;
|
|
24337
|
+
case "decimal":
|
|
24338
|
+
const decimalRule = convertDecimalRule(dvId++, dv);
|
|
24339
|
+
dvRules.push(decimalRule);
|
|
24340
|
+
break;
|
|
24341
|
+
case "list":
|
|
24342
|
+
const listRule = convertListrule(dvId++, dv);
|
|
24343
|
+
dvRules.push(listRule);
|
|
24344
|
+
break;
|
|
24345
|
+
case "date":
|
|
24346
|
+
if (dv.operator === "notEqual") {
|
|
24347
|
+
warningManager.generateNotSupportedWarning(WarningTypes.NotEqualDateDataValidationNotSupported);
|
|
24348
|
+
break;
|
|
24349
|
+
}
|
|
24350
|
+
const dateRule = convertDateRule(dvId++, dv);
|
|
24351
|
+
dvRules.push(dateRule);
|
|
24352
|
+
break;
|
|
24353
|
+
case "custom":
|
|
24354
|
+
const customRule = convertCustomRule(dvId++, dv);
|
|
24355
|
+
dvRules.push(customRule);
|
|
24356
|
+
break;
|
|
24357
|
+
}
|
|
24358
|
+
}
|
|
24359
|
+
return dvRules;
|
|
24360
|
+
}
|
|
24361
|
+
function convertDecimalRule(id, dv) {
|
|
24362
|
+
const values = [dv.formula1.toString()];
|
|
24363
|
+
if (dv.formula2) {
|
|
24364
|
+
values.push(dv.formula2.toString());
|
|
24365
|
+
}
|
|
24366
|
+
return {
|
|
24367
|
+
id: id.toString(),
|
|
24368
|
+
ranges: dv.sqref,
|
|
24369
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24370
|
+
criterion: {
|
|
24371
|
+
type: XLSX_DV_DECIMAL_OPERATOR_MAPPING[dv.operator],
|
|
24372
|
+
values,
|
|
24373
|
+
},
|
|
24374
|
+
};
|
|
24375
|
+
}
|
|
24376
|
+
function convertListrule(id, dv) {
|
|
24377
|
+
const formula1 = dv.formula1.toString();
|
|
24378
|
+
const isRangeRule = rangeReference.test(formula1);
|
|
24379
|
+
return {
|
|
24380
|
+
id: id.toString(),
|
|
24381
|
+
ranges: dv.sqref,
|
|
24382
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24383
|
+
criterion: {
|
|
24384
|
+
type: isRangeRule ? "isValueInRange" : "isValueInList",
|
|
24385
|
+
values: isRangeRule ? [formula1] : formula1.replaceAll('"', "").split(","),
|
|
24386
|
+
displayStyle: "arrow",
|
|
24387
|
+
},
|
|
24388
|
+
};
|
|
24389
|
+
}
|
|
24390
|
+
function convertDateRule(id, dv) {
|
|
24391
|
+
let criterion;
|
|
24392
|
+
const values = [dv.formula1.toString()];
|
|
24393
|
+
if (dv.formula2) {
|
|
24394
|
+
values.push(dv.formula2.toString());
|
|
24395
|
+
criterion = {
|
|
24396
|
+
type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
|
|
24397
|
+
values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
|
|
24398
|
+
};
|
|
24399
|
+
}
|
|
24400
|
+
else {
|
|
24401
|
+
criterion = {
|
|
24402
|
+
type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
|
|
24403
|
+
values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
|
|
24404
|
+
dateValue: "exactDate",
|
|
24405
|
+
};
|
|
24406
|
+
}
|
|
24407
|
+
return {
|
|
24408
|
+
id: id.toString(),
|
|
24409
|
+
ranges: dv.sqref,
|
|
24410
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24411
|
+
criterion: criterion,
|
|
24412
|
+
};
|
|
24413
|
+
}
|
|
24414
|
+
function convertCustomRule(id, dv) {
|
|
24415
|
+
return {
|
|
24416
|
+
id: id.toString(),
|
|
24417
|
+
ranges: dv.sqref,
|
|
24418
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24419
|
+
criterion: {
|
|
24420
|
+
type: "customFormula",
|
|
24421
|
+
values: [`=${dv.formula1.toString()}`],
|
|
24422
|
+
},
|
|
24423
|
+
};
|
|
24424
|
+
}
|
|
24425
|
+
|
|
24190
24426
|
/**
|
|
24191
24427
|
* Match external reference (ex. '[1]Sheet 3'!$B$4)
|
|
24192
24428
|
*
|
|
@@ -24307,6 +24543,7 @@ function convertSheets(data, warningManager) {
|
|
|
24307
24543
|
cols: convertCols(sheet, sheetDims[0], colHeaderGroups),
|
|
24308
24544
|
rows: convertRows(sheet, sheetDims[1], rowHeaderGroups),
|
|
24309
24545
|
conditionalFormats: convertConditionalFormats(sheet.cfs, data.dxfs, warningManager),
|
|
24546
|
+
dataValidationRules: convertDataValidationRules(sheet.dataValidations, warningManager),
|
|
24310
24547
|
figures: convertFigures(sheet),
|
|
24311
24548
|
isVisible: sheet.isVisible,
|
|
24312
24549
|
panes: sheetOptions
|
|
@@ -25586,6 +25823,41 @@ class XlsxCfExtractor extends XlsxBaseExtractor {
|
|
|
25586
25823
|
}
|
|
25587
25824
|
}
|
|
25588
25825
|
|
|
25826
|
+
class XlsxDataValidationExtractor extends XlsxBaseExtractor {
|
|
25827
|
+
theme;
|
|
25828
|
+
constructor(sheetFile, xlsxStructure, warningManager, theme) {
|
|
25829
|
+
super(sheetFile, xlsxStructure, warningManager);
|
|
25830
|
+
this.theme = theme;
|
|
25831
|
+
}
|
|
25832
|
+
extractDataValidations() {
|
|
25833
|
+
const dataValidations = this.mapOnElements({ parent: this.rootFile.file.xml, query: "worksheet > dataValidations > dataValidation" }, (dvElement) => {
|
|
25834
|
+
return {
|
|
25835
|
+
type: this.extractAttr(dvElement, "type", { required: true }).asString(),
|
|
25836
|
+
operator: this.extractAttr(dvElement, "operator", {
|
|
25837
|
+
default: "between",
|
|
25838
|
+
})?.asString(),
|
|
25839
|
+
sqref: this.extractAttr(dvElement, "sqref", { required: true }).asString().split(" "),
|
|
25840
|
+
errorStyle: this.extractAttr(dvElement, "errorStyle")?.asString(),
|
|
25841
|
+
formula1: this.extractDataValidationFormula(dvElement, 1)[0],
|
|
25842
|
+
formula2: this.extractDataValidationFormula(dvElement, 2)[0],
|
|
25843
|
+
showErrorMessage: this.extractAttr(dvElement, "showErrorMessage")?.asBool(),
|
|
25844
|
+
errorTitle: this.extractAttr(dvElement, "errorTitle")?.asString(),
|
|
25845
|
+
error: this.extractAttr(dvElement, "error")?.asString(),
|
|
25846
|
+
showInputMessage: this.extractAttr(dvElement, "showInputMessage")?.asBool(),
|
|
25847
|
+
promptTitle: this.extractAttr(dvElement, "promptTitle")?.asString(),
|
|
25848
|
+
prompt: this.extractAttr(dvElement, "prompt")?.asString(),
|
|
25849
|
+
allowBlank: this.extractAttr(dvElement, "allowBlank")?.asBool(),
|
|
25850
|
+
};
|
|
25851
|
+
});
|
|
25852
|
+
return dataValidations;
|
|
25853
|
+
}
|
|
25854
|
+
extractDataValidationFormula(dvElement, index) {
|
|
25855
|
+
return this.mapOnElements({ parent: dvElement, query: `formula${index}` }, (cfFormulaElements) => {
|
|
25856
|
+
return this.extractTextContent(cfFormulaElements, { required: true });
|
|
25857
|
+
});
|
|
25858
|
+
}
|
|
25859
|
+
}
|
|
25860
|
+
|
|
25589
25861
|
class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
25590
25862
|
extractChart() {
|
|
25591
25863
|
return this.mapOnElements({ parent: this.rootFile.file.xml, query: "c:chartSpace" }, (rootChartElement) => {
|
|
@@ -25953,6 +26225,7 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
|
|
|
25953
26225
|
sharedFormulas: this.extractSharedFormulas(sheetElement),
|
|
25954
26226
|
merges: this.extractMerges(sheetElement),
|
|
25955
26227
|
cfs: this.extractConditionalFormats(),
|
|
26228
|
+
dataValidations: this.extractDataValidations(),
|
|
25956
26229
|
figures: this.extractFigures(sheetElement),
|
|
25957
26230
|
hyperlinks: this.extractHyperLinks(sheetElement),
|
|
25958
26231
|
tables: this.extractTables(sheetElement),
|
|
@@ -26024,6 +26297,9 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
|
|
|
26024
26297
|
extractConditionalFormats() {
|
|
26025
26298
|
return new XlsxCfExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractConditionalFormattings();
|
|
26026
26299
|
}
|
|
26300
|
+
extractDataValidations() {
|
|
26301
|
+
return new XlsxDataValidationExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractDataValidations();
|
|
26302
|
+
}
|
|
26027
26303
|
extractFigures(worksheet) {
|
|
26028
26304
|
const figures = this.mapOnElements({ parent: worksheet, query: "drawing" }, (drawingElement) => {
|
|
26029
26305
|
const drawingId = this.extractAttr(drawingElement, "r:id", { required: true })?.asString();
|
|
@@ -27331,6 +27607,7 @@ function createEmptySheet(sheetId, name) {
|
|
|
27331
27607
|
rows: {},
|
|
27332
27608
|
merges: [],
|
|
27333
27609
|
conditionalFormats: [],
|
|
27610
|
+
dataValidationRules: [],
|
|
27334
27611
|
figures: [],
|
|
27335
27612
|
tables: [],
|
|
27336
27613
|
isVisible: true,
|
|
@@ -27405,17 +27682,15 @@ function interactivePasteFromOS(env, target, clipboardContent, pasteOption) {
|
|
|
27405
27682
|
});
|
|
27406
27683
|
}
|
|
27407
27684
|
catch (error) {
|
|
27408
|
-
const parsedSpreadsheetContent = clipboardContent
|
|
27409
|
-
|
|
27410
|
-
: {};
|
|
27411
|
-
if (parsedSpreadsheetContent.version && parsedSpreadsheetContent.version !== CURRENT_VERSION) {
|
|
27685
|
+
const parsedSpreadsheetContent = clipboardContent.data;
|
|
27686
|
+
if (parsedSpreadsheetContent?.version !== CURRENT_VERSION) {
|
|
27412
27687
|
env.raiseError(_t("An unexpected error occurred while pasting content.\
|
|
27413
27688
|
This is probably due to a spreadsheet version mismatch."));
|
|
27414
27689
|
}
|
|
27415
27690
|
result = env.model.dispatch("PASTE_FROM_OS_CLIPBOARD", {
|
|
27416
27691
|
target,
|
|
27417
27692
|
clipboardContent: {
|
|
27418
|
-
|
|
27693
|
+
text: clipboardContent.text,
|
|
27419
27694
|
},
|
|
27420
27695
|
pasteOption,
|
|
27421
27696
|
});
|
|
@@ -27540,6 +27815,13 @@ const DVTerms = {
|
|
|
27540
27815
|
numberValue: _t("The value must be a number"),
|
|
27541
27816
|
dateValue: _t("The value must be a date"),
|
|
27542
27817
|
validRange: _t("The value must be a valid range"),
|
|
27818
|
+
validFormula: _t("The formula must be valid"),
|
|
27819
|
+
},
|
|
27820
|
+
Errors: {
|
|
27821
|
+
["InvalidRange" /* CommandResult.InvalidRange */]: _t("The range is invalid."),
|
|
27822
|
+
["InvalidDataValidationCriterionValue" /* CommandResult.InvalidDataValidationCriterionValue */]: _t("One or more of the provided criteria values are invalid. Please review and correct them."),
|
|
27823
|
+
["InvalidNumberOfCriterionValues" /* CommandResult.InvalidNumberOfCriterionValues */]: _t("One or more of the provided criteria values are missing."),
|
|
27824
|
+
Unexpected: _t("The rule is invalid for an unknown reason."),
|
|
27543
27825
|
},
|
|
27544
27826
|
};
|
|
27545
27827
|
const TableTerms = {
|
|
@@ -27750,7 +28032,7 @@ function getBarChartData(definition, dataSets, labelRange, getters) {
|
|
|
27750
28032
|
labels.length > dataSetsValues[0].data.length) {
|
|
27751
28033
|
labels.shift();
|
|
27752
28034
|
}
|
|
27753
|
-
({ labels, dataSetsValues } =
|
|
28035
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27754
28036
|
if (definition.aggregated) {
|
|
27755
28037
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
27756
28038
|
}
|
|
@@ -27805,7 +28087,7 @@ function getLineChartData(definition, dataSets, labelRange, getters) {
|
|
|
27805
28087
|
labels.length > dataSetsValues[0].data.length) {
|
|
27806
28088
|
labels.shift();
|
|
27807
28089
|
}
|
|
27808
|
-
({ labels, dataSetsValues } =
|
|
28090
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27809
28091
|
if (axisType === "time") {
|
|
27810
28092
|
({ labels, dataSetsValues } = fixEmptyLabelsForDateCharts(labels, dataSetsValues));
|
|
27811
28093
|
}
|
|
@@ -27822,7 +28104,7 @@ function getLineChartData(definition, dataSets, labelRange, getters) {
|
|
|
27822
28104
|
if (definition.cumulative) {
|
|
27823
28105
|
let accumulator = 0;
|
|
27824
28106
|
data = data.map((value) => {
|
|
27825
|
-
if (!isNaN(value)) {
|
|
28107
|
+
if (!isNaN(parseFloat(value))) {
|
|
27826
28108
|
accumulator += parseFloat(value);
|
|
27827
28109
|
return accumulator;
|
|
27828
28110
|
}
|
|
@@ -27855,11 +28137,11 @@ function getPieChartData(definition, dataSets, labelRange, getters) {
|
|
|
27855
28137
|
labels.length > dataSetsValues[0].data.length) {
|
|
27856
28138
|
labels.shift();
|
|
27857
28139
|
}
|
|
27858
|
-
({ labels, dataSetsValues } =
|
|
28140
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27859
28141
|
if (definition.aggregated) {
|
|
27860
28142
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
27861
28143
|
}
|
|
27862
|
-
({ dataSetsValues, labels } =
|
|
28144
|
+
({ dataSetsValues, labels } = keepOnlyPositiveValues(labels, dataSetsValues));
|
|
27863
28145
|
const dataSetFormat = getChartDatasetFormat(getters, dataSets, "left");
|
|
27864
28146
|
return {
|
|
27865
28147
|
dataSetsValues,
|
|
@@ -27877,7 +28159,7 @@ function getRadarChartData(definition, dataSets, labelRange, getters) {
|
|
|
27877
28159
|
labels.length > dataSetsValues[0].data.length) {
|
|
27878
28160
|
labels.shift();
|
|
27879
28161
|
}
|
|
27880
|
-
({ labels, dataSetsValues } =
|
|
28162
|
+
({ labels, dataSetsValues } = filterInvalidDataPoints(labels, dataSetsValues));
|
|
27881
28163
|
if (definition.aggregated) {
|
|
27882
28164
|
({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
|
|
27883
28165
|
}
|
|
@@ -28056,26 +28338,16 @@ function isLuxonTimeAdapterInstalled() {
|
|
|
28056
28338
|
}
|
|
28057
28339
|
return isInstalled;
|
|
28058
28340
|
}
|
|
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 };
|
|
28341
|
+
function keepOnlyPositiveValues(labels, datasets) {
|
|
28342
|
+
const numberOfDataPoints = Math.max(labels.length, ...datasets.map((dataset) => dataset.data?.length || 0));
|
|
28343
|
+
const filteredIndexes = range(0, numberOfDataPoints).filter((i) => datasets.some((ds) => typeof ds.data[i] === "number" && ds.data[i] > 0));
|
|
28344
|
+
return {
|
|
28345
|
+
labels: filteredIndexes.map((i) => labels[i] || ""),
|
|
28346
|
+
dataSetsValues: datasets.map((ds) => ({
|
|
28347
|
+
...ds,
|
|
28348
|
+
data: filteredIndexes.map((i) => typeof ds.data[i] === "number" && ds.data[i] > 0 ? ds.data[i] : null),
|
|
28349
|
+
})),
|
|
28350
|
+
};
|
|
28079
28351
|
}
|
|
28080
28352
|
function fixEmptyLabelsForDateCharts(labels, dataSetsValues) {
|
|
28081
28353
|
if (labels.length === 0 || labels.every((label) => !label)) {
|
|
@@ -28108,18 +28380,23 @@ function getData(getters, ds) {
|
|
|
28108
28380
|
}
|
|
28109
28381
|
return [];
|
|
28110
28382
|
}
|
|
28111
|
-
|
|
28383
|
+
/**
|
|
28384
|
+
* Filter the data points that:
|
|
28385
|
+
* - have neither a label nor a value
|
|
28386
|
+
* - have no label and a non-numeric value
|
|
28387
|
+
*/
|
|
28388
|
+
function filterInvalidDataPoints(labels, datasets) {
|
|
28112
28389
|
const numberOfDataPoints = Math.max(labels.length, ...datasets.map((dataset) => dataset.data?.length || 0));
|
|
28113
28390
|
const dataPointsIndexes = range(0, numberOfDataPoints).filter((dataPointIndex) => {
|
|
28114
28391
|
const label = labels[dataPointIndex];
|
|
28115
28392
|
const values = datasets.map((dataset) => dataset.data?.[dataPointIndex]);
|
|
28116
|
-
return label || values.some((value) => value ===
|
|
28393
|
+
return label || values.some((value) => typeof value === "number");
|
|
28117
28394
|
});
|
|
28118
28395
|
return {
|
|
28119
28396
|
labels: dataPointsIndexes.map((i) => labels[i] || ""),
|
|
28120
28397
|
dataSetsValues: datasets.map((dataset) => ({
|
|
28121
28398
|
...dataset,
|
|
28122
|
-
data: dataPointsIndexes.map((i) => dataset.data[i]),
|
|
28399
|
+
data: dataPointsIndexes.map((i) => typeof dataset.data[i] === "number" ? dataset.data[i] : null),
|
|
28123
28400
|
})),
|
|
28124
28401
|
};
|
|
28125
28402
|
}
|
|
@@ -28262,8 +28539,8 @@ function getBarChartDatasets(definition, args) {
|
|
|
28262
28539
|
const dataset = {
|
|
28263
28540
|
label,
|
|
28264
28541
|
data,
|
|
28265
|
-
borderColor:
|
|
28266
|
-
borderWidth: 1,
|
|
28542
|
+
borderColor: definition.background || BACKGROUND_CHART_COLOR,
|
|
28543
|
+
borderWidth: definition.stacked ? 1 : 0,
|
|
28267
28544
|
backgroundColor,
|
|
28268
28545
|
yAxisID: definition.horizontal ? "y" : definition.dataSets?.[index].yAxisId || "y",
|
|
28269
28546
|
xAxisID: "x",
|
|
@@ -32935,13 +33212,11 @@ async function paste$1(env, pasteOption) {
|
|
|
32935
33212
|
const osClipboard = await env.clipboard.read();
|
|
32936
33213
|
switch (osClipboard.status) {
|
|
32937
33214
|
case "ok":
|
|
32938
|
-
const
|
|
32939
|
-
const
|
|
32940
|
-
const clipboardId = JSON.parse(osClipboardSpreadsheetContent).clipboardId ??
|
|
32941
|
-
htmlDocument.querySelector("div")?.getAttribute("data-clipboard-id");
|
|
33215
|
+
const clipboardContent = parseOSClipboardContent(osClipboard.content);
|
|
33216
|
+
const clipboardId = clipboardContent.data?.clipboardId;
|
|
32942
33217
|
const target = env.model.getters.getSelectedZones();
|
|
32943
33218
|
if (env.model.getters.getClipboardId() !== clipboardId) {
|
|
32944
|
-
interactivePasteFromOS(env, target,
|
|
33219
|
+
interactivePasteFromOS(env, target, clipboardContent, pasteOption);
|
|
32945
33220
|
}
|
|
32946
33221
|
else {
|
|
32947
33222
|
interactivePaste(env, target, pasteOption);
|
|
@@ -36161,7 +36436,7 @@ css /* scss */ `
|
|
|
36161
36436
|
flex-grow: 0;
|
|
36162
36437
|
}
|
|
36163
36438
|
|
|
36164
|
-
|
|
36439
|
+
/* Make the character a bit bigger
|
|
36165
36440
|
compared to its neighbor INPUT box */
|
|
36166
36441
|
.o-remove-selection {
|
|
36167
36442
|
font-size: calc(100% + 4px);
|
|
@@ -36766,7 +37041,7 @@ const CONTAINER_WIDTH = CONTENT_WIDTH + 2 * PICKER_PADDING;
|
|
|
36766
37041
|
css /* scss */ `
|
|
36767
37042
|
.o-color-picker {
|
|
36768
37043
|
padding: ${PICKER_PADDING}px 0;
|
|
36769
|
-
|
|
37044
|
+
/* FIXME: this is useless, overiden by the popover container */
|
|
36770
37045
|
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
|
|
36771
37046
|
background-color: white;
|
|
36772
37047
|
line-height: 1.2;
|
|
@@ -36909,7 +37184,7 @@ css /* scss */ `
|
|
|
36909
37184
|
margin-right: 2px;
|
|
36910
37185
|
}
|
|
36911
37186
|
.o-wrong-color {
|
|
36912
|
-
|
|
37187
|
+
/* FIXME bootstrap class instead? */
|
|
36913
37188
|
outline-color: red;
|
|
36914
37189
|
border-color: red;
|
|
36915
37190
|
&:focus {
|
|
@@ -38953,10 +39228,10 @@ css /* scss */ `
|
|
|
38953
39228
|
}
|
|
38954
39229
|
|
|
38955
39230
|
.fa-stack {
|
|
38956
|
-
|
|
38957
|
-
width:
|
|
38958
|
-
height:
|
|
38959
|
-
line-height:
|
|
39231
|
+
/* reset stack size which is doubled by default */
|
|
39232
|
+
width: ${CLOSE_ICON_RADIUS * 2}px;
|
|
39233
|
+
height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
39234
|
+
line-height: ${CLOSE_ICON_RADIUS * 2}px;
|
|
38960
39235
|
}
|
|
38961
39236
|
|
|
38962
39237
|
.force-open-assistant {
|
|
@@ -38973,7 +39248,7 @@ css /* scss */ `
|
|
|
38973
39248
|
margin: 1px 4px;
|
|
38974
39249
|
|
|
38975
39250
|
.o-semi-bold {
|
|
38976
|
-
|
|
39251
|
+
/* FIXME: to remove in favor of Bootstrap
|
|
38977
39252
|
* 'fw-semibold' when we upgrade to Bootstrap 5.2
|
|
38978
39253
|
*/
|
|
38979
39254
|
font-weight: 600 !important;
|
|
@@ -40209,7 +40484,7 @@ css /* scss */ `
|
|
|
40209
40484
|
.o-threshold-value {
|
|
40210
40485
|
flex-grow: 1;
|
|
40211
40486
|
flex-basis: 60%;
|
|
40212
|
-
min-width: 0px;
|
|
40487
|
+
min-width: 0px; /* input overflows in Firefox otherwise */
|
|
40213
40488
|
}
|
|
40214
40489
|
.o-threshold-value input:disabled {
|
|
40215
40490
|
background-color: #edebed;
|
|
@@ -41028,7 +41303,7 @@ dataValidationEvaluatorRegistry.add("dateIs", {
|
|
|
41028
41303
|
name: _t("Date is"),
|
|
41029
41304
|
getPreview: (criterion, getters) => {
|
|
41030
41305
|
return criterion.dateValue === "exactDate"
|
|
41031
|
-
? _t("Date is %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41306
|
+
? _t("Date is %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41032
41307
|
: _t("Date is %s", DVTerms.DateIs[criterion.dateValue]);
|
|
41033
41308
|
},
|
|
41034
41309
|
});
|
|
@@ -41053,7 +41328,7 @@ dataValidationEvaluatorRegistry.add("dateIsBefore", {
|
|
|
41053
41328
|
name: _t("Date is before"),
|
|
41054
41329
|
getPreview: (criterion, getters) => {
|
|
41055
41330
|
return criterion.dateValue === "exactDate"
|
|
41056
|
-
? _t("Date is before %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41331
|
+
? _t("Date is before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41057
41332
|
: _t("Date is before %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41058
41333
|
},
|
|
41059
41334
|
});
|
|
@@ -41078,7 +41353,7 @@ dataValidationEvaluatorRegistry.add("dateIsOnOrBefore", {
|
|
|
41078
41353
|
name: _t("Date is on or before"),
|
|
41079
41354
|
getPreview: (criterion, getters) => {
|
|
41080
41355
|
return criterion.dateValue === "exactDate"
|
|
41081
|
-
? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41356
|
+
? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41082
41357
|
: _t("Date is on or before %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41083
41358
|
},
|
|
41084
41359
|
});
|
|
@@ -41103,7 +41378,7 @@ dataValidationEvaluatorRegistry.add("dateIsAfter", {
|
|
|
41103
41378
|
name: _t("Date is after"),
|
|
41104
41379
|
getPreview: (criterion, getters) => {
|
|
41105
41380
|
return criterion.dateValue === "exactDate"
|
|
41106
|
-
? _t("Date is after %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41381
|
+
? _t("Date is after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41107
41382
|
: _t("Date is after %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41108
41383
|
},
|
|
41109
41384
|
});
|
|
@@ -41128,7 +41403,7 @@ dataValidationEvaluatorRegistry.add("dateIsOnOrAfter", {
|
|
|
41128
41403
|
name: _t("Date is on or after"),
|
|
41129
41404
|
getPreview: (criterion, getters) => {
|
|
41130
41405
|
return criterion.dateValue === "exactDate"
|
|
41131
|
-
? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
41406
|
+
? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
41132
41407
|
: _t("Date is on or after %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
41133
41408
|
},
|
|
41134
41409
|
});
|
|
@@ -41154,7 +41429,7 @@ dataValidationEvaluatorRegistry.add("dateIsBetween", {
|
|
|
41154
41429
|
numberOfValues: () => 2,
|
|
41155
41430
|
name: _t("Date is between"),
|
|
41156
41431
|
getPreview: (criterion, getters) => {
|
|
41157
|
-
const values = getDateCriterionFormattedValues(criterion, getters);
|
|
41432
|
+
const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
|
|
41158
41433
|
return _t("Date is between %s and %s", values[0], values[1]);
|
|
41159
41434
|
},
|
|
41160
41435
|
});
|
|
@@ -41180,7 +41455,7 @@ dataValidationEvaluatorRegistry.add("dateIsNotBetween", {
|
|
|
41180
41455
|
numberOfValues: () => 2,
|
|
41181
41456
|
name: _t("Date is not between"),
|
|
41182
41457
|
getPreview: (criterion, getters) => {
|
|
41183
|
-
const values = getDateCriterionFormattedValues(criterion, getters);
|
|
41458
|
+
const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
|
|
41184
41459
|
return _t("Date is not between %s and %s", values[0], values[1]);
|
|
41185
41460
|
},
|
|
41186
41461
|
});
|
|
@@ -41463,19 +41738,6 @@ function checkValueIsNumber(value) {
|
|
|
41463
41738
|
const valueAsNumber = tryToNumber(value, DEFAULT_LOCALE);
|
|
41464
41739
|
return valueAsNumber !== undefined;
|
|
41465
41740
|
}
|
|
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
41741
|
|
|
41480
41742
|
/** 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
41743
|
class SelectMenu extends Component {
|
|
@@ -41557,6 +41819,7 @@ class DataValidationInput extends Component {
|
|
|
41557
41819
|
focused: false,
|
|
41558
41820
|
onBlur: () => { },
|
|
41559
41821
|
};
|
|
41822
|
+
static components = { StandaloneComposer: StandaloneComposer };
|
|
41560
41823
|
inputRef = useRef("input");
|
|
41561
41824
|
setup() {
|
|
41562
41825
|
useEffect(() => {
|
|
@@ -41568,10 +41831,6 @@ class DataValidationInput extends Component {
|
|
|
41568
41831
|
state = useState({
|
|
41569
41832
|
shouldDisplayError: !!this.props.value, // Don't display error if user inputted nothing yet
|
|
41570
41833
|
});
|
|
41571
|
-
onValueChanged(ev) {
|
|
41572
|
-
this.state.shouldDisplayError = true;
|
|
41573
|
-
this.props.onValueChanged(ev.target.value);
|
|
41574
|
-
}
|
|
41575
41834
|
get placeholder() {
|
|
41576
41835
|
const evaluator = dataValidationEvaluatorRegistry.get(this.props.criterionType);
|
|
41577
41836
|
if (evaluator.allowedValues === "onlyFormulas") {
|
|
@@ -41582,6 +41841,27 @@ class DataValidationInput extends Component {
|
|
|
41582
41841
|
}
|
|
41583
41842
|
return _t("Value or formula");
|
|
41584
41843
|
}
|
|
41844
|
+
get allowedValues() {
|
|
41845
|
+
const evaluator = dataValidationEvaluatorRegistry.get(this.props.criterionType);
|
|
41846
|
+
return evaluator.allowedValues ?? "any";
|
|
41847
|
+
}
|
|
41848
|
+
onInputValueChanged(ev) {
|
|
41849
|
+
this.state.shouldDisplayError = true;
|
|
41850
|
+
this.props.onValueChanged(ev.target.value);
|
|
41851
|
+
}
|
|
41852
|
+
onChangeComposerValue(str) {
|
|
41853
|
+
this.state.shouldDisplayError = true;
|
|
41854
|
+
this.props.onValueChanged(str);
|
|
41855
|
+
}
|
|
41856
|
+
getDataValidationRuleInputComposerProps() {
|
|
41857
|
+
return {
|
|
41858
|
+
onConfirm: (str) => this.onChangeComposerValue(str),
|
|
41859
|
+
composerContent: this.props.value,
|
|
41860
|
+
placeholder: this.placeholder,
|
|
41861
|
+
class: "o-sidePanel-composer",
|
|
41862
|
+
defaultRangeSheetId: this.env.model.getters.getActiveSheetId(),
|
|
41863
|
+
};
|
|
41864
|
+
}
|
|
41585
41865
|
get errorMessage() {
|
|
41586
41866
|
if (!this.state.shouldDisplayError) {
|
|
41587
41867
|
return undefined;
|
|
@@ -41917,13 +42197,13 @@ function getDataValidationCriterionMenuItems(callback) {
|
|
|
41917
42197
|
|
|
41918
42198
|
class DataValidationEditor extends Component {
|
|
41919
42199
|
static template = "o-spreadsheet-DataValidationEditor";
|
|
41920
|
-
static components = { SelectionInput, SelectMenu, Section };
|
|
42200
|
+
static components = { SelectionInput, SelectMenu, Section, ValidationMessages };
|
|
41921
42201
|
static props = {
|
|
41922
42202
|
rule: { type: Object, optional: true },
|
|
41923
42203
|
onExit: Function,
|
|
41924
42204
|
onCloseSidePanel: { type: Function, optional: true },
|
|
41925
42205
|
};
|
|
41926
|
-
state = useState({ rule: this.defaultDataValidationRule });
|
|
42206
|
+
state = useState({ rule: this.defaultDataValidationRule, errors: [] });
|
|
41927
42207
|
setup() {
|
|
41928
42208
|
if (this.props.rule) {
|
|
41929
42209
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
@@ -41948,15 +42228,15 @@ class DataValidationEditor extends Component {
|
|
|
41948
42228
|
this.state.rule.isBlocking = isBlocking === "true";
|
|
41949
42229
|
}
|
|
41950
42230
|
onSave() {
|
|
41951
|
-
if (
|
|
41952
|
-
|
|
42231
|
+
if (this.state.rule) {
|
|
42232
|
+
const result = this.env.model.dispatch("ADD_DATA_VALIDATION_RULE", this.dispatchPayload);
|
|
42233
|
+
if (!result.isSuccessful) {
|
|
42234
|
+
this.state.errors = result.reasons;
|
|
42235
|
+
}
|
|
42236
|
+
else {
|
|
42237
|
+
this.props.onExit();
|
|
42238
|
+
}
|
|
41953
42239
|
}
|
|
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
42240
|
}
|
|
41961
42241
|
get dispatchPayload() {
|
|
41962
42242
|
const rule = { ...this.state.rule, ranges: undefined };
|
|
@@ -41997,6 +42277,9 @@ class DataValidationEditor extends Component {
|
|
|
41997
42277
|
get criterionComponent() {
|
|
41998
42278
|
return dataValidationPanelCriteriaRegistry.get(this.state.rule.criterion.type).component;
|
|
41999
42279
|
}
|
|
42280
|
+
get errorMessages() {
|
|
42281
|
+
return this.state.errors.map((error) => DVTerms.Errors[error] || DVTerms.Errors.Unexpected);
|
|
42282
|
+
}
|
|
42000
42283
|
}
|
|
42001
42284
|
|
|
42002
42285
|
css /* scss */ `
|
|
@@ -42008,7 +42291,7 @@ css /* scss */ `
|
|
|
42008
42291
|
border-bottom: 1px solid ${FIGURE_BORDER_COLOR};
|
|
42009
42292
|
|
|
42010
42293
|
.o-dv-container {
|
|
42011
|
-
min-width: 0;
|
|
42294
|
+
min-width: 0; /* otherwise flex won't shrink correctly */
|
|
42012
42295
|
}
|
|
42013
42296
|
|
|
42014
42297
|
.o-dv-preview-description {
|
|
@@ -45026,6 +45309,9 @@ class PivotSidePanel extends Component {
|
|
|
45026
45309
|
PivotLayoutConfigurator,
|
|
45027
45310
|
Section,
|
|
45028
45311
|
};
|
|
45312
|
+
setup() {
|
|
45313
|
+
useHighlights(this);
|
|
45314
|
+
}
|
|
45029
45315
|
get sidePanelEditor() {
|
|
45030
45316
|
const pivot = this.env.model.getters.getPivotCoreDefinition(this.props.pivotId);
|
|
45031
45317
|
if (!pivot) {
|
|
@@ -45033,6 +45319,9 @@ class PivotSidePanel extends Component {
|
|
|
45033
45319
|
}
|
|
45034
45320
|
return pivotSidePanelRegistry.get(pivot.type).editor;
|
|
45035
45321
|
}
|
|
45322
|
+
get highlights() {
|
|
45323
|
+
return getPivotHighlights(this.env.model.getters, this.props.pivotId);
|
|
45324
|
+
}
|
|
45036
45325
|
}
|
|
45037
45326
|
|
|
45038
45327
|
css /* scss */ `
|
|
@@ -45702,7 +45991,7 @@ class TableStylePreview extends Component {
|
|
|
45702
45991
|
|
|
45703
45992
|
css /* scss */ `
|
|
45704
45993
|
.o-table-style-popover {
|
|
45705
|
-
|
|
45994
|
+
/* 7 tables preview + padding by line */
|
|
45706
45995
|
width: calc((66px + 4px * 2) * 7 + 1.5rem * 2);
|
|
45707
45996
|
background: #fff;
|
|
45708
45997
|
font-size: 14px;
|
|
@@ -47254,7 +47543,7 @@ css /* scss */ `
|
|
|
47254
47543
|
box-sizing: border-box !important;
|
|
47255
47544
|
accent-color: #808080;
|
|
47256
47545
|
margin: ${MARGIN}px;
|
|
47257
|
-
|
|
47546
|
+
/* required to prevent the checkbox position to be sensible to the font-size (affects Firefox) */
|
|
47258
47547
|
position: absolute;
|
|
47259
47548
|
}
|
|
47260
47549
|
`;
|
|
@@ -47601,7 +47890,7 @@ css /*SCSS*/ `
|
|
|
47601
47890
|
}
|
|
47602
47891
|
}
|
|
47603
47892
|
.o-figure-container {
|
|
47604
|
-
-webkit-user-select: none;
|
|
47893
|
+
-webkit-user-select: none; /* safari */
|
|
47605
47894
|
user-select: none;
|
|
47606
47895
|
}
|
|
47607
47896
|
`;
|
|
@@ -50674,25 +50963,20 @@ class Grid extends Component {
|
|
|
50674
50963
|
if (!clipboardData) {
|
|
50675
50964
|
return;
|
|
50676
50965
|
}
|
|
50677
|
-
const
|
|
50678
|
-
|
|
50679
|
-
|
|
50680
|
-
|
|
50966
|
+
const osClipboard = {
|
|
50967
|
+
content: {
|
|
50968
|
+
[ClipboardMIMEType.PlainText]: clipboardData?.getData(ClipboardMIMEType.PlainText),
|
|
50969
|
+
[ClipboardMIMEType.Html]: clipboardData?.getData(ClipboardMIMEType.Html),
|
|
50970
|
+
},
|
|
50971
|
+
};
|
|
50681
50972
|
const target = this.env.model.getters.getSelectedZones();
|
|
50682
50973
|
const isCutOperation = this.env.model.getters.isCutOperation();
|
|
50683
|
-
const
|
|
50684
|
-
|
|
50974
|
+
const clipboardContent = parseOSClipboardContent(osClipboard.content);
|
|
50975
|
+
const clipboardId = clipboardContent.data?.clipboardId;
|
|
50685
50976
|
if (this.env.model.getters.getClipboardId() === clipboardId) {
|
|
50686
50977
|
interactivePaste(this.env, target);
|
|
50687
50978
|
}
|
|
50688
50979
|
else {
|
|
50689
|
-
const clipboardContent = {
|
|
50690
|
-
[ClipboardMIMEType.PlainText]: clipboardDataTextContent,
|
|
50691
|
-
[ClipboardMIMEType.Html]: clipboardDataHtmlContent,
|
|
50692
|
-
};
|
|
50693
|
-
if (osClipboardSpreadsheetContent !== "{}") {
|
|
50694
|
-
clipboardContent[ClipboardMIMEType.OSpreadsheet] = osClipboardSpreadsheetContent;
|
|
50695
|
-
}
|
|
50696
50980
|
interactivePasteFromOS(this.env, target, clipboardContent);
|
|
50697
50981
|
}
|
|
50698
50982
|
if (isCutOperation) {
|
|
@@ -52512,8 +52796,9 @@ class ConditionalFormatPlugin extends CorePlugin {
|
|
|
52512
52796
|
if (replaceIndex > -1) {
|
|
52513
52797
|
currentRanges = rules[replaceIndex].ranges.map(toUnboundedZone);
|
|
52514
52798
|
}
|
|
52515
|
-
|
|
52516
|
-
|
|
52799
|
+
// Remove the zones first in case the same position is in toAdd and toRemove
|
|
52800
|
+
const withRemovedZones = recomputeZones(currentRanges, toRemove);
|
|
52801
|
+
return recomputeZones([...toAdd, ...withRemovedZones], []).map((zone) => this.getters.getRangeDataFromZone(sheetId, zone));
|
|
52517
52802
|
}
|
|
52518
52803
|
// ---------------------------------------------------------------------------
|
|
52519
52804
|
// Private
|
|
@@ -52824,7 +53109,7 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
52824
53109
|
allowDispatch(cmd) {
|
|
52825
53110
|
switch (cmd.type) {
|
|
52826
53111
|
case "ADD_DATA_VALIDATION_RULE":
|
|
52827
|
-
return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
|
|
53112
|
+
return this.checkValidations(cmd, this.chainValidations(this.checkEmptyRange, this.checkValidRange, this.checkCriterionTypeIsValid, this.checkCriterionHasValidNumberOfValues, this.checkCriterionValuesAreValid));
|
|
52828
53113
|
case "REMOVE_DATA_VALIDATION_RULE":
|
|
52829
53114
|
if (!this.rules[cmd.sheetId].find((rule) => rule.id === cmd.id)) {
|
|
52830
53115
|
return "UnknownDataValidationRule" /* CommandResult.UnknownDataValidationRule */;
|
|
@@ -52983,6 +53268,20 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
52983
53268
|
}
|
|
52984
53269
|
}
|
|
52985
53270
|
}
|
|
53271
|
+
exportForExcel(data) {
|
|
53272
|
+
if (!data.sheets) {
|
|
53273
|
+
return;
|
|
53274
|
+
}
|
|
53275
|
+
for (const sheet of data.sheets) {
|
|
53276
|
+
sheet.dataValidationRules = [];
|
|
53277
|
+
for (const rule of this.rules[sheet.id]) {
|
|
53278
|
+
sheet.dataValidationRules.push({
|
|
53279
|
+
...rule,
|
|
53280
|
+
ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useFixedReference: true })),
|
|
53281
|
+
});
|
|
53282
|
+
}
|
|
53283
|
+
}
|
|
53284
|
+
}
|
|
52986
53285
|
checkCriterionTypeIsValid(cmd) {
|
|
52987
53286
|
return dataValidationEvaluatorRegistry.contains(cmd.rule.criterion.type)
|
|
52988
53287
|
? "Success" /* CommandResult.Success */
|
|
@@ -53001,21 +53300,28 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
53001
53300
|
checkCriterionValuesAreValid(cmd) {
|
|
53002
53301
|
const criterion = cmd.rule.criterion;
|
|
53003
53302
|
const evaluator = dataValidationEvaluatorRegistry.get(criterion.type);
|
|
53004
|
-
|
|
53005
|
-
if (value.startsWith("=")) {
|
|
53006
|
-
return evaluator.allowedValues === "onlyLiterals";
|
|
53007
|
-
}
|
|
53008
|
-
else if (evaluator.allowedValues === "onlyFormulas") {
|
|
53303
|
+
const isInvalid = (value) => {
|
|
53304
|
+
if (evaluator.allowedValues === "onlyFormulas" && !value.startsWith("=")) {
|
|
53009
53305
|
return true;
|
|
53010
53306
|
}
|
|
53011
|
-
|
|
53012
|
-
return
|
|
53307
|
+
if (value.startsWith("=")) {
|
|
53308
|
+
return evaluator.allowedValues === "onlyLiterals" || compile(value).isBadExpression;
|
|
53013
53309
|
}
|
|
53014
|
-
|
|
53310
|
+
return !evaluator.isCriterionValueValid(value);
|
|
53311
|
+
};
|
|
53312
|
+
if (criterion.values.some(isInvalid)) {
|
|
53015
53313
|
return "InvalidDataValidationCriterionValue" /* CommandResult.InvalidDataValidationCriterionValue */;
|
|
53016
53314
|
}
|
|
53017
53315
|
return "Success" /* CommandResult.Success */;
|
|
53018
53316
|
}
|
|
53317
|
+
checkValidRange(cmd) {
|
|
53318
|
+
const ranges = cmd.ranges.map((range) => this.getters.getRangeFromRangeData(range));
|
|
53319
|
+
const stringRanges = ranges.map((range) => this.getters.getRangeString(range, cmd.sheetId));
|
|
53320
|
+
if (stringRanges.some((xc) => !this.getters.isRangeValid(xc))) {
|
|
53321
|
+
return "InvalidRange" /* CommandResult.InvalidRange */;
|
|
53322
|
+
}
|
|
53323
|
+
return "Success" /* CommandResult.Success */;
|
|
53324
|
+
}
|
|
53019
53325
|
}
|
|
53020
53326
|
|
|
53021
53327
|
class FigurePlugin extends CorePlugin {
|
|
@@ -54750,6 +55056,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
54750
55056
|
formats: {},
|
|
54751
55057
|
borders: {},
|
|
54752
55058
|
conditionalFormats: [],
|
|
55059
|
+
dataValidationRules: [],
|
|
54753
55060
|
figures: [],
|
|
54754
55061
|
tables: [],
|
|
54755
55062
|
areGridLinesVisible: sheet.areGridLinesVisible === undefined ? true : sheet.areGridLinesVisible,
|
|
@@ -58119,6 +58426,9 @@ class Evaluator {
|
|
|
58119
58426
|
}
|
|
58120
58427
|
for (let i = 0; i < positions.length; ++i) {
|
|
58121
58428
|
const position = positions[i];
|
|
58429
|
+
if (this.nextPositionsToUpdate.has(position)) {
|
|
58430
|
+
continue;
|
|
58431
|
+
}
|
|
58122
58432
|
const evaluatedCell = this.computeCell(position);
|
|
58123
58433
|
if (evaluatedCell !== EMPTY_CELL) {
|
|
58124
58434
|
this.evaluatedCells.set(position, evaluatedCell);
|
|
@@ -58126,6 +58436,9 @@ class Evaluator {
|
|
|
58126
58436
|
}
|
|
58127
58437
|
onIterationEndEvaluationRegistry.getAll().forEach((callback) => callback(this.getters));
|
|
58128
58438
|
}
|
|
58439
|
+
if (currentIteration >= MAX_ITERATION) {
|
|
58440
|
+
console.warn("Maximum iteration reached while evaluating cells");
|
|
58441
|
+
}
|
|
58129
58442
|
}
|
|
58130
58443
|
computeCell(position) {
|
|
58131
58444
|
const evaluation = this.evaluatedCells.get(position);
|
|
@@ -58160,7 +58473,6 @@ class Evaluator {
|
|
|
58160
58473
|
}
|
|
58161
58474
|
finally {
|
|
58162
58475
|
this.cellsBeingComputed.delete(cellId);
|
|
58163
|
-
this.nextPositionsToUpdate.delete(position);
|
|
58164
58476
|
}
|
|
58165
58477
|
}
|
|
58166
58478
|
computeAndSave(position) {
|
|
@@ -58196,6 +58508,7 @@ class Evaluator {
|
|
|
58196
58508
|
invalidatePositionsDependingOnSpread(sheetId, resultZone) {
|
|
58197
58509
|
// the result matrix is split in 2 zones to exclude the array formula position
|
|
58198
58510
|
const invalidatedPositions = this.formulaDependencies().getCellsDependingOn(excludeTopLeft(resultZone).map((zone) => ({ sheetId, zone })));
|
|
58511
|
+
invalidatedPositions.delete({ sheetId, col: resultZone.left, row: resultZone.top });
|
|
58199
58512
|
this.nextPositionsToUpdate.addMany(invalidatedPositions);
|
|
58200
58513
|
}
|
|
58201
58514
|
assertSheetHasEnoughSpaceToSpreadFormulaResult({ sheetId, col, row }, matrixResult) {
|
|
@@ -59319,25 +59632,11 @@ class EvaluationDataValidationPlugin extends UIPlugin {
|
|
|
59319
59632
|
}
|
|
59320
59633
|
switch (cmd.type) {
|
|
59321
59634
|
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
59635
|
case "REMOVE_DATA_VALIDATION_RULE":
|
|
59329
59636
|
delete this.validationResults[cmd.sheetId];
|
|
59330
59637
|
break;
|
|
59331
59638
|
}
|
|
59332
59639
|
}
|
|
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
59640
|
isDataValidationInvalid(cellPosition) {
|
|
59342
59641
|
return !this.getValidationResultForCell(cellPosition).isValid;
|
|
59343
59642
|
}
|
|
@@ -59353,9 +59652,10 @@ class EvaluationDataValidationPlugin extends UIPlugin {
|
|
|
59353
59652
|
getDataValidationInvalidCriterionValueMessage(criterionType, value) {
|
|
59354
59653
|
const evaluator = dataValidationEvaluatorRegistry.get(criterionType);
|
|
59355
59654
|
if (value.startsWith("=")) {
|
|
59356
|
-
|
|
59357
|
-
|
|
59358
|
-
|
|
59655
|
+
if (evaluator.allowedValues === "onlyLiterals") {
|
|
59656
|
+
return _t("The value must not be a formula");
|
|
59657
|
+
}
|
|
59658
|
+
return this.isValidFormula(value) ? undefined : DVTerms.CriterionError.validFormula;
|
|
59359
59659
|
}
|
|
59360
59660
|
else if (evaluator.allowedValues === "onlyFormulas") {
|
|
59361
59661
|
return _t("The value must be a formula");
|
|
@@ -59381,6 +59681,9 @@ class EvaluationDataValidationPlugin extends UIPlugin {
|
|
|
59381
59681
|
const error = this.getRuleErrorForCellValue(cellValue, cellPosition, rule);
|
|
59382
59682
|
return error ? { error, rule, isValid: false } : VALID_RESULT;
|
|
59383
59683
|
}
|
|
59684
|
+
isValidFormula(value) {
|
|
59685
|
+
return !compile(value).isBadExpression;
|
|
59686
|
+
}
|
|
59384
59687
|
getValidationResultForCell(cellPosition) {
|
|
59385
59688
|
const { col, row, sheetId } = cellPosition;
|
|
59386
59689
|
if (!this.validationResults[sheetId]) {
|
|
@@ -61816,7 +62119,7 @@ class Session extends EventBus {
|
|
|
61816
62119
|
* Notify the server that the user client left the collaborative session
|
|
61817
62120
|
*/
|
|
61818
62121
|
async leave(data) {
|
|
61819
|
-
if (Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
|
|
62122
|
+
if (data && Object.keys(this.clients).length === 1 && this.processedRevisions.size) {
|
|
61820
62123
|
await this.snapshot(data());
|
|
61821
62124
|
}
|
|
61822
62125
|
delete this.clients[this.clientId];
|
|
@@ -63368,6 +63671,42 @@ class CellComputedStylePlugin extends UIPlugin {
|
|
|
63368
63671
|
}
|
|
63369
63672
|
}
|
|
63370
63673
|
|
|
63674
|
+
class DataValidationInsertionPlugin extends UIPlugin {
|
|
63675
|
+
handle(cmd) {
|
|
63676
|
+
switch (cmd.type) {
|
|
63677
|
+
case "ADD_DATA_VALIDATION_RULE":
|
|
63678
|
+
if (cmd.rule.criterion.type === "isBoolean") {
|
|
63679
|
+
const ranges = cmd.ranges.map((range) => this.getters.getRangeFromRangeData(range));
|
|
63680
|
+
for (const position of getCellPositionsInRanges(ranges)) {
|
|
63681
|
+
const cell = this.getters.getCell(position);
|
|
63682
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
63683
|
+
if (!cell?.content) {
|
|
63684
|
+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
63685
|
+
// In this case, a cell has been updated in the core plugin but
|
|
63686
|
+
// not yet evaluated. This can occur after a paste operation.
|
|
63687
|
+
}
|
|
63688
|
+
else if (cell?.content && evaluatedCell.type === CellValueType.empty) {
|
|
63689
|
+
let value;
|
|
63690
|
+
if (cell.content.startsWith("=")) {
|
|
63691
|
+
const result = this.getters.evaluateFormula(position.sheetId, cell.content);
|
|
63692
|
+
value = (isMatrix(result) ? result[0][0] : result)?.toString();
|
|
63693
|
+
}
|
|
63694
|
+
else {
|
|
63695
|
+
value = cell.content;
|
|
63696
|
+
}
|
|
63697
|
+
if (!value || !isBoolean(value)) {
|
|
63698
|
+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
63699
|
+
}
|
|
63700
|
+
}
|
|
63701
|
+
else if (evaluatedCell.type !== CellValueType.boolean) {
|
|
63702
|
+
this.dispatch("UPDATE_CELL", { ...position, content: "FALSE" });
|
|
63703
|
+
}
|
|
63704
|
+
}
|
|
63705
|
+
}
|
|
63706
|
+
}
|
|
63707
|
+
}
|
|
63708
|
+
}
|
|
63709
|
+
|
|
63371
63710
|
const genericRepeatsTransforms = [
|
|
63372
63711
|
repeatSheetDependantCommand,
|
|
63373
63712
|
repeatTargetDependantCommand,
|
|
@@ -64021,7 +64360,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
64021
64360
|
const zones = this.getters.getSelectedZones();
|
|
64022
64361
|
return this.isCutAllowedOn(zones);
|
|
64023
64362
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
64024
|
-
const copiedData = this.
|
|
64363
|
+
const copiedData = this.convertTextToClipboardData(cmd.clipboardContent.text ?? "");
|
|
64025
64364
|
const pasteOption = cmd.pasteOption;
|
|
64026
64365
|
return this.isPasteAllowed(cmd.target, copiedData, { pasteOption, isCutOperation: false });
|
|
64027
64366
|
}
|
|
@@ -64074,12 +64413,9 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
64074
64413
|
break;
|
|
64075
64414
|
case "PASTE_FROM_OS_CLIPBOARD": {
|
|
64076
64415
|
this._isCutOperation = false;
|
|
64077
|
-
|
|
64078
|
-
|
|
64079
|
-
|
|
64080
|
-
else {
|
|
64081
|
-
this.copiedData = this.convertOSClipboardData(cmd.clipboardContent[ClipboardMIMEType.PlainText] ?? "");
|
|
64082
|
-
}
|
|
64416
|
+
this.copiedData =
|
|
64417
|
+
cmd.clipboardContent.data ||
|
|
64418
|
+
this.convertTextToClipboardData(cmd.clipboardContent.text ?? "");
|
|
64083
64419
|
const pasteOption = cmd.pasteOption;
|
|
64084
64420
|
this.paste(cmd.target, this.copiedData, {
|
|
64085
64421
|
pasteOption,
|
|
@@ -64210,11 +64546,11 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
64210
64546
|
}
|
|
64211
64547
|
}
|
|
64212
64548
|
}
|
|
64213
|
-
|
|
64549
|
+
convertTextToClipboardData(clipboardData) {
|
|
64214
64550
|
const handlers = this.selectClipboardHandlers({ figureId: true }).concat(this.selectClipboardHandlers({}));
|
|
64215
64551
|
let copiedData = {};
|
|
64216
64552
|
for (const { handlerName, handler } of handlers) {
|
|
64217
|
-
const data = handler.
|
|
64553
|
+
const data = handler.convertTextToClipboardData(clipboardData);
|
|
64218
64554
|
copiedData[handlerName] = data;
|
|
64219
64555
|
const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
|
|
64220
64556
|
for (const key of minimalKeys) {
|
|
@@ -64383,21 +64719,20 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
64383
64719
|
return {
|
|
64384
64720
|
[ClipboardMIMEType.PlainText]: this.getPlainTextContent(),
|
|
64385
64721
|
[ClipboardMIMEType.Html]: this.getHTMLContent(),
|
|
64386
|
-
[ClipboardMIMEType.OSpreadsheet]: this.getSerializedGridData(),
|
|
64387
64722
|
};
|
|
64388
64723
|
}
|
|
64389
|
-
|
|
64724
|
+
getSheetData() {
|
|
64390
64725
|
const data = {
|
|
64391
64726
|
version: CURRENT_VERSION,
|
|
64392
64727
|
clipboardId: this.clipboardId,
|
|
64393
64728
|
};
|
|
64394
64729
|
if (this.copiedData && "figureId" in this.copiedData) {
|
|
64395
|
-
return
|
|
64730
|
+
return data;
|
|
64396
64731
|
}
|
|
64397
|
-
return
|
|
64732
|
+
return {
|
|
64398
64733
|
...data,
|
|
64399
64734
|
...this.copiedData,
|
|
64400
|
-
}
|
|
64735
|
+
};
|
|
64401
64736
|
}
|
|
64402
64737
|
getPlainTextContent() {
|
|
64403
64738
|
if (!this.copiedData?.cells) {
|
|
@@ -64414,31 +64749,36 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
64414
64749
|
.join("\n") || "\t");
|
|
64415
64750
|
}
|
|
64416
64751
|
getHTMLContent() {
|
|
64417
|
-
|
|
64418
|
-
|
|
64752
|
+
let innerHTML = "";
|
|
64753
|
+
const cells = this.copiedData?.cells;
|
|
64754
|
+
if (!cells) {
|
|
64755
|
+
innerHTML = "\t";
|
|
64419
64756
|
}
|
|
64420
|
-
|
|
64421
|
-
|
|
64422
|
-
return `<div data-clipboard-id="${this.clipboardId}">${this.getters.getCellText(cells[0][0].position)}</div>`;
|
|
64757
|
+
else if (cells.length === 1 && cells[0].length === 1) {
|
|
64758
|
+
innerHTML = `${this.getters.getCellText(cells[0][0].position)}`;
|
|
64423
64759
|
}
|
|
64424
|
-
if (!cells[0][0]) {
|
|
64760
|
+
else if (!cells[0][0]) {
|
|
64425
64761
|
return "";
|
|
64426
64762
|
}
|
|
64427
|
-
|
|
64428
|
-
|
|
64429
|
-
|
|
64430
|
-
|
|
64431
|
-
|
|
64432
|
-
|
|
64763
|
+
else {
|
|
64764
|
+
let htmlTable = `<table border="1" style="border-collapse:collapse">`;
|
|
64765
|
+
for (const row of cells) {
|
|
64766
|
+
htmlTable += "<tr>";
|
|
64767
|
+
for (const cell of row) {
|
|
64768
|
+
if (!cell) {
|
|
64769
|
+
continue;
|
|
64770
|
+
}
|
|
64771
|
+
const cssStyle = cssPropertiesToCss(cellStyleToCss(this.getters.getCellComputedStyle(cell.position)));
|
|
64772
|
+
const cellText = this.getters.getCellText(cell.position);
|
|
64773
|
+
htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
|
|
64433
64774
|
}
|
|
64434
|
-
|
|
64435
|
-
const cellText = this.getters.getCellText(cell.position);
|
|
64436
|
-
htmlTable += `<td style="${cssStyle}">` + xmlEscape(cellText) + "</td>";
|
|
64775
|
+
htmlTable += "</tr>";
|
|
64437
64776
|
}
|
|
64438
|
-
htmlTable += "</
|
|
64777
|
+
htmlTable += "</table>";
|
|
64778
|
+
innerHTML = htmlTable;
|
|
64439
64779
|
}
|
|
64440
|
-
|
|
64441
|
-
return
|
|
64780
|
+
const serializedData = JSON.stringify(this.getSheetData());
|
|
64781
|
+
return `<div data-osheet-clipboard='${xmlEscape(serializedData)}'>${innerHTML}</div>`;
|
|
64442
64782
|
}
|
|
64443
64783
|
isCutOperation() {
|
|
64444
64784
|
return this._isCutOperation ?? false;
|
|
@@ -66418,7 +66758,8 @@ const featurePluginRegistry = new Registry()
|
|
|
66418
66758
|
.add("history", HistoryPlugin)
|
|
66419
66759
|
.add("data_cleanup", DataCleanupPlugin)
|
|
66420
66760
|
.add("table_autofill", TableAutofillPlugin)
|
|
66421
|
-
.add("table_ui_resize", TableResizeUI)
|
|
66761
|
+
.add("table_ui_resize", TableResizeUI)
|
|
66762
|
+
.add("datavalidation_insert", DataValidationInsertionPlugin);
|
|
66422
66763
|
// Plugins which have a state, but which should not be shared in collaborative
|
|
66423
66764
|
const statefulUIPluginRegistry = new Registry()
|
|
66424
66765
|
.add("selection", GridSelectionPlugin)
|
|
@@ -67644,7 +67985,7 @@ css /* scss */ `
|
|
|
67644
67985
|
|
|
67645
67986
|
.o-header-group-main-pane {
|
|
67646
67987
|
&.o-group-rows {
|
|
67647
|
-
margin-top: -2px;
|
|
67988
|
+
margin-top: -2px; /* Counteract o-header-group-frozen-pane-border offset */
|
|
67648
67989
|
}
|
|
67649
67990
|
&.o-group-columns {
|
|
67650
67991
|
margin-left: -2px;
|
|
@@ -68677,10 +69018,6 @@ class WebClipboardWrapper {
|
|
|
68677
69018
|
[ClipboardMIMEType.PlainText]: this.getBlob(content, ClipboardMIMEType.PlainText),
|
|
68678
69019
|
[ClipboardMIMEType.Html]: this.getBlob(content, ClipboardMIMEType.Html),
|
|
68679
69020
|
};
|
|
68680
|
-
const spreadsheetData = content[ClipboardMIMEType.OSpreadsheet];
|
|
68681
|
-
if (spreadsheetData) {
|
|
68682
|
-
clipboardItemData[ClipboardMIMEType.OSpreadsheet] = this.getBlob(content, ClipboardMIMEType.OSpreadsheet);
|
|
68683
|
-
}
|
|
68684
69021
|
return [new ClipboardItem(clipboardItemData)];
|
|
68685
69022
|
}
|
|
68686
69023
|
getBlob(clipboardContent, type) {
|
|
@@ -68722,7 +69059,7 @@ css /* scss */ `
|
|
|
68722
69059
|
*:before,
|
|
68723
69060
|
*:after {
|
|
68724
69061
|
box-sizing: content-box;
|
|
68725
|
-
|
|
69062
|
+
/* rtl not supported ATM */
|
|
68726
69063
|
direction: ltr;
|
|
68727
69064
|
}
|
|
68728
69065
|
.o-separator {
|
|
@@ -71627,6 +71964,124 @@ function getExcelThresholdType(type, position) {
|
|
|
71627
71964
|
}
|
|
71628
71965
|
}
|
|
71629
71966
|
|
|
71967
|
+
function addDataValidationRules(dataValidationRules) {
|
|
71968
|
+
const dvRulesCount = dataValidationRules.length;
|
|
71969
|
+
if (dvRulesCount === 0) {
|
|
71970
|
+
return [];
|
|
71971
|
+
}
|
|
71972
|
+
const dvNodes = [new XMLString(`<dataValidations count="${dvRulesCount}">`)];
|
|
71973
|
+
for (const dvRule of dataValidationRules) {
|
|
71974
|
+
switch (dvRule.criterion.type) {
|
|
71975
|
+
case "dateIs":
|
|
71976
|
+
case "dateIsBefore":
|
|
71977
|
+
case "dateIsOnOrBefore":
|
|
71978
|
+
case "dateIsAfter":
|
|
71979
|
+
case "dateIsOnOrAfter":
|
|
71980
|
+
case "dateIsBetween":
|
|
71981
|
+
case "dateIsNotBetween":
|
|
71982
|
+
dvNodes.push(addDateRule(dvRule));
|
|
71983
|
+
break;
|
|
71984
|
+
case "isEqual":
|
|
71985
|
+
case "isNotEqual":
|
|
71986
|
+
case "isGreaterThan":
|
|
71987
|
+
case "isGreaterOrEqualTo":
|
|
71988
|
+
case "isLessThan":
|
|
71989
|
+
case "isLessOrEqualTo":
|
|
71990
|
+
case "isBetween":
|
|
71991
|
+
case "isNotBetween":
|
|
71992
|
+
dvNodes.push(addDecimalRule(dvRule));
|
|
71993
|
+
break;
|
|
71994
|
+
case "isValueInRange":
|
|
71995
|
+
case "isValueInList":
|
|
71996
|
+
dvNodes.push(addListRule(dvRule));
|
|
71997
|
+
break;
|
|
71998
|
+
case "customFormula":
|
|
71999
|
+
dvNodes.push(addCustomFormulaRule(dvRule));
|
|
72000
|
+
break;
|
|
72001
|
+
default:
|
|
72002
|
+
console.warn(`Data validation ${dvRule.criterion.type} is not supported in xlsx.`);
|
|
72003
|
+
break;
|
|
72004
|
+
}
|
|
72005
|
+
}
|
|
72006
|
+
dvNodes.push(new XMLString("</dataValidations>"));
|
|
72007
|
+
return dvNodes;
|
|
72008
|
+
}
|
|
72009
|
+
function addDateRule(dvRule) {
|
|
72010
|
+
const rule = dvRule.criterion;
|
|
72011
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
72012
|
+
const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
|
|
72013
|
+
const operator = convertDateCriterionTypeToExcelOperator(dvRule.criterion.type);
|
|
72014
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72015
|
+
attributes.push(["type", "date"], ["operator", operator]);
|
|
72016
|
+
if (formula2) {
|
|
72017
|
+
return escapeXml /*xml*/ `
|
|
72018
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72019
|
+
<formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
|
|
72020
|
+
<formula2>${toNumber(formula2, DEFAULT_LOCALE)}</formula2>
|
|
72021
|
+
</dataValidation>
|
|
72022
|
+
`;
|
|
72023
|
+
}
|
|
72024
|
+
return escapeXml /*xml*/ `
|
|
72025
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72026
|
+
<formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
|
|
72027
|
+
</dataValidation>
|
|
72028
|
+
`;
|
|
72029
|
+
}
|
|
72030
|
+
function addDecimalRule(dvRule) {
|
|
72031
|
+
const rule = dvRule.criterion;
|
|
72032
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
72033
|
+
const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
|
|
72034
|
+
const operator = convertDecimalCriterionTypeToExcelOperator(dvRule.criterion.type);
|
|
72035
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72036
|
+
attributes.push(["type", "decimal"], ["operator", operator]);
|
|
72037
|
+
if (formula2) {
|
|
72038
|
+
return escapeXml /*xml*/ `
|
|
72039
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72040
|
+
<formula1>${formula1}</formula1>
|
|
72041
|
+
<formula2>${formula2}</formula2>
|
|
72042
|
+
</dataValidation>
|
|
72043
|
+
`;
|
|
72044
|
+
}
|
|
72045
|
+
return escapeXml /*xml*/ `
|
|
72046
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72047
|
+
<formula1>${formula1}</formula1>
|
|
72048
|
+
</dataValidation>
|
|
72049
|
+
`;
|
|
72050
|
+
}
|
|
72051
|
+
function addListRule(dvRule) {
|
|
72052
|
+
const rule = dvRule.criterion;
|
|
72053
|
+
const formula1 = dvRule.criterion.type === "isValueInRange"
|
|
72054
|
+
? adaptFormulaToExcel(rule.values[0])
|
|
72055
|
+
: `"${rule.values.join(",")}"`;
|
|
72056
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72057
|
+
attributes.push(["type", "list"]);
|
|
72058
|
+
return escapeXml /*xml*/ `
|
|
72059
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72060
|
+
<formula1>${formula1}</formula1>
|
|
72061
|
+
</dataValidation>
|
|
72062
|
+
`;
|
|
72063
|
+
}
|
|
72064
|
+
function addCustomFormulaRule(dvRule) {
|
|
72065
|
+
const rule = dvRule.criterion;
|
|
72066
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
72067
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
72068
|
+
attributes.push(["type", "custom"]);
|
|
72069
|
+
return escapeXml /*xml*/ `
|
|
72070
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
72071
|
+
<formula1>${formula1}</formula1>
|
|
72072
|
+
</dataValidation>
|
|
72073
|
+
`;
|
|
72074
|
+
}
|
|
72075
|
+
function commonDataValidationAttributes(dvRule) {
|
|
72076
|
+
return [
|
|
72077
|
+
["allowBlank", "1"],
|
|
72078
|
+
["showInputMessage", "1"],
|
|
72079
|
+
["showErrorMessage", "1"],
|
|
72080
|
+
["errorStyle", !dvRule.isBlocking ? "warning" : ""],
|
|
72081
|
+
["sqref", dvRule.ranges.join(" ")],
|
|
72082
|
+
];
|
|
72083
|
+
}
|
|
72084
|
+
|
|
71630
72085
|
function createDrawing(drawingRelIds, sheet, figures) {
|
|
71631
72086
|
const namespaces = [
|
|
71632
72087
|
["xmlns:xdr", NAMESPACE.drawing],
|
|
@@ -72411,6 +72866,7 @@ function createWorksheets(data, construct) {
|
|
|
72411
72866
|
${addRows(construct, data, sheet)}
|
|
72412
72867
|
${addMerges(sheet.merges)}
|
|
72413
72868
|
${joinXmlNodes(addConditionalFormatting(construct.dxfs, sheet.conditionalFormats))}
|
|
72869
|
+
${joinXmlNodes(addDataValidationRules(sheet.dataValidationRules))}
|
|
72414
72870
|
${addHyperlinks(construct, data, sheetIndex)}
|
|
72415
72871
|
${drawingNode}
|
|
72416
72872
|
${tablesNode}
|
|
@@ -72734,7 +73190,8 @@ class Model extends EventBus {
|
|
|
72734
73190
|
this.session.join(this.config.client);
|
|
72735
73191
|
}
|
|
72736
73192
|
async leaveSession() {
|
|
72737
|
-
|
|
73193
|
+
const snapshot = this.getters.isReadonly() ? undefined : lazy(() => this.exportData());
|
|
73194
|
+
await this.session.leave(snapshot);
|
|
72738
73195
|
}
|
|
72739
73196
|
setupUiPlugin(Plugin) {
|
|
72740
73197
|
const plugin = new Plugin(this.uiPluginConfig);
|
|
@@ -73292,6 +73749,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
|
|
|
73292
73749
|
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
73750
|
|
|
73294
73751
|
|
|
73295
|
-
__info__.version = "18.1.0-alpha.
|
|
73296
|
-
__info__.date = "2024-11-
|
|
73297
|
-
__info__.hash = "
|
|
73752
|
+
__info__.version = "18.1.0-alpha.5";
|
|
73753
|
+
__info__.date = "2024-11-22T14:22:50.899Z";
|
|
73754
|
+
__info__.hash = "e13bd67";
|