@odoo/o-spreadsheet 17.4.0-alpha.7 → 17.4.0-alpha.9

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 17.4.0-alpha.7
6
- * @date 2024-06-21T08:42:22.370Z
7
- * @hash a99db9a
5
+ * @version 17.4.0-alpha.9
6
+ * @date 2024-06-26T11:09:20.284Z
7
+ * @hash 526be20
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -1902,9 +1902,10 @@
1902
1902
  sortedValues[indexLow] * (indexSup - percentIndex));
1903
1903
  }
1904
1904
 
1905
- // define a mock translation function, when o-spreadsheet runs in standalone it doesn't translate any string
1906
- let _translate = (s) => s;
1907
- let _loaded = () => false;
1905
+ const defaultTranslate = (s) => s;
1906
+ const defaultLoaded = () => false;
1907
+ let _translate = defaultTranslate;
1908
+ let _loaded = defaultLoaded;
1908
1909
  function sprintf(s, ...values) {
1909
1910
  if (values.length === 1 && typeof values[0] === "object" && !(values[0] instanceof String)) {
1910
1911
  const valuesDict = values[0];
@@ -1916,7 +1917,8 @@
1916
1917
  return s;
1917
1918
  }
1918
1919
  /***
1919
- * Allow to inject a translation function from outside o-spreadsheet.
1920
+ * Allow to inject a translation function from outside o-spreadsheet. This should be called before instantiating
1921
+ * a model.
1920
1922
  * @param tfn the function that will do the translation
1921
1923
  * @param loaded a function that returns true when the translation is loaded
1922
1924
  */
@@ -1924,6 +1926,18 @@
1924
1926
  _translate = tfn;
1925
1927
  _loaded = loaded;
1926
1928
  }
