@odoo/o-spreadsheet 18.0.9 → 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.9
6
- * @date 2025-01-14T11:33:47.429Z
7
- * @hash 0c5220e
5
+ * @version 18.0.11
6
+ * @date 2025-01-27T10:08:13.567Z
7
+ * @hash e8c6bd1
8
8
  */
9
9
 
10
10
  'use strict';
@@ -4211,15 +4211,18 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4211
4211
  let currentIndex;
4212
4212
  let currentVal;
4213
4213
  let currentType;
4214
+ const getValue = sortOrder === "desc"
4215
+ ? (i) => normalizeValue(getValueInData(data, rangeLength - i - 1))
4216
+ : (i) => normalizeValue(getValueInData(data, i));
4214
4217
  while (indexRight - indexLeft >= 0) {
4215
4218
  indexMedian = Math.floor((indexLeft + indexRight) / 2);
4216
4219
  currentIndex = indexMedian;
4217
- currentVal = normalizeValue(getValueInData(data, currentIndex));
4220
+ currentVal = getValue(currentIndex);
4218
4221
  currentType = typeof currentVal;
4219
4222
  // 1 - linear search to find value with the same type
4220
4223
  while (indexLeft < currentIndex && targetType !== currentType) {
4221
4224
  currentIndex--;
4222
- currentVal = normalizeValue(getValueInData(data, currentIndex));
4225
+ currentVal = getValue(currentIndex);
4223
4226
  currentType = typeof currentVal;
4224
4227
  }
4225
4228
  if (currentType !== targetType || currentVal === undefined || currentVal === null) {
@@ -4235,8 +4238,7 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4235
4238
  if (matchVal === undefined ||
4236
4239
  matchVal === null ||
4237
4240
  matchVal < currentVal ||
4238
- (matchVal === currentVal && sortOrder === "asc" && matchValIndex < currentIndex) ||
4239
- (matchVal === currentVal && sortOrder === "desc" && matchValIndex > currentIndex)) {
4241
+ (matchVal === currentVal && matchValIndex < currentIndex)) {
4240
4242
  matchVal = currentVal;
4241
4243
  matchValIndex = currentIndex;
4242
4244
  }
@@ -4244,15 +4246,13 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4244
4246
  else if (mode === "nextGreater" && currentVal >= _target) {
4245
4247
  if (matchVal === undefined ||
4246
4248
  matchVal > currentVal ||
4247
- (matchVal === currentVal && sortOrder === "asc" && matchValIndex < currentIndex) ||
4248
- (matchVal === currentVal && sortOrder === "desc" && matchValIndex > currentIndex)) {
4249
+ (matchVal === currentVal && matchValIndex < currentIndex)) {
4249
4250
  matchVal = currentVal;
4250
4251
  matchValIndex = currentIndex;
4251
4252
  }
4252
4253
  }
4253
4254
  // 3 - give new indexes for the Binary search
4254
- if ((sortOrder === "asc" && currentVal > _target) ||
4255
- (sortOrder === "desc" && currentVal <= _target)) {
4255
+ if (currentVal > _target || (mode === "strict" && currentVal === _target)) {
4256
4256
  indexRight = currentIndex - 1;
4257
4257
  }
4258
4258
  else {
@@ -4260,7 +4260,10 @@ function dichotomicSearch(data, target, mode, sortOrder, rangeLength, getValueIn
4260
4260
  }
4261
4261
  }
4262
4262
  // note that valMinIndex could be 0
4263
- return matchValIndex !== undefined ? matchValIndex : -1;
4263
+ if (matchValIndex === undefined) {
4264
+ return -1;
4265
+ }
4266
+ return sortOrder === "desc" ? rangeLength - matchValIndex - 1 : matchValIndex;
4264
4267
  }
4265
4268
  /**
4266
4269
  * Perform a linear search and return the index of the match.
@@ -4392,47 +4395,83 @@ function isDataNonEmpty(data) {
4392
4395
  return true;
4393
4396
  }
4394
4397
 
4395
- function toCriterionDateNumber(dateValue) {
4396
- const today = DateTime.now();
4397
- switch (dateValue) {
4398
- case "today":
4399
- return jsDateToNumber(today);
4400
- case "yesterday":
4401
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
4402
- case "tomorrow":
4403
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
4404
- case "lastWeek":
4405
- return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
4406
- case "lastMonth":
4407
- return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
4408
- case "lastYear":
4409
- return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
4410
- }
4398
+ /**
4399
+ * Add the `https` prefix to the url if it's missing
4400
+ */
4401
+ function withHttps(url) {
4402
+ return !/^https?:\/\//i.test(url) ? `https://${url}` : url;
4411
4403
  }
4412
- /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
4413
- function getDateNumberCriterionValues(criterion, locale) {
4414
- if ("dateValue" in criterion && criterion.dateValue !== "exactDate") {
4415
- return [toCriterionDateNumber(criterion.dateValue)];
4416
- }
4417
- return criterion.values.map((value) => valueToDateNumber(value, locale));
4404
+ const urlRegistry = new Registry();
4405
+ function createWebLink(url, label) {
4406
+ url = withHttps(url);
4407
+ return {
4408
+ url,
4409
+ label: label || url,
4410
+ isExternal: true,
4411
+ isUrlEditable: true,
4412
+ };
4418
4413
  }
4419
- /** Convert the criterion values to numbers. Return undefined values if they cannot be converted to numbers. */
4420
- function getCriterionValuesAsNumber(criterion, locale) {
4421
- return criterion.values.map((value) => tryToNumber(value, locale));
4414
+ urlRegistry.add("sheet_URL", {
4415
+ match: (url) => isSheetUrl(url),
4416
+ createLink: (url, label) => {
4417
+ return {
4418
+ label,
4419
+ url,
4420
+ isExternal: false,
4421
+ isUrlEditable: false,
4422
+ };
4423
+ },
4424
+ urlRepresentation(url, getters) {
4425
+ const sheetId = parseSheetUrl(url);
4426
+ return getters.tryGetSheetName(sheetId) || _t("Invalid sheet");
4427
+ },
4428
+ open(url, env) {
4429
+ const sheetId = parseSheetUrl(url);
4430
+ const result = env.model.dispatch("ACTIVATE_SHEET", {
4431
+ sheetIdFrom: env.model.getters.getActiveSheetId(),
4432
+ sheetIdTo: sheetId,
4433
+ });
4434
+ if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
4435
+ env.notifyUser({
4436
+ type: "warning",
4437
+ sticky: false,
4438
+ text: _t("Cannot open the link because the linked sheet is hidden."),
4439
+ });
4440
+ }
4441
+ },
4442
+ sequence: 0,
4443
+ });
4444
+ const WebUrlSpec = {
4445
+ createLink: createWebLink,
4446
+ match: (url) => isWebLink(url),
4447
+ open: (url) => window.open(url, "_blank"),
4448
+ urlRepresentation: (url) => url,
4449
+ sequence: 0,
4450
+ };
4451
+ function findMatchingSpec(url) {
4452
+ return (urlRegistry
4453
+ .getAll()
4454
+ .sort((a, b) => a.sequence - b.sequence)
4455
+ .find((urlType) => urlType.match(url)) || WebUrlSpec);
4422
4456
  }
4423
-
4424
- const MAX_DELAY = 140;
4425
- const MIN_DELAY = 20;
4426
- const ACCELERATION = 0.035;
4427
- /**
4428
- * Decreasing exponential function used to determine the "speed" of edge-scrolling
4429
- * as the timeout delay.
4430
- *
4431
- * Returns a timeout delay in milliseconds.
4432
- */
4433
- function scrollDelay(value) {
4434
- // decreasing exponential from MAX_DELAY to MIN_DELAY
4435
- return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
4457
+ function urlRepresentation(link, getters) {
4458
+ return findMatchingSpec(link.url).urlRepresentation(link.url, getters);
4459
+ }
4460
+ function openLink(link, env) {
4461
+ findMatchingSpec(link.url).open(link.url, env);
4462
+ }
4463
+ function detectLink(value) {
4464
+ if (typeof value !== "string") {
4465
+ return undefined;
4466
+ }
4467
+ if (isMarkdownLink(value)) {
4468
+ const { label, url } = parseMarkdownLink(value);
4469
+ return findMatchingSpec(url).createLink(url, label);
4470
+ }
4471
+ else if (isWebLink(value)) {
4472
+ return createWebLink(value);
4473
+ }
4474
+ return undefined;
4436
4475
  }
4437
4476
 
4438
4477
  function tokenizeFormat(str) {
@@ -5494,6 +5533,193 @@ function isTextFormat(format) {
5494
5533
  }
5495
5534
  }
5496
5535
 
5536
+ function evaluateLiteral(literalCell, localeFormat) {
5537
+ const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
5538
+ const functionResult = { value, format: localeFormat.format };
5539
+ return createEvaluatedCell(functionResult, localeFormat.locale);
5540
+ }
5541
+ function parseLiteral(content, locale) {
5542
+ if (content.startsWith("=")) {
5543
+ throw new Error(`Cannot parse "${content}" because it's not a literal value. It's a formula`);
5544
+ }
5545
+ if (content === "") {
5546
+ return null;
5547
+ }
5548
+ if (isNumber(content, DEFAULT_LOCALE)) {
5549
+ return parseNumber(content, DEFAULT_LOCALE);
5550
+ }
5551
+ const internalDate = parseDateTime(content, locale);
5552
+ if (internalDate) {
5553
+ return internalDate.value;
5554
+ }
5555
+ if (isBoolean(content)) {
5556
+ return content.toUpperCase() === "TRUE" ? true : false;
5557
+ }
5558
+ return content;
5559
+ }
5560
+ function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
5561
+ const link = detectLink(functionResult.value);
5562
+ if (!link) {
5563
+ return _createEvaluatedCell(functionResult, locale, cell);
5564
+ }
5565
+ const value = parseLiteral(link.label, locale);
5566
+ const format = functionResult.format ||
5567
+ (typeof value === "number"
5568
+ ? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
5569
+ : undefined);
5570
+ const linkPayload = {
5571
+ value,
5572
+ format,
5573
+ };
5574
+ return {
5575
+ ..._createEvaluatedCell(linkPayload, locale, cell),
5576
+ link,
5577
+ };
5578
+ }
5579
+ function _createEvaluatedCell(functionResult, locale, cell) {
5580
+ let { value, format, message } = functionResult;
5581
+ format = cell?.format || format;
5582
+ const formattedValue = formatValue(value, { format, locale });
5583
+ if (isEvaluationError(value)) {
5584
+ return errorCell(value, message);
5585
+ }
5586
+ if (isTextFormat(format)) {
5587
+ // TO DO:
5588
+ // with the next line, the value of the cell is transformed depending on the format.
5589
+ // This shouldn't happen, by doing this, the formulas handling numbers are not able
5590
+ // to interpret the value as a number.
5591
+ return textCell(toString(value), format, formattedValue);
5592
+ }
5593
+ if (value === null) {
5594
+ return emptyCell(format);
5595
+ }
5596
+ if (typeof value === "number") {
5597
+ if (isDateTimeFormat(format || "")) {
5598
+ return dateTimeCell(value, format, formattedValue);
5599
+ }
5600
+ return numberCell(value, format, formattedValue);
5601
+ }
5602
+ if (typeof value === "boolean") {
5603
+ return booleanCell(value, format, formattedValue);
5604
+ }
5605
+ return textCell(value, format, formattedValue);
5606
+ }
5607
+ function textCell(value, format, formattedValue) {
5608
+ return {
5609
+ value,
5610
+ format,
5611
+ formattedValue,
5612
+ type: CellValueType.text,
5613
+ isAutoSummable: true,
5614
+ defaultAlign: "left",
5615
+ };
5616
+ }
5617
+ function numberCell(value, format, formattedValue) {
5618
+ return {
5619
+ value: value || 0, // necessary to avoid "-0" and NaN values,
5620
+ format,
5621
+ formattedValue,
5622
+ type: CellValueType.number,
5623
+ isAutoSummable: true,
5624
+ defaultAlign: "right",
5625
+ };
5626
+ }
5627
+ const emptyCell = memoize(function emptyCell(format) {
5628
+ return {
5629
+ value: null,
5630
+ format,
5631
+ formattedValue: "",
5632
+ type: CellValueType.empty,
5633
+ isAutoSummable: true,
5634
+ defaultAlign: "left",
5635
+ };
5636
+ });
5637
+ function dateTimeCell(value, format, formattedValue) {
5638
+ return {
5639
+ value,
5640
+ format,
5641
+ formattedValue,
5642
+ type: CellValueType.number,
5643
+ isAutoSummable: false,
5644
+ defaultAlign: "right",
5645
+ };
5646
+ }
5647
+ function booleanCell(value, format, formattedValue) {
5648
+ return {
5649
+ value,
5650
+ format,
5651
+ formattedValue,
5652
+ type: CellValueType.boolean,
5653
+ isAutoSummable: false,
5654
+ defaultAlign: "center",
5655
+ };
5656
+ }
5657
+ function errorCell(value, message) {
5658
+ return {
5659
+ value,
5660
+ formattedValue: value,
5661
+ message,
5662
+ type: CellValueType.error,
5663
+ isAutoSummable: false,
5664
+ defaultAlign: "center",
5665
+ };
5666
+ }
5667
+
5668
+ function toCriterionDateNumber(dateValue) {
5669
+ const today = DateTime.now();
5670
+ switch (dateValue) {
5671
+ case "today":
5672
+ return jsDateToNumber(today);
5673
+ case "yesterday":
5674
+ return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 1)));
5675
+ case "tomorrow":
5676
+ return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() + 1)));
5677
+ case "lastWeek":
5678
+ return jsDateToNumber(DateTime.fromTimestamp(today.setDate(today.getDate() - 7)));
5679
+ case "lastMonth":
5680
+ return jsDateToNumber(DateTime.fromTimestamp(today.setMonth(today.getMonth() - 1)));
5681
+ case "lastYear":
5682
+ return jsDateToNumber(DateTime.fromTimestamp(today.setFullYear(today.getFullYear() - 1)));
5683
+ }
5684
+ }
5685
+ /** Get all the dates values of a criterion converted to numbers, converting date values such as "today" to actual dates */
5686
+ function getDateNumberCriterionValues(criterion, locale) {
5687
+ if ("dateValue" in criterion && criterion.dateValue !== "exactDate") {
5688
+ return [toCriterionDateNumber(criterion.dateValue)];
5689
+ }
5690
+ return criterion.values.map((value) => valueToDateNumber(value, locale));
5691
+ }
5692
+ /** Convert the criterion values to numbers. Return undefined values if they cannot be converted to numbers. */
5693
+ function getCriterionValuesAsNumber(criterion, locale) {
5694
+ return criterion.values.map((value) => tryToNumber(value, locale));
5695
+ }
5696
+ function getDateCriterionFormattedValues(values, locale) {
5697
+ return values.map((valueStr) => {
5698
+ if (valueStr.startsWith("=")) {
5699
+ return valueStr;
5700
+ }
5701
+ const value = parseLiteral(valueStr, locale);
5702
+ if (typeof value === "number") {
5703
+ return formatValue(value, { format: locale.dateFormat, locale });
5704
+ }
5705
+ return "";
5706
+ });
5707
+ }
5708
+
5709
+ const MAX_DELAY = 140;
5710
+ const MIN_DELAY = 20;
5711
+ const ACCELERATION = 0.035;
5712
+ /**
5713
+ * Decreasing exponential function used to determine the "speed" of edge-scrolling
5714
+ * as the timeout delay.
5715
+ *
5716
+ * Returns a timeout delay in milliseconds.
5717
+ */
5718
+ function scrollDelay(value) {
5719
+ // decreasing exponential from MAX_DELAY to MIN_DELAY
5720
+ return MIN_DELAY + (MAX_DELAY - MIN_DELAY) * Math.exp(-ACCELERATION * (value - 1));
5721
+ }
5722
+
5497
5723
  class RangeImpl {
5498
5724
  getSheetSize;
5499
5725
  _zone;
@@ -22095,217 +22321,6 @@ autoCompleteProviders.add("sheet_names", {
22095
22321
  },
22096
22322
  });
