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