@odoo/o-spreadsheet 18.0.10 → 18.0.12
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 +752 -336
- package/dist/o-spreadsheet.d.ts +38 -20
- package/dist/o-spreadsheet.esm.js +752 -336
- package/dist/o-spreadsheet.iife.js +752 -336
- package/dist/o-spreadsheet.iife.min.js +391 -364
- package/dist/o_spreadsheet.xml +11 -5
- 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.0.
|
|
6
|
-
* @date 2025-01-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.0.12
|
|
6
|
+
* @date 2025-01-29T06:24:22.122Z
|
|
7
|
+
* @hash a881cff
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -4211,15 +4211,18 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
|
|
|
4211
4211
|
let currentIndex;
|
|
4212
4212
|
let currentVal;
|
|
4213
4213
|
let currentType;
|
|
4214
|
+
const getValue = sortOrder === "desc"
|
|
4215
|
+
? (i) => normalizeValue(getValueInData(data, rangeLength - i - 1))
|
|
4216
|
+
: (i) => normalizeValue(getValueInData(data, i));
|
|
4214
4217
|
while (indexRight - indexLeft >= 0) {
|
|
4215
4218
|
indexMedian = Math.floor((indexLeft + indexRight) / 2);
|
|
4216
4219
|
currentIndex = indexMedian;
|
|
4217
|
-
currentVal =
|
|
4220
|
+
currentVal = getValue(currentIndex);
|
|
4218
4221
|
currentType = typeof currentVal;
|
|
4219
4222
|
// 1 - linear search to find value with the same type
|
|
4220
4223
|
while (indexLeft < currentIndex && targetType !== currentType) {
|
|
4221
4224
|
currentIndex--;
|
|
4222
|
-
currentVal =
|
|
4225
|
+
currentVal = getValue(currentIndex);
|
|
4223
4226
|
currentType = typeof currentVal;
|
|
4224
4227
|
}
|
|
4225
4228
|
if (currentType !== targetType || currentVal === undefined || currentVal === null) {
|
|
@@ -4235,8 +4238,7 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
|
|
|
4235
4238
|
if (matchVal === undefined ||
|
|
4236
4239
|
matchVal === null ||
|
|
4237
4240
|
matchVal < currentVal ||
|
|
4238
|
-
(matchVal === currentVal &&
|
|
4239
|
-
(matchVal === currentVal && sortOrder === "desc" && matchValIndex > currentIndex)) {
|
|
4241
|
+
(matchVal === currentVal && matchValIndex < currentIndex)) {
|
|
4240
4242
|
matchVal = currentVal;
|
|
4241
4243
|
matchValIndex = currentIndex;
|
|
4242
4244
|
}
|
|
@@ -4244,15 +4246,13 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
|
|
|
4244
4246
|
else if (mode === "nextGreater" && currentVal >= _target) {
|
|
4245
4247
|
if (matchVal === undefined ||
|
|
4246
4248
|
matchVal > currentVal ||
|
|
4247
|
-
(matchVal === currentVal &&
|
|
4248
|
-
(matchVal === currentVal && sortOrder === "desc" && matchValIndex > currentIndex)) {
|
|
4249
|
+
(matchVal === currentVal && matchValIndex < currentIndex)) {
|
|
4249
4250
|
matchVal = currentVal;
|
|
4250
4251
|
matchValIndex = currentIndex;
|
|
4251
4252
|
}
|
|
4252
4253
|
}
|
|
4253
4254
|
// 3 - give new indexes for the Binary search
|
|
4254
|
-
if ((
|
|
4255
|
-
(sortOrder === "desc" && currentVal <= _target)) {
|
|
4255
|
+
if (currentVal > _target || (mode === "strict" && currentVal === _target)) {
|
|
4256
4256
|
indexRight = currentIndex - 1;
|
|
4257
4257
|
}
|
|
4258
4258
|
else {
|
|
@@ -4260,7 +4260,10 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
|
|
|
4260
4260
|
}
|
|
4261
4261
|
}
|
|
4262
4262
|
// note that valMinIndex could be 0
|
|
4263
|
-
|
|
4263
|
+
if (matchValIndex === undefined) {
|
|
4264
|
+
return -1;
|
|
4265
|
+
}
|
|
4266
|
+
return sortOrder === "desc" ? rangeLength - matchValIndex - 1 : matchValIndex;
|
|
4264
4267
|
}
|
|
4265
4268
|
/**
|
|
4266
4269
|
* Perform a linear search and return the index of the match.
|
|
@@ -4392,47 +4395,83 @@ function isDataNonEmpty(data) {
|
|
|
4392
4395
|
return true;
|
|
4393
4396
|
}
|
|
4394
4397
|
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
case "yesterday":
|
|
4401
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
|
|
4402
|
-
case "tomorrow":
|
|
4403
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
|
|
4404
|
-
case "lastWeek":
|
|
4405
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
|
|
4406
|
-
case "lastMonth":
|
|
4407
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
|
|
4408
|
-
case "lastYear":
|
|
4409
|
-
return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
|
|
4410
|
-
}
|
|
4398
|
+
/**
|
|
4399
|
+
* Add the `https` prefix to the url if it's missing
|
|
4400
|
+
*/
|
|
4401
|
+
function withHttps(url) {
|
|
4402
|
+
return !/^https?:\/\//i.test(url) ? `https://${url}` : url;
|
|
4411
4403
|
}
|
|
4412
|
-
|
|
4413
|
-
function
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4404
|
+
const urlRegistry = new Registry();
|
|
4405
|
+
function createWebLink(url, label) {
|
|
4406
|
+
url = withHttps(url);
|
|
4407
|
+
return {
|
|
4408
|
+
url,
|
|
4409
|
+
label: label || url,
|
|
4410
|
+
isExternal: true,
|
|
4411
|
+
isUrlEditable: true,
|
|
4412
|
+
};
|
|
4418
4413
|
}
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4414
|
+
urlRegistry.add("sheet_URL", {
|
|
4415
|
+
match: (url) => isSheetUrl(url),
|
|
4416
|
+
createLink: (url, label) => {
|
|
4417
|
+
return {
|
|
4418
|
+
label,
|
|
4419
|
+
url,
|
|
4420
|
+
isExternal: false,
|
|
4421
|
+
isUrlEditable: false,
|
|
4422
|
+
};
|
|
4423
|
+
},
|
|
4424
|
+
urlRepresentation(url, getters) {
|
|
4425
|
+
const sheetId = parseSheetUrl(url);
|
|
4426
|
+
return getters.tryGetSheetName(sheetId) || _t("Invalid sheet");
|
|
4427
|
+
},
|
|
4428
|
+
open(url, env) {
|
|
4429
|
+
const sheetId = parseSheetUrl(url);
|
|
4430
|
+
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
4431
|
+
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
4432
|
+
sheetIdTo: sheetId,
|
|
4433
|
+
});
|
|
4434
|
+
if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
|
|
4435
|
+
env.notifyUser({
|
|
4436
|
+
type: "warning",
|
|
4437
|
+
sticky: false,
|
|
4438
|
+
text: _t("Cannot open the link because the linked sheet is hidden."),
|
|
4439
|
+
});
|
|
4440
|
+
}
|
|
4441
|
+
},
|
|
4442
|
+
sequence: 0,
|
|
4443
|
+
});
|
|
4444
|
+
const WebUrlSpec = {
|
|
4445
|
+
createLink: createWebLink,
|
|
4446
|
+
match: (url) => isWebLink(url),
|
|
4447
|
+
open: (url) => window.open(url, "_blank"),
|
|
4448
|
+
urlRepresentation: (url) => url,
|
|
4449
|
+
sequence: 0,
|
|
4450
|
+
};
|
|
4451
|
+
function findMatchingSpec(url) {
|
|
4452
|
+
return (urlRegistry
|
|
4453
|
+
.getAll()
|
|
4454
|
+
.sort((a, b) => a.sequence - b.sequence)
|
|
4455
|
+
.find((urlType) => urlType.match(url)) || WebUrlSpec);
|
|
4422
4456
|
}
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4457
|
+
function urlRepresentation(link, getters) {
|
|
4458
|
+
return findMatchingSpec(link.url).urlRepresentation(link.url, getters);
|
|
4459
|
+
}
|
|
4460
|
+
function openLink(link, env) {
|
|
4461
|
+
findMatchingSpec(link.url).open(link.url, env);
|
|
4462
|
+
}
|
|
4463
|
+
function detectLink(value) {
|
|
4464
|
+
if (typeof value !== "string") {
|
|
4465
|
+
return undefined;
|
|
4466
|
+
}
|
|
4467
|
+
if (isMarkdownLink(value)) {
|
|
4468
|
+
const { label, url } = parseMarkdownLink(value);
|
|
4469
|
+
return findMatchingSpec(url).createLink(url, label);
|
|
4470
|
+
}
|
|
4471
|
+
else if (isWebLink(value)) {
|
|
4472
|
+
return createWebLink(value);
|
|
4473
|
+
}
|
|
4474
|
+
return undefined;
|
|
4436
4475
|
}
|
|
4437
4476
|
|
|
4438
4477
|
function tokenizeFormat(str) {
|
|
@@ -5494,6 +5533,193 @@ function isTextFormat(format) {
|
|
|
5494
5533
|
}
|
|
5495
5534
|
}
|
|
5496
5535
|
|
|
5536
|
+
function evaluateLiteral(literalCell, localeFormat) {
|
|
5537
|
+
const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
|
|
5538
|
+
const functionResult = { value, format: localeFormat.format };
|
|
5539
|
+
return createEvaluatedCell(functionResult, localeFormat.locale);
|
|
5540
|
+
}
|
|
5541
|
+
function parseLiteral(content, locale) {
|
|
5542
|
+
if (content.startsWith("=")) {
|
|
5543
|
+
throw new Error(`Cannot parse "${content}" because it's not a literal value. It's a formula`);
|
|
5544
|
+
}
|
|
5545
|
+
if (content === "") {
|
|
5546
|
+
return null;
|
|
5547
|
+
}
|
|
5548
|
+
if (isNumber(content, DEFAULT_LOCALE)) {
|
|
5549
|
+
return parseNumber(content, DEFAULT_LOCALE);
|
|
5550
|
+
}
|
|
5551
|
+
const internalDate = parseDateTime(content, locale);
|
|
5552
|
+
if (internalDate) {
|
|
5553
|
+
return internalDate.value;
|
|
5554
|
+
}
|
|
5555
|
+
if (isBoolean(content)) {
|
|
5556
|
+
return content.toUpperCase() === "TRUE" ? true : false;
|
|
5557
|
+
}
|
|
5558
|
+
return content;
|
|
5559
|
+
}
|
|
5560
|
+
function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
|
|
5561
|
+
const link = detectLink(functionResult.value);
|
|
5562
|
+
if (!link) {
|
|
5563
|
+
return _createEvaluatedCell(functionResult, locale, cell);
|
|
5564
|
+
}
|
|
5565
|
+
const value = parseLiteral(link.label, locale);
|
|
5566
|
+
const format = functionResult.format ||
|
|
5567
|
+
(typeof value === "number"
|
|
5568
|
+
? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
|
|
5569
|
+
: undefined);
|
|
5570
|
+
const linkPayload = {
|
|
5571
|
+
value,
|
|
5572
|
+
format,
|
|
5573
|
+
};
|
|
5574
|
+
return {
|
|
5575
|
+
..._createEvaluatedCell(linkPayload, locale, cell),
|
|
5576
|
+
link,
|
|
5577
|
+
};
|
|
5578
|
+
}
|
|
5579
|
+
function _createEvaluatedCell(functionResult, locale, cell) {
|
|
5580
|
+
let { value, format, message } = functionResult;
|
|
5581
|
+
format = cell?.format || format;
|
|
5582
|
+
const formattedValue = formatValue(value, { format, locale });
|
|
5583
|
+
if (isEvaluationError(value)) {
|
|
5584
|
+
return errorCell(value, message);
|
|
5585
|
+
}
|
|
5586
|
+
if (isTextFormat(format)) {
|
|
5587
|
+
// TO DO:
|
|
5588
|
+
// with the next line, the value of the cell is transformed depending on the format.
|
|
5589
|
+
// This shouldn't happen, by doing this, the formulas handling numbers are not able
|
|
5590
|
+
// to interpret the value as a number.
|
|
5591
|
+
return textCell(toString(value), format, formattedValue);
|
|
5592
|
+
}
|
|
5593
|
+
if (value === null) {
|
|
5594
|
+
return emptyCell(format);
|
|
5595
|
+
}
|
|
5596
|
+
if (typeof value === "number") {
|
|
5597
|
+
if (isDateTimeFormat(format || "")) {
|
|
5598
|
+
return dateTimeCell(value, format, formattedValue);
|
|
5599
|
+
}
|
|
5600
|
+
return numberCell(value, format, formattedValue);
|
|
5601
|
+
}
|
|
5602
|
+
if (typeof value === "boolean") {
|
|
5603
|
+
return booleanCell(value, format, formattedValue);
|
|
5604
|
+
}
|
|
5605
|
+
return textCell(value, format, formattedValue);
|
|
5606
|
+
}
|
|
5607
|
+
function textCell(value, format, formattedValue) {
|
|
5608
|
+
return {
|
|
5609
|
+
value,
|
|
5610
|
+
format,
|
|
5611
|
+
formattedValue,
|
|
5612
|
+
type: CellValueType.text,
|
|
5613
|
+
isAutoSummable: true,
|
|
5614
|
+
defaultAlign: "left",
|
|
5615
|
+
};
|
|
5616
|
+
}
|
|
5617
|
+
function numberCell(value, format, formattedValue) {
|
|
5618
|
+
return {
|
|
5619
|
+
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
5620
|
+
format,
|
|
5621
|
+
formattedValue,
|
|
5622
|
+
type: CellValueType.number,
|
|
5623
|
+
isAutoSummable: true,
|
|
5624
|
+
defaultAlign: "right",
|
|
5625
|
+
};
|
|
5626
|
+
}
|
|
5627
|
+
const emptyCell = memoize(function emptyCell(format) {
|
|
5628
|
+
return {
|
|
5629
|
+
value: null,
|
|
5630
|
+
format,
|
|
5631
|
+
formattedValue: "",
|
|
5632
|
+
type: CellValueType.empty,
|
|
5633
|
+
isAutoSummable: true,
|
|
5634
|
+
defaultAlign: "left",
|
|
5635
|
+
};
|
|
5636
|
+
});
|
|
5637
|
+
function dateTimeCell(value, format, formattedValue) {
|
|
5638
|
+
return {
|
|
5639
|
+
value,
|
|
5640
|
+
format,
|
|
5641
|
+
formattedValue,
|
|
5642
|
+
type: CellValueType.number,
|
|
5643
|
+
isAutoSummable: false,
|
|
5644
|
+
defaultAlign: "right",
|
|
5645
|
+
};
|
|
5646
|
+
}
|
|
5647
|
+
function booleanCell(value, format, formattedValue) {
|
|
5648
|
+
return {
|
|
5649
|
+
value,
|
|
5650
|
+
format,
|
|
5651
|
+
formattedValue,
|
|
5652
|
+
type: CellValueType.boolean,
|
|
5653
|
+
isAutoSummable: false,
|
|
5654
|
+
defaultAlign: "center",
|
|
5655
|
+
};
|
|
5656
|
+
}
|
|
5657
|
+
function errorCell(value, message) {
|
|
5658
|
+
return {
|
|
5659
|
+
value,
|
|
5660
|
+
formattedValue: value,
|
|
5661
|
+
message,
|
|
5662
|
+
type: CellValueType.error,
|
|
5663
|
+
isAutoSummable: false,
|
|
5664
|
+
defaultAlign: "center",
|
|
5665
|
+
};
|
|
5666
|
+
}
|
|
5667
|
+
|
|
5668
|
+
function toCriterionDateNumber(dateValue) {
|
|
5669
|
+
const today = DateTime.now();
|
|
5670
|
+
switch (dateValue) {
|
|
5671
|
+
case "today":
|
|
5672
|
+
return jsDateToNumber(today);
|
|
5673
|
+
case "yesterday":
|
|
5674
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
|
|
5675
|
+
case "tomorrow":
|
|
5676
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
|
|
5677
|
+
case "lastWeek":
|
|
5678
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
|
|
5679
|
+
case "lastMonth":
|
|
5680
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
|
|
5681
|
+
case "lastYear":
|
|
5682
|
+
return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5685
|
+
/** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
|
|
5686
|
+
function getDateNumberCriterionValues(criterion, locale) {
|
|
5687
|
+
if ("dateValue" in criterion && criterion.dateValue !== "exactDate") {
|
|
5688
|
+
return [toCriterionDateNumber(criterion.dateValue)];
|
|
5689
|
+
}
|
|
5690
|
+
return criterion.values.map((value) => valueToDateNumber(value, locale));
|
|
5691
|
+
}
|
|
5692
|
+
/** Convert the criterion values to numbers. Return undefined values if they cannot be converted to numbers. */
|
|
5693
|
+
function getCriterionValuesAsNumber(criterion, locale) {
|
|
5694
|
+
return criterion.values.map((value) => tryToNumber(value, locale));
|
|
5695
|
+
}
|
|
5696
|
+
function getDateCriterionFormattedValues(values, locale) {
|
|
5697
|
+
return values.map((valueStr) => {
|
|
5698
|
+
if (valueStr.startsWith("=")) {
|
|
5699
|
+
return valueStr;
|
|
5700
|
+
}
|
|
5701
|
+
const value = parseLiteral(valueStr, locale);
|
|
5702
|
+
if (typeof value === "number") {
|
|
5703
|
+
return formatValue(value, { format: locale.dateFormat, locale });
|
|
5704
|
+
}
|
|
5705
|
+
return "";
|
|
5706
|
+
});
|
|
5707
|
+
}
|
|
5708
|
+
|
|
5709
|
+
const MAX_DELAY = 140;
|
|
5710
|
+
const MIN_DELAY = 20;
|
|
5711
|
+
const ACCELERATION = 0.035;
|
|
5712
|
+
/**
|
|
5713
|
+
* Decreasing exponential function used to determine the "speed" of edge-scrolling
|
|
5714
|
+
* as the timeout delay.
|
|
5715
|
+
*
|
|
5716
|
+
* Returns a timeout delay in milliseconds.
|
|
5717
|
+
*/
|
|
5718
|
+
function scrollDelay(value) {
|
|
5719
|
+
// decreasing exponential from MAX_DELAY to MIN_DELAY
|
|
5720
|
+
return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
|
|
5721
|
+
}
|
|
5722
|
+
|
|
5497
5723
|
class RangeImpl {
|
|
5498
5724
|
getSheetSize;
|
|
5499
5725
|
_zone;
|
|
@@ -9231,13 +9457,13 @@ function toExcelDataset(getters, ds) {
|
|
|
9231
9457
|
else if (ds.labelCell) {
|
|
9232
9458
|
label = {
|
|
9233
9459
|
reference: getters.getRangeString(ds.labelCell, "forceSheetReference", {
|
|
9234
|
-
|
|
9460
|
+
useBoundedReference: true,
|
|
9235
9461
|
}),
|
|
9236
9462
|
};
|
|
9237
9463
|
}
|
|
9238
9464
|
return {
|
|
9239
9465
|
label,
|
|
9240
|
-
range: getters.getRangeString(dataRange, "forceSheetReference", {
|
|
9466
|
+
range: getters.getRangeString(dataRange, "forceSheetReference", { useBoundedReference: true }),
|
|
9241
9467
|
backgroundColor: ds.backgroundColor,
|
|
9242
9468
|
rightYAxis: ds.rightYAxis,
|
|
9243
9469
|
};
|
|
@@ -9252,7 +9478,7 @@ function toExcelLabelRange(getters, labelRange, shouldRemoveFirstLabel) {
|
|
|
9252
9478
|
zone.top = zone.top + 1;
|
|
9253
9479
|
}
|
|
9254
9480
|
const range = labelRange.clone({ zone });
|
|
9255
|
-
return getters.getRangeString(range, "forceSheetReference", {
|
|
9481
|
+
return getters.getRangeString(range, "forceSheetReference", { useBoundedReference: true });
|
|
9256
9482
|
}
|
|
9257
9483
|
/**
|
|
9258
9484
|
* Transform a chart definition which supports dataSets (dataSets and LabelRange)
|
|
@@ -9416,11 +9642,7 @@ function interpolateData(config, values, labels, newLabels) {
|
|
|
9416
9642
|
if (values.length < 2 || labels.length < 2 || newLabels.length === 0) {
|
|
9417
9643
|
return [];
|
|
9418
9644
|
}
|
|
9419
|
-
const
|
|
9420
|
-
const labelMax = Math.max(...labels);
|
|
9421
|
-
const labelRange = labelMax - labelMin;
|
|
9422
|
-
const normalizedLabels = labels.map((v) => (v - labelMin) / labelRange);
|
|
9423
|
-
const normalizedNewLabels = newLabels.map((v) => (v - labelMin) / labelRange);
|
|
9645
|
+
const { normalizedLabels, normalizedNewLabels } = normalizeLabels(labels, newLabels, config);
|
|
9424
9646
|
try {
|
|
9425
9647
|
switch (config.type) {
|
|
9426
9648
|
case "polynomial": {
|
|
@@ -9459,6 +9681,30 @@ function interpolateData(config, values, labels, newLabels) {
|
|
|
9459
9681
|
return Array.from({ length: newLabels.length }, () => NaN);
|
|
9460
9682
|
}
|
|
9461
9683
|
}
|
|
9684
|
+
function normalizeLabels(labels, newLabels, config) {
|
|
9685
|
+
let normalizedLabels = [];
|
|
9686
|
+
let normalizedNewLabels = [];
|
|
9687
|
+
if (config.type === "logarithmic") {
|
|
9688
|
+
// Logarithmic trends in charts are used to visualize proportional growth or
|
|
9689
|
+
// relative changes. Therefore, we change the normalization technique for
|
|
9690
|
+
// logarithmic trend lines for a better fit. The method used here is Max Absolute
|
|
9691
|
+
// Scaling. This Technique is ideal for data spanning several orders of magnitude,
|
|
9692
|
+
// as it balances differences between small and large values by compressing larger
|
|
9693
|
+
// values while preserving proportionality and ensuring all values are scaled relative
|
|
9694
|
+
// to the largest magnitude.
|
|
9695
|
+
const labelMax = Math.max(...labels.map(Math.abs));
|
|
9696
|
+
normalizedLabels = labels.map((l) => l / labelMax);
|
|
9697
|
+
normalizedNewLabels = newLabels.map((l) => l / labelMax);
|
|
9698
|
+
}
|
|
9699
|
+
else {
|
|
9700
|
+
const labelMax = Math.max(...labels);
|
|
9701
|
+
const labelMin = Math.min(...labels);
|
|
9702
|
+
const labelRange = labelMax - labelMin;
|
|
9703
|
+
normalizedLabels = labels.map((l) => (l - labelMax) / labelRange);
|
|
9704
|
+
normalizedNewLabels = newLabels.map((l) => (l - labelMax) / labelRange);
|
|
9705
|
+
}
|
|
9706
|
+
return { normalizedLabels, normalizedNewLabels };
|
|
9707
|
+
}
|
|
9462
9708
|
function formatTickValue(localeFormat) {
|
|
9463
9709
|
return (value) => {
|
|
9464
9710
|
value = Number(value);
|
|
@@ -22095,217 +22341,6 @@ autoCompleteProviders.add("sheet_names", {
|
|
|
22095
22341
|
},
|
|
22096
22342
|
});
|
|
22097
22343
|
|
|
22098
|
-
/**
|
|
22099
|
-
* Add the `https` prefix to the url if it's missing
|
|
22100
|
-
*/
|
|
22101
|
-
function withHttps(url) {
|
|
22102
|
-
return !/^https?:\/\//i.test(url) ? `https://${url}` : url;
|
|
22103
|
-
}
|
|
22104
|
-
const urlRegistry = new Registry();
|
|
22105
|
-
function createWebLink(url, label) {
|
|
22106
|
-
url = withHttps(url);
|
|
22107
|
-
return {
|
|
22108
|
-
url,
|
|
22109
|
-
label: label || url,
|
|
22110
|
-
isExternal: true,
|
|
22111
|
-
isUrlEditable: true,
|
|
22112
|
-
};
|
|
22113
|
-
}
|
|
22114
|
-
urlRegistry.add("sheet_URL", {
|
|
22115
|
-
match: (url) => isSheetUrl(url),
|
|
22116
|
-
createLink: (url, label) => {
|
|
22117
|
-
return {
|
|
22118
|
-
label,
|
|
22119
|
-
url,
|
|
22120
|
-
isExternal: false,
|
|
22121
|
-
isUrlEditable: false,
|
|
22122
|
-
};
|
|
22123
|
-
},
|
|
22124
|
-
urlRepresentation(url, getters) {
|
|
22125
|
-
const sheetId = parseSheetUrl(url);
|
|
22126
|
-
return getters.tryGetSheetName(sheetId) || _t("Invalid sheet");
|
|
22127
|
-
},
|
|
22128
|
-
open(url, env) {
|
|
22129
|
-
const sheetId = parseSheetUrl(url);
|
|
22130
|
-
const result = env.model.dispatch("ACTIVATE_SHEET", {
|
|
22131
|
-
sheetIdFrom: env.model.getters.getActiveSheetId(),
|
|
22132
|
-
sheetIdTo: sheetId,
|
|
22133
|
-
});
|
|
22134
|
-
if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
|
|
22135
|
-
env.notifyUser({
|
|
22136
|
-
type: "warning",
|
|
22137
|
-
sticky: false,
|
|
22138
|
-
text: _t("Cannot open the link because the linked sheet is hidden."),
|
|
22139
|
-
});
|
|
22140
|
-
}
|
|
22141
|
-
},
|
|
22142
|
-
sequence: 0,
|
|
22143
|
-
});
|
|
22144
|
-
const WebUrlSpec = {
|
|
22145
|
-
createLink: createWebLink,
|
|
22146
|
-
match: (url) => isWebLink(url),
|
|
22147
|
-
open: (url) => window.open(url, "_blank"),
|
|
22148
|
-
urlRepresentation: (url) => url,
|
|
22149
|
-
sequence: 0,
|
|
22150
|
-
};
|
|
22151
|
-
function findMatchingSpec(url) {
|
|
22152
|
-
return (urlRegistry
|
|
22153
|
-
.getAll()
|
|
22154
|
-
.sort((a, b) => a.sequence - b.sequence)
|
|
22155
|
-
.find((urlType) => urlType.match(url)) || WebUrlSpec);
|
|
22156
|
-
}
|
|
22157
|
-
function urlRepresentation(link, getters) {
|
|
22158
|
-
return findMatchingSpec(link.url).urlRepresentation(link.url, getters);
|
|
22159
|
-
}
|
|
22160
|
-
function openLink(link, env) {
|
|
22161
|
-
findMatchingSpec(link.url).open(link.url, env);
|
|
22162
|
-
}
|
|
22163
|
-
function detectLink(value) {
|
|
22164
|
-
if (typeof value !== "string") {
|
|
22165
|
-
return undefined;
|
|
22166
|
-
}
|
|
22167
|
-
if (isMarkdownLink(value)) {
|
|
22168
|
-
const { label, url } = parseMarkdownLink(value);
|
|
22169
|
-
return findMatchingSpec(url).createLink(url, label);
|
|
22170
|
-
}
|
|
22171
|
-
else if (isWebLink(value)) {
|
|
22172
|
-
return createWebLink(value);
|
|
22173
|
-
}
|
|
22174
|
-
return undefined;
|
|
22175
|
-
}
|
|
22176
|
-
|
|
22177
|
-
function evaluateLiteral(literalCell, localeFormat) {
|
|
22178
|
-
const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
|
|
22179
|
-
const functionResult = { value, format: localeFormat.format };
|
|
22180
|
-
return createEvaluatedCell(functionResult, localeFormat.locale);
|
|
22181
|
-
}
|
|
22182
|
-
function parseLiteral(content, locale) {
|
|
22183
|
-
if (content.startsWith("=")) {
|
|
22184
|
-
throw new Error(`Cannot parse "${content}" because it's not a literal value. It's a formula`);
|
|
22185
|
-
}
|
|
22186
|
-
if (content === "") {
|
|
22187
|
-
return null;
|
|
22188
|
-
}
|
|
22189
|
-
if (isNumber(content, DEFAULT_LOCALE)) {
|
|
22190
|
-
return parseNumber(content, DEFAULT_LOCALE);
|
|
22191
|
-
}
|
|
22192
|
-
const internalDate = parseDateTime(content, locale);
|
|
22193
|
-
if (internalDate) {
|
|
22194
|
-
return internalDate.value;
|
|
22195
|
-
}
|
|
22196
|
-
if (isBoolean(content)) {
|
|
22197
|
-
return content.toUpperCase() === "TRUE" ? true : false;
|
|
22198
|
-
}
|
|
22199
|
-
return content;
|
|
22200
|
-
}
|
|
22201
|
-
function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
|
|
22202
|
-
const link = detectLink(functionResult.value);
|
|
22203
|
-
if (!link) {
|
|
22204
|
-
return _createEvaluatedCell(functionResult, locale, cell);
|
|
22205
|
-
}
|
|
22206
|
-
const value = parseLiteral(link.label, locale);
|
|
22207
|
-
const format = functionResult.format ||
|
|
22208
|
-
(typeof value === "number"
|
|
22209
|
-
? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
|
|
22210
|
-
: undefined);
|
|
22211
|
-
const linkPayload = {
|
|
22212
|
-
value,
|
|
22213
|
-
format,
|
|
22214
|
-
};
|
|
22215
|
-
return {
|
|
22216
|
-
..._createEvaluatedCell(linkPayload, locale, cell),
|
|
22217
|
-
link,
|
|
22218
|
-
};
|
|
22219
|
-
}
|
|
22220
|
-
function _createEvaluatedCell(functionResult, locale, cell) {
|
|
22221
|
-
let { value, format, message } = functionResult;
|
|
22222
|
-
format = cell?.format || format;
|
|
22223
|
-
const formattedValue = formatValue(value, { format, locale });
|
|
22224
|
-
if (isEvaluationError(value)) {
|
|
22225
|
-
return errorCell(value, message);
|
|
22226
|
-
}
|
|
22227
|
-
if (isTextFormat(format)) {
|
|
22228
|
-
// TO DO:
|
|
22229
|
-
// with the next line, the value of the cell is transformed depending on the format.
|
|
22230
|
-
// This shouldn't happen, by doing this, the formulas handling numbers are not able
|
|
22231
|
-
// to interpret the value as a number.
|
|
22232
|
-
return textCell(toString(value), format, formattedValue);
|
|
22233
|
-
}
|
|
22234
|
-
if (value === null) {
|
|
22235
|
-
return emptyCell(format);
|
|
22236
|
-
}
|
|
22237
|
-
if (typeof value === "number") {
|
|
22238
|
-
if (isDateTimeFormat(format || "")) {
|
|
22239
|
-
return dateTimeCell(value, format, formattedValue);
|
|
22240
|
-
}
|
|
22241
|
-
return numberCell(value, format, formattedValue);
|
|
22242
|
-
}
|
|
22243
|
-
if (typeof value === "boolean") {
|
|
22244
|
-
return booleanCell(value, format, formattedValue);
|
|
22245
|
-
}
|
|
22246
|
-
return textCell(value, format, formattedValue);
|
|
22247
|
-
}
|
|
22248
|
-
function textCell(value, format, formattedValue) {
|
|
22249
|
-
return {
|
|
22250
|
-
value,
|
|
22251
|
-
format,
|
|
22252
|
-
formattedValue,
|
|
22253
|
-
type: CellValueType.text,
|
|
22254
|
-
isAutoSummable: true,
|
|
22255
|
-
defaultAlign: "left",
|
|
22256
|
-
};
|
|
22257
|
-
}
|
|
22258
|
-
function numberCell(value, format, formattedValue) {
|
|
22259
|
-
return {
|
|
22260
|
-
value: value || 0, // necessary to avoid "-0" and NaN values,
|
|
22261
|
-
format,
|
|
22262
|
-
formattedValue,
|
|
22263
|
-
type: CellValueType.number,
|
|
22264
|
-
isAutoSummable: true,
|
|
22265
|
-
defaultAlign: "right",
|
|
22266
|
-
};
|
|
22267
|
-
}
|
|
22268
|
-
const emptyCell = memoize(function emptyCell(format) {
|
|
22269
|
-
return {
|
|
22270
|
-
value: null,
|
|
22271
|
-
format,
|
|
22272
|
-
formattedValue: "",
|
|
22273
|
-
type: CellValueType.empty,
|
|
22274
|
-
isAutoSummable: true,
|
|
22275
|
-
defaultAlign: "left",
|
|
22276
|
-
};
|
|
22277
|
-
});
|
|
22278
|
-
function dateTimeCell(value, format, formattedValue) {
|
|
22279
|
-
return {
|
|
22280
|
-
value,
|
|
22281
|
-
format,
|
|
22282
|
-
formattedValue,
|
|
22283
|
-
type: CellValueType.number,
|
|
22284
|
-
isAutoSummable: false,
|
|
22285
|
-
defaultAlign: "right",
|
|
22286
|
-
};
|
|
22287
|
-
}
|
|
22288
|
-
function booleanCell(value, format, formattedValue) {
|
|
22289
|
-
return {
|
|
22290
|
-
value,
|
|
22291
|
-
format,
|
|
22292
|
-
formattedValue,
|
|
22293
|
-
type: CellValueType.boolean,
|
|
22294
|
-
isAutoSummable: false,
|
|
22295
|
-
defaultAlign: "center",
|
|
22296
|
-
};
|
|
22297
|
-
}
|
|
22298
|
-
function errorCell(value, message) {
|
|
22299
|
-
return {
|
|
22300
|
-
value,
|
|
22301
|
-
formattedValue: value,
|
|
22302
|
-
message,
|
|
22303
|
-
type: CellValueType.error,
|
|
22304
|
-
isAutoSummable: false,
|
|
22305
|
-
defaultAlign: "center",
|
|
22306
|
-
};
|
|
22307
|
-
}
|
|
22308
|
-
|
|
22309
22344
|
/**
|
|
22310
22345
|
* An AutofillModifierImplementation is used to describe how to handle a
|
|
22311
22346
|
* AutofillModifier.
|
|
@@ -23451,6 +23486,10 @@ var WarningTypes;
|
|
|
23451
23486
|
WarningTypes["CfIconSetEmptyIconNotSupported"] = "IconSets with empty icons";
|
|
23452
23487
|
WarningTypes["BadlyFormattedHyperlink"] = "Badly formatted hyperlink";
|
|
23453
23488
|
WarningTypes["NumFmtIdNotSupported"] = "Number format";
|
|
23489
|
+
WarningTypes["TimeDataValidationNotSupported"] = "Time data validation rules";
|
|
23490
|
+
WarningTypes["TextLengthDataValidationNotSupported"] = "Text length data validation rules";
|
|
23491
|
+
WarningTypes["WholeNumberDataValidationNotSupported"] = "Whole number data validation rules";
|
|
23492
|
+
WarningTypes["NotEqualDateDataValidationNotSupported"] = "Not equal date data validation rules";
|
|
23454
23493
|
})(WarningTypes || (WarningTypes = {}));
|
|
23455
23494
|
class XLSXImportWarningManager {
|
|
23456
23495
|
_parsingWarnings = new Set();
|
|
@@ -23829,6 +23868,25 @@ const IMAGE_EXTENSION_TO_MIMETYPE_MAPPING = {
|
|
|
23829
23868
|
webp: "image/webp",
|
|
23830
23869
|
jpg: "image/jpeg",
|
|
23831
23870
|
};
|
|
23871
|
+
const XLSX_DV_DECIMAL_OPERATOR_MAPPING = {
|
|
23872
|
+
between: "isBetween",
|
|
23873
|
+
notBetween: "isNotBetween",
|
|
23874
|
+
equal: "isEqual",
|
|
23875
|
+
notEqual: "isNotEqual",
|
|
23876
|
+
greaterThan: "isGreaterThan",
|
|
23877
|
+
greaterThanOrEqual: "isGreaterOrEqualTo",
|
|
23878
|
+
lessThan: "isLessThan",
|
|
23879
|
+
lessThanOrEqual: "isLessOrEqualTo",
|
|
23880
|
+
};
|
|
23881
|
+
const XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING = {
|
|
23882
|
+
between: "dateIsBetween",
|
|
23883
|
+
notBetween: "dateIsNotBetween",
|
|
23884
|
+
equal: "dateIs",
|
|
23885
|
+
greaterThan: "dateIsAfter",
|
|
23886
|
+
greaterThanOrEqual: "dateIsOnOrAfter",
|
|
23887
|
+
lessThan: "dateIsBefore",
|
|
23888
|
+
lessThanOrEqual: "dateIsOnOrBefore",
|
|
23889
|
+
};
|
|
23832
23890
|
|
|
23833
23891
|
/**
|
|
23834
23892
|
* Most of the functions could stay private, but are exported for testing purposes
|
|
@@ -24598,6 +24656,20 @@ function getRowPosition(rowIndex, sheetData) {
|
|
|
24598
24656
|
}
|
|
24599
24657
|
return position / HEIGHT_FACTOR;
|
|
24600
24658
|
}
|
|
24659
|
+
/**
|
|
24660
|
+
* Convert the o-spreadsheet data validation decimal
|
|
24661
|
+
* criterion type to the corresponding excel operator.
|
|
24662
|
+
*/
|
|
24663
|
+
function convertDecimalCriterionTypeToExcelOperator(operator) {
|
|
24664
|
+
return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
|
|
24665
|
+
}
|
|
24666
|
+
/**
|
|
24667
|
+
* Convert the o-spreadsheet data validation date
|
|
24668
|
+
* criterion type to the corresponding excel operator.
|
|
24669
|
+
*/
|
|
24670
|
+
function convertDateCriterionTypeToExcelOperator(operator) {
|
|
24671
|
+
return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
|
|
24672
|
+
}
|
|
24601
24673
|
|
|
24602
24674
|
function convertFigures(sheetData) {
|
|
24603
24675
|
let id = 1;
|
|
@@ -24707,6 +24779,112 @@ function getPositionFromAnchor(anchor, sheetData) {
|
|
|
24707
24779
|
};
|
|
24708
24780
|
}
|
|
24709
24781
|
|
|
24782
|
+
function convertDataValidationRules(xlsxDataValidations, warningManager) {
|
|
24783
|
+
const dvRules = [];
|
|
24784
|
+
let dvId = 1;
|
|
24785
|
+
for (const dv of xlsxDataValidations) {
|
|
24786
|
+
if (!dv) {
|
|
24787
|
+
continue;
|
|
24788
|
+
}
|
|
24789
|
+
switch (dv.type) {
|
|
24790
|
+
case "time":
|
|
24791
|
+
warningManager.generateNotSupportedWarning(WarningTypes.TimeDataValidationNotSupported);
|
|
24792
|
+
break;
|
|
24793
|
+
case "textLength":
|
|
24794
|
+
warningManager.generateNotSupportedWarning(WarningTypes.TextLengthDataValidationNotSupported);
|
|
24795
|
+
break;
|
|
24796
|
+
case "whole":
|
|
24797
|
+
warningManager.generateNotSupportedWarning(WarningTypes.WholeNumberDataValidationNotSupported);
|
|
24798
|
+
break;
|
|
24799
|
+
case "decimal":
|
|
24800
|
+
const decimalRule = convertDecimalRule(dvId++, dv);
|
|
24801
|
+
dvRules.push(decimalRule);
|
|
24802
|
+
break;
|
|
24803
|
+
case "list":
|
|
24804
|
+
const listRule = convertListrule(dvId++, dv);
|
|
24805
|
+
dvRules.push(listRule);
|
|
24806
|
+
break;
|
|
24807
|
+
case "date":
|
|
24808
|
+
if (dv.operator === "notEqual") {
|
|
24809
|
+
warningManager.generateNotSupportedWarning(WarningTypes.NotEqualDateDataValidationNotSupported);
|
|
24810
|
+
break;
|
|
24811
|
+
}
|
|
24812
|
+
const dateRule = convertDateRule(dvId++, dv);
|
|
24813
|
+
dvRules.push(dateRule);
|
|
24814
|
+
break;
|
|
24815
|
+
case "custom":
|
|
24816
|
+
const customRule = convertCustomRule(dvId++, dv);
|
|
24817
|
+
dvRules.push(customRule);
|
|
24818
|
+
break;
|
|
24819
|
+
}
|
|
24820
|
+
}
|
|
24821
|
+
return dvRules;
|
|
24822
|
+
}
|
|
24823
|
+
function convertDecimalRule(id, dv) {
|
|
24824
|
+
const values = [dv.formula1.toString()];
|
|
24825
|
+
if (dv.formula2) {
|
|
24826
|
+
values.push(dv.formula2.toString());
|
|
24827
|
+
}
|
|
24828
|
+
return {
|
|
24829
|
+
id: id.toString(),
|
|
24830
|
+
ranges: dv.sqref,
|
|
24831
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24832
|
+
criterion: {
|
|
24833
|
+
type: XLSX_DV_DECIMAL_OPERATOR_MAPPING[dv.operator],
|
|
24834
|
+
values,
|
|
24835
|
+
},
|
|
24836
|
+
};
|
|
24837
|
+
}
|
|
24838
|
+
function convertListrule(id, dv) {
|
|
24839
|
+
const formula1 = dv.formula1.toString();
|
|
24840
|
+
const isRangeRule = rangeReference.test(formula1);
|
|
24841
|
+
return {
|
|
24842
|
+
id: id.toString(),
|
|
24843
|
+
ranges: dv.sqref,
|
|
24844
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24845
|
+
criterion: {
|
|
24846
|
+
type: isRangeRule ? "isValueInRange" : "isValueInList",
|
|
24847
|
+
values: isRangeRule ? [formula1] : formula1.replaceAll('"', "").split(","),
|
|
24848
|
+
displayStyle: "arrow",
|
|
24849
|
+
},
|
|
24850
|
+
};
|
|
24851
|
+
}
|
|
24852
|
+
function convertDateRule(id, dv) {
|
|
24853
|
+
let criterion;
|
|
24854
|
+
const values = [dv.formula1.toString()];
|
|
24855
|
+
if (dv.formula2) {
|
|
24856
|
+
values.push(dv.formula2.toString());
|
|
24857
|
+
criterion = {
|
|
24858
|
+
type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
|
|
24859
|
+
values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
|
|
24860
|
+
};
|
|
24861
|
+
}
|
|
24862
|
+
else {
|
|
24863
|
+
criterion = {
|
|
24864
|
+
type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
|
|
24865
|
+
values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
|
|
24866
|
+
dateValue: "exactDate",
|
|
24867
|
+
};
|
|
24868
|
+
}
|
|
24869
|
+
return {
|
|
24870
|
+
id: id.toString(),
|
|
24871
|
+
ranges: dv.sqref,
|
|
24872
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24873
|
+
criterion: criterion,
|
|
24874
|
+
};
|
|
24875
|
+
}
|
|
24876
|
+
function convertCustomRule(id, dv) {
|
|
24877
|
+
return {
|
|
24878
|
+
id: id.toString(),
|
|
24879
|
+
ranges: dv.sqref,
|
|
24880
|
+
isBlocking: dv.errorStyle !== "warning",
|
|
24881
|
+
criterion: {
|
|
24882
|
+
type: "customFormula",
|
|
24883
|
+
values: [`=${dv.formula1.toString()}`],
|
|
24884
|
+
},
|
|
24885
|
+
};
|
|
24886
|
+
}
|
|
24887
|
+
|
|
24710
24888
|
/**
|
|
24711
24889
|
* Match external reference (ex. '[1]Sheet 3'!$B$4)
|
|
24712
24890
|
*
|
|
@@ -24827,6 +25005,7 @@ function convertSheets(data, warningManager) {
|
|
|
24827
25005
|
cols: convertCols(sheet, sheetDims[0], colHeaderGroups),
|
|
24828
25006
|
rows: convertRows(sheet, sheetDims[1], rowHeaderGroups),
|
|
24829
25007
|
conditionalFormats: convertConditionalFormats(sheet.cfs, data.dxfs, warningManager),
|
|
25008
|
+
dataValidationRules: convertDataValidationRules(sheet.dataValidations, warningManager),
|
|
24830
25009
|
figures: convertFigures(sheet),
|
|
24831
25010
|
isVisible: sheet.isVisible,
|
|
24832
25011
|
panes: sheetOptions
|
|
@@ -26107,6 +26286,41 @@ class XlsxCfExtractor extends XlsxBaseExtractor {
|
|
|
26107
26286
|
}
|
|
26108
26287
|
}
|
|
26109
26288
|
|
|
26289
|
+
class XlsxDataValidationExtractor extends XlsxBaseExtractor {
|
|
26290
|
+
theme;
|
|
26291
|
+
constructor(sheetFile, xlsxStructure, warningManager, theme) {
|
|
26292
|
+
super(sheetFile, xlsxStructure, warningManager);
|
|
26293
|
+
this.theme = theme;
|
|
26294
|
+
}
|
|
26295
|
+
extractDataValidations() {
|
|
26296
|
+
const dataValidations = this.mapOnElements({ parent: this.rootFile.file.xml, query: "worksheet > dataValidations > dataValidation" }, (dvElement) => {
|
|
26297
|
+
return {
|
|
26298
|
+
type: this.extractAttr(dvElement, "type", { required: true }).asString(),
|
|
26299
|
+
operator: this.extractAttr(dvElement, "operator", {
|
|
26300
|
+
default: "between",
|
|
26301
|
+
})?.asString(),
|
|
26302
|
+
sqref: this.extractAttr(dvElement, "sqref", { required: true }).asString().split(" "),
|
|
26303
|
+
errorStyle: this.extractAttr(dvElement, "errorStyle")?.asString(),
|
|
26304
|
+
formula1: this.extractDataValidationFormula(dvElement, 1)[0],
|
|
26305
|
+
formula2: this.extractDataValidationFormula(dvElement, 2)[0],
|
|
26306
|
+
showErrorMessage: this.extractAttr(dvElement, "showErrorMessage")?.asBool(),
|
|
26307
|
+
errorTitle: this.extractAttr(dvElement, "errorTitle")?.asString(),
|
|
26308
|
+
error: this.extractAttr(dvElement, "error")?.asString(),
|
|
26309
|
+
showInputMessage: this.extractAttr(dvElement, "showInputMessage")?.asBool(),
|
|
26310
|
+
promptTitle: this.extractAttr(dvElement, "promptTitle")?.asString(),
|
|
26311
|
+
prompt: this.extractAttr(dvElement, "prompt")?.asString(),
|
|
26312
|
+
allowBlank: this.extractAttr(dvElement, "allowBlank")?.asBool(),
|
|
26313
|
+
};
|
|
26314
|
+
});
|
|
26315
|
+
return dataValidations;
|
|
26316
|
+
}
|
|
26317
|
+
extractDataValidationFormula(dvElement, index) {
|
|
26318
|
+
return this.mapOnElements({ parent: dvElement, query: `formula${index}` }, (cfFormulaElements) => {
|
|
26319
|
+
return this.extractTextContent(cfFormulaElements, { required: true });
|
|
26320
|
+
});
|
|
26321
|
+
}
|
|
26322
|
+
}
|
|
26323
|
+
|
|
26110
26324
|
class XlsxChartExtractor extends XlsxBaseExtractor {
|
|
26111
26325
|
extractChart() {
|
|
26112
26326
|
return this.mapOnElements({ parent: this.rootFile.file.xml, query: "c:chartSpace" }, (rootChartElement) => {
|
|
@@ -26474,6 +26688,7 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
|
|
|
26474
26688
|
sharedFormulas: this.extractSharedFormulas(sheetElement),
|
|
26475
26689
|
merges: this.extractMerges(sheetElement),
|
|
26476
26690
|
cfs: this.extractConditionalFormats(),
|
|
26691
|
+
dataValidations: this.extractDataValidations(),
|
|
26477
26692
|
figures: this.extractFigures(sheetElement),
|
|
26478
26693
|
hyperlinks: this.extractHyperLinks(sheetElement),
|
|
26479
26694
|
tables: this.extractTables(sheetElement),
|
|
@@ -26545,6 +26760,9 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
|
|
|
26545
26760
|
extractConditionalFormats() {
|
|
26546
26761
|
return new XlsxCfExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractConditionalFormattings();
|
|
26547
26762
|
}
|
|
26763
|
+
extractDataValidations() {
|
|
26764
|
+
return new XlsxDataValidationExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractDataValidations();
|
|
26765
|
+
}
|
|
26548
26766
|
extractFigures(worksheet) {
|
|
26549
26767
|
const figures = this.mapOnElements({ parent: worksheet, query: "drawing" }, (drawingElement) => {
|
|
26550
26768
|
const drawingId = this.extractAttr(drawingElement, "r:id", { required: true })?.asString();
|
|
@@ -27825,6 +28043,7 @@ function createEmptySheet(sheetId, name) {
|
|
|
27825
28043
|
rows: {},
|
|
27826
28044
|
merges: [],
|
|
27827
28045
|
conditionalFormats: [],
|
|
28046
|
+
dataValidationRules: [],
|
|
27828
28047
|
figures: [],
|
|
27829
28048
|
tables: [],
|
|
27830
28049
|
isVisible: true,
|
|
@@ -31388,6 +31607,10 @@ class Popover extends owl.Component {
|
|
|
31388
31607
|
this.currentDisplayValue = newDisplay;
|
|
31389
31608
|
if (!anchor)
|
|
31390
31609
|
return;
|
|
31610
|
+
el.style.top = "";
|
|
31611
|
+
el.style.left = "";
|
|
31612
|
+
el.style["max-height"] = "";
|
|
31613
|
+
el.style["max-width"] = "";
|
|
31391
31614
|
const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
|
|
31392
31615
|
let elDims = {
|
|
31393
31616
|
width: el.getBoundingClientRect().width,
|
|
@@ -40248,7 +40471,7 @@ dataValidationEvaluatorRegistry.add("dateIs", {
|
|
|
40248
40471
|
name: _t("Date is"),
|
|
40249
40472
|
getPreview: (criterion, getters) => {
|
|
40250
40473
|
return criterion.dateValue === "exactDate"
|
|
40251
|
-
? _t("Date is %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
40474
|
+
? _t("Date is %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
40252
40475
|
: _t("Date is %s", DVTerms.DateIs[criterion.dateValue]);
|
|
40253
40476
|
},
|
|
40254
40477
|
});
|
|
@@ -40273,7 +40496,7 @@ dataValidationEvaluatorRegistry.add("dateIsBefore", {
|
|
|
40273
40496
|
name: _t("Date is before"),
|
|
40274
40497
|
getPreview: (criterion, getters) => {
|
|
40275
40498
|
return criterion.dateValue === "exactDate"
|
|
40276
|
-
? _t("Date is before %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
40499
|
+
? _t("Date is before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
40277
40500
|
: _t("Date is before %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
40278
40501
|
},
|
|
40279
40502
|
});
|
|
@@ -40298,7 +40521,7 @@ dataValidationEvaluatorRegistry.add("dateIsOnOrBefore", {
|
|
|
40298
40521
|
name: _t("Date is on or before"),
|
|
40299
40522
|
getPreview: (criterion, getters) => {
|
|
40300
40523
|
return criterion.dateValue === "exactDate"
|
|
40301
|
-
? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
40524
|
+
? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
40302
40525
|
: _t("Date is on or before %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
40303
40526
|
},
|
|
40304
40527
|
});
|
|
@@ -40323,7 +40546,7 @@ dataValidationEvaluatorRegistry.add("dateIsAfter", {
|
|
|
40323
40546
|
name: _t("Date is after"),
|
|
40324
40547
|
getPreview: (criterion, getters) => {
|
|
40325
40548
|
return criterion.dateValue === "exactDate"
|
|
40326
|
-
? _t("Date is after %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
40549
|
+
? _t("Date is after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
40327
40550
|
: _t("Date is after %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
40328
40551
|
},
|
|
40329
40552
|
});
|
|
@@ -40348,7 +40571,7 @@ dataValidationEvaluatorRegistry.add("dateIsOnOrAfter", {
|
|
|
40348
40571
|
name: _t("Date is on or after"),
|
|
40349
40572
|
getPreview: (criterion, getters) => {
|
|
40350
40573
|
return criterion.dateValue === "exactDate"
|
|
40351
|
-
? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion, getters)[0])
|
|
40574
|
+
? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
|
|
40352
40575
|
: _t("Date is on or after %s", DVTerms.DateIsBefore[criterion.dateValue]);
|
|
40353
40576
|
},
|
|
40354
40577
|
});
|
|
@@ -40374,7 +40597,7 @@ dataValidationEvaluatorRegistry.add("dateIsBetween", {
|
|
|
40374
40597
|
numberOfValues: () => 2,
|
|
40375
40598
|
name: _t("Date is between"),
|
|
40376
40599
|
getPreview: (criterion, getters) => {
|
|
40377
|
-
const values = getDateCriterionFormattedValues(criterion, getters);
|
|
40600
|
+
const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
|
|
40378
40601
|
return _t("Date is between %s and %s", values[0], values[1]);
|
|
40379
40602
|
},
|
|
40380
40603
|
});
|
|
@@ -40400,7 +40623,7 @@ dataValidationEvaluatorRegistry.add("dateIsNotBetween", {
|
|
|
40400
40623
|
numberOfValues: () => 2,
|
|
40401
40624
|
name: _t("Date is not between"),
|
|
40402
40625
|
getPreview: (criterion, getters) => {
|
|
40403
|
-
const values = getDateCriterionFormattedValues(criterion, getters);
|
|
40626
|
+
const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
|
|
40404
40627
|
return _t("Date is not between %s and %s", values[0], values[1]);
|
|
40405
40628
|
},
|
|
40406
40629
|
});
|
|
@@ -40683,19 +40906,6 @@ function checkValueIsNumber(value) {
|
|
|
40683
40906
|
const valueAsNumber = tryToNumber(value, DEFAULT_LOCALE);
|
|
40684
40907
|
return valueAsNumber !== undefined;
|
|
40685
40908
|
}
|
|
40686
|
-
function getDateCriterionFormattedValues(criterion, getters) {
|
|
40687
|
-
const locale = getters.getLocale();
|
|
40688
|
-
return criterion.values.map((valueStr) => {
|
|
40689
|
-
if (valueStr.startsWith("=")) {
|
|
40690
|
-
return valueStr;
|
|
40691
|
-
}
|
|
40692
|
-
const value = parseLiteral(valueStr, locale);
|
|
40693
|
-
if (typeof value === "number") {
|
|
40694
|
-
return formatValue(value, { format: locale.dateFormat, locale });
|
|
40695
|
-
}
|
|
40696
|
-
return "";
|
|
40697
|
-
});
|
|
40698
|
-
}
|
|
40699
40909
|
|
|
40700
40910
|
/** This component looks like a select input, but on click it opens a Menu with the items given as props instead of a dropdown */
|
|
40701
40911
|
class SelectMenu extends owl.Component {
|
|
@@ -46398,7 +46608,7 @@ class GridComposer extends owl.Component {
|
|
|
46398
46608
|
return;
|
|
46399
46609
|
}
|
|
46400
46610
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
46401
|
-
const zone = this.env.model.getters.
|
|
46611
|
+
const zone = positionToZone(this.env.model.getters.getSelection().anchor.cell);
|
|
46402
46612
|
const rect = this.env.model.getters.getVisibleRect(zone);
|
|
46403
46613
|
if (!deepEquals(rect, this.rect) || sheetId !== this.composerStore.currentEditedCell.sheetId) {
|
|
46404
46614
|
this.isCellReferenceVisible = true;
|
|
@@ -48232,13 +48442,23 @@ class GridRenderer {
|
|
|
48232
48442
|
drawLayer(renderingContext, layer) {
|
|
48233
48443
|
switch (layer) {
|
|
48234
48444
|
case "Background":
|
|
48235
|
-
|
|
48236
|
-
this.
|
|
48237
|
-
|
|
48238
|
-
|
|
48239
|
-
|
|
48240
|
-
|
|
48241
|
-
|
|
48445
|
+
this.drawGlobalBackground(renderingContext);
|
|
48446
|
+
for (const zone of this.getters.getAllActiveViewportsZones()) {
|
|
48447
|
+
const { ctx } = renderingContext;
|
|
48448
|
+
ctx.save();
|
|
48449
|
+
ctx.beginPath();
|
|
48450
|
+
const rect = this.getters.getVisibleRect(zone);
|
|
48451
|
+
ctx.rect(rect.x, rect.y, rect.width, rect.height);
|
|
48452
|
+
ctx.clip();
|
|
48453
|
+
const boxes = this.getGridBoxes(zone);
|
|
48454
|
+
this.drawBackground(renderingContext, boxes);
|
|
48455
|
+
this.drawOverflowingCellBackground(renderingContext, boxes);
|
|
48456
|
+
this.drawCellBackground(renderingContext, boxes);
|
|
48457
|
+
this.drawBorders(renderingContext, boxes);
|
|
48458
|
+
this.drawTexts(renderingContext, boxes);
|
|
48459
|
+
this.drawIcon(renderingContext, boxes);
|
|
48460
|
+
ctx.restore();
|
|
48461
|
+
}
|
|
48242
48462
|
this.drawFrozenPanes(renderingContext);
|
|
48243
48463
|
break;
|
|
48244
48464
|
case "Headers":
|
|
@@ -48249,12 +48469,15 @@ class GridRenderer {
|
|
|
48249
48469
|
break;
|
|
48250
48470
|
}
|
|
48251
48471
|
}
|
|
48252
|
-
|
|
48253
|
-
const { ctx
|
|
48472
|
+
drawGlobalBackground(renderingContext) {
|
|
48473
|
+
const { ctx } = renderingContext;
|
|
48254
48474
|
const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
|
|
48255
48475
|
// white background
|
|
48256
48476
|
ctx.fillStyle = "#ffffff";
|
|
48257
48477
|
ctx.fillRect(0, 0, width + CANVAS_SHIFT, height + CANVAS_SHIFT);
|
|
48478
|
+
}
|
|
48479
|
+
drawBackground(renderingContext, boxes) {
|
|
48480
|
+
const { ctx, thinLineWidth } = renderingContext;
|
|
48258
48481
|
const areGridLinesVisible = !this.getters.isDashboard() &&
|
|
48259
48482
|
this.getters.getGridLinesVisibility(this.getters.getActiveSheetId());
|
|
48260
48483
|
const inset = areGridLinesVisible ? 0.1 * thinLineWidth : 0;
|
|
@@ -48685,7 +48908,7 @@ class GridRenderer {
|
|
|
48685
48908
|
const position = { sheetId, col, row };
|
|
48686
48909
|
const cell = this.getters.getEvaluatedCell(position);
|
|
48687
48910
|
const showFormula = this.getters.shouldShowFormulas();
|
|
48688
|
-
const { x, y, width, height } = this.getters.
|
|
48911
|
+
const { x, y, width, height } = this.getters.getRect(zone);
|
|
48689
48912
|
const { verticalAlign } = this.getters.getCellStyle(position);
|
|
48690
48913
|
const box = {
|
|
48691
48914
|
x,
|
|
@@ -48803,12 +49026,16 @@ class GridRenderer {
|
|
|
48803
49026
|
}
|
|
48804
49027
|
return box;
|
|
48805
49028
|
}
|
|
48806
|
-
getGridBoxes() {
|
|
49029
|
+
getGridBoxes(zone) {
|
|
48807
49030
|
const boxes = [];
|
|
48808
|
-
const visibleCols = this.getters
|
|
49031
|
+
const visibleCols = this.getters
|
|
49032
|
+
.getSheetViewVisibleCols()
|
|
49033
|
+
.filter((col) => col >= zone.left && col <= zone.right);
|
|
48809
49034
|
const left = visibleCols[0];
|
|
48810
49035
|
const right = visibleCols[visibleCols.length - 1];
|
|
48811
|
-
const visibleRows = this.getters
|
|
49036
|
+
const visibleRows = this.getters
|
|
49037
|
+
.getSheetViewVisibleRows()
|
|
49038
|
+
.filter((row) => row >= zone.top && row <= zone.bottom);
|
|
48812
49039
|
const top = visibleRows[0];
|
|
48813
49040
|
const bottom = visibleRows[visibleRows.length - 1];
|
|
48814
49041
|
const viewport = { left, right, top, bottom };
|
|
@@ -51158,7 +51385,7 @@ class CellPlugin extends CorePlugin {
|
|
|
51158
51385
|
/*
|
|
51159
51386
|
* Reconstructs the original formula string based on new dependencies
|
|
51160
51387
|
*/
|
|
51161
|
-
getFormulaString(sheetId, tokens, dependencies,
|
|
51388
|
+
getFormulaString(sheetId, tokens, dependencies, useBoundedReference = false) {
|
|
51162
51389
|
if (!dependencies.length) {
|
|
51163
51390
|
return concat(tokens.map((token) => token.value));
|
|
51164
51391
|
}
|
|
@@ -51166,7 +51393,7 @@ class CellPlugin extends CorePlugin {
|
|
|
51166
51393
|
return concat(tokens.map((token) => {
|
|
51167
51394
|
if (token.type === "REFERENCE") {
|
|
51168
51395
|
const range = dependencies[rangeIndex++];
|
|
51169
|
-
return this.getters.getRangeString(range, sheetId, {
|
|
51396
|
+
return this.getters.getRangeString(range, sheetId, { useBoundedReference });
|
|
51170
51397
|
}
|
|
51171
51398
|
return token.value;
|
|
51172
51399
|
}));
|
|
@@ -51446,7 +51673,7 @@ class FormulaCellWithDependencies {
|
|
|
51446
51673
|
if (token.type === "REFERENCE") {
|
|
51447
51674
|
const index = rangeIndex++;
|
|
51448
51675
|
return this.getRangeString(this.compiledFormula.dependencies[index], this.sheetId, {
|
|
51449
|
-
|
|
51676
|
+
useBoundedReference: true,
|
|
51450
51677
|
});
|
|
51451
51678
|
}
|
|
51452
51679
|
return token.value;
|
|
@@ -51783,7 +52010,7 @@ class ConditionalFormatPlugin extends CorePlugin {
|
|
|
51783
52010
|
if (data.sheets) {
|
|
51784
52011
|
for (let sheet of data.sheets) {
|
|
51785
52012
|
if (this.cfRules[sheet.id]) {
|
|
51786
|
-
sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, {
|
|
52013
|
+
sheet.conditionalFormats = this.cfRules[sheet.id].map((rule) => this.mapToConditionalFormat(sheet.id, rule, { useBoundedReference: true }));
|
|
51787
52014
|
}
|
|
51788
52015
|
}
|
|
51789
52016
|
}
|
|
@@ -51852,9 +52079,9 @@ class ConditionalFormatPlugin extends CorePlugin {
|
|
|
51852
52079
|
// ---------------------------------------------------------------------------
|
|
51853
52080
|
// Private
|
|
51854
52081
|
// ---------------------------------------------------------------------------
|
|
51855
|
-
mapToConditionalFormat(sheetId, cf, {
|
|
52082
|
+
mapToConditionalFormat(sheetId, cf, { useBoundedReference } = { useBoundedReference: false }) {
|
|
51856
52083
|
const ranges = cf.ranges.map((range) => {
|
|
51857
|
-
return this.getters.getRangeString(range, sheetId, {
|
|
52084
|
+
return this.getters.getRangeString(range, sheetId, { useBoundedReference });
|
|
51858
52085
|
});
|
|
51859
52086
|
if (cf.rule.type !== "DataBarRule") {
|
|
51860
52087
|
return {
|
|
@@ -51869,7 +52096,7 @@ class ConditionalFormatPlugin extends CorePlugin {
|
|
|
51869
52096
|
...cf.rule,
|
|
51870
52097
|
rangeValues: cf.rule.rangeValues &&
|
|
51871
52098
|
this.getters.getRangeString(cf.rule.rangeValues, sheetId, {
|
|
51872
|
-
|
|
52099
|
+
useBoundedReference,
|
|
51873
52100
|
}),
|
|
51874
52101
|
},
|
|
51875
52102
|
ranges,
|
|
@@ -52292,6 +52519,30 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
52292
52519
|
}
|
|
52293
52520
|
}
|
|
52294
52521
|
}
|
|
52522
|
+
exportForExcel(data) {
|
|
52523
|
+
if (!data.sheets) {
|
|
52524
|
+
return;
|
|
52525
|
+
}
|
|
52526
|
+
for (const sheet of data.sheets) {
|
|
52527
|
+
sheet.dataValidationRules = [];
|
|
52528
|
+
for (const rule of this.rules[sheet.id]) {
|
|
52529
|
+
const excelRule = {
|
|
52530
|
+
...deepCopy(rule),
|
|
52531
|
+
ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useBoundedReference: true })),
|
|
52532
|
+
};
|
|
52533
|
+
if (rule.criterion.type === "isValueInRange") {
|
|
52534
|
+
excelRule.criterion.values = rule.criterion.values.map((value) => {
|
|
52535
|
+
const range = this.getters.getRangeFromSheetXC(sheet.id, value);
|
|
52536
|
+
return this.getters.getRangeString(range, sheet.id, {
|
|
52537
|
+
useBoundedReference: true,
|
|
52538
|
+
useFixedReference: true,
|
|
52539
|
+
});
|
|
52540
|
+
});
|
|
52541
|
+
}
|
|
52542
|
+
sheet.dataValidationRules.push(excelRule);
|
|
52543
|
+
}
|
|
52544
|
+
}
|
|
52545
|
+
}
|
|
52295
52546
|
checkCriterionTypeIsValid(cmd) {
|
|
52296
52547
|
return dataValidationEvaluatorRegistry.contains(cmd.rule.criterion.type)
|
|
52297
52548
|
? "Success" /* CommandResult.Success */
|
|
@@ -53702,9 +53953,10 @@ class RangeAdapter {
|
|
|
53702
53953
|
* @param range the range (received from getRangeFromXC or getRangeFromZone)
|
|
53703
53954
|
* @param forSheetId the id of the sheet where the range string is supposed to be used.
|
|
53704
53955
|
* @param options
|
|
53956
|
+
* @param options.useBoundedReference if true, the range will be returned with bounded row and column
|
|
53705
53957
|
* @param options.useFixedReference if true, the range will be returned with fixed row and column
|
|
53706
53958
|
*/
|
|
53707
|
-
getRangeString(range, forSheetId, options = { useFixedReference: false }) {
|
|
53959
|
+
getRangeString(range, forSheetId, options = { useBoundedReference: false, useFixedReference: false }) {
|
|
53708
53960
|
if (!range) {
|
|
53709
53961
|
return CellErrorType.InvalidReference;
|
|
53710
53962
|
}
|
|
@@ -53807,13 +54059,13 @@ class RangeAdapter {
|
|
|
53807
54059
|
/**
|
|
53808
54060
|
* Get a Xc string that represent a part of a range
|
|
53809
54061
|
*/
|
|
53810
|
-
getRangePartString(range, part, options = { useFixedReference: false }) {
|
|
53811
|
-
const colFixed = range.parts
|
|
54062
|
+
getRangePartString(range, part, options = { useBoundedReference: false, useFixedReference: false }) {
|
|
54063
|
+
const colFixed = range.parts[part]?.colFixed || options.useFixedReference ? "$" : "";
|
|
53812
54064
|
const col = part === 0 ? numberToLetters(range.zone.left) : numberToLetters(range.zone.right);
|
|
53813
|
-
const rowFixed = range.parts
|
|
54065
|
+
const rowFixed = range.parts[part]?.rowFixed || options.useFixedReference ? "$" : "";
|
|
53814
54066
|
const row = part === 0 ? String(range.zone.top + 1) : String(range.zone.bottom + 1);
|
|
53815
54067
|
let str = "";
|
|
53816
|
-
if (range.isFullCol && !options.
|
|
54068
|
+
if (range.isFullCol && !options.useBoundedReference) {
|
|
53817
54069
|
if (part === 0 && range.unboundedZone.hasHeader) {
|
|
53818
54070
|
str = colFixed + col + rowFixed + row;
|
|
53819
54071
|
}
|
|
@@ -53821,7 +54073,7 @@ class RangeAdapter {
|
|
|
53821
54073
|
str = colFixed + col;
|
|
53822
54074
|
}
|
|
53823
54075
|
}
|
|
53824
|
-
else if (range.isFullRow && !options.
|
|
54076
|
+
else if (range.isFullRow && !options.useBoundedReference) {
|
|
53825
54077
|
if (part === 0 && range.unboundedZone.hasHeader) {
|
|
53826
54078
|
str = colFixed + col + rowFixed + row;
|
|
53827
54079
|
}
|
|
@@ -54059,6 +54311,7 @@ class SheetPlugin extends CorePlugin {
|
|
|
54059
54311
|
formats: {},
|
|
54060
54312
|
borders: {},
|
|
54061
54313
|
conditionalFormats: [],
|
|
54314
|
+
dataValidationRules: [],
|
|
54062
54315
|
figures: [],
|
|
54063
54316
|
tables: [],
|
|
54064
54317
|
areGridLinesVisible: sheet.areGridLinesVisible === undefined ? true : sheet.areGridLinesVisible,
|
|
@@ -61020,6 +61273,7 @@ class Session extends EventBus {
|
|
|
61020
61273
|
waitingUndoRedoAck = false;
|
|
61021
61274
|
isReplayingInitialRevisions = false;
|
|
61022
61275
|
processedRevisions = new Set();
|
|
61276
|
+
lastRevisionMessage = undefined;
|
|
61023
61277
|
uuidGenerator = new UuidGenerator();
|
|
61024
61278
|
lastLocalOperation;
|
|
61025
61279
|
/**
|
|
@@ -61120,7 +61374,10 @@ class Session extends EventBus {
|
|
|
61120
61374
|
* Notify the server that the user client left the collaborative session
|
|
61121
61375
|
*/
|
|
61122
61376
|
async leave(data) {
|
|
61123
|
-
if (data &&
|
|
61377
|
+
if (data &&
|
|
61378
|
+
Object.keys(this.clients).length === 1 &&
|
|
61379
|
+
this.lastRevisionMessage &&
|
|
61380
|
+
this.lastRevisionMessage?.type !== "SNAPSHOT_CREATED") {
|
|
61124
61381
|
await this.snapshot(data());
|
|
61125
61382
|
}
|
|
61126
61383
|
delete this.clients[this.clientId];
|
|
@@ -61341,6 +61598,7 @@ class Session extends EventBus {
|
|
|
61341
61598
|
this.pendingMessages = this.pendingMessages.filter((msg) => msg.nextRevisionId !== message.nextRevisionId);
|
|
61342
61599
|
this.serverRevisionId = message.nextRevisionId;
|
|
61343
61600
|
this.processedRevisions.add(message.nextRevisionId);
|
|
61601
|
+
this.lastRevisionMessage = message;
|
|
61344
61602
|
this.sendPendingMessage();
|
|
61345
61603
|
break;
|
|
61346
61604
|
}
|
|
@@ -64542,8 +64800,12 @@ class GridSelectionPlugin extends UIPlugin {
|
|
|
64542
64800
|
},
|
|
64543
64801
|
];
|
|
64544
64802
|
handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
|
|
64803
|
+
const selection = pasteTarget[0];
|
|
64804
|
+
const col = selection.left;
|
|
64805
|
+
const row = selection.top;
|
|
64806
|
+
this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
|
|
64545
64807
|
const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
|
|
64546
|
-
let currentIndex = cmd.base;
|
|
64808
|
+
let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
|
|
64547
64809
|
for (const element of toRemove) {
|
|
64548
64810
|
const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
|
|
64549
64811
|
this.dispatch("RESIZE_COLUMNS_ROWS", {
|
|
@@ -64826,22 +65088,33 @@ class InternalViewport {
|
|
|
64826
65088
|
}
|
|
64827
65089
|
/**
|
|
64828
65090
|
*
|
|
64829
|
-
*
|
|
64830
|
-
*
|
|
65091
|
+
* Computes the visible coordinates & dimensions of a given zone inside the viewport
|
|
65092
|
+
*
|
|
64831
65093
|
*/
|
|
64832
|
-
|
|
65094
|
+
getVisibleRect(zone) {
|
|
64833
65095
|
const targetZone = intersection(zone, this);
|
|
64834
65096
|
if (targetZone) {
|
|
64835
65097
|
const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
|
|
64836
65098
|
const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
|
|
64837
65099
|
const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
|
|
64838
65100
|
const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
|
|
64839
|
-
return {
|
|
64840
|
-
|
|
64841
|
-
|
|
64842
|
-
|
|
64843
|
-
|
|
64844
|
-
|
|
65101
|
+
return { x, y, width, height };
|
|
65102
|
+
}
|
|
65103
|
+
return undefined;
|
|
65104
|
+
}
|
|
65105
|
+
/**
|
|
65106
|
+
*
|
|
65107
|
+
* @returns Computes the absolute coordinates & dimensions of a given zone inside the viewport
|
|
65108
|
+
*
|
|
65109
|
+
*/
|
|
65110
|
+
getFullRect(zone) {
|
|
65111
|
+
const targetZone = intersection(zone, this);
|
|
65112
|
+
if (targetZone) {
|
|
65113
|
+
const x = this.getters.getColRowOffset("COL", this.left, zone.left) + this.offsetCorrectionX;
|
|
65114
|
+
const y = this.getters.getColRowOffset("ROW", this.top, zone.top) + this.offsetCorrectionY;
|
|
65115
|
+
const width = this.getters.getColRowOffset("COL", zone.left, zone.right + 1);
|
|
65116
|
+
const height = this.getters.getColRowOffset("ROW", zone.top, zone.bottom + 1);
|
|
65117
|
+
return { x, y, width, height };
|
|
64845
65118
|
}
|
|
64846
65119
|
return undefined;
|
|
64847
65120
|
}
|
|
@@ -65011,6 +65284,8 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
65011
65284
|
"isPositionVisible",
|
|
65012
65285
|
"getColDimensionsInViewport",
|
|
65013
65286
|
"getRowDimensionsInViewport",
|
|
65287
|
+
"getAllActiveViewportsZones",
|
|
65288
|
+
"getRect",
|
|
65014
65289
|
];
|
|
65015
65290
|
viewports = {};
|
|
65016
65291
|
/**
|
|
@@ -65397,16 +65672,27 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
65397
65672
|
getVisibleRectWithoutHeaders(zone) {
|
|
65398
65673
|
const sheetId = this.getters.getActiveSheetId();
|
|
65399
65674
|
const viewportRects = this.getSubViewports(sheetId)
|
|
65400
|
-
.map((viewport) => viewport.
|
|
65675
|
+
.map((viewport) => viewport.getVisibleRect(zone))
|
|
65401
65676
|
.filter(isDefined);
|
|
65402
65677
|
if (viewportRects.length === 0) {
|
|
65403
65678
|
return { x: 0, y: 0, width: 0, height: 0 };
|
|
65404
65679
|
}
|
|
65405
|
-
|
|
65406
|
-
|
|
65407
|
-
|
|
65408
|
-
|
|
65409
|
-
|
|
65680
|
+
return this.recomposeRect(viewportRects);
|
|
65681
|
+
}
|
|
65682
|
+
/**
|
|
65683
|
+
* Computes the actual size and position (:Rect) of the zone on the canvas
|
|
65684
|
+
* regardless of the viewport dimensions.
|
|
65685
|
+
*/
|
|
65686
|
+
getRect(zone) {
|
|
65687
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
65688
|
+
const viewportRects = this.getSubViewports(sheetId)
|
|
65689
|
+
.map((viewport) => viewport.getFullRect(zone))
|
|
65690
|
+
.filter(isDefined);
|
|
65691
|
+
if (viewportRects.length === 0) {
|
|
65692
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
65693
|
+
}
|
|
65694
|
+
const rect = this.recomposeRect(viewportRects);
|
|
65695
|
+
return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
|
|
65410
65696
|
}
|
|
65411
65697
|
/**
|
|
65412
65698
|
* Returns the position of the MainViewport relatively to the start of the grid (without headers)
|
|
@@ -65450,6 +65736,10 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
65450
65736
|
end: start + (isRowHidden ? 0 : size),
|
|
65451
65737
|
};
|
|
65452
65738
|
}
|
|
65739
|
+
getAllActiveViewportsZones() {
|
|
65740
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
65741
|
+
return this.getSubViewports(sheetId);
|
|
65742
|
+
}
|
|
65453
65743
|
// ---------------------------------------------------------------------------
|
|
65454
65744
|
// Private
|
|
65455
65745
|
// ---------------------------------------------------------------------------
|
|
@@ -65640,6 +65930,13 @@ class SheetViewPlugin extends UIPlugin {
|
|
|
65640
65930
|
const height = this.sheetViewHeight + this.gridOffsetY;
|
|
65641
65931
|
return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
|
|
65642
65932
|
}
|
|
65933
|
+
recomposeRect(viewportRects) {
|
|
65934
|
+
const x = Math.min(...viewportRects.map((rect) => rect.x));
|
|
65935
|
+
const y = Math.min(...viewportRects.map((rect) => rect.y));
|
|
65936
|
+
const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
|
|
65937
|
+
const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
|
|
65938
|
+
return { x, y, width, height };
|
|
65939
|
+
}
|
|
65643
65940
|
}
|
|
65644
65941
|
|
|
65645
65942
|
class HeaderPositionsUIPlugin extends UIPlugin {
|
|
@@ -70945,6 +71242,124 @@ function getExcelThresholdType(type, position) {
|
|
|
70945
71242
|
}
|
|
70946
71243
|
}
|
|
70947
71244
|
|
|
71245
|
+
function addDataValidationRules(dataValidationRules) {
|
|
71246
|
+
const dvRulesCount = dataValidationRules.length;
|
|
71247
|
+
if (dvRulesCount === 0) {
|
|
71248
|
+
return [];
|
|
71249
|
+
}
|
|
71250
|
+
const dvNodes = [new XMLString(`<dataValidations count="${dvRulesCount}">`)];
|
|
71251
|
+
for (const dvRule of dataValidationRules) {
|
|
71252
|
+
switch (dvRule.criterion.type) {
|
|
71253
|
+
case "dateIs":
|
|
71254
|
+
case "dateIsBefore":
|
|
71255
|
+
case "dateIsOnOrBefore":
|
|
71256
|
+
case "dateIsAfter":
|
|
71257
|
+
case "dateIsOnOrAfter":
|
|
71258
|
+
case "dateIsBetween":
|
|
71259
|
+
case "dateIsNotBetween":
|
|
71260
|
+
dvNodes.push(addDateRule(dvRule));
|
|
71261
|
+
break;
|
|
71262
|
+
case "isEqual":
|
|
71263
|
+
case "isNotEqual":
|
|
71264
|
+
case "isGreaterThan":
|
|
71265
|
+
case "isGreaterOrEqualTo":
|
|
71266
|
+
case "isLessThan":
|
|
71267
|
+
case "isLessOrEqualTo":
|
|
71268
|
+
case "isBetween":
|
|
71269
|
+
case "isNotBetween":
|
|
71270
|
+
dvNodes.push(addDecimalRule(dvRule));
|
|
71271
|
+
break;
|
|
71272
|
+
case "isValueInRange":
|
|
71273
|
+
case "isValueInList":
|
|
71274
|
+
dvNodes.push(addListRule(dvRule));
|
|
71275
|
+
break;
|
|
71276
|
+
case "customFormula":
|
|
71277
|
+
dvNodes.push(addCustomFormulaRule(dvRule));
|
|
71278
|
+
break;
|
|
71279
|
+
default:
|
|
71280
|
+
console.warn(`Data validation ${dvRule.criterion.type} is not supported in xlsx.`);
|
|
71281
|
+
break;
|
|
71282
|
+
}
|
|
71283
|
+
}
|
|
71284
|
+
dvNodes.push(new XMLString("</dataValidations>"));
|
|
71285
|
+
return dvNodes;
|
|
71286
|
+
}
|
|
71287
|
+
function addDateRule(dvRule) {
|
|
71288
|
+
const rule = dvRule.criterion;
|
|
71289
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
71290
|
+
const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
|
|
71291
|
+
const operator = convertDateCriterionTypeToExcelOperator(dvRule.criterion.type);
|
|
71292
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
71293
|
+
attributes.push(["type", "date"], ["operator", operator]);
|
|
71294
|
+
if (formula2) {
|
|
71295
|
+
return escapeXml /*xml*/ `
|
|
71296
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
71297
|
+
<formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
|
|
71298
|
+
<formula2>${toNumber(formula2, DEFAULT_LOCALE)}</formula2>
|
|
71299
|
+
</dataValidation>
|
|
71300
|
+
`;
|
|
71301
|
+
}
|
|
71302
|
+
return escapeXml /*xml*/ `
|
|
71303
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
71304
|
+
<formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
|
|
71305
|
+
</dataValidation>
|
|
71306
|
+
`;
|
|
71307
|
+
}
|
|
71308
|
+
function addDecimalRule(dvRule) {
|
|
71309
|
+
const rule = dvRule.criterion;
|
|
71310
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
71311
|
+
const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
|
|
71312
|
+
const operator = convertDecimalCriterionTypeToExcelOperator(dvRule.criterion.type);
|
|
71313
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
71314
|
+
attributes.push(["type", "decimal"], ["operator", operator]);
|
|
71315
|
+
if (formula2) {
|
|
71316
|
+
return escapeXml /*xml*/ `
|
|
71317
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
71318
|
+
<formula1>${formula1}</formula1>
|
|
71319
|
+
<formula2>${formula2}</formula2>
|
|
71320
|
+
</dataValidation>
|
|
71321
|
+
`;
|
|
71322
|
+
}
|
|
71323
|
+
return escapeXml /*xml*/ `
|
|
71324
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
71325
|
+
<formula1>${formula1}</formula1>
|
|
71326
|
+
</dataValidation>
|
|
71327
|
+
`;
|
|
71328
|
+
}
|
|
71329
|
+
function addListRule(dvRule) {
|
|
71330
|
+
const rule = dvRule.criterion;
|
|
71331
|
+
const formula1 = dvRule.criterion.type === "isValueInRange"
|
|
71332
|
+
? adaptFormulaToExcel(rule.values[0])
|
|
71333
|
+
: `"${rule.values.join(",")}"`;
|
|
71334
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
71335
|
+
attributes.push(["type", "list"]);
|
|
71336
|
+
return escapeXml /*xml*/ `
|
|
71337
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
71338
|
+
<formula1>${formula1}</formula1>
|
|
71339
|
+
</dataValidation>
|
|
71340
|
+
`;
|
|
71341
|
+
}
|
|
71342
|
+
function addCustomFormulaRule(dvRule) {
|
|
71343
|
+
const rule = dvRule.criterion;
|
|
71344
|
+
const formula1 = adaptFormulaToExcel(rule.values[0]);
|
|
71345
|
+
const attributes = commonDataValidationAttributes(dvRule);
|
|
71346
|
+
attributes.push(["type", "custom"]);
|
|
71347
|
+
return escapeXml /*xml*/ `
|
|
71348
|
+
<dataValidation ${formatAttributes(attributes)}>
|
|
71349
|
+
<formula1>${formula1}</formula1>
|
|
71350
|
+
</dataValidation>
|
|
71351
|
+
`;
|
|
71352
|
+
}
|
|
71353
|
+
function commonDataValidationAttributes(dvRule) {
|
|
71354
|
+
return [
|
|
71355
|
+
["allowBlank", "1"],
|
|
71356
|
+
["showInputMessage", "1"],
|
|
71357
|
+
["showErrorMessage", "1"],
|
|
71358
|
+
["errorStyle", !dvRule.isBlocking ? "warning" : ""],
|
|
71359
|
+
["sqref", dvRule.ranges.join(" ")],
|
|
71360
|
+
];
|
|
71361
|
+
}
|
|
71362
|
+
|
|
70948
71363
|
function createDrawing(drawingRelIds, sheet, figures) {
|
|
70949
71364
|
const namespaces = [
|
|
70950
71365
|
["xmlns:xdr", NAMESPACE.drawing],
|
|
@@ -71723,6 +72138,7 @@ function createWorksheets(data, construct) {
|
|
|
71723
72138
|
${addRows(construct, data, sheet)}
|
|
71724
72139
|
${addMerges(sheet.merges)}
|
|
71725
72140
|
${joinXmlNodes(addConditionalFormatting(construct.dxfs, sheet.conditionalFormats))}
|
|
72141
|
+
${joinXmlNodes(addDataValidationRules(sheet.dataValidationRules))}
|
|
71726
72142
|
${addHyperlinks(construct, data, sheetIndex)}
|
|
71727
72143
|
${drawingNode}
|
|
71728
72144
|
${tablesNode}
|
|
@@ -72674,6 +73090,6 @@ exports.tokenColors = tokenColors;
|
|
|
72674
73090
|
exports.tokenize = tokenize;
|
|
72675
73091
|
|
|
72676
73092
|
|
|
72677
|
-
__info__.version = "18.0.
|
|
72678
|
-
__info__.date = "2025-01-
|
|
72679
|
-
__info__.hash = "
|
|
73093
|
+
__info__.version = "18.0.12";
|
|
73094
|
+
__info__.date = "2025-01-29T06:24:22.122Z";
|
|
73095
|
+
__info__.hash = "a881cff";
|