22097
22323
 
22098
- /**
22099
- * Add the `https` prefix to the url if it's missing
22100
- */
22101
- function withHttps(url) {
22102
- return !/^https?:\/\//i.test(url) ? `https://${url}` : url;
22103
- }
22104
- const urlRegistry = new Registry();
22105
- function createWebLink(url, label) {
22106
- url = withHttps(url);
22107
- return {
22108
- url,
22109
- label: label || url,
22110
- isExternal: true,
22111
- isUrlEditable: true,
22112
- };
22113
- }
22114
- urlRegistry.add("sheet_URL", {
22115
- match: (url) => isSheetUrl(url),
22116
- createLink: (url, label) => {
22117
- return {
22118
- label,
22119
- url,
22120
- isExternal: false,
22121
- isUrlEditable: false,
22122
- };
22123
- },
22124
- urlRepresentation(url, getters) {
22125
- const sheetId = parseSheetUrl(url);
22126
- return getters.tryGetSheetName(sheetId) || _t("Invalid sheet");
22127
- },
22128
- open(url, env) {
22129
- const sheetId = parseSheetUrl(url);
22130
- const result = env.model.dispatch("ACTIVATE_SHEET", {
22131
- sheetIdFrom: env.model.getters.getActiveSheetId(),
22132
- sheetIdTo: sheetId,
22133
- });
22134
- if (result.isCancelledBecause("SheetIsHidden" /* CommandResult.SheetIsHidden */)) {
22135
- env.notifyUser({
22136
- type: "warning",
22137
- sticky: false,
22138
- text: _t("Cannot open the link because the linked sheet is hidden."),
22139
- });
22140
- }
22141
- },
22142
- sequence: 0,
22143
- });
22144
- const WebUrlSpec = {
22145
- createLink: createWebLink,
22146
- match: (url) => isWebLink(url),
22147
- open: (url) => window.open(url, "_blank"),
22148
- urlRepresentation: (url) => url,
22149
- sequence: 0,
22150
- };
22151
- function findMatchingSpec(url) {
22152
- return (urlRegistry
22153
- .getAll()
22154
- .sort((a, b) => a.sequence - b.sequence)
22155
- .find((urlType) => urlType.match(url)) || WebUrlSpec);
22156
- }
22157
- function urlRepresentation(link, getters) {
22158
- return findMatchingSpec(link.url).urlRepresentation(link.url, getters);
22159
- }
22160
- function openLink(link, env) {
22161
- findMatchingSpec(link.url).open(link.url, env);
22162
- }
22163
- function detectLink(value) {
22164
- if (typeof value !== "string") {
22165
- return undefined;
22166
- }
22167
- if (isMarkdownLink(value)) {
22168
- const { label, url } = parseMarkdownLink(value);
22169
- return findMatchingSpec(url).createLink(url, label);
22170
- }
22171
- else if (isWebLink(value)) {
22172
- return createWebLink(value);
22173
- }
22174
- return undefined;
22175
- }
22176
-
22177
- function evaluateLiteral(literalCell, localeFormat) {
22178
- const value = isTextFormat(localeFormat.format) ? literalCell.content : literalCell.parsedValue;
22179
- const functionResult = { value, format: localeFormat.format };
22180
- return createEvaluatedCell(functionResult, localeFormat.locale);
22181
- }
22182
- function parseLiteral(content, locale) {
22183
- if (content.startsWith("=")) {
22184
- throw new Error(`Cannot parse "${content}" because it's not a literal value. It's a formula`);
22185
- }
22186
- if (content === "") {
22187
- return null;
22188
- }
22189
- if (isNumber(content, DEFAULT_LOCALE)) {
22190
- return parseNumber(content, DEFAULT_LOCALE);
22191
- }
22192
- const internalDate = parseDateTime(content, locale);
22193
- if (internalDate) {
22194
- return internalDate.value;
22195
- }
22196
- if (isBoolean(content)) {
22197
- return content.toUpperCase() === "TRUE" ? true : false;
22198
- }
22199
- return content;
22200
- }
22201
- function createEvaluatedCell(functionResult, locale = DEFAULT_LOCALE, cell) {
22202
- const link = detectLink(functionResult.value);
22203
- if (!link) {
22204
- return _createEvaluatedCell(functionResult, locale, cell);
22205
- }
22206
- const value = parseLiteral(link.label, locale);
22207
- const format = functionResult.format ||
22208
- (typeof value === "number"
22209
- ? detectDateFormat(link.label, locale) || detectNumberFormat(link.label)
22210
- : undefined);
22211
- const linkPayload = {
22212
- value,
22213
- format,
22214
- };
22215
- return {
22216
- ..._createEvaluatedCell(linkPayload, locale, cell),
22217
- link,
22218
- };
22219
- }
22220
- function _createEvaluatedCell(functionResult, locale, cell) {
22221
- let { value, format, message } = functionResult;
22222
- format = cell?.format || format;
22223
- const formattedValue = formatValue(value, { format, locale });
22224
- if (isEvaluationError(value)) {
22225
- return errorCell(value, message);
22226
- }
22227
- if (isTextFormat(format)) {
22228
- // TO DO:
22229
- // with the next line, the value of the cell is transformed depending on the format.
22230
- // This shouldn't happen, by doing this, the formulas handling numbers are not able
22231
- // to interpret the value as a number.
22232
- return textCell(toString(value), format, formattedValue);
22233
- }
22234
- if (value === null) {
22235
- return emptyCell(format);
22236
- }
22237
- if (typeof value === "number") {
22238
- if (isDateTimeFormat(format || "")) {
22239
- return dateTimeCell(value, format, formattedValue);
22240
- }
22241
- return numberCell(value, format, formattedValue);
22242
- }
22243
- if (typeof value === "boolean") {
22244
- return booleanCell(value, format, formattedValue);
22245
- }
22246
- return textCell(value, format, formattedValue);
22247
- }
22248
- function textCell(value, format, formattedValue) {
22249
- return {
22250
- value,
22251
- format,
22252
- formattedValue,
22253
- type: CellValueType.text,
22254
- isAutoSummable: true,
22255
- defaultAlign: "left",
22256
- };
22257
- }
22258
- function numberCell(value, format, formattedValue) {
22259
- return {
22260
- value: value || 0, // necessary to avoid "-0" and NaN values,
22261
- format,
22262
- formattedValue,
22263
- type: CellValueType.number,
22264
- isAutoSummable: true,
22265
- defaultAlign: "right",
22266
- };
22267
- }
22268
- const emptyCell = memoize(function emptyCell(format) {
22269
- return {
22270
- value: null,
22271
- format,
22272
- formattedValue: "",
22273
- type: CellValueType.empty,
22274
- isAutoSummable: true,
22275
- defaultAlign: "left",
22276
- };
22277
- });
22278
- function dateTimeCell(value, format, formattedValue) {
22279
- return {
22280
- value,
22281
- format,
22282
- formattedValue,
22283
- type: CellValueType.number,
22284
- isAutoSummable: false,
22285
- defaultAlign: "right",
22286
- };
22287
- }
22288
- function booleanCell(value, format, formattedValue) {
22289
- return {
22290
- value,
22291
- format,
22292
- formattedValue,
22293
- type: CellValueType.boolean,
22294
- isAutoSummable: false,
22295
- defaultAlign: "center",
22296
- };
22297
- }
22298
- function errorCell(value, message) {
22299
- return {
22300
- value,
22301
- formattedValue: value,
22302
- message,
22303
- type: CellValueType.error,
22304
- isAutoSummable: false,
22305
- defaultAlign: "center",
22306
- };
22307
- }
22308
-
22309
22324
  /**
22310
22325
  * An AutofillModifierImplementation is used to describe how to handle a
22311
22326
  * AutofillModifier.
@@ -23451,6 +23466,10 @@ var WarningTypes;
23451
23466
  WarningTypes["CfIconSetEmptyIconNotSupported"] = "IconSets with empty icons";
23452
23467
  WarningTypes["BadlyFormattedHyperlink"] = "Badly formatted hyperlink";
23453
23468
  WarningTypes["NumFmtIdNotSupported"] = "Number format";
23469
+ WarningTypes["TimeDataValidationNotSupported"] = "Time data validation rules";
23470
+ WarningTypes["TextLengthDataValidationNotSupported"] = "Text length data validation rules";
23471
+ WarningTypes["WholeNumberDataValidationNotSupported"] = "Whole number data validation rules";
23472
+ WarningTypes["NotEqualDateDataValidationNotSupported"] = "Not equal date data validation rules";
23454
23473
  })(WarningTypes || (WarningTypes = {}));
23455
23474
  class XLSXImportWarningManager {
23456
23475
  _parsingWarnings = new Set();
@@ -23829,6 +23848,25 @@ const IMAGE_EXTENSION_TO_MIMETYPE_MAPPING = {
23829
23848
  webp: "image/webp",
23830
23849
  jpg: "image/jpeg",
23831
23850
  };
23851
+ const XLSX_DV_DECIMAL_OPERATOR_MAPPING = {
23852
+ between: "isBetween",
23853
+ notBetween: "isNotBetween",
23854
+ equal: "isEqual",
23855
+ notEqual: "isNotEqual",
23856
+ greaterThan: "isGreaterThan",
23857
+ greaterThanOrEqual: "isGreaterOrEqualTo",
23858
+ lessThan: "isLessThan",
23859
+ lessThanOrEqual: "isLessOrEqualTo",
23860
+ };
23861
+ const XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING = {
23862
+ between: "dateIsBetween",
23863
+ notBetween: "dateIsNotBetween",
23864
+ equal: "dateIs",
23865
+ greaterThan: "dateIsAfter",
23866
+ greaterThanOrEqual: "dateIsOnOrAfter",
23867
+ lessThan: "dateIsBefore",
23868
+ lessThanOrEqual: "dateIsOnOrBefore",
23869
+ };
23832
23870
 
23833
23871
  /**
23834
23872
  * Most of the functions could stay private, but are exported for testing purposes
@@ -24598,6 +24636,20 @@ function getRowPosition(rowIndex, sheetData) {
24598
24636
  }
24599
24637
  return position / HEIGHT_FACTOR;
24600
24638
  }
24639
+ /**
24640
+ * Convert the o-spreadsheet data validation decimal
24641
+ * criterion type to the corresponding excel operator.
24642
+ */
24643
+ function convertDecimalCriterionTypeToExcelOperator(operator) {
24644
+ return Object.keys(XLSX_DV_DECIMAL_OPERATOR_MAPPING).find((key) => XLSX_DV_DECIMAL_OPERATOR_MAPPING[key] === operator);
24645
+ }
24646
+ /**
24647
+ * Convert the o-spreadsheet data validation date
24648
+ * criterion type to the corresponding excel operator.
24649
+ */
24650
+ function convertDateCriterionTypeToExcelOperator(operator) {
24651
+ return Object.keys(XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING).find((key) => XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[key] === operator);
24652
+ }
24601
24653
 
