@odoo/o-spreadsheet 18.0.10 → 18.0.11

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.
@@ -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.10
6
- * @date 2025-01-15T08:05:47.616Z
7
- * @hash 94c45c7
5
+ * @version 18.0.11
6
+ * @date 2025-01-27T10:08:13.567Z
7
+ * @hash e8c6bd1
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 = normalizeValue(getValueInData(data, currentIndex));
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 = normalizeValue(getValueInData(data, currentIndex));
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 && sortOrder === "asc" && matchValIndex < currentIndex) ||
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 && sortOrder === "asc" && matchValIndex < currentIndex) ||
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 ((sortOrder === "asc" && currentVal > _target) ||
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
- return matchValIndex !== undefined ? matchValIndex : -1;
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
- function toCriterionDateNumber(dateValue) {
4394
- const today = DateTime.now();
4395
- switch (dateValue) {
4396
- case "today":
4397
- return jsDateToNumber(today);
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
- /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
4411
- function getDateNumberCriterionValues(criterion, locale) {
4412
- if ("dateValue" in criterion && criterion.dateValue !== "exactDate") {
4413
- return [toCriterionDateNumber(criterion.dateValue)];
4414
- }
4415
- return criterion.values.map((value) => valueToDateNumber(value, locale));
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
- /** Convert the criterion values to numbers. Return undefined values if they cannot be converted to numbers. */
4418
- function getCriterionValuesAsNumber(criterion, locale) {
4419
- return criterion.values.map((value) => tryToNumber(value, locale));
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
- const MAX_DELAY = 140;
4423
- const MIN_DELAY = 20;
4424
- const ACCELERATION = 0.035;
4425
- /**
4426
- * Decreasing exponential function used to determine the "speed" of edge-scrolling
4427
- * as the timeout delay.
4428
- *
4429
- * Returns a timeout delay in milliseconds.
4430
- */
4431
- function scrollDelay(value) {
4432
- // decreasing exponential from MAX_DELAY to MIN_DELAY
4433
- return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
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;
@@ -22093,217 +22319,6 @@ autoCompleteProviders.add("sheet_names", {
22093
22319
  },
22094
22320
  });
22095
22321
 
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
22322
  /**
22308
22323
  * An AutofillModifierImplementation is used to describe how to handle a
22309
22324
  * AutofillModifier.
@@ -23449,6 +23464,10 @@ var WarningTypes;
23449
23464
  WarningTypes["CfIconSetEmptyIconNotSupported"] = "IconSets with empty icons";
23450
23465
  WarningTypes["BadlyFormattedHyperlink"] = "Badly formatted hyperlink";
23451
23466
  WarningTypes["NumFmtIdNotSupported"] = "Number format";
23467
+ WarningTypes["TimeDataValidationNotSupported"] = "Time data validation rules";
23468
+ WarningTypes["TextLengthDataValidationNotSupported"] = "Text length data validation rules";
23469
+ WarningTypes["WholeNumberDataValidationNotSupported"] = "Whole number data validation rules";
23470
+ WarningTypes["NotEqualDateDataValidationNotSupported"] = "Not equal date data validation rules";
23452
23471
  })(WarningTypes || (WarningTypes = {}));
23453
23472
  class XLSXImportWarningManager {
23454
23473
  _parsingWarnings = new Set();
@@ -23827,6 +23846,25 @@ const IMAGE_EXTENSION_TO_MIMETYPE_MAPPING = {
23827
23846
  webp: "image/webp",
23828
23847
  jpg: "image/jpeg",
23829
23848
  };
23849
+ const XLSX_DV_DECIMAL_OPERATOR_MAPPING = {
23850
+ between: "isBetween",
23851
+ notBetween: "isNotBetween",
23852
+ equal: "isEqual",
23853
+ notEqual: "isNotEqual",
23854
+ greaterThan: "isGreaterThan",
23855
+ greaterThanOrEqual: "isGreaterOrEqualTo",
23856
+ lessThan: "isLessThan",
23857
+ lessThanOrEqual: "isLessOrEqualTo",
23858
+ };
23859
+ const XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING = {
23860
+ between: "dateIsBetween",
23861
+ notBetween: "dateIsNotBetween",
23862
+ equal: "dateIs",
23863
+ greaterThan: "dateIsAfter",
23864
+ greaterThanOrEqual: "dateIsOnOrAfter",
23865
+ lessThan: "dateIsBefore",
23866
+ lessThanOrEqual: "dateIsOnOrBefore",
23867
+ };
23830
23868
 
23831
23869
  /**
23832
23870
  * Most of the functions could stay private, but are exported for testing purposes
@@ -24596,6 +24634,20 @@ function getRowPosition(rowIndex, sheetData) {
24596
24634
  }
24597
24635
  return position / HEIGHT_FACTOR;
24598
24636
  }
24637
+ /**
24638
+ * Convert the o-spreadsheet data validation decimal
24639
+ * criterion type to the corresponding excel operator.
24640
+ */
24641
+ function convertDecimalCriterionTypeToExcelOperator(operator) {
24642
+ return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
24643
+ }
24644
+ /**
24645
+ * Convert the o-spreadsheet data validation date
24646
+ * criterion type to the corresponding excel operator.
24647
+ */
24648
+ function convertDateCriterionTypeToExcelOperator(operator) {
24649
+ return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
24650
+ }
24599
24651
 
24600
24652
  function convertFigures(sheetData) {
24601
24653
  let id = 1;
@@ -24705,6 +24757,112 @@ function getPositionFromAnchor(anchor, sheetData) {
24705
24757
  };
24706
24758
  }
24707
24759
 
24760
+ function convertDataValidationRules(xlsxDataValidations, warningManager) {
24761
+ const dvRules = [];
24762
+ let dvId = 1;
24763
+ for (const dv of xlsxDataValidations) {
24764
+ if (!dv) {
24765
+ continue;
24766
+ }
24767
+ switch (dv.type) {
24768
+ case "time":
24769
+ warningManager.generateNotSupportedWarning(WarningTypes.TimeDataValidationNotSupported);
24770
+ break;
24771
+ case "textLength":
24772
+ warningManager.generateNotSupportedWarning(WarningTypes.TextLengthDataValidationNotSupported);
24773
+ break;
24774
+ case "whole":
24775
+ warningManager.generateNotSupportedWarning(WarningTypes.WholeNumberDataValidationNotSupported);
24776
+ break;
24777
+ case "decimal":
24778
+ const decimalRule = convertDecimalRule(dvId++, dv);
24779
+ dvRules.push(decimalRule);
24780
+ break;
24781
+ case "list":
24782
+ const listRule = convertListrule(dvId++, dv);
24783
+ dvRules.push(listRule);
24784
+ break;
24785
+ case "date":
24786
+ if (dv.operator === "notEqual") {
24787
+ warningManager.generateNotSupportedWarning(WarningTypes.NotEqualDateDataValidationNotSupported);
24788
+ break;
24789
+ }
24790
+ const dateRule = convertDateRule(dvId++, dv);
24791
+ dvRules.push(dateRule);
24792
+ break;
24793
+ case "custom":
24794
+ const customRule = convertCustomRule(dvId++, dv);
24795
+ dvRules.push(customRule);
24796
+ break;
24797
+ }
24798
+ }
24799
+ return dvRules;
24800
+ }
24801
+ function convertDecimalRule(id, dv) {
24802
+ const values = [dv.formula1.toString()];
24803
+ if (dv.formula2) {
24804
+ values.push(dv.formula2.toString());
24805
+ }
24806
+ return {
24807
+ id: id.toString(),
24808
+ ranges: dv.sqref,
24809
+ isBlocking: dv.errorStyle !== "warning",
24810
+ criterion: {
24811
+ type: XLSX_DV_DECIMAL_OPERATOR_MAPPING[dv.operator],
24812
+ values,
24813
+ },
24814
+ };
24815
+ }
24816
+ function convertListrule(id, dv) {
24817
+ const formula1 = dv.formula1.toString();
24818
+ const isRangeRule = rangeReference.test(formula1);
24819
+ return {
24820
+ id: id.toString(),
24821
+ ranges: dv.sqref,
24822
+ isBlocking: dv.errorStyle !== "warning",
24823
+ criterion: {
24824
+ type: isRangeRule ? "isValueInRange" : "isValueInList",
24825
+ values: isRangeRule ? [formula1] : formula1.replaceAll('"', "").split(","),
24826
+ displayStyle: "arrow",
24827
+ },
24828
+ };
24829
+ }
24830
+ function convertDateRule(id, dv) {
24831
+ let criterion;
24832
+ const values = [dv.formula1.toString()];
24833
+ if (dv.formula2) {
24834
+ values.push(dv.formula2.toString());
24835
+ criterion = {
24836
+ type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
24837
+ values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
24838
+ };
24839
+ }
24840
+ else {
24841
+ criterion = {
24842
+ type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
24843
+ values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
24844
+ dateValue: "exactDate",
24845
+ };
24846
+ }
24847
+ return {
24848
+ id: id.toString(),
24849
+ ranges: dv.sqref,
24850
+ isBlocking: dv.errorStyle !== "warning",
24851
+ criterion: criterion,
24852
+ };
24853
+ }
24854
+ function convertCustomRule(id, dv) {
24855
+ return {
24856
+ id: id.toString(),
24857
+ ranges: dv.sqref,
24858
+ isBlocking: dv.errorStyle !== "warning",
24859
+ criterion: {
24860
+ type: "customFormula",
24861
+ values: [`=${dv.formula1.toString()}`],
24862
+ },
24863
+ };
24864
+ }
24865
+
24708
24866
  /**
24709
24867
  * Match external reference (ex. '[1]Sheet 3'!$B$4)
24710
24868
  *
@@ -24825,6 +24983,7 @@ function convertSheets(data, warningManager) {
24825
24983
  cols: convertCols(sheet, sheetDims[0], colHeaderGroups),
24826
24984
  rows: convertRows(sheet, sheetDims[1], rowHeaderGroups),
24827
24985
  conditionalFormats: convertConditionalFormats(sheet.cfs, data.dxfs, warningManager),
24986
+ dataValidationRules: convertDataValidationRules(sheet.dataValidations, warningManager),
24828
24987
  figures: convertFigures(sheet),
24829
24988
  isVisible: sheet.isVisible,
24830
24989
  panes: sheetOptions
@@ -26105,6 +26264,41 @@ class XlsxCfExtractor extends XlsxBaseExtractor {
26105
26264
  }
26106
26265
  }
26107
26266
 
26267
+ class XlsxDataValidationExtractor extends XlsxBaseExtractor {
26268
+ theme;
26269
+ constructor(sheetFile, xlsxStructure, warningManager, theme) {
26270
+ super(sheetFile, xlsxStructure, warningManager);
26271
+ this.theme = theme;
26272
+ }
26273
+ extractDataValidations() {
26274
+ const dataValidations = this.mapOnElements({ parent: this.rootFile.file.xml, query: "worksheet > dataValidations > dataValidation" }, (dvElement) => {
26275
+ return {
26276
+ type: this.extractAttr(dvElement, "type", { required: true }).asString(),
26277
+ operator: this.extractAttr(dvElement, "operator", {
26278
+ default: "between",
26279
+ })?.asString(),
26280
+ sqref: this.extractAttr(dvElement, "sqref", { required: true }).asString().split(" "),
26281
+ errorStyle: this.extractAttr(dvElement, "errorStyle")?.asString(),
26282
+ formula1: this.extractDataValidationFormula(dvElement, 1)[0],
26283
+ formula2: this.extractDataValidationFormula(dvElement, 2)[0],
26284
+ showErrorMessage: this.extractAttr(dvElement, "showErrorMessage")?.asBool(),
26285
+ errorTitle: this.extractAttr(dvElement, "errorTitle")?.asString(),
26286
+ error: this.extractAttr(dvElement, "error")?.asString(),
26287
+ showInputMessage: this.extractAttr(dvElement, "showInputMessage")?.asBool(),
26288
+ promptTitle: this.extractAttr(dvElement, "promptTitle")?.asString(),
26289
+ prompt: this.extractAttr(dvElement, "prompt")?.asString(),
26290
+ allowBlank: this.extractAttr(dvElement, "allowBlank")?.asBool(),
26291
+ };
26292
+ });
26293
+ return dataValidations;
26294
+ }
26295
+ extractDataValidationFormula(dvElement, index) {
26296
+ return this.mapOnElements({ parent: dvElement, query: `formula${index}` }, (cfFormulaElements) => {
26297
+ return this.extractTextContent(cfFormulaElements, { required: true });
26298
+ });
26299
+ }
26300
+ }
26301
+
26108
26302
  class XlsxChartExtractor extends XlsxBaseExtractor {
26109
26303
  extractChart() {
26110
26304
  return this.mapOnElements({ parent: this.rootFile.file.xml, query: "c:chartSpace" }, (rootChartElement) => {
@@ -26472,6 +26666,7 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
26472
26666
  sharedFormulas: this.extractSharedFormulas(sheetElement),
26473
26667
  merges: this.extractMerges(sheetElement),
26474
26668
  cfs: this.extractConditionalFormats(),
26669
+ dataValidations: this.extractDataValidations(),
26475
26670
  figures: this.extractFigures(sheetElement),
26476
26671
  hyperlinks: this.extractHyperLinks(sheetElement),
26477
26672
  tables: this.extractTables(sheetElement),
@@ -26543,6 +26738,9 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
26543
26738
  extractConditionalFormats() {
26544
26739
  return new XlsxCfExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractConditionalFormattings();
26545
26740
  }
26741
+ extractDataValidations() {
26742
+ return new XlsxDataValidationExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractDataValidations();
26743
+ }
26546
26744
  extractFigures(worksheet) {
26547
26745
  const figures = this.mapOnElements({ parent: worksheet, query: "drawing" }, (drawingElement) => {
26548
26746
  const drawingId = this.extractAttr(drawingElement, "r:id", { required: true })?.asString();
@@ -27823,6 +28021,7 @@ function createEmptySheet(sheetId, name) {
27823
28021
  rows: {},
27824
28022
  merges: [],
27825
28023
  conditionalFormats: [],
28024
+ dataValidationRules: [],
27826
28025
  figures: [],
27827
28026
  tables: [],
27828
28027
  isVisible: true,
@@ -31386,6 +31585,10 @@ class Popover extends Component {
31386
31585
  this.currentDisplayValue = newDisplay;
31387
31586
  if (!anchor)
31388
31587
  return;
31588
+ el.style.top = "";
31589
+ el.style.left = "";
31590
+ el.style["max-height"] = "";
31591
+ el.style["max-width"] = "";
31389
31592
  const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
31390
31593
  let elDims = {
31391
31594
  width: el.getBoundingClientRect().width,
@@ -40246,7 +40449,7 @@ dataValidationEvaluatorRegistry.add("dateIs", {
40246
40449
  name: _t("Date is"),
40247
40450
  getPreview: (criterion, getters) => {
40248
40451
  return criterion.dateValue === "exactDate"
40249
- ? _t("Date is %s", getDateCriterionFormattedValues(criterion, getters)[0])
40452
+ ? _t("Date is %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40250
40453
  : _t("Date is %s", DVTerms.DateIs[criterion.dateValue]);
40251
40454
  },
40252
40455
  });
@@ -40271,7 +40474,7 @@ dataValidationEvaluatorRegistry.add("dateIsBefore", {
40271
40474
  name: _t("Date is before"),
40272
40475
  getPreview: (criterion, getters) => {
40273
40476
  return criterion.dateValue === "exactDate"
40274
- ? _t("Date is before %s", getDateCriterionFormattedValues(criterion, getters)[0])
40477
+ ? _t("Date is before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40275
40478
  : _t("Date is before %s", DVTerms.DateIsBefore[criterion.dateValue]);
40276
40479
  },
40277
40480
  });
@@ -40296,7 +40499,7 @@ dataValidationEvaluatorRegistry.add("dateIsOnOrBefore", {
40296
40499
  name: _t("Date is on or before"),
40297
40500
  getPreview: (criterion, getters) => {
40298
40501
  return criterion.dateValue === "exactDate"
40299
- ? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion, getters)[0])
40502
+ ? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40300
40503
  : _t("Date is on or before %s", DVTerms.DateIsBefore[criterion.dateValue]);
40301
40504
  },
40302
40505
  });
@@ -40321,7 +40524,7 @@ dataValidationEvaluatorRegistry.add("dateIsAfter", {
40321
40524
  name: _t("Date is after"),
40322
40525
  getPreview: (criterion, getters) => {
40323
40526
  return criterion.dateValue === "exactDate"
40324
- ? _t("Date is after %s", getDateCriterionFormattedValues(criterion, getters)[0])
40527
+ ? _t("Date is after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40325
40528
  : _t("Date is after %s", DVTerms.DateIsBefore[criterion.dateValue]);
40326
40529
  },
40327
40530
  });
@@ -40346,7 +40549,7 @@ dataValidationEvaluatorRegistry.add("dateIsOnOrAfter", {
40346
40549
  name: _t("Date is on or after"),
40347
40550
  getPreview: (criterion, getters) => {
40348
40551
  return criterion.dateValue === "exactDate"
40349
- ? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion, getters)[0])
40552
+ ? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40350
40553
  : _t("Date is on or after %s", DVTerms.DateIsBefore[criterion.dateValue]);
40351
40554
  },
40352
40555
  });
@@ -40372,7 +40575,7 @@ dataValidationEvaluatorRegistry.add("dateIsBetween", {
40372
40575
  numberOfValues: () => 2,
40373
40576
  name: _t("Date is between"),
40374
40577
  getPreview: (criterion, getters) => {
40375
- const values = getDateCriterionFormattedValues(criterion, getters);
40578
+ const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
40376
40579
  return _t("Date is between %s and %s", values[0], values[1]);
40377
40580
  },
40378
40581
  });
@@ -40398,7 +40601,7 @@ dataValidationEvaluatorRegistry.add("dateIsNotBetween", {
40398
40601
  numberOfValues: () => 2,
40399
40602
  name: _t("Date is not between"),
40400
40603
  getPreview: (criterion, getters) => {
40401
- const values = getDateCriterionFormattedValues(criterion, getters);
40604
+ const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
40402
40605
  return _t("Date is not between %s and %s", values[0], values[1]);
40403
40606
  },
40404
40607
  });
@@ -40681,19 +40884,6 @@ function checkValueIsNumber(value) {
40681
40884
  const valueAsNumber = tryToNumber(value, DEFAULT_LOCALE);
40682
40885
  return valueAsNumber !== undefined;
40683
40886
  }
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
40887
 
40698
40888
  /** 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
40889
  class SelectMenu extends Component {
@@ -46396,7 +46586,7 @@ class GridComposer extends Component {
46396
46586
  return;
46397
46587
  }
46398
46588
  const sheetId = this.env.model.getters.getActiveSheetId();
46399
- const zone = this.env.model.getters.getSelectedZone();
46589
+ const zone = positionToZone(this.env.model.getters.getSelection().anchor.cell);
46400
46590
  const rect = this.env.model.getters.getVisibleRect(zone);
46401
46591
  if (!deepEquals(rect, this.rect) || sheetId !== this.composerStore.currentEditedCell.sheetId) {
46402
46592
  this.isCellReferenceVisible = true;
@@ -48230,13 +48420,23 @@ class GridRenderer {
48230
48420
  drawLayer(renderingContext, layer) {
48231
48421
  switch (layer) {
48232
48422
  case "Background":
48233
- const boxes = this.getGridBoxes();
48234
- this.drawBackground(renderingContext, boxes);
48235
- this.drawOverflowingCellBackground(renderingContext, boxes);
48236
- this.drawCellBackground(renderingContext, boxes);
48237
- this.drawBorders(renderingContext, boxes);
48238
- this.drawTexts(renderingContext, boxes);
48239
- this.drawIcon(renderingContext, boxes);
48423
+ this.drawGlobalBackground(renderingContext);
48424
+ for (const zone of this.getters.getAllActiveViewportsZones()) {
48425
+ const { ctx } = renderingContext;
48426
+ ctx.save();
48427
+ ctx.beginPath();
48428
+ const rect = this.getters.getVisibleRect(zone);
48429
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
48430
+ ctx.clip();
48431
+ const boxes = this.getGridBoxes(zone);
48432
+ this.drawBackground(renderingContext, boxes);
48433
+ this.drawOverflowingCellBackground(renderingContext, boxes);
48434
+ this.drawCellBackground(renderingContext, boxes);
48435
+ this.drawBorders(renderingContext, boxes);
48436
+ this.drawTexts(renderingContext, boxes);
48437
+ this.drawIcon(renderingContext, boxes);
48438
+ ctx.restore();
48439
+ }
48240
48440
  this.drawFrozenPanes(renderingContext);
48241
48441
  break;
48242
48442
  case "Headers":
@@ -48247,12 +48447,15 @@ class GridRenderer {
48247
48447
  break;
48248
48448
  }
48249
48449
  }
48250
- drawBackground(renderingContext, boxes) {
48251
- const { ctx, thinLineWidth } = renderingContext;
48450
+ drawGlobalBackground(renderingContext) {
48451
+ const { ctx } = renderingContext;
48252
48452
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
48253
48453
  // white background
48254
48454
  ctx.fillStyle = "#ffffff";
48255
48455
  ctx.fillRect(0, 0, width + CANVAS_SHIFT, height + CANVAS_SHIFT);
48456
+ }
48457
+ drawBackground(renderingContext, boxes) {
48458
+ const { ctx, thinLineWidth } = renderingContext;
48256
48459
  const areGridLinesVisible = !this.getters.isDashboard() &&
48257
48460
  this.getters.getGridLinesVisibility(this.getters.getActiveSheetId());
48258
48461
  const inset = areGridLinesVisible ? 0.1 * thinLineWidth : 0;
@@ -48683,7 +48886,7 @@ class GridRenderer {
48683
48886
  const position = { sheetId, col, row };
48684
48887
  const cell = this.getters.getEvaluatedCell(position);
48685
48888
  const showFormula = this.getters.shouldShowFormulas();
48686
- const { x, y, width, height } = this.getters.getVisibleRect(zone);
48889
+ const { x, y, width, height } = this.getters.getRect(zone);
48687
48890
  const { verticalAlign } = this.getters.getCellStyle(position);
48688
48891
  const box = {
48689
48892
  x,
@@ -48801,12 +49004,16 @@ class GridRenderer {
48801
49004
  }
48802
49005
  return box;
48803
49006
  }
48804
- getGridBoxes() {
49007
+ getGridBoxes(zone) {
48805
49008
  const boxes = [];
48806
- const visibleCols = this.getters.getSheetViewVisibleCols();
49009
+ const visibleCols = this.getters
49010
+ .getSheetViewVisibleCols()
49011
+ .filter((col) => col >= zone.left && col <= zone.right);
48807
49012
  const left = visibleCols[0];
48808
49013
  const right = visibleCols[visibleCols.length - 1];
48809
- const visibleRows = this.getters.getSheetViewVisibleRows();
49014
+ const visibleRows = this.getters
49015
+ .getSheetViewVisibleRows()
49016
+ .filter((row) => row >= zone.top && row <= zone.bottom);
48810
49017
  const top = visibleRows[0];
48811
49018
  const bottom = visibleRows[visibleRows.length - 1];
48812
49019
  const viewport = { left, right, top, bottom };
@@ -52290,6 +52497,20 @@ class DataValidationPlugin extends CorePlugin {
52290
52497
  }
52291
52498
  }
52292
52499
  }
52500
+ exportForExcel(data) {
52501
+ if (!data.sheets) {
52502
+ return;
52503
+ }
52504
+ for (const sheet of data.sheets) {
52505
+ sheet.dataValidationRules = [];
52506
+ for (const rule of this.rules[sheet.id]) {
52507
+ sheet.dataValidationRules.push({
52508
+ ...rule,
52509
+ ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useFixedReference: true })),
52510
+ });
52511
+ }
52512
+ }
52513
+ }
52293
52514
  checkCriterionTypeIsValid(cmd) {
52294
52515
  return dataValidationEvaluatorRegistry.contains(cmd.rule.criterion.type)
52295
52516
  ? "Success" /* CommandResult.Success */
@@ -54057,6 +54278,7 @@ class SheetPlugin extends CorePlugin {
54057
54278
  formats: {},
54058
54279
  borders: {},
54059
54280
  conditionalFormats: [],
54281
+ dataValidationRules: [],
54060
54282
  figures: [],
54061
54283
  tables: [],
54062
54284
  areGridLinesVisible: sheet.areGridLinesVisible === undefined ? true : sheet.areGridLinesVisible,
@@ -64540,8 +64762,12 @@ class GridSelectionPlugin extends UIPlugin {
64540
64762
  },
64541
64763
  ];
64542
64764
  handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
64765
+ const selection = pasteTarget[0];
64766
+ const col = selection.left;
64767
+ const row = selection.top;
64768
+ this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
64543
64769
  const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
64544
- let currentIndex = cmd.base;
64770
+ let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
64545
64771
  for (const element of toRemove) {
64546
64772
  const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
64547
64773
  this.dispatch("RESIZE_COLUMNS_ROWS", {
@@ -64824,22 +65050,33 @@ class InternalViewport {
64824
65050
  }
64825
65051
  /**
64826
65052
  *
64827
- * @param zone
64828
- * @returns Computes the absolute coordinate of a given zone inside the viewport
65053
+ * Computes the visible coordinates & dimensions of a given zone inside the viewport
65054
+ *
64829
65055
  */
64830
- getRect(zone) {
65056
+ getVisibleRect(zone) {
64831
65057
  const targetZone = intersection(zone, this);
64832
65058
  if (targetZone) {
64833
65059
  const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
64834
65060
  const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
64835
65061
  const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
64836
65062
  const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
64837
- return {
64838
- x,
64839
- y,
64840
- width,
64841
- height,
64842
- };
65063
+ return { x, y, width, height };
65064
+ }
65065
+ return undefined;
65066
+ }
65067
+ /**
65068
+ *
65069
+ * @returns Computes the absolute coordinates & dimensions of a given zone inside the viewport
65070
+ *
65071
+ */
65072
+ getFullRect(zone) {
65073
+ const targetZone = intersection(zone, this);
65074
+ if (targetZone) {
65075
+ const x = this.getters.getColRowOffset("COL", this.left, zone.left) + this.offsetCorrectionX;
65076
+ const y = this.getters.getColRowOffset("ROW", this.top, zone.top) + this.offsetCorrectionY;
65077
+ const width = this.getters.getColRowOffset("COL", zone.left, zone.right + 1);
65078
+ const height = this.getters.getColRowOffset("ROW", zone.top, zone.bottom + 1);
65079
+ return { x, y, width, height };
64843
65080
  }
64844
65081
  return undefined;
64845
65082
  }
@@ -65009,6 +65246,8 @@ class SheetViewPlugin extends UIPlugin {
65009
65246
  "isPositionVisible",
65010
65247
  "getColDimensionsInViewport",
65011
65248
  "getRowDimensionsInViewport",
65249
+ "getAllActiveViewportsZones",
65250
+ "getRect",
65012
65251
  ];
65013
65252
  viewports = {};
65014
65253
  /**
@@ -65395,16 +65634,27 @@ class SheetViewPlugin extends UIPlugin {
65395
65634
  getVisibleRectWithoutHeaders(zone) {
65396
65635
  const sheetId = this.getters.getActiveSheetId();
65397
65636
  const viewportRects = this.getSubViewports(sheetId)
65398
- .map((viewport) => viewport.getRect(zone))
65637
+ .map((viewport) => viewport.getVisibleRect(zone))
65399
65638
  .filter(isDefined);
65400
65639
  if (viewportRects.length === 0) {
65401
65640
  return { x: 0, y: 0, width: 0, height: 0 };
65402
65641
  }
65403
- const x = Math.min(...viewportRects.map((rect) => rect.x));
65404
- const y = Math.min(...viewportRects.map((rect) => rect.y));
65405
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
65406
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
65407
- return { x, y, width, height };
65642
+ return this.recomposeRect(viewportRects);
65643
+ }
65644
+ /**
65645
+ * Computes the actual size and position (:Rect) of the zone on the canvas
65646
+ * regardless of the viewport dimensions.
65647
+ */
65648
+ getRect(zone) {
65649
+ const sheetId = this.getters.getActiveSheetId();
65650
+ const viewportRects = this.getSubViewports(sheetId)
65651
+ .map((viewport) => viewport.getFullRect(zone))
65652
+ .filter(isDefined);
65653
+ if (viewportRects.length === 0) {
65654
+ return { x: 0, y: 0, width: 0, height: 0 };
65655
+ }
65656
+ const rect = this.recomposeRect(viewportRects);
65657
+ return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
65408
65658
  }
65409
65659
  /**
65410
65660
  * Returns the position of the MainViewport relatively to the start of the grid (without headers)
@@ -65448,6 +65698,10 @@ class SheetViewPlugin extends UIPlugin {
65448
65698
  end: start + (isRowHidden ? 0 : size),
65449
65699
  };
65450
65700
  }
65701
+ getAllActiveViewportsZones() {
65702
+ const sheetId = this.getters.getActiveSheetId();
65703
+ return this.getSubViewports(sheetId);
65704
+ }
65451
65705
  // ---------------------------------------------------------------------------
65452
65706
  // Private
65453
65707
  // ---------------------------------------------------------------------------
@@ -65638,6 +65892,13 @@ class SheetViewPlugin extends UIPlugin {
65638
65892
  const height = this.sheetViewHeight + this.gridOffsetY;
65639
65893
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
65640
65894
  }
65895
+ recomposeRect(viewportRects) {
65896
+ const x = Math.min(...viewportRects.map((rect) => rect.x));
65897
+ const y = Math.min(...viewportRects.map((rect) => rect.y));
65898
+ const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
65899
+ const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
65900
+ return { x, y, width, height };
65901
+ }
65641
65902
  }
65642
65903
 
65643
65904
  class HeaderPositionsUIPlugin extends UIPlugin {
@@ -70943,6 +71204,124 @@ function getExcelThresholdType(type, position) {
70943
71204
  }
70944
71205
  }
70945
71206
 
71207
+ function addDataValidationRules(dataValidationRules) {
71208
+ const dvRulesCount = dataValidationRules.length;
71209
+ if (dvRulesCount === 0) {
71210
+ return [];
71211
+ }
71212
+ const dvNodes = [new XMLString(`<dataValidations count="${dvRulesCount}">`)];
71213
+ for (const dvRule of dataValidationRules) {
71214
+ switch (dvRule.criterion.type) {
71215
+ case "dateIs":
71216
+ case "dateIsBefore":
71217
+ case "dateIsOnOrBefore":
71218
+ case "dateIsAfter":
71219
+ case "dateIsOnOrAfter":
71220
+ case "dateIsBetween":
71221
+ case "dateIsNotBetween":
71222
+ dvNodes.push(addDateRule(dvRule));
71223
+ break;
71224
+ case "isEqual":
71225
+ case "isNotEqual":
71226
+ case "isGreaterThan":
71227
+ case "isGreaterOrEqualTo":
71228
+ case "isLessThan":
71229
+ case "isLessOrEqualTo":
71230
+ case "isBetween":
71231
+ case "isNotBetween":
71232
+ dvNodes.push(addDecimalRule(dvRule));
71233
+ break;
71234
+ case "isValueInRange":
71235
+ case "isValueInList":
71236
+ dvNodes.push(addListRule(dvRule));
71237
+ break;
71238
+ case "customFormula":
71239
+ dvNodes.push(addCustomFormulaRule(dvRule));
71240
+ break;
71241
+ default:
71242
+ console.warn(`Data validation ${dvRule.criterion.type} is not supported in xlsx.`);
71243
+ break;
71244
+ }
71245
+ }
71246
+ dvNodes.push(new XMLString("</dataValidations>"));
71247
+ return dvNodes;
71248
+ }
71249
+ function addDateRule(dvRule) {
71250
+ const rule = dvRule.criterion;
71251
+ const formula1 = adaptFormulaToExcel(rule.values[0]);
71252
+ const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
71253
+ const operator = convertDateCriterionTypeToExcelOperator(dvRule.criterion.type);
71254
+ const attributes = commonDataValidationAttributes(dvRule);
71255
+ attributes.push(["type", "date"], ["operator", operator]);
71256
+ if (formula2) {
71257
+ return escapeXml /*xml*/ `
71258
+ <dataValidation ${formatAttributes(attributes)}>
71259
+ <formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
71260
+ <formula2>${toNumber(formula2, DEFAULT_LOCALE)}</formula2>
71261
+ </dataValidation>
71262
+ `;
71263
+ }
71264
+ return escapeXml /*xml*/ `
71265
+ <dataValidation ${formatAttributes(attributes)}>
71266
+ <formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
71267
+ </dataValidation>
71268
+ `;
71269
+ }
71270
+ function addDecimalRule(dvRule) {
71271
+ const rule = dvRule.criterion;
71272
+ const formula1 = adaptFormulaToExcel(rule.values[0]);
71273
+ const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
71274
+ const operator = convertDecimalCriterionTypeToExcelOperator(dvRule.criterion.type);
71275
+ const attributes = commonDataValidationAttributes(dvRule);
71276
+ attributes.push(["type", "decimal"], ["operator", operator]);
71277
+ if (formula2) {
71278
+ return escapeXml /*xml*/ `
71279
+ <dataValidation ${formatAttributes(attributes)}>
71280
+ <formula1>${formula1}</formula1>
71281
+ <formula2>${formula2}</formula2>
71282
+ </dataValidation>
71283
+ `;
71284
+ }
71285
+ return escapeXml /*xml*/ `
71286
+ <dataValidation ${formatAttributes(attributes)}>
71287
+ <formula1>${formula1}</formula1>
71288
+ </dataValidation>
71289
+ `;
71290
+ }
71291
+ function addListRule(dvRule) {
71292
+ const rule = dvRule.criterion;
71293
+ const formula1 = dvRule.criterion.type === "isValueInRange"
71294
+ ? adaptFormulaToExcel(rule.values[0])
71295
+ : `"${rule.values.join(",")}"`;
71296
+ const attributes = commonDataValidationAttributes(dvRule);
71297
+ attributes.push(["type", "list"]);
71298
+ return escapeXml /*xml*/ `
71299
+ <dataValidation ${formatAttributes(attributes)}>
71300
+ <formula1>${formula1}</formula1>
71301
+ </dataValidation>
71302
+ `;
71303
+ }
71304
+ function addCustomFormulaRule(dvRule) {
71305
+ const rule = dvRule.criterion;
71306
+ const formula1 = adaptFormulaToExcel(rule.values[0]);
71307
+ const attributes = commonDataValidationAttributes(dvRule);
71308
+ attributes.push(["type", "custom"]);
71309
+ return escapeXml /*xml*/ `
71310
+ <dataValidation ${formatAttributes(attributes)}>
71311
+ <formula1>${formula1}</formula1>
71312
+ </dataValidation>
71313
+ `;
71314
+ }
71315
+ function commonDataValidationAttributes(dvRule) {
71316
+ return [
71317
+ ["allowBlank", "1"],
71318
+ ["showInputMessage", "1"],
71319
+ ["showErrorMessage", "1"],
71320
+ ["errorStyle", !dvRule.isBlocking ? "warning" : ""],
71321
+ ["sqref", dvRule.ranges.join(" ")],
71322
+ ];
71323
+ }
71324
+
70946
71325
  function createDrawing(drawingRelIds, sheet, figures) {
70947
71326
  const namespaces = [
70948
71327
  ["xmlns:xdr", NAMESPACE.drawing],
@@ -71721,6 +72100,7 @@ function createWorksheets(data, construct) {
71721
72100
  ${addRows(construct, data, sheet)}
71722
72101
  ${addMerges(sheet.merges)}
71723
72102
  ${joinXmlNodes(addConditionalFormatting(construct.dxfs, sheet.conditionalFormats))}
72103
+ ${joinXmlNodes(addDataValidationRules(sheet.dataValidationRules))}
71724
72104
  ${addHyperlinks(construct, data, sheetIndex)}
71725
72105
  ${drawingNode}
71726
72106
  ${tablesNode}
@@ -72629,6 +73009,6 @@ const constants = {
72629
73009
  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
73010
 
72631
73011
 
72632
- __info__.version = "18.0.10";
72633
- __info__.date = "2025-01-15T08:05:47.616Z";
72634
- __info__.hash = "94c45c7";
73012
+ __info__.version = "18.0.11";
73013
+ __info__.date = "2025-01-27T10:08:13.567Z";
73014
+ __info__.hash = "e8c6bd1";