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