24602
24654
  function convertFigures(sheetData) {
24603
24655
  let id = 1;
@@ -24707,6 +24759,112 @@ function getPositionFromAnchor(anchor, sheetData) {
24707
24759
  };
24708
24760
  }
24709
24761
 
24762
+ function convertDataValidationRules(xlsxDataValidations, warningManager) {
24763
+ const dvRules = [];
24764
+ let dvId = 1;
24765
+ for (const dv of xlsxDataValidations) {
24766
+ if (!dv) {
24767
+ continue;
24768
+ }
24769
+ switch (dv.type) {
24770
+ case "time":
24771
+ warningManager.generateNotSupportedWarning(WarningTypes.TimeDataValidationNotSupported);
24772
+ break;
24773
+ case "textLength":
24774
+ warningManager.generateNotSupportedWarning(WarningTypes.TextLengthDataValidationNotSupported);
24775
+ break;
24776
+ case "whole":
24777
+ warningManager.generateNotSupportedWarning(WarningTypes.WholeNumberDataValidationNotSupported);
24778
+ break;
24779
+ case "decimal":
24780
+ const decimalRule = convertDecimalRule(dvId++, dv);
24781
+ dvRules.push(decimalRule);
24782
+ break;
24783
+ case "list":
24784
+ const listRule = convertListrule(dvId++, dv);
24785
+ dvRules.push(listRule);
24786
+ break;
24787
+ case "date":
24788
+ if (dv.operator === "notEqual") {
24789
+ warningManager.generateNotSupportedWarning(WarningTypes.NotEqualDateDataValidationNotSupported);
24790
+ break;
24791
+ }
24792
+ const dateRule = convertDateRule(dvId++, dv);
24793
+ dvRules.push(dateRule);
24794
+ break;
24795
+ case "custom":
24796
+ const customRule = convertCustomRule(dvId++, dv);
24797
+ dvRules.push(customRule);
24798
+ break;
24799
+ }
24800
+ }
24801
+ return dvRules;
24802
+ }
24803
+ function convertDecimalRule(id, dv) {
24804
+ const values = [dv.formula1.toString()];
24805
+ if (dv.formula2) {
24806
+ values.push(dv.formula2.toString());
24807
+ }
24808
+ return {
24809
+ id: id.toString(),
24810
+ ranges: dv.sqref,
24811
+ isBlocking: dv.errorStyle !== "warning",
24812
+ criterion: {
24813
+ type: XLSX_DV_DECIMAL_OPERATOR_MAPPING[dv.operator],
24814
+ values,
24815
+ },
24816
+ };
24817
+ }
24818
+ function convertListrule(id, dv) {
24819
+ const formula1 = dv.formula1.toString();
24820
+ const isRangeRule = rangeReference.test(formula1);
24821
+ return {
24822
+ id: id.toString(),
24823
+ ranges: dv.sqref,
24824
+ isBlocking: dv.errorStyle !== "warning",
24825
+ criterion: {
24826
+ type: isRangeRule ? "isValueInRange" : "isValueInList",
24827
+ values: isRangeRule ? [formula1] : formula1.replaceAll('"', "").split(","),
24828
+ displayStyle: "arrow",
24829
+ },
24830
+ };
24831
+ }
24832
+ function convertDateRule(id, dv) {
24833
+ let criterion;
24834
+ const values = [dv.formula1.toString()];
24835
+ if (dv.formula2) {
24836
+ values.push(dv.formula2.toString());
24837
+ criterion = {
24838
+ type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
24839
+ values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
24840
+ };
24841
+ }
24842
+ else {
24843
+ criterion = {
24844
+ type: XLSX_DV_DATE_OPERATOR_TO_DV_TYPE_MAPPING[dv.operator],
24845
+ values: getDateCriterionFormattedValues(values, DEFAULT_LOCALE),
24846
+ dateValue: "exactDate",
24847
+ };
24848
+ }
24849
+ return {
24850
+ id: id.toString(),
24851
+ ranges: dv.sqref,
24852
+ isBlocking: dv.errorStyle !== "warning",
24853
+ criterion: criterion,
24854
+ };
24855
+ }
24856
+ function convertCustomRule(id, dv) {
24857
+ return {
24858
+ id: id.toString(),
24859
+ ranges: dv.sqref,
24860
+ isBlocking: dv.errorStyle !== "warning",
24861
+ criterion: {
24862
+ type: "customFormula",
24863
+ values: [`=${dv.formula1.toString()}`],
24864
+ },
24865
+ };
24866
+ }
24867
+
24710
24868
  /**
24711
24869
  * Match external reference (ex. '[1]Sheet 3'!$B$4)
24712
24870
  *
@@ -24827,6 +24985,7 @@ function convertSheets(data, warningManager) {
24827
24985
  cols: convertCols(sheet, sheetDims[0], colHeaderGroups),
24828
24986
  rows: convertRows(sheet, sheetDims[1], rowHeaderGroups),
24829
24987
  conditionalFormats: convertConditionalFormats(sheet.cfs, data.dxfs, warningManager),
24988
+ dataValidationRules: convertDataValidationRules(sheet.dataValidations, warningManager),
24830
24989
  figures: convertFigures(sheet),
24831
24990
  isVisible: sheet.isVisible,
24832
24991
  panes: sheetOptions
@@ -26107,6 +26266,41 @@ class XlsxCfExtractor extends XlsxBaseExtractor {
26107
26266
  }
26108
26267
  }
26109
26268
 
26269
+ class XlsxDataValidationExtractor extends XlsxBaseExtractor {
26270
+ theme;
26271
+ constructor(sheetFile, xlsxStructure, warningManager, theme) {
26272
+ super(sheetFile, xlsxStructure, warningManager);
26273
+ this.theme = theme;
26274
+ }
26275
+ extractDataValidations() {
26276
+ const dataValidations = this.mapOnElements({ parent: this.rootFile.file.xml, query: "worksheet > dataValidations > dataValidation" }, (dvElement) => {
26277
+ return {
26278
+ type: this.extractAttr(dvElement, "type", { required: true }).asString(),
26279
+ operator: this.extractAttr(dvElement, "operator", {
26280
+ default: "between",
26281
+ })?.asString(),
26282
+ sqref: this.extractAttr(dvElement, "sqref", { required: true }).asString().split(" "),
26283
+ errorStyle: this.extractAttr(dvElement, "errorStyle")?.asString(),
26284
+ formula1: this.extractDataValidationFormula(dvElement, 1)[0],
26285
+ formula2: this.extractDataValidationFormula(dvElement, 2)[0],
26286
+ showErrorMessage: this.extractAttr(dvElement, "showErrorMessage")?.asBool(),
26287
+ errorTitle: this.extractAttr(dvElement, "errorTitle")?.asString(),
26288
+ error: this.extractAttr(dvElement, "error")?.asString(),
26289
+ showInputMessage: this.extractAttr(dvElement, "showInputMessage")?.asBool(),
26290
+ promptTitle: this.extractAttr(dvElement, "promptTitle")?.asString(),
26291
+ prompt: this.extractAttr(dvElement, "prompt")?.asString(),
26292
+ allowBlank: this.extractAttr(dvElement, "allowBlank")?.asBool(),
26293
+ };
26294
+ });
26295
+ return dataValidations;
26296
+ }
26297
+ extractDataValidationFormula(dvElement, index) {
26298
+ return this.mapOnElements({ parent: dvElement, query: `formula${index}` }, (cfFormulaElements) => {
26299
+ return this.extractTextContent(cfFormulaElements, { required: true });
26300
+ });
26301
+ }
26302
+ }
26303
+
26110
26304
  class XlsxChartExtractor extends XlsxBaseExtractor {
26111
26305
  extractChart() {
26112
26306
  return this.mapOnElements({ parent: this.rootFile.file.xml, query: "c:chartSpace" }, (rootChartElement) => {
@@ -26474,6 +26668,7 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
26474
26668
  sharedFormulas: this.extractSharedFormulas(sheetElement),
26475
26669
  merges: this.extractMerges(sheetElement),
26476
26670
  cfs: this.extractConditionalFormats(),
26671
+ dataValidations: this.extractDataValidations(),
26477
26672
  figures: this.extractFigures(sheetElement),
26478
26673
  hyperlinks: this.extractHyperLinks(sheetElement),
26479
26674
  tables: this.extractTables(sheetElement),
@@ -26545,6 +26740,9 @@ class XlsxSheetExtractor extends XlsxBaseExtractor {
26545
26740
  extractConditionalFormats() {
26546
26741
  return new XlsxCfExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractConditionalFormattings();
26547
26742
  }
26743
+ extractDataValidations() {
26744
+ return new XlsxDataValidationExtractor(this.rootFile, this.xlsxFileStructure, this.warningManager, this.theme).extractDataValidations();
26745
+ }
26548
26746
  extractFigures(worksheet) {
26549
26747
  const figures = this.mapOnElements({ parent: worksheet, query: "drawing" }, (drawingElement) => {
26550
26748
  const drawingId = this.extractAttr(drawingElement, "r:id", { required: true })?.asString();
@@ -27825,6 +28023,7 @@ function createEmptySheet(sheetId, name) {
27825
28023
  rows: {},
27826
28024
  merges: [],
27827
28025
  conditionalFormats: [],
28026
+ dataValidationRules: [],
27828
28027
  figures: [],
27829
28028
  tables: [],
27830
28029
  isVisible: true,
@@ -31388,6 +31587,10 @@ class Popover extends owl.Component {
31388
31587
  this.currentDisplayValue = newDisplay;
31389
31588
  if (!anchor)
31390
31589
  return;
31590
+ el.style.top = "";
31591
+ el.style.left = "";
31592
+ el.style["max-height"] = "";
31593
+ el.style["max-width"] = "";
31391
31594
  const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
31392
31595
  let elDims = {
31393
31596
  width: el.getBoundingClientRect().width,
@@ -40248,7 +40451,7 @@ dataValidationEvaluatorRegistry.add("dateIs", {
40248
40451
  name: _t("Date is"),
40249
40452
  getPreview: (criterion, getters) => {
40250
40453
  return criterion.dateValue === "exactDate"
40251
- ? _t("Date is %s", getDateCriterionFormattedValues(criterion, getters)[0])
40454
+ ? _t("Date is %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40252
40455
  : _t("Date is %s", DVTerms.DateIs[criterion.dateValue]);
40253
40456
  },
40254
40457
  });
@@ -40273,7 +40476,7 @@ dataValidationEvaluatorRegistry.add("dateIsBefore", {
40273
40476
  name: _t("Date is before"),
40274
40477
  getPreview: (criterion, getters) => {
40275
40478
  return criterion.dateValue === "exactDate"
40276
- ? _t("Date is before %s", getDateCriterionFormattedValues(criterion, getters)[0])
40479
+ ? _t("Date is before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40277
40480
  : _t("Date is before %s", DVTerms.DateIsBefore[criterion.dateValue]);
40278
40481
  },
40279
40482
  });
@@ -40298,7 +40501,7 @@ dataValidationEvaluatorRegistry.add("dateIsOnOrBefore", {
40298
40501
  name: _t("Date is on or before"),
40299
40502
  getPreview: (criterion, getters) => {
40300
40503
  return criterion.dateValue === "exactDate"
40301
- ? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion, getters)[0])
40504
+ ? _t("Date is on or before %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40302
40505
  : _t("Date is on or before %s", DVTerms.DateIsBefore[criterion.dateValue]);
40303
40506
  },
40304
40507
  });
@@ -40323,7 +40526,7 @@ dataValidationEvaluatorRegistry.add("dateIsAfter", {
40323
40526
  name: _t("Date is after"),
40324
40527
  getPreview: (criterion, getters) => {
40325
40528
  return criterion.dateValue === "exactDate"
40326
- ? _t("Date is after %s", getDateCriterionFormattedValues(criterion, getters)[0])
40529
+ ? _t("Date is after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40327
40530
  : _t("Date is after %s", DVTerms.DateIsBefore[criterion.dateValue]);
40328
40531
  },
40329
40532
  });
@@ -40348,7 +40551,7 @@ dataValidationEvaluatorRegistry.add("dateIsOnOrAfter", {
40348
40551
  name: _t("Date is on or after"),
40349
40552
  getPreview: (criterion, getters) => {
40350
40553
  return criterion.dateValue === "exactDate"
40351
- ? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion, getters)[0])
40554
+ ? _t("Date is on or after %s", getDateCriterionFormattedValues(criterion.values, getters.getLocale())[0])
40352
40555
  : _t("Date is on or after %s", DVTerms.DateIsBefore[criterion.dateValue]);
40353
40556
  },
40354
40557
  });
@@ -40374,7 +40577,7 @@ dataValidationEvaluatorRegistry.add("dateIsBetween", {
40374
40577
  numberOfValues: () => 2,
40375
40578
  name: _t("Date is between"),
40376
40579
  getPreview: (criterion, getters) => {
40377
- const values = getDateCriterionFormattedValues(criterion, getters);
40580
+ const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
40378
40581
  return _t("Date is between %s and %s", values[0], values[1]);
40379
40582
  },
40380
40583
  });
@@ -40400,7 +40603,7 @@ dataValidationEvaluatorRegistry.add("dateIsNotBetween", {
40400
40603
  numberOfValues: () => 2,
40401
40604
  name: _t("Date is not between"),
40402
40605
  getPreview: (criterion, getters) => {
40403
- const values = getDateCriterionFormattedValues(criterion, getters);
40606
+ const values = getDateCriterionFormattedValues(criterion.values, getters.getLocale());
40404
40607
  return _t("Date is not between %s and %s", values[0], values[1]);
40405
40608
  },
40406
40609
  });
@@ -40683,19 +40886,6 @@ function checkValueIsNumber(value) {
40683
40886
  const valueAsNumber = tryToNumber(value, DEFAULT_LOCALE);
40684
40887
  return valueAsNumber !== undefined;
40685
40888
  }
40686
- function getDateCriterionFormattedValues(criterion, getters) {
40687
- const locale = getters.getLocale();
40688
- return criterion.values.map((valueStr) => {
40689
- if (valueStr.startsWith("=")) {
40690
- return valueStr;
40691
- }
40692
- const value = parseLiteral(valueStr, locale);
40693
- if (typeof value === "number") {
40694
- return formatValue(value, { format: locale.dateFormat, locale });
40695
- }
40696
- return "";
40697
- });
40698
- }
40699
40889
 
40700
40890
  /** This component looks like a select input, but on click it opens a Menu with the items given as props instead of a dropdown */