1929
+ /**
1930
+ * If no translation function has been set, this will mark the translation are loaded.
1931
+ *
1932
+ * By default, the translations should not be set as loaded, otherwise top-level translated constants will never be
1933
+ * translated. But if by the time the model is instantiated no custom translation function has been set, we can set
1934
+ * the default translation function as loaded so o-spreadsheet can be run in standalone with no translations.
1935
+ */
1936
+ function setDefaultTranslationMethod() {
1937
+ if (_translate === defaultTranslate && _loaded === defaultLoaded) {
1938
+ _loaded = () => true;
1939
+ }
1940
+ }
1927
1941
  const _t = function (s, ...values) {
1928
1942
  if (!_loaded()) {
1929
1943
  return new LazyTranslatedString(s, values);
@@ -5619,18 +5633,12 @@
5619
5633
  return { borders };
5620
5634
  }
5621
5635
  paste(target, content, options) {
5622
- if (!content) {
5623
- return;
5624
- }
5625
5636
  const sheetId = target.sheetId;
5626
- if (options?.pasteOption === "asValue") {
5627
- return;
5628
- }
5629
- if (!("borders" in content) || !("zones" in target) || !target.zones.length) {
5637
+ if (options.pasteOption === "asValue") {
5630
5638
  return;
5631
5639
  }
5632
5640
  const zones = target.zones;
5633
- if (!options?.isCutOperation) {
5641
+ if (!options.isCutOperation) {
5634
5642
  this.pasteFromCopy(sheetId, zones, content.borders);
5635
5643
  }
5636
5644
  else {
@@ -6141,6 +6149,530 @@
6141
6149
  return localizedFormula;
6142
6150
  }
6143
6151
 
6152
+ function boolAnd(args) {
6153
+ let foundBoolean = false;
6154
+ let acc = true;
6155
+ conditionalVisitBoolean(args, (arg) => {
6156
+ foundBoolean = true;
6157
+ acc = acc && arg;
6158
+ return acc;
6159
+ });
6160
+ return {
6161
+ foundBoolean,
6162
+ result: acc,
6163
+ };
6164
+ }
6165
+ function boolOr(args) {
6166
+ let foundBoolean = false;
6167
+ let acc = false;
6168
+ conditionalVisitBoolean(args, (arg) => {
6169
+ foundBoolean = true;
6170
+ acc = acc || arg;
6171
+ return !acc;
6172
+ });
6173
+ return {
6174
+ foundBoolean,
6175
+ result: acc,
6176
+ };
6177
+ }
6178
+
6179
+ function sum(values, locale) {
6180
+ return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
6181
+ }
6182
+ function countUnique(args) {
6183
+ return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
6184
+ }
6185
+
6186
+ function assertSameNumberOfElements(...args) {
6187
+ const dims = args[0].length;
6188
+ args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
6189
+ }
6190
+ function average(values, locale) {
6191
+ let count = 0;
6192
+ const sum = reduceNumbers(values, (acc, a) => {
6193
+ count += 1;
6194
+ return acc + a;
6195
+ }, 0, locale);
6196
+ assertNotZero(count);
6197
+ return sum / count;
6198
+ }
6199
+ function countNumbers(values, locale) {
6200
+ let count = 0;
6201
+ for (let n of values) {
6202
+ if (isMatrix(n)) {
6203
+ for (let i of n) {
6204
+ for (let j of i) {
6205
+ if (typeof j.value === "number") {
6206
+ count += 1;
6207
+ }
6208
+ }
6209
+ }
6210
+ }
6211
+ else {
6212
+ const value = n?.value;
6213
+ if (!isEvaluationError(value) &&
6214
+ (typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
6215
+ count += 1;
6216
+ }
6217
+ }
6218
+ }
6219
+ return count;
6220
+ }
6221
+ function countAny(values) {
6222
+ return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
6223
+ }
6224
+ function max(values, locale) {
6225
+ const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
6226
+ return result === -Infinity ? 0 : result;
6227
+ }
6228
+ function min(values, locale) {
6229
+ const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
6230
+ return result === Infinity ? 0 : result;
6231
+ }
6232
+
6233
+ const pivotTimeAdapterRegistry = new Registry();
6234
+ function pivotTimeAdapter(granularity) {
6235
+ return pivotTimeAdapterRegistry.get(granularity);
6236
+ }
6237
+ /**
6238
+ * The Time Adapter: Managing Time Periods for Pivot Functions
6239
+ *
6240
+ * Overview:
6241
+ * A time adapter is responsible for managing time periods associated with pivot functions.
6242
+ * Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
6243
+ * The adapter's primary role is to normalize period values between spreadsheet functions,
6244
+ * and the pivot.
6245
+ * By normalizing the period value, it can be stored consistently in the pivot.
6246
+ *
6247
+ * Normalization Process:
6248
+ * When working with functions in the spreadsheet, the time adapter normalizes
6249
+ * the provided period to facilitate accurate lookup of values in the pivot.
6250
+ * For instance, if the spreadsheet function represents a day period as a number generated
6251
+ * by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
6252
+ *
6253
+ */
6254
+ /**
6255
+ * Normalized value: "12/25/2023"
6256
+ *
6257
+ * Note: Those two format are equivalent:
6258
+ * - "MM/dd/yyyy" (luxon format)
6259
+ * - "mm/dd/yyyy" (spreadsheet format)
6260
+ **/
6261
+ const dayAdapter = {
6262
+ normalizeFunctionValue(value) {
6263
+ return toNumber(value, DEFAULT_LOCALE);
6264
+ },
6265
+ toValueAndFormat(normalizedValue, locale) {
6266
+ return {
6267
+ value: toNumber(normalizedValue, DEFAULT_LOCALE),
6268
+ format: (locale ?? DEFAULT_LOCALE).dateFormat,
6269
+ };
6270
+ },
6271
+ toFunctionValue(normalizedValue) {
6272
+ const date = toNumber(normalizedValue, DEFAULT_LOCALE);
6273
+ return `"${formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" })}"`;
6274
+ },
6275
+ };
6276
+ /**
6277
+ * normalizes day of month number
6278
+ */
6279
+ const dayOfMonthAdapter = {
6280
+ normalizeFunctionValue(value) {
6281
+ const day = toNumber(value, DEFAULT_LOCALE);
6282
+ if (day < 1 || day > 31) {
6283
+ throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
6284
+ }
6285
+ return day;
6286
+ },
6287
+ toValueAndFormat(normalizedValue) {
6288
+ return {
6289
+ value: toNumber(normalizedValue, DEFAULT_LOCALE),
6290
+ format: "0",
6291
+ };
6292
+ },
6293
+ toFunctionValue(normalizedValue) {
6294
+ return `${normalizedValue}`;
6295
+ },
6296
+ };
6297
+ /**
6298
+ * Normalized value: "2/2023" for week 2 of 2023
6299
+ */
6300
+ const weekAdapter = {
6301
+ normalizeFunctionValue(value) {
6302
+ const [week, year] = toString(value).split("/");
6303
+ return `${Number(week)}/${Number(year)}`;
6304
+ },
6305
+ toValueAndFormat(normalizedValue, locale) {
6306
+ const [week, year] = normalizedValue.split("/");
6307
+ return {
6308
+ value: _t("W%(week)s %(year)s", { week, year }),
6309
+ };
6310
+ },
6311
+ toFunctionValue(normalizedValue) {
6312
+ return `"${normalizedValue}"`;
6313
+ },
6314
+ };
6315
+ /**
6316
+ * normalizes iso week number
6317
+ */
6318
+ const isoWeekNumberAdapter = {
6319
+ normalizeFunctionValue(value) {
6320
+ const isoWeek = toNumber(value, DEFAULT_LOCALE);
6321
+ if (isoWeek < 0 || isoWeek > 53) {
6322
+ throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
6323
+ }
6324
+ return isoWeek;
6325
+ },
6326
+ toValueAndFormat(normalizedValue) {
6327
+ return {
6328
+ value: toNumber(normalizedValue, DEFAULT_LOCALE),
6329
+ format: "0",
6330
+ };
6331
+ },
6332
+ toFunctionValue(normalizedValue) {
6333
+ return `${normalizedValue}`;
6334
+ },
6335
+ };
6336
+ /**
6337
+ * normalized month value is a string formatted as "MM/yyyy" (luxon format)
6338
+ * e.g. "01/2020" for January 2020
6339
+ */
6340
+ const monthAdapter = {
6341
+ normalizeFunctionValue(value) {
6342
+ const date = toNumber(value, DEFAULT_LOCALE);
6343
+ return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
6344
+ },
6345
+ toValueAndFormat(normalizedValue) {
6346
+ return {
6347
+ value: toNumber(normalizedValue, DEFAULT_LOCALE),
6348
+ format: "mmmm yyyy",
6349
+ };
6350
+ },
6351
+ toFunctionValue(normalizedValue) {
6352
+ return `"${normalizedValue}"`;
6353
+ },
6354
+ };
6355
+ /**
6356
+ * normalizes month number
6357
+ */
6358
+ const monthNumberAdapter = {
6359
+ normalizeFunctionValue(value) {
6360
+ const month = toNumber(value, DEFAULT_LOCALE);
6361
+ if (month < 1 || month > 12) {
6362
+ throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
6363
+ }
6364
+ return month;
6365
+ },
6366
+ toValueAndFormat(normalizedValue) {
6367
+ return {
6368
+ value: MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString(),
6369
+ format: "0",
6370
+ };
6371
+ },
6372
+ toFunctionValue(normalizedValue) {
6373
+ return `${normalizedValue}`;
6374
+ },
6375
+ };
6376
+ /**
6377
+ * normalized quarter value is "quarter/year"
6378
+ * e.g. "1/2020" for Q1 2020
6379
+ */
6380
+ const quarterAdapter = {
6381
+ normalizeFunctionValue(value) {
6382
+ const [quarter, year] = toString(value).split("/");
6383
+ return `${quarter}/${year}`;
6384
+ },
6385
+ toValueAndFormat(normalizedValue) {
6386
+ const [quarter, year] = normalizedValue.split("/");
6387
+ return {
6388
+ value: _t("Q%(quarter)s %(year)s", { quarter, year }),
6389
+ };
6390
+ },
6391
+ toFunctionValue(normalizedValue) {
6392
+ return `"${normalizedValue}"`;
6393
+ },
6394
+ };
6395
+ /**
6396
+ * normalizes quarter number
6397
+ */
6398
+ const quarterNumberAdapter = {
6399
+ normalizeFunctionValue(value) {
6400
+ const quarter = toNumber(value, DEFAULT_LOCALE);
6401
+ if (quarter < 1 || quarter > 4) {
6402
+ throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
6403
+ }
6404
+ return quarter;
6405
+ },
6406
+ toValueAndFormat(normalizedValue) {
6407
+ return {
6408
+ value: toNumber(normalizedValue, DEFAULT_LOCALE),
6409
+ format: "0",
6410
+ };
6411
+ },
6412
+ toFunctionValue(normalizedValue) {
6413
+ return `${normalizedValue}`;
6414
+ },
6415
+ };
6416
+ const yearAdapter = {
6417
+ normalizeFunctionValue(value) {
6418
+ return toNumber(value, DEFAULT_LOCALE);
6419
+ },
6420
+ toValueAndFormat(normalizedValue) {
6421
+ return {
6422
+ value: toNumber(normalizedValue, DEFAULT_LOCALE),
6423
+ format: "0",
6424
+ };
6425
+ },
6426
+ toFunctionValue(normalizedValue) {
6427
+ return `${normalizedValue}`;
6428
+ },
6429
+ };
6430
+ /**
6431
+ * This function takes an adapter and wraps it with a null handler.
6432
+ * null value means that the value is not set.
6433
+ */
6434
+ function nullHandlerDecorator(adapter) {
6435
+ return {
6436
+ normalizeFunctionValue(value) {
6437
+ if (value === null) {
6438
+ return null;
6439
+ }
6440
+ return adapter.normalizeFunctionValue(value);
6441
+ },
6442
+ toValueAndFormat(normalizedValue, locale) {
6443
+ if (normalizedValue === null) {
6444
+ return { value: _t("(Undefined)") }; //TODO Return NA ?
6445
+ }
6446
+ return adapter.toValueAndFormat(normalizedValue, locale);
6447
+ },
6448
+ toFunctionValue(normalizedValue) {
6449
+ if (normalizedValue === null) {
6450
+ return "false"; //TODO Return NA ?
6451
+ }
6452
+ return adapter.toFunctionValue(normalizedValue);
6453
+ },
6454
+ };
6455
+ }
6456
+ pivotTimeAdapterRegistry
6457
+ .add("day", nullHandlerDecorator(dayAdapter))
6458
+ .add("week", nullHandlerDecorator(weekAdapter))
6459
+ .add("month", nullHandlerDecorator(monthAdapter))
6460
+ .add("quarter", nullHandlerDecorator(quarterAdapter))
6461
+ .add("year", nullHandlerDecorator(yearAdapter))
6462
+ .add("day_of_month", nullHandlerDecorator(dayOfMonthAdapter))
6463
+ .add("iso_week_number", nullHandlerDecorator(isoWeekNumberAdapter))
6464
+ .add("month_number", nullHandlerDecorator(monthNumberAdapter))
6465
+ .add("quarter_number", nullHandlerDecorator(quarterNumberAdapter))
6466
+ .add("year_number", nullHandlerDecorator(yearAdapter));
6467
+
6468
+ const AGGREGATOR_NAMES = {
6469
+ count: _t("Count"),
6470
+ count_distinct: _t("Count Distinct"),
6471
+ bool_and: _t("Boolean And"),
6472
+ bool_or: _t("Boolean Or"),
6473
+ max: _t("Maximum"),
6474
+ min: _t("Minimum"),
6475
+ avg: _t("Average"),
6476
+ sum: _t("Sum"),
6477
+ };
6478
+ const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
6479
+ const AGGREGATORS_BY_FIELD_TYPE = {
6480
+ integer: NUMBER_CHAR_AGGREGATORS,
6481
+ char: NUMBER_CHAR_AGGREGATORS,
6482
+ boolean: ["count_distinct", "count", "bool_and", "bool_or"],
6483
+ };
6484
+ const AGGREGATORS = {};
6485
+ for (const type in AGGREGATORS_BY_FIELD_TYPE) {
6486
+ AGGREGATORS[type] = {};
6487
+ for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
6488
+ AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
6489
+ }
6490
+ }
6491
+ const AGGREGATORS_FN = {
6492
+ count: {
6493
+ fn: (args) => countAny([args]),
6494
+ format: () => "0",
6495
+ },
6496
+ count_distinct: {
6497
+ fn: (args) => countUnique([args]),
6498
+ format: () => "0",
6499
+ },
6500
+ bool_and: {
6501
+ fn: (args) => boolAnd([args]).result,
6502
+ format: () => undefined,
6503
+ },
6504
+ bool_or: {
6505
+ fn: (args) => boolOr([args]).result,
6506
+ format: () => undefined,
6507
+ },
6508
+ max: {
6509
+ fn: (args, locale) => max([args], locale),
6510
+ format: inferFormat,
6511
+ },
6512
+ min: {
6513
+ fn: (args, locale) => min([args], locale),
6514
+ format: inferFormat,
6515
+ },
6516
+ avg: {
6517
+ fn: (args, locale) => average([args], locale),
6518
+ format: inferFormat,
6519
+ },
6520
+ sum: {
6521
+ fn: (args, locale) => sum([args], locale),
6522
+ format: inferFormat,
6523
+ },
6524
+ };
6525
+ /**
6526
+ * Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
6527
+ * in this object
6528
+ * If the object has no keys, return 0
6529
+ *
6530
+ */
6531
+ function getMaxObjectId(o) {
6532
+ const keys = Object.keys(o);
6533
+ if (!keys.length) {
6534
+ return 0;
6535
+ }
6536
+ const nums = keys.map((id) => parseInt(id, 10));
6537
+ const max = Math.max(...nums);
6538
+ return max;
6539
+ }
6540
+ const ALL_PERIODS = {
6541
+ year: _t("Year"),
6542
+ quarter: _t("Quarter"),
6543
+ month: _t("Month"),
6544
+ week: _t("Week"),
6545
+ day: _t("Day"),
6546
+ year_number: _t("Year"),
6547
+ quarter_number: _t("Quarter"),
6548
+ month_number: _t("Month"),
6549
+ iso_week_number: _t("Week"),
6550
+ day_of_month: _t("Day of Month"),
6551
+ };
6552
+ const DATE_FIELDS = ["date", "datetime"];
6553
+ /**
6554
+ * Parse a dimension string into a pivot dimension definition.
6555
+ * e.g "create_date:month" => { name: "create_date", granularity: "month" }
6556
+ */
6557
+ function parseDimension(dimension) {
6558
+ const [name, granularity] = dimension.split(":");
6559
+ if (granularity) {
6560
+ return { name, granularity };
6561
+ }
6562
+ return { name };
6563
+ }
6564
+ function isDateField(field) {
6565
+ return DATE_FIELDS.includes(field.type);
6566
+ }
6567
+ function generatePivotArgs(formulaId, domain, measure) {
6568
+ const args = [formulaId];
6569
+ if (measure) {
6570
+ args.push(`"${measure}"`);
6571
+ }
6572
+ for (const { field, value, type } of domain) {
6573
+ if (field === "measure") {
6574
+ args.push(`"measure"`, `"${value}"`);
6575
+ continue;
6576
+ }
6577
+ const { granularity } = parseDimension(field);
6578
+ const formattedValue = toFunctionPivotValue(value, { type, granularity });
6579
+ args.push(`"${field}"`, formattedValue);
6580
+ }
6581
+ return args;
6582
+ }
6583
+ /**
6584
+ * Check if the fields in the domain part of
6585
+ * a pivot function are valid according to the pivot definition.
6586
+ * e.g. =PIVOT.VALUE(1,"revenue","country_id",...,"create_date:month",...,"source_id",...)
6587
+ */
6588
+ function areDomainArgsFieldsValid(dimensions, definition) {
6589
+ let argIndex = 0;
6590
+ let definitionIndex = 0;
6591
+ const cols = definition.columns.map((col) => col.nameWithGranularity);
6592
+ const rows = definition.rows.map((row) => row.nameWithGranularity);
6593
+ while (dimensions[argIndex] !== undefined && dimensions[argIndex] === rows[definitionIndex]) {
6594
+ argIndex++;
6595
+ definitionIndex++;
6596
+ }
6597
+ definitionIndex = 0;
6598
+ while (dimensions[argIndex] !== undefined && dimensions[argIndex] === cols[definitionIndex]) {
6599
+ argIndex++;
6600
+ definitionIndex++;
6601
+ }
6602
+ return dimensions.length === argIndex;
6603
+ }
6604
+ function createPivotFormula(formulaId, cell) {
6605
+ switch (cell.type) {
6606
+ case "HEADER":
6607
+ return `=PIVOT.HEADER(${generatePivotArgs(formulaId, cell.domain).join(",")})`;
6608
+ case "VALUE":
6609
+ return `=PIVOT.VALUE(${generatePivotArgs(formulaId, cell.domain, cell.measure).join(",")})`;
6610
+ case "MEASURE_HEADER":
6611
+ return `=PIVOT.HEADER(${generatePivotArgs(formulaId, [
6612
+ ...cell.domain,
6613
+ { field: "measure", value: cell.measure, type: "char" },
6614
+ ]).join(",")})`;
6615
+ }
6616
+ return "";
6617
+ }
6618
+ /**
6619
+ * Parses the value defining a pivot group in a PIVOT formula
6620
+ * e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
6621
+ * the two group values are "42" and "won".
6622
+ */
6623
+ function toNormalizedPivotValue(dimension, groupValue) {
6624
+ if (groupValue === null || groupValue === "null") {
6625
+ return null;
6626
+ }
6627
+ const groupValueString = typeof groupValue === "boolean"
6628
+ ? toString(groupValue).toLocaleLowerCase()
6629
+ : toString(groupValue);
6630
+ if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
6631
+ throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
6632
+ field: dimension.displayName,
6633
+ type: dimension.type,
6634
+ }));
6635
+ }
6636
+ // represents a field which is not set (=False server side)
6637
+ if (groupValueString.toLowerCase() === "false") {
6638
+ return false;
6639
+ }
6640
+ const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
6641
+ return normalizer(groupValueString, dimension.granularity);
6642
+ }
6643
+ function normalizeDateTime(value, granularity) {
6644
+ if (!granularity) {
6645
+ throw new Error("Missing granularity");
6646
+ }
6647
+ return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
6648
+ }
6649
+ function toFunctionPivotValue(value, dimension) {
6650
+ if (!pivotToFunctionValueRegistry.contains(dimension.type)) {
6651
+ return `"${value}"`;
6652
+ }
6653
+ return pivotToFunctionValueRegistry.get(dimension.type)(value, dimension.granularity);
6654
+ }
6655
+ function toFunctionValueDateTime(value, granularity) {
6656
+ if (!granularity) {
6657
+ throw new Error("Missing granularity");
6658
+ }
6659
+ return pivotTimeAdapter(granularity).toFunctionValue(value);
6660
+ }
6661
+ const pivotNormalizationValueRegistry = new Registry();
6662
+ pivotNormalizationValueRegistry
6663
+ .add("date", normalizeDateTime)
6664
+ .add("datetime", normalizeDateTime)
6665
+ .add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
6666
+ .add("boolean", (value) => toBoolean(value))
6667
+ .add("char", (value) => toString(value));
6668
+ const pivotToFunctionValueRegistry = new Registry();
6669
+ pivotToFunctionValueRegistry
6670
+ .add("date", toFunctionValueDateTime)
6671
+ .add("datetime", toFunctionValueDateTime)
6672
+ .add("integer", (value) => `${toNumber(value, DEFAULT_LOCALE)}`)
6673
+ .add("boolean", (value) => (toBoolean(value) ? "TRUE" : "FALSE"))
6674
+ .add("char", (value) => `"${toString(value).replace(/"/g, '\\"')}"`);
6675
+
6144
6676
  class CellClipboardHandler extends AbstractCellClipboardHandler {
6145
6677
  isCutAllowed(data) {
6146
6678
  if (data.zones.length !== 1) {
@@ -6149,40 +6681,48 @@
6149
6681
  return "Success" /* CommandResult.Success */;
6150
6682
  }
6151
6683
  copy(data) {
6152
- if (!("zones" in data) || !data.zones.length) {
6153
- return;
6154
- }
6155
6684
  const sheetId = data.sheetId;
6156
- const zones = data.zones;
6157
- if (!zones.length) {
6158
- return {
6159
- cells: [[]],
6160
- zones: [],
6161
- sheetId,
6162
- };
6163
- }
6164
6685
  const { clippedZones, rowsIndexes, columnsIndexes } = data;
6165
6686
  const clippedCells = [];
6687
+ const isCopyingOneCell = rowsIndexes.length == 1 && columnsIndexes.length == 1;
6166
6688
  for (let row of rowsIndexes) {
6167
6689
  let cellsInRow = [];
6168
6690
  for (let col of columnsIndexes) {
6169
6691
  const position = { col, row, sheetId };
6170
- const spreader = this.getters.getArrayFormulaSpreadingOn(position);
6171
6692
  let cell = this.getters.getCell(position);
6172
6693
  const evaluatedCell = this.getters.getEvaluatedCell(position);
6173
- if (spreader && !deepEquals(spreader, position)) {
6174
- const isSpreaderCopied = rowsIndexes.includes(spreader.row) && columnsIndexes.includes(spreader.col);
6175
- const content = isSpreaderCopied
6176
- ? ""
6177
- : formatValue(evaluatedCell.value, { locale: this.getters.getLocale() });
6178
- cell = {
6179
- id: cell?.id || "",
6180
- style: cell?.style,
6181
- format: evaluatedCell.format,
6182
- content,
6183
- isFormula: false,
6184
- parsedValue: evaluatedCell.value,
6185
- };
6694
+ const pivotId = this.getters.getPivotIdFromPosition(position);
6695
+ const spreader = this.getters.getArrayFormulaSpreadingOn(position);
6696
+ if (pivotId) {
6697
+ if (!deepEquals(spreader, position) || !isCopyingOneCell) {
6698
+ const pivotCell = this.getters.getPivotCellFromPosition(position);
6699
+ const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
6700
+ const pivotFormula = createPivotFormula(formulaPivotId, pivotCell);
6701
+ cell = {
6702
+ id: cell?.id || "",
6703
+ style: cell?.style,
6704
+ format: evaluatedCell.format,
6705
+ content: pivotFormula,
6706
+ isFormula: false,
6707
+ parsedValue: evaluatedCell.value,
6708
+ };
6709
+ }
6710
+ }
6711
+ else {
6712
+ if (spreader && !deepEquals(spreader, position)) {
6713
+ const isSpreaderCopied = rowsIndexes.includes(spreader.row) && columnsIndexes.includes(spreader.col);
6714
+ const content = isSpreaderCopied
6715
+ ? ""
6716
+ : formatValue(evaluatedCell.value, { locale: this.getters.getLocale() });
6717
+ cell = {
6718
+ id: cell?.id || "",
6719
+ style: cell?.style,
6720
+ format: evaluatedCell.format,
6721
+ content,
6722
+ isFormula: false,
6723
+ parsedValue: evaluatedCell.value,
6724
+ };
6725
+ }
6186
6726
  }
6187
6727
  cellsInRow.push({
6188
6728
  cell,
@@ -6231,12 +6771,9 @@
6231
6771
  * Paste the clipboard content in the given target
6232
6772
  */
6233
6773
  paste(target, content, options) {
6234
- if (!("cells" in content) || !("zones" in target) || !target.zones.length) {
6235
- return;
6236
- }
6237
6774
  const zones = target.zones;
6238
6775
  const sheetId = target.sheetId;
6239
- if (!options?.isCutOperation) {
6776
+ if (!options.isCutOperation) {
6240
6777
  this.pasteFromCopy(sheetId, zones, content.cells, options);
6241
6778
  }
6242
6779
  else {
@@ -6406,14 +6943,11 @@
6406
6943
  };
6407
6944
  }
6408
6945
  getPasteTarget(sheetId, target, content, options) {
6409
- if (!content?.copiedFigure || !content?.copiedChart) {
6410
- return { zones: [], sheetId };
6411
- }
6412
6946
  const newId = new UuidGenerator().uuidv4();
6413
6947
  return { zones: [], figureId: newId, sheetId };
6414
6948
  }
6415
6949
  paste(target, clippedContent, options) {
6416
- if (!clippedContent?.copiedFigure || !clippedContent?.copiedChart || !target.figureId) {
6950
+ if (!target.figureId) {
6417
6951
  return;
6418
6952
  }
6419
6953
  const { zones, figureId } = target;
@@ -6437,7 +6971,7 @@
6437
6971
  size: { height, width },
6438
6972
  definition: copy.getDefinition(),
6439
6973
  });
6440
- if (options?.isCutOperation) {
6974
+ if (options.isCutOperation) {
6441
6975
  this.dispatch("DELETE_FIGURE", {
6442
6976
  sheetId: clippedContent.copiedChart.sheetId,
6443
6977
  id: clippedContent.copiedFigure.id,
@@ -6479,15 +7013,12 @@
6479
7013
  return { cfRules };
6480
7014
  }
6481
7015
  paste(target, clippedContent, options) {
6482
- if (!clippedContent?.cfRules ||
6483
- options?.pasteOption === "asValue" ||
6484
- !("zones" in target) ||
6485
- !target.zones.length) {
7016
+ if (options.pasteOption === "asValue") {
6486
7017
  return;
6487
7018
  }
6488
7019
  const zones = target.zones;
6489
7020
  const sheetId = target.sheetId;
6490
- if (!options?.isCutOperation) {
7021
+ if (!options.isCutOperation) {
6491
7022
  this.pasteFromCopy(sheetId, zones, clippedContent.cfRules, options);
6492
7023
  }
6493
7024
  else {
@@ -6561,9 +7092,6 @@
6561
7092
  class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
6562
7093
  uuidGenerator = new UuidGenerator();
6563
7094
  copy(data) {
6564
- if (!data.zones.length) {
6565
- return;
6566
- }
6567
7095
  const { rowsIndexes, columnsIndexes } = data;
6568
7096
  const sheetId = data.sheetId;
6569
7097
  const dvRules = [];
@@ -6579,18 +7107,12 @@
6579
7107
  return { dvRules };
6580
7108
  }
6581
7109
  paste(target, clippedContent, options) {
6582
- if (!clippedContent?.dvRules) {
6583
- return;
6584
- }
6585
- if (options?.pasteOption) {
6586
- return;
6587
- }
6588
- if (!("zones" in target) || !target.zones.length) {
7110
+ if (options.pasteOption) {
6589
7111
  return;
6590
7112
  }
6591
7113
  const zones = target.zones;
6592
7114
  const sheetId = target.sheetId;
6593
- if (!options?.isCutOperation) {
7115
+ if (!options.isCutOperation) {
6594
7116
  this.pasteFromCopy(sheetId, zones, clippedContent.dvRules);
6595
7117
  }
6596
7118
  else {
@@ -6689,14 +7211,11 @@
6689
7211
  };
6690
7212
  }
6691
7213
  getPasteTarget(sheetId, target, content, options) {
6692
- if (!content?.copiedFigure || !content?.copiedImage) {
6693
- return { zones: [], sheetId };
6694
- }
6695
7214
  const newId = new UuidGenerator().uuidv4();
6696
7215
  return { sheetId, zones: [], figureId: newId };
6697
7216
  }
6698
7217
  paste(target, clippedContent, options) {
6699
- if (!clippedContent?.copiedFigure || !clippedContent?.copiedImage || !target.figureId) {
7218
+ if (!target.figureId) {
6700
7219
  return;
6701
7220
  }
6702
7221
  const { zones, figureId } = target;
@@ -6720,7 +7239,7 @@
6720
7239
  size: { height, width },
6721
7240
  definition: copy,
6722
7241
  });
6723
- if (options?.isCutOperation) {
7242
+ if (options.isCutOperation) {
6724
7243
  this.dispatch("DELETE_FIGURE", {
6725
7244
  sheetId: clippedContent.sheetId,
6726
7245
  id: clippedContent.copiedFigure.id,
@@ -6741,9 +7260,6 @@
6741
7260
 
6742
7261
  class MergeClipboardHandler extends AbstractCellClipboardHandler {
6743
7262
  copy(data) {
6744
- if (!data.zones.length) {
6745
- return;
6746
- }
6747
7263
  const sheetId = this.getters.getActiveSheetId();
6748
7264
  const { rowsIndexes, columnsIndexes } = data;
6749
7265
  const merges = [];
@@ -6761,10 +7277,7 @@
6761
7277
  * Paste the clipboard content in the given target
6762
7278
  */
6763
7279
  paste(target, content, options) {
6764
- if (!content.merges ||
6765
- options?.isCutOperation ||
6766
- !("zones" in target) ||
6767
- !target.zones.length) {
7280
+ if (options.isCutOperation) {
6768
7281
  return;
6769
7282
  }
6770
7283
  this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
@@ -6820,9 +7333,6 @@
6820
7333
  copy(data) {
6821
7334
  const sheetId = data.sheetId;
6822
7335
  const { rowsIndexes, columnsIndexes, zones } = data;
6823
- if (!zones || !rowsIndexes.length || !columnsIndexes.length) {
6824
- return { tableCells: [[]], sheetId };
6825
- }
6826
7336
  const copiedTablesIds = new Set();
6827
7337
  const tableCells = [];
6828
7338
  for (let row of rowsIndexes) {
@@ -6878,12 +7388,9 @@
6878
7388
  };
6879
7389
  }
6880
7390
  paste(target, content, options) {
6881
- if (!content || !content.tableCells) {
6882
- return;
6883
- }
6884
7391
  const zones = target.zones;
6885
7392
  const sheetId = target.sheetId;
6886
- if (!options?.isCutOperation) {
7393
+ if (!options.isCutOperation) {
6887
7394
  this.pasteFromCopy(sheetId, zones, content.tableCells, options);
6888
7395
  }
6889
7396
  else {
@@ -7776,486 +8283,6 @@
7776
8283
  };
7777
8284
  }
7778
8285
 
7779
- function boolAnd(args) {
7780
- let foundBoolean = false;
7781
- let acc = true;
7782
- conditionalVisitBoolean(args, (arg) => {
7783
- foundBoolean = true;
7784
- acc = acc && arg;
7785
- return acc;
7786
- });
7787
- return {
7788
- foundBoolean,
7789
- result: acc,
7790
- };
7791
- }
7792
- function boolOr(args) {
7793
- let foundBoolean = false;
7794
- let acc = false;
7795
- conditionalVisitBoolean(args, (arg) => {
7796
- foundBoolean = true;
7797
- acc = acc || arg;
7798
- return !acc;
7799
- });
7800
- return {
7801
- foundBoolean,
7802
- result: acc,
7803
- };
7804
- }
7805
-
7806
- function sum(values, locale) {
7807
- return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
7808
- }
7809
- function countUnique(args) {
7810
- return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
7811
- }
7812
-
7813
- function assertSameNumberOfElements(...args) {
7814
- const dims = args[0].length;
7815
- args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
7816
- }
7817
- function average(values, locale) {
7818
- let count = 0;
7819
- const sum = reduceNumbers(values, (acc, a) => {
7820
- count += 1;
7821
- return acc + a;
7822
- }, 0, locale);
7823
- assertNotZero(count);
7824
- return sum / count;
7825
- }
7826
- function countNumbers(values, locale) {
7827
- let count = 0;
7828
- for (let n of values) {
7829
- if (isMatrix(n)) {
7830
- for (let i of n) {
7831
- for (let j of i) {
7832
- if (typeof j.value === "number") {
7833
- count += 1;
7834
- }
7835
- }
7836
- }
7837
- }
7838
- else {
7839
- const value = n?.value;
7840
- if (!isEvaluationError(value) &&
7841
- (typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
7842
- count += 1;
7843
- }
7844
- }
7845
- }
7846
- return count;
7847
- }
7848
- function countAny(values) {
7849
- return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
7850
- }
7851
- function max(values, locale) {
7852
- const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
7853
- return result === -Infinity ? 0 : result;
7854
- }
7855
- function min(values, locale) {
7856
- const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
7857
- return result === Infinity ? 0 : result;
7858
- }
7859
-
7860
- const pivotTimeAdapterRegistry = new Registry();
7861
- function pivotTimeAdapter(granularity) {
7862
- return pivotTimeAdapterRegistry.get(granularity);
7863
- }
7864
- /**
7865
- * The Time Adapter: Managing Time Periods for Pivot Functions
7866
- *
7867
- * Overview:
7868
- * A time adapter is responsible for managing time periods associated with pivot functions.
7869
- * Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
7870
- * The adapter's primary role is to normalize period values between spreadsheet functions,
7871
- * and the pivot.
7872
- * By normalizing the period value, it can be stored consistently in the pivot.
7873
- *
7874
- * Normalization Process:
7875
- * When working with functions in the spreadsheet, the time adapter normalizes
7876
- * the provided period to facilitate accurate lookup of values in the pivot.
7877
- * For instance, if the spreadsheet function represents a day period as a number generated
7878
- * by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
7879
- *
7880
- */
7881
- /**
7882
- * Normalized value: "12/25/2023"
7883
- *
7884
- * Note: Those two format are equivalent:
7885
- * - "MM/dd/yyyy" (luxon format)
7886
- * - "mm/dd/yyyy" (spreadsheet format)
7887
- **/
7888
- const dayAdapter = {
7889
- normalizeFunctionValue(value) {
7890
- const date = toNumber(value, DEFAULT_LOCALE);
7891
- return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" });
7892
- },
7893
- getFormat(locale) {
7894
- return (locale ?? DEFAULT_LOCALE).dateFormat;
7895
- },
7896
- formatValue(normalizedValue, locale) {
7897
- locale = locale ?? DEFAULT_LOCALE;
7898
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
7899
- return formatValue(value, { locale, format: this.getFormat(locale) });
7900
- },
7901
- toCellValue(normalizedValue) {
7902
- return toNumber(normalizedValue, DEFAULT_LOCALE);
7903
- },
7904
- };
7905
- /**
7906
- * normalizes day of month number
7907
- */
7908
- const dayOfMonthAdapter = {
7909
- normalizeFunctionValue(value) {
7910
- const day = toNumber(value, DEFAULT_LOCALE);
7911
- if (day < 1 || day > 31) {
7912
- throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
7913
- }
7914
- return day;
7915
- },
7916
- getFormat() {
7917
- return "0";
7918
- },
7919
- formatValue(normalizedValue, locale) {
7920
- locale = locale ?? DEFAULT_LOCALE;
7921
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
7922
- return formatValue(value, { locale, format: this.getFormat(locale) });
7923
- },
7924
- toCellValue(normalizedValue) {
7925
- return toNumber(normalizedValue, DEFAULT_LOCALE);
7926
- },
7927
- };
7928
- /**
7929
- * Normalized value: "2/2023" for week 2 of 2023
7930
- */
7931
- const weekAdapter = {
7932
- normalizeFunctionValue(value) {
7933
- const [week, year] = value.split("/");
7934
- return `${Number(week)}/${Number(year)}`;
7935
- },
7936
- getFormat() {
7937
- return undefined;
7938
- },
7939
- formatValue(normalizedValue) {
7940
- const [week, year] = normalizedValue.split("/");
7941
- return _t("W%(week)s %(year)s", { week, year });
7942
- },
7943
- toCellValue(normalizedValue) {
7944
- return this.formatValue(normalizedValue);
7945
- },
7946
- };
7947
- /**
7948
- * normalizes iso week number
7949
- */
7950
- const isoWeekNumberAdapter = {
7951
- normalizeFunctionValue(value) {
7952
- const isoWeek = toNumber(value, DEFAULT_LOCALE);
7953
- if (isoWeek < 0 || isoWeek > 53) {
7954
- throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
7955
- }
7956
- return isoWeek;
7957
- },
7958
- getFormat() {
7959
- return "0";
7960
- },
7961
- formatValue(normalizedValue, locale) {
7962
- locale = locale ?? DEFAULT_LOCALE;
7963
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
7964
- return formatValue(value, { locale, format: this.getFormat(locale) });
7965
- },
7966
- toCellValue(normalizedValue) {
7967
- return toNumber(normalizedValue, DEFAULT_LOCALE);
7968
- },
7969
- };
7970
- /**
7971
- * normalized month value is a string formatted as "MM/yyyy" (luxon format)
7972
- * e.g. "01/2020" for January 2020
7973
- */
7974
- const monthAdapter = {
7975
- normalizeFunctionValue(value) {
7976
- const date = toNumber(value, DEFAULT_LOCALE);
7977
- return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
7978
- },
7979
- getFormat() {
7980
- return "mmmm yyyy";
7981
- },
7982
- formatValue(normalizedValue, locale) {
7983
- locale = locale ?? DEFAULT_LOCALE;
7984
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
7985
- return formatValue(value, { locale, format: this.getFormat(locale) });
7986
- },
7987
- toCellValue(normalizedValue) {
7988
- return toNumber(normalizedValue, DEFAULT_LOCALE);
7989
- },
7990
- };
7991
- /**
7992
- * normalizes month number
7993
- */
7994
- const monthNumberAdapter = {
7995
- normalizeFunctionValue(value) {
7996
- const month = toNumber(value, DEFAULT_LOCALE);
7997
- if (month < 1 || month > 12) {
7998
- throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
7999
- }
8000
- return month;
8001
- },
8002
- getFormat() {
8003
- return "0";
8004
- },
8005
- formatValue(normalizedValue, locale) {
8006
- locale = locale ?? DEFAULT_LOCALE;
8007
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
8008
- return formatValue(value, { locale, format: this.getFormat(locale) });
8009
- },
8010
- toCellValue(normalizedValue) {
8011
- return MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString();
8012
- },
8013
- };
8014
- /**
8015
- * normalized quarter value is "quarter/year"
8016
- * e.g. "1/2020" for Q1 2020
8017
- */
8018
- const quarterAdapter = {
8019
- normalizeFunctionValue(value) {
8020
- const [quarter, year] = value.split("/");
8021
- return `${quarter}/${year}`;
8022
- },
8023
- getFormat() {
8024
- return undefined;
8025
- },
8026
- formatValue(normalizedValue) {
8027
- const [quarter, year] = normalizedValue.split("/");
8028
- return _t("Q%(quarter)s %(year)s", { quarter, year });
8029
- },
8030
- toCellValue(normalizedValue) {
8031
- return this.formatValue(normalizedValue);
8032
- },
8033
- };
8034
- /**
8035
- * normalizes quarter number
8036
- */
8037
- const quarterNumberAdapter = {
8038
- normalizeFunctionValue(value) {
8039
- const quarter = toNumber(value, DEFAULT_LOCALE);
8040
- if (quarter < 1 || quarter > 4) {
8041
- throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
8042
- }
8043
- return quarter;
8044
- },
8045
- getFormat() {
8046
- return "0";
8047
- },
8048
- formatValue(normalizedValue, locale) {
8049
- locale = locale ?? DEFAULT_LOCALE;
8050
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
8051
- return formatValue(value, { locale, format: this.getFormat(locale) });
8052
- },
8053
- toCellValue(normalizedValue) {
8054
- return toNumber(normalizedValue, DEFAULT_LOCALE);
8055
- },
8056
- };
8057
- const yearAdapter = {
8058
- normalizeFunctionValue(value) {
8059
- return toNumber(value, DEFAULT_LOCALE);
8060
- },
8061
- getFormat() {
8062
- return "0";
8063
- },
8064
- formatValue(normalizedValue, locale) {
8065
- locale = locale ?? DEFAULT_LOCALE;
8066
- return formatValue(normalizedValue, { locale, format: "0" });
8067
- },
8068
- toCellValue(normalizedValue) {
8069
- return toNumber(normalizedValue, DEFAULT_LOCALE);
8070
- },
8071
- };
8072
- pivotTimeAdapterRegistry
8073
- .add("day", dayAdapter)
8074
- .add("week", weekAdapter)
8075
- .add("month", monthAdapter)
8076
- .add("quarter", quarterAdapter)
8077
- .add("year", yearAdapter)
8078
- .add("day_of_month", dayOfMonthAdapter)
8079
- .add("iso_week_number", isoWeekNumberAdapter)
8080
- .add("month_number", monthNumberAdapter)
8081
- .add("quarter_number", quarterNumberAdapter)
8082
- .add("year_number", yearAdapter);
8083
-
8084
- const AGGREGATOR_NAMES = {
8085
- count: _t("Count"),
8086
- count_distinct: _t("Count Distinct"),
8087
- bool_and: _t("Boolean And"),
8088
- bool_or: _t("Boolean Or"),
8089
- max: _t("Maximum"),
8090
- min: _t("Minimum"),
8091
- avg: _t("Average"),
8092
- sum: _t("Sum"),
8093
- };
8094
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8095
- const AGGREGATORS_BY_FIELD_TYPE = {
8096
- integer: NUMBER_CHAR_AGGREGATORS,
8097
- char: NUMBER_CHAR_AGGREGATORS,
8098
- boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8099
- };
8100
- const AGGREGATORS = {};
8101
- for (const type in AGGREGATORS_BY_FIELD_TYPE) {
8102
- AGGREGATORS[type] = {};
8103
- for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
8104
- AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
8105
- }
8106
- }
8107
- const AGGREGATORS_FN = {
8108
- count: {
8109
- fn: (args) => countAny([args]),
8110
- format: () => "0",
8111
- },
8112
- count_distinct: {
8113
- fn: (args) => countUnique([args]),
8114
- format: () => "0",
8115
- },
8116
- bool_and: {
8117
- fn: (args) => boolAnd([args]).result,
8118
- format: () => undefined,
8119
- },
8120
- bool_or: {
8121
- fn: (args) => boolOr([args]).result,
8122
- format: () => undefined,
8123
- },
8124
- max: {
8125
- fn: (args, locale) => max([args], locale),
8126
- format: inferFormat,
8127
- },
8128
- min: {
8129
- fn: (args, locale) => min([args], locale),
8130
- format: inferFormat,
8131
- },
8132
- avg: {
8133
- fn: (args, locale) => average([args], locale),
8134
- format: inferFormat,
8135
- },
8136
- sum: {
8137
- fn: (args, locale) => sum([args], locale),
8138
- format: inferFormat,
8139
- },
8140
- };
8141
- function makePivotFormulaFromPivotCell(pivotFormulaId, pivotCell) {
8142
- switch (pivotCell.type) {
8143
- case "HEADER":
8144
- return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
8145
- case "MEASURE_HEADER":
8146
- return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain), "measure", pivotCell.measure].filter(isDefined));
8147
- case "VALUE":
8148
- return makePivotFormula("PIVOT.VALUE", [pivotFormulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
8149
- case "EMPTY":
8150
- return "";
8151
- }
8152
- }
8153
- /**
8154
- * Build a pivot formula expression
8155
- */
8156
- function makePivotFormula(formula, args) {
8157
- return `=${formula}(${args
8158
- .map((arg) => {
8159
- const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
8160
- const convertToNumber = typeof arg == "number" || stringIsNumber;
8161
- return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
8162
- })
8163
- .join(",")})`;
8164
- }
8165
- /**
8166
- * Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
8167
- * in this object
8168
- * If the object has no keys, return 0
8169
- *
8170
- */
8171
- function getMaxObjectId(o) {
8172
- const keys = Object.keys(o);
8173
- if (!keys.length) {
8174
- return 0;
8175
- }
8176
- const nums = keys.map((id) => parseInt(id, 10));
8177
- const max = Math.max(...nums);
8178
- return max;
8179
- }
8180
- const ALL_PERIODS = {
8181
- year: _t("Year"),
8182
- quarter: _t("Quarter"),
8183
- month: _t("Month"),
8184
- week: _t("Week"),
8185
- day: _t("Day"),
8186
- year_number: _t("Year"),
8187
- quarter_number: _t("Quarter"),
8188
- month_number: _t("Month"),
8189
- iso_week_number: _t("Week"),
8190
- day_of_month: _t("Day of Month"),
8191
- };
8192
- const DATE_FIELDS = ["date", "datetime"];
8193
- /**
8194
- * Parse a dimension string into a pivot dimension definition.
8195
- * e.g "create_date:month" => { name: "create_date", granularity: "month" }
8196
- */
8197
- function parseDimension(dimension) {
8198
- const [name, granularity] = dimension.split(":");
8199
- if (granularity) {
8200
- return { name, granularity };
8201
- }
8202
- return { name };
8203
- }
8204
- function isDateField(field) {
8205
- return DATE_FIELDS.includes(field.type);
8206
- }
8207
- function toPivotDomain(domainStr) {
8208
- if (domainStr.length % 2 !== 0) {
8209
- throw new Error("Invalid domain: odd number of elements");
8210
- }
8211
- const domain = [];
8212
- for (let i = 0; i < domainStr.length - 1; i += 2) {
8213
- domain.push({ field: domainStr[i], value: domainStr[i + 1] });
8214
- }
8215
- return domain;
8216
- }
8217
- function flatPivotDomain(domain) {
8218
- return domain.flatMap((arg) => [arg.field, arg.value]);
8219
- }
8220
- /**
8221
- * Parses the value defining a pivot group in a PIVOT formula
8222
- * e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
8223
- * the two group values are "42" and "won".
8224
- */
8225
- function toNormalizedPivotValue(dimension, groupValue) {
8226
- if (groupValue === null || groupValue === "null") {
8227
- return null;
8228
- }
8229
- const groupValueString = typeof groupValue === "boolean"
8230
- ? toString(groupValue).toLocaleLowerCase()
8231
- : toString(groupValue);
8232
- if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
8233
- throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
8234
- field: dimension.displayName,
8235
- type: dimension.type,
8236
- }));
8237
- }
8238
- // represents a field which is not set (=False server side)
8239
- if (groupValueString === "false") {
8240
- return false;
8241
- }
8242
- const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
8243
- return normalizer(groupValueString, dimension.granularity);
8244
- }
8245
- function normalizeDateTime(value, granularity) {
8246
- if (!granularity) {
8247
- throw "";
8248
- }
8249
- return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
8250
- }
8251
- const pivotNormalizationValueRegistry = new Registry();
8252
- pivotNormalizationValueRegistry
8253
- .add("date", normalizeDateTime)
8254
- .add("datetime", normalizeDateTime)
8255
- .add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
8256
- .add("boolean", (value) => toBoolean(value))
8257
- .add("char", (value) => toString(value));
8258
-
8259
8286
  /**
8260
8287
  * Change the reference types inside the given token, if the token represent a range or a cell
8261
8288
  *
@@ -9995,7 +10022,7 @@ stores.inject(MyMetaStore, storeInstance);
9995
10022
  const cell = this.getters.getCell(position);
9996
10023
  if (pivotId && pivotCell.type !== "EMPTY" && !cell?.isFormula) {
9997
10024
  const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
9998
- const formula = makePivotFormulaFromPivotCell(formulaPivotId, pivotCell);
10025
+ const formula = createPivotFormula(formulaPivotId, pivotCell);
9999
10026
  return formula.slice(1); // strip leading =
10000
10027
  }
10001
10028
  }
@@ -19087,10 +19114,9 @@ stores.inject(MyMetaStore, storeInstance);
19087
19114
  compute: function (formulaId, measureName, ...domainArgs) {
19088
19115
  const _pivotFormulaId = toString(formulaId);
19089
19116
  const _measure = toString(measureName);
19090
- const _domainArgs = domainArgs.map(toString);
19091
19117
  const pivotId = getPivotId(_pivotFormulaId, this.getters);
19092
19118
  assertMeasureExist(pivotId, _measure, this.getters);
19093
- assertDomainLength(_domainArgs);
19119
+ assertDomainLength(domainArgs);
19094
19120
  const pivot = this.getters.getPivot(pivotId);
19095
19121
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
19096
19122
  addPivotDependencies(this, coreDefinition);
@@ -19098,15 +19124,14 @@ stores.inject(MyMetaStore, storeInstance);
19098
19124
  if (error) {
19099
19125
  return error;
19100
19126
  }
19101
- const domain = toPivotDomain(_domainArgs);
19102
- const { value, format } = pivot.getPivotCellValueAndFormat(_measure, domain);
19103
- if (!value && !this.getters.areDomainArgsFieldsValid(pivotId, domain)) {
19127
+ if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
19104
19128
  return {
19105
19129
  value: CellErrorType.GenericError,
19106
19130
  message: _t("Dimensions don't match the pivot definition"),
19107
19131
  };
19108
19132
  }
19109
- return { value, format };
19133
+ const domain = pivot.parseArgsToPivotDomain(domainArgs);
19134
+ return pivot.getPivotCellValueAndFormat(_measure, domain);
19110
19135
  },
19111
19136
  };
19112
19137
  const PIVOT_HEADER = {
@@ -19118,9 +19143,8 @@ stores.inject(MyMetaStore, storeInstance);
19118
19143
  ],
19119
19144
  compute: function (pivotId, ...domainArgs) {
19120
19145
  const _pivotFormulaId = toString(pivotId);
19121
- const _domainArgs = domainArgs.map(toString);
19122
19146
  const _pivotId = getPivotId(_pivotFormulaId, this.getters);
19123
- assertDomainLength(_domainArgs);
19147
+ assertDomainLength(domainArgs);
19124
19148
  const pivot = this.getters.getPivot(_pivotId);
19125
19149
  const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
19126
19150
  addPivotDependencies(this, coreDefinition);
@@ -19128,14 +19152,14 @@ stores.inject(MyMetaStore, storeInstance);
19128
19152
  if (error) {
19129
19153
  return error;
19130
19154
  }
19131
- const domain = toPivotDomain(_domainArgs);
19132
- const lastNode = domain.at(-1);
19133
- if (!this.getters.areDomainArgsFieldsValid(_pivotId, lastNode?.field === "measure" ? domain.slice(0, -1) : domain)) {
19155
+ if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
19134
19156
  return {
19135
19157
  value: CellErrorType.GenericError,
19136
19158
  message: _t("Dimensions don't match the pivot definition"),
19137
19159
  };
19138
19160
  }
19161
+ const domain = pivot.parseArgsToPivotDomain(domainArgs);
19162
+ const lastNode = domain.at(-1);
19139
19163
  if (lastNode?.field === "measure") {
19140
19164
  return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
19141
19165
  }
@@ -19152,13 +19176,21 @@ stores.inject(MyMetaStore, storeInstance);
19152
19176
  description: _t("Get a pivot table."),
19153
19177
  args: [
19154
19178
  arg("pivot_id (string)", _t("ID of the pivot.")),
19155
- arg("row_count (number, optional, default=10000)", _t("number of rows")),
19179
+ arg("row_count (number, optional)", _t("number of rows")),
19156
19180
  arg("include_total (boolean, default=TRUE)", _t("Whether to include total/sub-totals or not.")),
19157
19181
  arg("include_column_titles (boolean, default=TRUE)", _t("Whether to include the column titles or not.")),
19182
+ arg("column_count (number, optional)", _t("number of columns")),
19158
19183
  ],
19159
- compute: function (pivotFormulaId, rowCount = { value: 10000 }, includeTotal = { value: true }, includeColumnHeaders = { value: true }) {
19184
+ compute: function (pivotFormulaId, rowCount = { value: Number.MAX_VALUE }, includeTotal = { value: true }, includeColumnHeaders = { value: true }, columnCount = { value: Number.MAX_VALUE }) {
19160
19185
  const _pivotFormulaId = toString(pivotFormulaId);
19161
19186
  const _rowCount = toNumber(rowCount, this.locale);
19187
+ if (_rowCount < 0) {
19188
+ throw new EvaluationError(_t("The number of rows must be positive."));
19189
+ }
19190
+ const _columnCount = toNumber(columnCount, this.locale);
19191
+ if (_columnCount < 0) {
19192
+ throw new EvaluationError(_t("The number of columns must be positive."));
19193
+ }
19162
19194
  const _includeColumnHeaders = toBoolean(includeColumnHeaders);
19163
19195
  const _includedTotal = toBoolean(includeTotal);
19164
19196
  const pivotId = getPivotId(_pivotFormulaId, this.getters);
@@ -19174,19 +19206,15 @@ stores.inject(MyMetaStore, storeInstance);
19174
19206
  const cells = table.getPivotCells(_includedTotal, _includeColumnHeaders);
19175
19207
  const headerRows = _includeColumnHeaders ? table.columns.length : 0;
19176
19208
  const pivotTitle = this.getters.getPivotDisplayName(pivotId);
19177
- if (_rowCount < 0) {
19178
- throw new EvaluationError(_t("The number of rows must be positive."));
19179
- }
19180
- const end = Math.min(headerRows + _rowCount, cells[0].length);
19181
- if (end === 0) {
19209
+ const tableHeight = Math.min(headerRows + _rowCount, cells[0].length);
19210
+ if (tableHeight === 0) {
19182
19211
  return [[{ value: pivotTitle }]];
19183
19212
  }
19184
- const tableWidth = cells.length;
19185
- const tableRows = range(0, end);
19213
+ const tableWidth = Math.min(1 + _columnCount, cells.length);
19186
19214
  const result = [];
19187
19215
  for (const col of range(0, tableWidth)) {
19188
19216
  result[col] = [];
19189
- for (const row of tableRows) {
19217
+ for (const row of range(0, tableHeight)) {
19190
19218
  const pivotCell = cells[col][row];
19191
19219
  switch (pivotCell.type) {
19192
19220
  case "EMPTY":
@@ -21876,9 +21904,6 @@ stores.inject(MyMetaStore, storeInstance);
21876
21904
  const pivot = this.getters.getPivot(pivotId);
21877
21905
  pivot.init();
21878
21906
  const fields = pivot.getFields();
21879
- if (!fields) {
21880
- return [];
21881
- }
21882
21907
  const definition = this.getters.getPivotCoreDefinition(pivotId);
21883
21908
  return definition.measures
21884
21909
  .map((measure) => {
@@ -21918,9 +21943,6 @@ stores.inject(MyMetaStore, storeInstance);
21918
21943
  const pivot = this.getters.getPivot(pivotId);
21919
21944
  pivot.init();
21920
21945
  const fields = pivot.getFields();
21921
- if (!fields) {
21922
- return;
21923
- }
21924
21946
  const { columns, rows } = pivot.definition;
21925
21947
  let args = functionContext.args;
21926
21948
  if (functionContext?.parent.toUpperCase() === "PIVOT.VALUE") {
@@ -27069,7 +27091,7 @@ stores.inject(MyMetaStore, storeInstance);
27069
27091
  if (getZoneArea(zone) === 1 && topLeftCell?.content) {
27070
27092
  return {
27071
27093
  type: "scorecard",
27072
- title: { text: "" },
27094
+ title: {},
27073
27095
  background: topLeftCell.style?.fillColor || undefined,
27074
27096
  keyValue: zoneToXc(zone),
27075
27097
  baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
@@ -27077,22 +27099,11 @@ stores.inject(MyMetaStore, storeInstance);
27077
27099
  baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
27078
27100
  };
27079
27101
  }
27080
- let title = "";
27081
27102
  const cellsInFirstRow = getters.getEvaluatedCellsInZone(sheetId, {
27082
27103
  ...dataSetZone,
27083
27104
  bottom: dataSetZone.top,
27084
27105
  });
27085
27106
  const dataSetsHaveTitle = !!cellsInFirstRow.find((cell) => cell.type !== CellValueType.empty && cell.type !== CellValueType.number);
27086
- if (dataSetsHaveTitle) {
27087
- const texts = cellsInFirstRow
27088
- .filter((cell) => cell.type !== CellValueType.error && cell.type !== CellValueType.empty)
27089
- .map((cell) => cell.formattedValue);
27090
- const lastElement = texts.splice(-1)[0];
27091
- title = texts.join(", ");
27092
- if (lastElement) {
27093
- title += (title ? " " + _t("and") + " " : "") + lastElement;
27094
- }
27095
- }
27096
27107
  let labelRangeXc;
27097
27108
  if (!singleColumn) {
27098
27109
  labelRangeXc = zoneToXc({
@@ -27105,7 +27116,7 @@ stores.inject(MyMetaStore, storeInstance);
27105
27116
  const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
27106
27117
  if (canChartParseLabels(labelRange, getters)) {
27107
27118
  return {
27108
- title: { text: title },
27119
+ title: {},
27109
27120
  dataSets,
27110
27121
  labelsAsText: false,
27111
27122
  stacked: false,
@@ -27121,7 +27132,7 @@ stores.inject(MyMetaStore, storeInstance);
27121
27132
  if (singleColumn &&
27122
27133
  getData(getters, _dataSets[0]).every((e) => typeof e === "string" && !isEvaluationError(e))) {
27123
27134
  return {
27124
- title: { text: "" },
27135
+ title: {},
27125
27136
  dataSets: [{ dataRange }],
27126
27137
  aggregated: true,
27127
27138
  labelRange: dataRange,
@@ -27131,7 +27142,7 @@ stores.inject(MyMetaStore, storeInstance);
27131
27142
  };
27132
27143
  }
27133
27144
  return {
27134
- title: { text: title },
27145
+ title: {},
27135
27146
  dataSets,
27136
27147
  labelRange: labelRangeXc,
27137
27148
  type: "bar",
@@ -31674,7 +31685,7 @@ stores.inject(MyMetaStore, storeInstance);
31674
31685
  static template = "o-spreadsheet.ChartTitle";
31675
31686
  static components = { Section, ColorPickerWidget };
31676
31687
  static props = {
31677
- title: String,
31688
+ title: { type: String, optional: true },
31678
31689
  updateTitle: Function,
31679
31690
  name: { type: String, optional: true },
31680
31691
  toggleItalic: { type: Function, optional: true },
@@ -31683,6 +31694,9 @@ stores.inject(MyMetaStore, storeInstance);
31683
31694
  updateColor: { type: Function, optional: true },
31684
31695
  style: { type: Object, optional: true },
31685
31696
  };
31697
+ static defaultProps = {
31698
+ title: "",
31699
+ };
31686
31700
  openedEl = null;
31687
31701
  setup() {
31688
31702
  owl.useExternalListener(window, "click", this.onExternalClick);
@@ -34554,6 +34568,7 @@ stores.inject(MyMetaStore, storeInstance);
34554
34568
  currentSearchRegex = null;
34555
34569
  isSearchDirty = false;
34556
34570
  initialShowFormulaState;
34571
+ preserveSelectedMatchIndex = false;
34557
34572
  // fixme: why do we make selectedMatchIndex on top of a selected
34558
34573
  // property in the matches?
34559
34574
  selectedMatchIndex = null;
@@ -34667,7 +34682,9 @@ stores.inject(MyMetaStore, storeInstance);
34667
34682
  * refresh the matches according to the current search options
34668
34683
  */
34669
34684
  refreshSearch(jumpToMatchSheet = true) {
34670
- this.selectedMatchIndex = null;
34685
+ if (!this.preserveSelectedMatchIndex) {
34686
+ this.selectedMatchIndex = null;
34687
+ }
34671
34688
  this.findMatches();
34672
34689
  this.selectNextCell(Direction.current, jumpToMatchSheet);
34673
34690
  }
@@ -34766,10 +34783,16 @@ stores.inject(MyMetaStore, storeInstance);
34766
34783
  const selectedMatch = matches[nextIndex];
34767
34784
  // Switch to the sheet where the match is located
34768
34785
  if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
34786
+ // We set `preserveSelectedMatchIndex` to true to avoid resetting the selected search
34787
+ // index in the `refreshSearch` function when a new sheet is activated. The reason being
34788
+ // that, when we automatically go back to previous sheet while performing a search, the
34789
+ // search index is reset to the first occurrence each time.
34790
+ this.preserveSelectedMatchIndex = true;
34769
34791
  this.model.dispatch("ACTIVATE_SHEET", {
34770
34792
  sheetIdFrom: this.getters.getActiveSheetId(),
34771
34793
  sheetIdTo: selectedMatch.sheetId,
34772
34794
  });
34795
+ this.preserveSelectedMatchIndex = false;
34773
34796
  // We do not want to reset the selection at finalize in this case
34774
34797
  this.isSearchDirty = false;
34775
34798
  }
@@ -35410,6 +35433,10 @@ stores.inject(MyMetaStore, storeInstance);
35410
35433
  }
35411
35434
  });
35412
35435
  }
35436
+ onClick(item) {
35437
+ item.onClick();
35438
+ this.popover.isOpen = false;
35439
+ }
35413
35440
  get popoverProps() {
35414
35441
  const { x, y, width, height } = this.buttonRef.el.getBoundingClientRect();
35415
35442
  return {
@@ -35452,16 +35479,22 @@ stores.inject(MyMetaStore, storeInstance);
35452
35479
  static components = { CogWheelMenu, Section, EditableName };
35453
35480
  static props = {
35454
35481
  pivotId: String,
35482
+ flipAxis: Function,
35455
35483
  };
35456
35484
  get cogWheelMenuItems() {
35457
35485
  return [
35458
35486
  {
35459
- name: "Duplicate",
35487
+ name: _t("Flip axes"),
35488
+ icon: "fa-exchange",
35489
+ onClick: this.props.flipAxis,
35490
+ },
35491
+ {
35492
+ name: _t("Duplicate"),
35460
35493
  icon: "fa-copy",
35461
35494
  onClick: () => this.duplicatePivot(),
35462
35495
  },
35463
35496
  {
35464
- name: "Delete",
35497
+ name: _t("Delete"),
35465
35498
  icon: "fa-trash",
35466
35499
  onClick: () => this.delete(),
35467
35500
  },
@@ -35658,9 +35691,10 @@ stores.inject(MyMetaStore, storeInstance);
35658
35691
  columns;
35659
35692
  rows;
35660
35693
  measures;
35694
+ fieldsType;
35661
35695
  maxIndent;
35662
35696
  pivotCells = {};
35663
- constructor(columns, rows, measures) {
35697
+ constructor(columns, rows, measures, fieldsType) {
35664
35698
  this.columns = columns.map((row) => {
35665
35699
  // offset in the pivot table
35666
35700
  // starts at 1 because the first column is the row title
@@ -35673,6 +35707,7 @@ stores.inject(MyMetaStore, storeInstance);
35673
35707
  });
35674
35708
  this.rows = rows;
35675
35709
  this.measures = measures;
35710
+ this.fieldsType = fieldsType;
35676
35711
  this.maxIndent = Math.max(...this.rows.map((row) => row.indent));
35677
35712
  }
35678
35713
  /**
@@ -35719,7 +35754,7 @@ stores.inject(MyMetaStore, storeInstance);
35719
35754
  if (!domain) {
35720
35755
  return EMPTY_PIVOT_CELL;
35721
35756
  }
35722
- const measure = domain.at(-1)?.value.toString() || "";
35757
+ const measure = domain.at(-1)?.value?.toString() || "";
35723
35758
  return { type: "MEASURE_HEADER", domain: domain.slice(0, -1), measure };
35724
35759
  }
35725
35760
  else if (row <= colHeadersHeight - 1) {
@@ -35751,9 +35786,13 @@ stores.inject(MyMetaStore, storeInstance);
35751
35786
  return undefined;
35752
35787
  }
35753
35788
  for (let i = 0; i < pivotCol.fields.length; i++) {
35789
+ const fieldWithGranularity = pivotCol.fields[i];
35790
+ const { name, granularity } = parseDimension(fieldWithGranularity);
35791
+ const type = this.fieldsType[name] || "char";
35754
35792
  domain.push({
35755
- field: pivotCol.fields[i],
35756
- value: pivotCol.values[i],
35793
+ type,
35794
+ field: fieldWithGranularity,
35795
+ value: toNormalizedPivotValue({ displayName: name, type, granularity }, pivotCol.values[i]),
35757
35796
  });
35758
35797
  }
35759
35798
  return domain;
@@ -35765,17 +35804,21 @@ stores.inject(MyMetaStore, storeInstance);
35765
35804
  getColMeasure(col) {
35766
35805
  const domain = this.getColHeaderDomain(col, this.columns.length - 1);
35767
35806
  const measure = domain?.at(-1)?.value;
35768
- if (measure === undefined) {
35769
- throw new Error("Measure isd missing");
35807
+ if (measure === undefined || measure === null) {
35808
+ throw new Error("Measure is missing");
35770
35809
  }
35771
35810
  return measure.toString();
35772
35811
  }
35773
35812
  getRowDomain(row) {
35774
35813
  const domain = [];
35775
35814
  for (let i = 0; i < this.rows[row].fields.length; i++) {
35815
+ const fieldWithGranularity = this.rows[row].fields[i];
35816
+ const { name, granularity } = parseDimension(fieldWithGranularity);
35817
+ const type = this.fieldsType[name] || "char";
35776
35818
  domain.push({
35777
- field: this.rows[row].fields[i],
35778
- value: this.rows[row].values[i],
35819
+ type,
35820
+ field: fieldWithGranularity,
35821
+ value: toNormalizedPivotValue({ displayName: name, type, granularity }, this.rows[row].values[i]),
35779
35822
  });
35780
35823
  }
35781
35824
  return domain;
@@ -35785,6 +35828,7 @@ stores.inject(MyMetaStore, storeInstance);
35785
35828
  cols: this.columns,
35786
35829
  rows: this.rows,
35787
35830
  measures: this.measures,
35831
+ fieldsType: this.fieldsType,
35788
35832
  };
35789
35833
  }
35790
35834
  }
@@ -35805,7 +35849,14 @@ stores.inject(MyMetaStore, storeInstance);
35805
35849
  indent: 0,
35806
35850
  });
35807
35851
  const measureNames = definition.measures.map((m) => m.name);
35808
- return new SpreadsheetPivotTable(cols, rows, measureNames);
35852
+ const fieldsType = {};
35853
+ for (const columns of definition.columns) {
35854
+ fieldsType[columns.name] = columns.type;
35855
+ }
35856
+ for (const row of definition.rows) {
35857
+ fieldsType[row.name] = row.type;
35858
+ }
35859
+ return new SpreadsheetPivotTable(cols, rows, measureNames, fieldsType);
35809
35860
  }
35810
35861
  // -----------------------------------------------------------------------------
35811
35862
  // ROWS
@@ -35981,41 +36032,42 @@ stores.inject(MyMetaStore, storeInstance);
35981
36032
  return dimension.order === "asc" ? a.localeCompare(b) : b.localeCompare(a);
35982
36033
  }
35983
36034
 
36035
+ const NULL_SYMBOL = Symbol("NULL");
35984
36036
  function createDate(dimension, value, locale) {
35985
36037
  const granularity = dimension.granularity;
35986
36038
  if (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
35987
36039
  throw new Error(`Unknown date granularity: ${granularity}`);
35988
36040
  }
35989
- if (value === null) {
35990
- return null;
35991
- }
36041
+ const keyInMap = typeof value === "number" || typeof value === "string" ? value : NULL_SYMBOL;
35992
36042
  if (!MAP_VALUE_DIMENSION_DATE[granularity].set.has(value)) {
35993
36043
  MAP_VALUE_DIMENSION_DATE[granularity].set.add(value);
35994
- const date = toJsDate(value, locale);
35995
- let number = 0;
35996
- switch (granularity) {
35997
- case "year_number":
35998
- number = date.getFullYear();
35999
- break;
36000
- case "quarter_number":
36001
- number = Math.floor(date.getMonth() / 3) + 1;
36002
- break;
36003
- case "month_number":
36004
- number = date.getMonth() + 1;
36005
- break;
36006
- case "iso_week_number":
36007
- number = date.getIsoWeek();
36008
- break;
36009
- case "day_of_month":
36010
- number = date.getDate();
36011
- break;
36012
- case "day":
36013
- number = Math.floor(toNumber(value, locale));
36014
- break;
36044
+ let number = null;
36045
+ if (typeof value === "number" || typeof value === "string") {
36046
+ const date = toJsDate(value, locale);
36047
+ switch (granularity) {
36048
+ case "year_number":
36049
+ number = date.getFullYear();
36050
+ break;
36051
+ case "quarter_number":
36052
+ number = Math.floor(date.getMonth() / 3) + 1;
36053
+ break;
36054
+ case "month_number":
36055
+ number = date.getMonth() + 1;
36056
+ break;
36057
+ case "iso_week_number":
36058
+ number = date.getIsoWeek();
36059
+ break;
36060
+ case "day_of_month":
36061
+ number = date.getDate();
36062
+ break;
36063
+ case "day":
36064
+ number = Math.floor(toNumber(value, locale));
36065
+ break;
36066
+ }
36015
36067
  }
36016
- MAP_VALUE_DIMENSION_DATE[granularity].values[`${value}`] = toNormalizedPivotValue(dimension, number);
36068
+ MAP_VALUE_DIMENSION_DATE[granularity].values[keyInMap] = toNormalizedPivotValue(dimension, number);
36017
36069
  }
36018
- return MAP_VALUE_DIMENSION_DATE[granularity].values[`${value}`];
36070
+ return MAP_VALUE_DIMENSION_DATE[granularity].values[keyInMap];
36019
36071
  }
36020
36072
  /**
36021
36073
  * This map is used to cache the different values of a pivot date value
@@ -36197,6 +36249,25 @@ stores.inject(MyMetaStore, storeInstance);
36197
36249
  }
36198
36250
  return undefined;
36199
36251
  }
36252
+ areDomainArgsFieldsValid(args) {
36253
+ let dimensions = args.filter((_, index) => index % 2 === 0).map(toString);
36254
+ if (dimensions.length && dimensions.at(-1) === "measure") {
36255
+ dimensions = dimensions.slice(0, -1);
36256
+ }
36257
+ return areDomainArgsFieldsValid(dimensions, this.definition);
36258
+ }
36259
+ parseArgsToPivotDomain(args) {
36260
+ const domain = [];
36261
+ for (let i = 0; i < args.length - 1; i += 2) {
36262
+ const fieldWithGranularity = toString(args[i]);
36263
+ const type = this.getTypeOfDimension(fieldWithGranularity);
36264
+ const normalizedValue = fieldWithGranularity === "measure"
36265
+ ? toString(args[i + 1])
36266
+ : toNormalizedPivotValue(this.getDimension(fieldWithGranularity), args[i + 1]);
36267
+ domain.push({ field: fieldWithGranularity, value: normalizedValue, type });
36268
+ }
36269
+ return domain;
36270
+ }
36200
36271
  markAsDirtyForEvaluation() {
36201
36272
  this.needsReevaluation = true;
36202
36273
  }
@@ -36218,18 +36289,12 @@ stores.inject(MyMetaStore, storeInstance);
36218
36289
  const finalCell = cells[0]?.[dimension.nameWithGranularity];
36219
36290
  if (dimension.type === "date") {
36220
36291
  const adapter = pivotTimeAdapter(dimension.granularity);
36221
- return {
36222
- value: lastNode.value !== "null"
36223
- ? adapter.toCellValue(toNormalizedPivotValue(dimension, lastNode.value))
36224
- : _t("(Undefined)"),
36225
- format: adapter.getFormat(this.getters.getLocale()),
36226
- };
36292
+ return adapter.toValueAndFormat(lastNode.value, this.getters.getLocale());
36227
36293
  }
36228
36294
  if (!finalCell) {
36229
36295
  return { value: "" };
36230
36296
  }
36231
- // Value can be null but stringified (e.g. an empty date, as for now every date is stringified)
36232
- if (finalCell.value === null || finalCell.value === `${null}`) {
36297
+ if (finalCell.value === null) {
36233
36298
  return { value: _t("(Undefined)") };
36234
36299
  }
36235
36300
  return {
@@ -36280,14 +36345,24 @@ stores.inject(MyMetaStore, storeInstance);
36280
36345
  getFields() {
36281
36346
  return this.fields;
36282
36347
  }
36348
+ getTypeOfDimension(fieldWithGranularity) {
36349
+ if (fieldWithGranularity === "measure") {
36350
+ return "char";
36351
+ }
36352
+ const { name } = parseDimension(fieldWithGranularity);
36353
+ const type = this.fields[name]?.type;
36354
+ if (!type) {
36355
+ throw new Error(`Field ${name} does not exist`);
36356
+ }
36357
+ return type;
36358
+ }
36283
36359
  filterDataEntriesFromDomain(dataEntries, domain) {
36284
36360
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
36285
36361
  }
36286
36362
  filterDataEntriesFromDomainNode(dataEntries, domain) {
36287
36363
  const { field, value } = domain;
36288
- const dimension = this.getDimension(field);
36289
- return dataEntries.filter((entry) => `${entry[dimension.nameWithGranularity]?.value}` ===
36290
- `${toNormalizedPivotValue(dimension, value)}`);
36364
+ const { nameWithGranularity } = this.getDimension(field);
36365
+ return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
36291
36366
  }
36292
36367
  getDimension(nameWithGranularity) {
36293
36368
  return this.definition.getDimension(nameWithGranularity);
@@ -36392,7 +36467,7 @@ stores.inject(MyMetaStore, storeInstance);
36392
36467
  for (const entry of dataEntries) {
36393
36468
  for (const dimension of dateDimensions) {
36394
36469
  entry[dimension.nameWithGranularity] = {
36395
- value: `${createDate(dimension, entry[dimension.name]?.value || null, this.getters.getLocale())}`,
36470
+ value: createDate(dimension, entry[dimension.name]?.value || null, this.getters.getLocale()),
36396
36471
  type: entry[dimension.name]?.type || CellValueType.empty,
36397
36472
  format: entry[dimension.name]?.format,
36398
36473
  };
@@ -36439,11 +36514,7 @@ stores.inject(MyMetaStore, storeInstance);
36439
36514
  }
36440
36515
  }
36441
36516
  get fields() {
36442
- const fields = this.pivot.getFields();
36443
- if (!fields) {
36444
- throw new Error("Fields not found");
36445
- }
36446
- return fields;
36517
+ return this.pivot.getFields();
36447
36518
  }
36448
36519
  get pivot() {
36449
36520
  return this.getters.getPivot(this.pivotId);
@@ -36677,6 +36748,13 @@ stores.inject(MyMetaStore, storeInstance);
36677
36748
  this.store.applyUpdate();
36678
36749
  }
36679
36750
  }
36751
+ flipAxis() {
36752
+ const { rows, columns } = this.definition;
36753
+ this.onDimensionsUpdated({
36754
+ rows: columns,
36755
+ columns: rows,
36756
+ });
36757
+ }
36680
36758
  onDimensionsUpdated(definition) {
36681
36759
  this.store.update(definition);
36682
36760
  }
@@ -51548,8 +51626,8 @@ stores.inject(MyMetaStore, storeInstance);
51548
51626
  case "INSERT_PIVOT": {
51549
51627
  const { sheetId, col, row, pivotId, table } = cmd;
51550
51628
  const position = { sheetId, col, row };
51551
- const { cols, rows, measures } = table;
51552
- const spTable = new SpreadsheetPivotTable(cols, rows, measures);
51629
+ const { cols, rows, measures, fieldsType } = table;
51630
+ const spTable = new SpreadsheetPivotTable(cols, rows, measures, fieldsType || {});
51553
51631
  const formulaId = this.getPivotFormulaId(pivotId);
51554
51632
  this.insertPivot(position, formulaId, spTable);
51555
51633
  break;
@@ -51630,7 +51708,7 @@ stores.inject(MyMetaStore, storeInstance);
51630
51708
  sheetId: position.sheetId,
51631
51709
  col: position.col + col,
51632
51710
  row: position.row + row,
51633
- content: makePivotFormulaFromPivotCell(formulaId, pivotCell),
51711
+ content: createPivotFormula(formulaId, pivotCell),
51634
51712
  });
51635
51713
  }
51636
51714
  }
@@ -54896,7 +54974,6 @@ stores.inject(MyMetaStore, storeInstance);
54896
54974
  "getPivotIdFromPosition",
54897
54975
  "getPivotCellFromPosition",
54898
54976
  "isPivotUnused",
54899
- "areDomainArgsFieldsValid",
54900
54977
  "isSpillPivotFormula",
54901
54978
  ];
54902
54979
  pivots = {};
@@ -54968,7 +55045,7 @@ stores.inject(MyMetaStore, storeInstance);
54968
55045
  getPivotIdFromPosition(position) {
54969
55046
  const cell = this.getters.getCorrespondingFormulaCell(position);
54970
55047
  if (cell && cell.isFormula) {
54971
- const pivotFunction = this.getFirstPivotFunction(cell.compiledFormula.tokens);
55048
+ const pivotFunction = this.getFirstPivotFunction(position.sheetId, cell.compiledFormula.tokens);
54972
55049
  if (pivotFunction) {
54973
55050
  const pivotId = pivotFunction.args[0]?.toString();
54974
55051
  return pivotId && this.getters.getPivotId(pivotId);
@@ -54979,12 +55056,12 @@ stores.inject(MyMetaStore, storeInstance);
54979
55056
  isSpillPivotFormula(position) {
54980
55057
  const cell = this.getters.getCorrespondingFormulaCell(position);
54981
55058
  if (cell && cell.isFormula) {
54982
- const pivotFunction = this.getFirstPivotFunction(cell.compiledFormula.tokens);
55059
+ const pivotFunction = this.getFirstPivotFunction(position.sheetId, cell.compiledFormula.tokens);
54983
55060
  return pivotFunction?.functionName === "PIVOT";
54984
55061
  }
54985
55062
  return false;
54986
55063
  }
54987
- getFirstPivotFunction(tokens) {
55064
+ getFirstPivotFunction(sheetId, tokens) {
54988
55065
  const pivotFunction = getFirstPivotFunction(tokens);
54989
55066
  if (!pivotFunction) {
54990
55067
  return undefined;
@@ -55000,7 +55077,7 @@ stores.inject(MyMetaStore, storeInstance);
55000
55077
  return argAst.value;
55001
55078
  }
55002
55079
  const argsString = astToFormula(argAst);
55003
- return this.getters.evaluateFormula(this.getters.getActiveSheetId(), argsString);
55080
+ return this.getters.evaluateFormula(sheetId, argsString);
55004
55081
  });
55005
55082
  return { functionName, args: evaluatedArgs };
55006
55083
  }
@@ -55023,24 +55100,24 @@ stores.inject(MyMetaStore, storeInstance);
55023
55100
  return EMPTY_PIVOT_CELL;
55024
55101
  }
55025
55102
  const mainPosition = this.getters.getCellPosition(cell.id);
55026
- const result = this.getters.getFirstPivotFunction(cell.compiledFormula.tokens);
55103
+ const result = this.getters.getFirstPivotFunction(position.sheetId, cell.compiledFormula.tokens);
55027
55104
  if (!result) {
55028
55105
  return EMPTY_PIVOT_CELL;
55029
55106
  }
55030
55107
  const { functionName, args } = result;
55108
+ const formulaId = args[0];
55109
+ if (!formulaId) {
55110
+ return EMPTY_PIVOT_CELL;
55111
+ }
55112
+ const pivotId = this.getters.getPivotId(formulaId.toString());
55113
+ if (!pivotId) {
55114
+ return EMPTY_PIVOT_CELL;
55115
+ }
55116
+ const pivot = this.getPivot(pivotId);
55117
+ if (!pivot.isValid()) {
55118
+ return EMPTY_PIVOT_CELL;
55119
+ }
55031
55120
  if (functionName === "PIVOT") {
55032
- const formulaId = args[0];
55033
- if (!formulaId) {
55034
- return EMPTY_PIVOT_CELL;
55035
- }
55036
- const pivotId = this.getters.getPivotId(formulaId.toString());
55037
- if (!pivotId) {
55038
- return EMPTY_PIVOT_CELL;
55039
- }
55040
- const pivot = this.getPivot(pivotId);
55041
- if (!pivot.isValid()) {
55042
- return EMPTY_PIVOT_CELL;
55043
- }
55044
55121
  const includeTotal = args[2] === false ? false : undefined;
55045
55122
  const includeColumnHeaders = args[3] === false ? false : undefined;
55046
55123
  const pivotCells = pivot
@@ -55051,7 +55128,7 @@ stores.inject(MyMetaStore, storeInstance);
55051
55128
  return pivotCells[pivotCol][pivotRow];
55052
55129
  }
55053
55130
  if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
55054
- const domain = toPivotDomain(args.slice(1, -2).map((x) => `${x}`));
55131
+ const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
55055
55132
  return {
55056
55133
  type: "MEASURE_HEADER",
55057
55134
  domain,
@@ -55059,15 +55136,17 @@ stores.inject(MyMetaStore, storeInstance);
55059
55136
  };
55060
55137
  }
55061
55138
  else if (functionName === "PIVOT.HEADER") {
55139
+ const domain = pivot.parseArgsToPivotDomain(args.slice(1).map((value) => ({ value })));
55062
55140
  return {
55063
55141
  type: "HEADER",
55064
- domain: toPivotDomain(args.slice(1).map((x) => `${x}`)),
55142
+ domain,
55065
55143
  };
55066
55144
  }
55067
55145
  const [measure, ...domainArgs] = args.slice(1);
55146
+ const domain = pivot.parseArgsToPivotDomain(domainArgs.map((value) => ({ value })));
55068
55147
  return {
55069
55148
  type: "VALUE",
55070
- domain: toPivotDomain(domainArgs.map((x) => `${x}`)),
55149
+ domain,
55071
55150
  measure: measure?.toString() || "",
55072
55151
  };
55073
55152
  }
@@ -55077,32 +55156,6 @@ stores.inject(MyMetaStore, storeInstance);
55077
55156
  isPivotUnused(pivotId) {
55078
55157
  return this._getUnusedPivots().includes(pivotId);
55079
55158
  }
55080
- /**
55081
- * Check if the fields in the domain part of
55082
- * a pivot function are valid according to the pivot definition.
55083
- * e.g. =PIVOT.VALUE(1,"revenue","country_id",...,"create_date:month",...,"source_id",...)
55084
- */
55085
- areDomainArgsFieldsValid(pivotId, domain) {
55086
- const dimensions = domain
55087
- .map((node) => node.field)
55088
- .map((name) => (name.startsWith("#") ? name.slice(1) : name));
55089
- let argIndex = 0;
55090
- let definitionIndex = 0;
55091
- const pivot = this.getPivot(pivotId);
55092
- const definition = pivot.definition;
55093
- const cols = definition.columns.map((col) => col.nameWithGranularity);
55094
- const rows = definition.rows.map((row) => row.nameWithGranularity);
55095
- while (dimensions[argIndex] !== undefined && dimensions[argIndex] === rows[definitionIndex]) {
55096
- argIndex++;
55097
- definitionIndex++;
55098
- }
55099
- definitionIndex = 0;
55100
- while (dimensions[argIndex] !== undefined && dimensions[argIndex] === cols[definitionIndex]) {
55101
- argIndex++;
55102
- definitionIndex++;
55103
- }
55104
- return dimensions.length === argIndex;
55105
- }
55106
55159
  // ---------------------------------------------------------------------
55107
55160
  // Private
55108
55161
  // ---------------------------------------------------------------------
@@ -58585,32 +58638,32 @@ stores.inject(MyMetaStore, storeInstance);
58585
58638
  }
58586
58639
  }
58587
58640
  convertOSClipboardData(clipboardData) {
58588
- const handlers = clipboardHandlersRegistries.figureHandlers
58589
- .getAll()
58590
- .map((handler) => new handler(this.getters, this.dispatch));
58591
- clipboardHandlersRegistries.cellHandlers
58592
- .getAll()
58593
- .forEach((handler) => handlers.push(new handler(this.getters, this.dispatch)));
58641
+ const handlers = this.selectClipboardHandlers({ figureId: true }).concat(this.selectClipboardHandlers({}));
58594
58642
  let copiedData = {};
58595
- for (const handler of handlers) {
58643
+ for (const { handlerName, handler } of handlers) {
58596
58644
  const data = handler.convertOSClipboardData(clipboardData);
58597
- copiedData = { ...copiedData, ...data };
58645
+ copiedData[handlerName] = data;
58646
+ const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
58647
+ for (const key of minimalKeys) {
58648
+ if (data && key in data) {
58649
+ copiedData[key] = data[key];
58650
+ }
58651
+ }
58598
58652
  }
58599
58653
  return copiedData;
58600
58654
  }
58601
58655
  selectClipboardHandlers(data) {
58602
- if ("figureId" in data) {
58603
- return clipboardHandlersRegistries.figureHandlers
58604
- .getAll()
58605
- .map((handler) => new handler(this.getters, this.dispatch));
58606
- }
58607
- return clipboardHandlersRegistries.cellHandlers
58608
- .getAll()
58609
- .map((handler) => new handler(this.getters, this.dispatch));
58656
+ const handlersRegistry = "figureId" in data
58657
+ ? clipboardHandlersRegistries.figureHandlers
58658
+ : clipboardHandlersRegistries.cellHandlers;
58659
+ return handlersRegistry.getKeys().map((handlerName) => {
58660
+ const Handler = handlersRegistry.get(handlerName);
58661
+ return { handlerName, handler: new Handler(this.getters, this.dispatch) };
58662
+ });
58610
58663
  }
58611
58664
  isCutAllowedOn(zones) {
58612
58665
  const clipboardData = this.getClipboardData(zones);
58613
- for (const handler of this.selectClipboardHandlers(clipboardData)) {
58666
+ for (const { handler } of this.selectClipboardHandlers(clipboardData)) {
58614
58667
  const result = handler.isCutAllowed(clipboardData);
58615
58668
  if (result !== "Success" /* CommandResult.Success */) {
58616
58669
  return result;
@@ -58619,7 +58672,7 @@ stores.inject(MyMetaStore, storeInstance);
58619
58672
  return "Success" /* CommandResult.Success */;
58620
58673
  }
58621
58674
  isPasteAllowed(target, copiedData, options) {
58622
- for (const handler of this.selectClipboardHandlers(copiedData)) {
58675
+ for (const { handler } of this.selectClipboardHandlers(copiedData)) {
58623
58676
  const result = handler.isPasteAllowed(this.getters.getActiveSheetId(), target, copiedData, {
58624
58677
  ...options,
58625
58678
  });
@@ -58647,9 +58700,15 @@ stores.inject(MyMetaStore, storeInstance);
58647
58700
  copy(zones) {
58648
58701
  let copiedData = {};
58649
58702
  const clipboardData = this.getClipboardData(zones);
58650
- for (const handler of this.selectClipboardHandlers(clipboardData)) {
58703
+ for (const { handlerName, handler } of this.selectClipboardHandlers(clipboardData)) {
58651
58704
  const data = handler.copy(clipboardData);
58652
- copiedData = { ...copiedData, ...data };
58705
+ copiedData[handlerName] = data;
58706
+ const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
58707
+ for (const key of minimalKeys) {
58708
+ if (data && key in data) {
58709
+ copiedData[key] = data[key];
58710
+ }
58711
+ }
58653
58712
  }
58654
58713
  return copiedData;
58655
58714
  }
@@ -58665,8 +58724,12 @@ stores.inject(MyMetaStore, storeInstance);
58665
58724
  zones,
58666
58725
  };
58667
58726
  const handlers = this.selectClipboardHandlers(copiedData);
58668
- for (const handler of handlers) {
58669
- const currentTarget = handler.getPasteTarget(sheetId, zones, copiedData, options);
58727
+ for (const { handlerName, handler } of handlers) {
58728
+ const handlerData = copiedData[handlerName];
58729
+ if (!handlerData) {
58730
+ continue;
58731
+ }
58732
+ const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
58670
58733
  if (currentTarget.figureId) {
58671
58734
  target.figureId = currentTarget.figureId;
58672
58735
  }
@@ -58682,7 +58745,12 @@ stores.inject(MyMetaStore, storeInstance);
58682
58745
  if (zone !== undefined) {
58683
58746
  this.addMissingDimensions(this.getters.getActiveSheetId(), zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
58684
58747
  }
58685
- handlers.forEach((handler) => handler.paste(target, copiedData, options));
58748
+ handlers.forEach(({ handlerName, handler }) => {
58749
+ const handlerData = copiedData[handlerName];
58750
+ if (handlerData) {
58751
+ handler.paste(target, handlerData, options);
58752
+ }
58753
+ });
58686
58754
  if (!options?.selectTarget) {
58687
58755
  return;
58688
58756
  }
@@ -66802,6 +66870,7 @@ stores.inject(MyMetaStore, storeInstance);
66802
66870
  const start = performance.now();
66803
66871
  console.group("Model creation");
66804
66872
  super();
66873
+ setDefaultTranslationMethod();
66805
66874
  stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
66806
66875
  const workbookData = load(data, verboseImport);
66807
66876
  this.state = new StateObserver();
@@ -67297,6 +67366,7 @@ stores.inject(MyMetaStore, storeInstance);
67297
67366
  pivotSidePanelRegistry,
67298
67367
  pivotNormalizationValueRegistry,
67299
67368
  supportedPivotPositionalFormulaRegistry,
67369
+ pivotToFunctionValueRegistry,
67300
67370
  };
67301
67371
  const helpers = {
67302
67372
  arg,
@@ -67342,7 +67412,6 @@ stores.inject(MyMetaStore, storeInstance);
67342
67412
  expandZoneOnInsertion,
67343
67413
  reduceZoneOnDeletion,
67344
67414
  unquote,
67345
- makePivotFormula,
67346
67415
  getMaxObjectId,
67347
67416
  getFunctionsFromTokens,
67348
67417
  getFirstPivotFunction,
@@ -67354,10 +67423,10 @@ stores.inject(MyMetaStore, storeInstance);
67354
67423
  insertTokenAfterLeftParenthesis,
67355
67424
  mergeContiguousZones,
67356
67425
  getPivotHighlights,
67357
- toPivotDomain,
67358
- flatPivotDomain,
67359
67426
  pivotTimeAdapter,
67360
67427
  UNDO_REDO_PIVOT_COMMANDS,
67428
+ createPivotFormula,
67429
+ areDomainArgsFieldsValid,
67361
67430
  };
67362
67431
  const links = {
67363
67432
  isMarkdownLink,
@@ -67488,9 +67557,9 @@ stores.inject(MyMetaStore, storeInstance);
67488
67557
  exports.tokenize = tokenize;
67489
67558
 
67490
67559
 
67491
- __info__.version = "17.4.0-alpha.7";
67492
- __info__.date = "2024-06-21T08:42:22.370Z";
67493
- __info__.hash = "a99db9a";
67560
+ __info__.version = "17.4.0-alpha.9";
67561
+ __info__.date = "2024-06-26T11:09:20.284Z";
67562
+ __info__.hash = "526be20";
67494
67563
 
67495
67564
 
67496
67565
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);