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