40701
40891
  class SelectMenu extends owl.Component {
@@ -46398,7 +46588,7 @@ class GridComposer extends owl.Component {
46398
46588
  return;
46399
46589
  }
46400
46590
  const sheetId = this.env.model.getters.getActiveSheetId();
46401
- const zone = this.env.model.getters.getSelectedZone();
46591
+ const zone = positionToZone(this.env.model.getters.getSelection().anchor.cell);
46402
46592
  const rect = this.env.model.getters.getVisibleRect(zone);
46403
46593
  if (!deepEquals(rect, this.rect) || sheetId !== this.composerStore.currentEditedCell.sheetId) {
46404
46594
  this.isCellReferenceVisible = true;
@@ -48232,13 +48422,23 @@ class GridRenderer {
48232
48422
  drawLayer(renderingContext, layer) {
48233
48423
  switch (layer) {
48234
48424
  case "Background":
48235
- const boxes = this.getGridBoxes();
48236
- this.drawBackground(renderingContext, boxes);
48237
- this.drawOverflowingCellBackground(renderingContext, boxes);
48238
- this.drawCellBackground(renderingContext, boxes);
48239
- this.drawBorders(renderingContext, boxes);
48240
- this.drawTexts(renderingContext, boxes);
48241
- this.drawIcon(renderingContext, boxes);
48425
+ this.drawGlobalBackground(renderingContext);
48426
+ for (const zone of this.getters.getAllActiveViewportsZones()) {
48427
+ const { ctx } = renderingContext;
48428
+ ctx.save();
48429
+ ctx.beginPath();
48430
+ const rect = this.getters.getVisibleRect(zone);
48431
+ ctx.rect(rect.x, rect.y, rect.width, rect.height);
48432
+ ctx.clip();
48433
+ const boxes = this.getGridBoxes(zone);
48434
+ this.drawBackground(renderingContext, boxes);
48435
+ this.drawOverflowingCellBackground(renderingContext, boxes);
48436
+ this.drawCellBackground(renderingContext, boxes);
48437
+ this.drawBorders(renderingContext, boxes);
48438
+ this.drawTexts(renderingContext, boxes);
48439
+ this.drawIcon(renderingContext, boxes);
48440
+ ctx.restore();
48441
+ }
48242
48442
  this.drawFrozenPanes(renderingContext);
48243
48443
  break;
48244
48444
  case "Headers":
@@ -48249,12 +48449,15 @@ class GridRenderer {
48249
48449
  break;
48250
48450
  }
48251
48451
  }
48252
- drawBackground(renderingContext, boxes) {
48253
- const { ctx, thinLineWidth } = renderingContext;
48452
+ drawGlobalBackground(renderingContext) {
48453
+ const { ctx } = renderingContext;
48254
48454
  const { width, height } = this.getters.getSheetViewDimensionWithHeaders();
48255
48455
  // white background
48256
48456
  ctx.fillStyle = "#ffffff";
48257
48457
  ctx.fillRect(0, 0, width + CANVAS_SHIFT, height + CANVAS_SHIFT);
48458
+ }
48459
+ drawBackground(renderingContext, boxes) {
48460
+ const { ctx, thinLineWidth } = renderingContext;
48258
48461
  const areGridLinesVisible = !this.getters.isDashboard() &&
48259
48462
  this.getters.getGridLinesVisibility(this.getters.getActiveSheetId());
48260
48463
  const inset = areGridLinesVisible ? 0.1 * thinLineWidth : 0;
@@ -48685,7 +48888,7 @@ class GridRenderer {
48685
48888
  const position = { sheetId, col, row };
48686
48889
  const cell = this.getters.getEvaluatedCell(position);
48687
48890
  const showFormula = this.getters.shouldShowFormulas();
48688
- const { x, y, width, height } = this.getters.getVisibleRect(zone);
48891
+ const { x, y, width, height } = this.getters.getRect(zone);
48689
48892
  const { verticalAlign } = this.getters.getCellStyle(position);
48690
48893
  const box = {
48691
48894
  x,
@@ -48803,12 +49006,16 @@ class GridRenderer {
48803
49006
  }
48804
49007
  return box;
48805
49008
  }
48806
- getGridBoxes() {
49009
+ getGridBoxes(zone) {
48807
49010
  const boxes = [];
48808
- const visibleCols = this.getters.getSheetViewVisibleCols();
49011
+ const visibleCols = this.getters
49012
+ .getSheetViewVisibleCols()
49013
+ .filter((col) => col >= zone.left && col <= zone.right);
48809
49014
  const left = visibleCols[0];
48810
49015
  const right = visibleCols[visibleCols.length - 1];
48811
- const visibleRows = this.getters.getSheetViewVisibleRows();
49016
+ const visibleRows = this.getters
49017
+ .getSheetViewVisibleRows()
49018
+ .filter((row) => row >= zone.top && row <= zone.bottom);
48812
49019
  const top = visibleRows[0];
48813
49020
  const bottom = visibleRows[visibleRows.length - 1];
48814
49021
  const viewport = { left, right, top, bottom };
@@ -52292,6 +52499,20 @@ class DataValidationPlugin extends CorePlugin {
52292
52499
  }
52293
52500
  }
52294
52501
  }
52502
+ exportForExcel(data) {
52503
+ if (!data.sheets) {
52504
+ return;
52505
+ }
52506
+ for (const sheet of data.sheets) {
52507
+ sheet.dataValidationRules = [];
52508
+ for (const rule of this.rules[sheet.id]) {
52509
+ sheet.dataValidationRules.push({
52510
+ ...rule,
52511
+ ranges: rule.ranges.map((range) => this.getters.getRangeString(range, sheet.id, { useFixedReference: true })),
52512
+ });
52513
+ }
52514
+ }
52515
+ }
52295
52516
  checkCriterionTypeIsValid(cmd) {
52296
52517
  return dataValidationEvaluatorRegistry.contains(cmd.rule.criterion.type)
52297
52518
  ? "Success" /* CommandResult.Success */
@@ -54059,6 +54280,7 @@ class SheetPlugin extends CorePlugin {
54059
54280
  formats: {},
54060
54281
  borders: {},
54061
54282
  conditionalFormats: [],
54283
+ dataValidationRules: [],
54062
54284
  figures: [],
54063
54285
  tables: [],
54064
54286
  areGridLinesVisible: sheet.areGridLinesVisible === undefined ? true : sheet.areGridLinesVisible,
@@ -61249,9 +61471,6 @@ class Session extends EventBus {
61249
61471
  }
61250
61472
  }
61251
61473
  this.acknowledge(message);
61252
- if (message.type === "REMOTE_REVISION" && message.clientId === this.clientId) {
61253
- return;
61254
- }
61255
61474
  this.trigger("collaborative-event-received");
61256
61475
  }
61257
61476
  onClientMoved(message) {
@@ -64545,8 +64764,12 @@ class GridSelectionPlugin extends UIPlugin {
64545
64764
  },
64546
64765
  ];
64547
64766
  handler.paste({ zones: pasteTarget, sheetId }, data, { isCutOperation: true });
64767
+ const selection = pasteTarget[0];
64768
+ const col = selection.left;
64769
+ const row = selection.top;
64770
+ this.setSelectionMixin({ zone: selection, cell: { col, row } }, [selection]);
64548
64771
  const toRemove = isBasedBefore ? cmd.elements.map((el) => el + thickness) : cmd.elements;
64549
- let currentIndex = cmd.base;
64772
+ let currentIndex = isBasedBefore ? cmd.base : cmd.base + 1;
64550
64773
  for (const element of toRemove) {
64551
64774
  const size = this.getters.getHeaderSize(cmd.sheetId, cmd.dimension, element);
64552
64775
  this.dispatch("RESIZE_COLUMNS_ROWS", {
@@ -64829,22 +65052,33 @@ class InternalViewport {
64829
65052
  }
64830
65053
  /**
64831
65054
  *
64832
- * @param zone
64833
- * @returns Computes the absolute coordinate of a given zone inside the viewport
65055
+ * Computes the visible coordinates & dimensions of a given zone inside the viewport
65056
+ *
64834
65057
  */
64835
- getRect(zone) {
65058
+ getVisibleRect(zone) {
64836
65059
  const targetZone = intersection(zone, this);
64837
65060
  if (targetZone) {
64838
65061
  const x = this.getters.getColRowOffset("COL", this.left, targetZone.left) + this.offsetCorrectionX;
64839
65062
  const y = this.getters.getColRowOffset("ROW", this.top, targetZone.top) + this.offsetCorrectionY;
64840
65063
  const width = Math.min(this.getters.getColRowOffset("COL", targetZone.left, targetZone.right + 1), this.viewportWidth);
64841
65064
  const height = Math.min(this.getters.getColRowOffset("ROW", targetZone.top, targetZone.bottom + 1), this.viewportHeight);
64842
- return {
64843
- x,
64844
- y,
64845
- width,
64846
- height,
64847
- };
65065
+ return { x, y, width, height };
65066
+ }
65067
+ return undefined;
65068
+ }
65069
+ /**
65070
+ *
65071
+ * @returns Computes the absolute coordinates & dimensions of a given zone inside the viewport
65072
+ *
65073
+ */
65074
+ getFullRect(zone) {
65075
+ const targetZone = intersection(zone, this);
65076
+ if (targetZone) {
65077
+ const x = this.getters.getColRowOffset("COL", this.left, zone.left) + this.offsetCorrectionX;
65078
+ const y = this.getters.getColRowOffset("ROW", this.top, zone.top) + this.offsetCorrectionY;
65079
+ const width = this.getters.getColRowOffset("COL", zone.left, zone.right + 1);
65080
+ const height = this.getters.getColRowOffset("ROW", zone.top, zone.bottom + 1);
65081
+ return { x, y, width, height };
64848
65082
  }
64849
65083
  return undefined;
64850
65084
  }
@@ -65014,6 +65248,8 @@ class SheetViewPlugin extends UIPlugin {
65014
65248
  "isPositionVisible",
65015
65249
  "getColDimensionsInViewport",
65016
65250
  "getRowDimensionsInViewport",
65251
+ "getAllActiveViewportsZones",
65252
+ "getRect",
65017
65253
  ];
65018
65254
  viewports = {};
65019
65255
  /**
@@ -65400,16 +65636,27 @@ class SheetViewPlugin extends UIPlugin {
65400
65636
  getVisibleRectWithoutHeaders(zone) {
65401
65637
  const sheetId = this.getters.getActiveSheetId();
65402
65638
  const viewportRects = this.getSubViewports(sheetId)
65403
- .map((viewport) => viewport.getRect(zone))
65639
+ .map((viewport) => viewport.getVisibleRect(zone))
65404
65640
  .filter(isDefined);
65405
65641
  if (viewportRects.length === 0) {
65406
65642
  return { x: 0, y: 0, width: 0, height: 0 };
65407
65643
  }
65408
- const x = Math.min(...viewportRects.map((rect) => rect.x));
65409
- const y = Math.min(...viewportRects.map((rect) => rect.y));
65410
- const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
65411
- const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
65412
- return { x, y, width, height };
65644
+ return this.recomposeRect(viewportRects);
65645
+ }
65646
+ /**
65647
+ * Computes the actual size and position (:Rect) of the zone on the canvas
65648
+ * regardless of the viewport dimensions.
65649
+ */
65650
+ getRect(zone) {
65651
+ const sheetId = this.getters.getActiveSheetId();
65652
+ const viewportRects = this.getSubViewports(sheetId)
65653
+ .map((viewport) => viewport.getFullRect(zone))
65654
+ .filter(isDefined);
65655
+ if (viewportRects.length === 0) {
65656
+ return { x: 0, y: 0, width: 0, height: 0 };
65657
+ }
65658
+ const rect = this.recomposeRect(viewportRects);
65659
+ return { ...rect, x: rect.x + this.gridOffsetX, y: rect.y + this.gridOffsetY };
65413
65660
  }
65414
65661
  /**
65415
65662
  * Returns the position of the MainViewport relatively to the start of the grid (without headers)
@@ -65453,6 +65700,10 @@ class SheetViewPlugin extends UIPlugin {
65453
65700
  end: start + (isRowHidden ? 0 : size),
65454
65701
  };
65455
65702
  }
65703
+ getAllActiveViewportsZones() {
65704
+ const sheetId = this.getters.getActiveSheetId();
65705
+ return this.getSubViewports(sheetId);
65706
+ }
65456
65707
  // ---------------------------------------------------------------------------
65457
65708
  // Private
65458
65709
  // ---------------------------------------------------------------------------
@@ -65643,6 +65894,13 @@ class SheetViewPlugin extends UIPlugin {
65643
65894
  const height = this.sheetViewHeight + this.gridOffsetY;
65644
65895
  return { xRatio: offsetCorrectionX / width, yRatio: offsetCorrectionY / height };
65645
65896
  }
65897
+ recomposeRect(viewportRects) {
65898
+ const x = Math.min(...viewportRects.map((rect) => rect.x));
65899
+ const y = Math.min(...viewportRects.map((rect) => rect.y));
65900
+ const width = Math.max(...viewportRects.map((rect) => rect.x + rect.width)) - x;
65901
+ const height = Math.max(...viewportRects.map((rect) => rect.y + rect.height)) - y;
65902
+ return { x, y, width, height };
65903
+ }
65646
65904
  }
65647
65905
 
65648
65906
  class HeaderPositionsUIPlugin extends UIPlugin {
@@ -70948,6 +71206,124 @@ function getExcelThresholdType(type, position) {
70948
71206
  }
70949
71207
  }
70950
71208
 
71209
+ function addDataValidationRules(dataValidationRules) {
71210
+ const dvRulesCount = dataValidationRules.length;
71211
+ if (dvRulesCount === 0) {
71212
+ return [];
71213
+ }
71214
+ const dvNodes = [new XMLString(`<dataValidations count="${dvRulesCount}">`)];
71215
+ for (const dvRule of dataValidationRules) {
71216
+ switch (dvRule.criterion.type) {
71217
+ case "dateIs":
71218
+ case "dateIsBefore":
71219
+ case "dateIsOnOrBefore":
71220
+ case "dateIsAfter":
71221
+ case "dateIsOnOrAfter":
71222
+ case "dateIsBetween":
71223
+ case "dateIsNotBetween":
71224
+ dvNodes.push(addDateRule(dvRule));
71225
+ break;
71226
+ case "isEqual":
71227
+ case "isNotEqual":
71228
+ case "isGreaterThan":
71229
+ case "isGreaterOrEqualTo":
71230
+ case "isLessThan":
71231
+ case "isLessOrEqualTo":
71232
+ case "isBetween":
71233
+ case "isNotBetween":
71234
+ dvNodes.push(addDecimalRule(dvRule));
71235
+ break;
71236
+ case "isValueInRange":
71237
+ case "isValueInList":
71238
+ dvNodes.push(addListRule(dvRule));
71239
+ break;
71240
+ case "customFormula":
71241
+ dvNodes.push(addCustomFormulaRule(dvRule));
71242
+ break;
71243
+ default:
71244
+ console.warn(`Data validation ${dvRule.criterion.type} is not supported in xlsx.`);
71245
+ break;
71246
+ }
71247
+ }
71248
+ dvNodes.push(new XMLString("</dataValidations>"));
71249
+ return dvNodes;
71250
+ }
71251
+ function addDateRule(dvRule) {
71252
+ const rule = dvRule.criterion;
71253
+ const formula1 = adaptFormulaToExcel(rule.values[0]);
71254
+ const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
71255
+ const operator = convertDateCriterionTypeToExcelOperator(dvRule.criterion.type);
71256
+ const attributes = commonDataValidationAttributes(dvRule);
71257
+ attributes.push(["type", "date"], ["operator", operator]);
71258
+ if (formula2) {
71259
+ return escapeXml /*xml*/ `
71260
+ <dataValidation ${formatAttributes(attributes)}>
71261
+ <formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
71262
+ <formula2>${toNumber(formula2, DEFAULT_LOCALE)}</formula2>
71263
+ </dataValidation>
71264
+ `;
71265
+ }
71266
+ return escapeXml /*xml*/ `
71267
+ <dataValidation ${formatAttributes(attributes)}>
71268
+ <formula1>${toNumber(formula1, DEFAULT_LOCALE)}</formula1>
71269
+ </dataValidation>
71270
+ `;
71271
+ }
71272
+ function addDecimalRule(dvRule) {
71273
+ const rule = dvRule.criterion;
71274
+ const formula1 = adaptFormulaToExcel(rule.values[0]);
71275
+ const formula2 = rule.values[1] ? adaptFormulaToExcel(rule.values[1]) : undefined;
71276
+ const operator = convertDecimalCriterionTypeToExcelOperator(dvRule.criterion.type);
71277
+ const attributes = commonDataValidationAttributes(dvRule);
71278
+ attributes.push(["type", "decimal"], ["operator", operator]);
71279
+ if (formula2) {
71280
+ return escapeXml /*xml*/ `
71281
+ <dataValidation ${formatAttributes(attributes)}>
71282
+ <formula1>${formula1}</formula1>
71283
+ <formula2>${formula2}</formula2>
71284
+ </dataValidation>
71285
+ `;
71286
+ }
71287
+ return escapeXml /*xml*/ `
71288
+ <dataValidation ${formatAttributes(attributes)}>
71289
+ <formula1>${formula1}</formula1>
71290
+ </dataValidation>
71291
+ `;
71292
+ }
71293
+ function addListRule(dvRule) {
71294
+ const rule = dvRule.criterion;
71295
+ const formula1 = dvRule.criterion.type === "isValueInRange"
71296
+ ? adaptFormulaToExcel(rule.values[0])
71297
+ : `"${rule.values.join(",")}"`;
71298
+ const attributes = commonDataValidationAttributes(dvRule);
71299
+ attributes.push(["type", "list"]);
71300
+ return escapeXml /*xml*/ `
71301
+ <dataValidation ${formatAttributes(attributes)}>
71302
+ <formula1>${formula1}</formula1>
71303
+ </dataValidation>
71304
+ `;
71305
+ }
71306
+ function addCustomFormulaRule(dvRule) {
71307
+ const rule = dvRule.criterion;
71308
+ const formula1 = adaptFormulaToExcel(rule.values[0]);
71309
+ const attributes = commonDataValidationAttributes(dvRule);
71310
+ attributes.push(["type", "custom"]);
71311
+ return escapeXml /*xml*/ `
71312
+ <dataValidation ${formatAttributes(attributes)}>
71313
+ <formula1>${formula1}</formula1>
71314
+ </dataValidation>
71315
+ `;
71316
+ }
71317
+ function commonDataValidationAttributes(dvRule) {
71318
+ return [
71319
+ ["allowBlank", "1"],
71320
+ ["showInputMessage", "1"],
71321
+ ["showErrorMessage", "1"],
71322
+ ["errorStyle", !dvRule.isBlocking ? "warning" : ""],
71323
+ ["sqref", dvRule.ranges.join(" ")],
71324
+ ];
71325
+ }
71326
+
70951
71327
  function createDrawing(drawingRelIds, sheet, figures) {
70952
71328
  const namespaces = [
70953
71329
  ["xmlns:xdr", NAMESPACE.drawing],
@@ -71726,6 +72102,7 @@ function createWorksheets(data, construct) {
71726
72102
  ${addRows(construct, data, sheet)}
71727
72103
  ${addMerges(sheet.merges)}
71728
72104
  ${joinXmlNodes(addConditionalFormatting(construct.dxfs, sheet.conditionalFormats))}
72105
+ ${joinXmlNodes(addDataValidationRules(sheet.dataValidationRules))}
71729
72106
  ${addHyperlinks(construct, data, sheetIndex)}
71730
72107
  ${drawingNode}
71731
72108
  ${tablesNode}
@@ -72677,6 +73054,6 @@ exports.tokenColors = tokenColors;
72677
73054
  exports.tokenize = tokenize;
72678
73055
 
72679
73056
 
72680
- __info__.version = "18.0.9";
72681
- __info__.date = "2025-01-14T11:33:47.429Z";
72682
- __info__.hash = "0c5220e";
73057
+ __info__.version = "18.0.11";
73058
+ __info__.date = "2025-01-27T10:08:13.567Z";
73059
+ __info__.hash = "e8c6bd1";