@odoo/o-spreadsheet 17.4.0-alpha.6 → 17.4.0-alpha.8

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.
@@ -1,11 +1,10 @@
1
1
 
2
- // @odoo-module ignore
3
2
  /**
4
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
4
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.4.0-alpha.6
7
- * @date 2024-06-19T13:46:27.157Z
8
- * @hash a4f22e4
5
+ * @version 17.4.0-alpha.8
6
+ * @date 2024-06-24T19:51:16.144Z
7
+ * @hash ccd30df
9
8
  */
10
9
 
11
10
  (function (exports, owl) {
@@ -1903,9 +1902,10 @@
1903
1902
  sortedValues[indexLow] * (indexSup - percentIndex));
1904
1903
  }
1905
1904
 
1906
- // define a mock translation function, when o-spreadsheet runs in standalone it doesn't translate any string
1907
- let _translate = (s) => s;
1908
- let _loaded = () => false;
1905
+ const defaultTranslate = (s) => s;
1906
+ const defaultLoaded = () => false;
1907
+ let _translate = defaultTranslate;
1908
+ let _loaded = defaultLoaded;
1909
1909
  function sprintf(s, ...values) {
1910
1910
  if (values.length === 1 && typeof values[0] === "object" && !(values[0] instanceof String)) {
1911
1911
  const valuesDict = values[0];
@@ -1917,7 +1917,8 @@
1917
1917
  return s;
1918
1918
  }
1919
1919
  /***
1920
- * 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.
1921
1922
  * @param tfn the function that will do the translation
1922
1923
  * @param loaded a function that returns true when the translation is loaded
1923
1924
  */
@@ -1925,6 +1926,18 @@
1925
1926
  _translate = tfn;
1926
1927
  _loaded = loaded;
1927
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
+ }
1928
1941
  const _t = function (s, ...values) {
1929
1942
  if (!_loaded()) {
1930
1943
  return new LazyTranslatedString(s, values);
@@ -5620,18 +5633,12 @@
5620
5633
  return { borders };
5621
5634
  }
5622
5635
  paste(target, content, options) {
5623
- if (!content) {
5624
- return;
5625
- }
5626
5636
  const sheetId = target.sheetId;
5627
- if (options?.pasteOption === "asValue") {
5628
- return;
5629
- }
5630
- if (!("borders" in content) || !("zones" in target) || !target.zones.length) {
5637
+ if (options.pasteOption === "asValue") {
5631
5638
  return;
5632
5639
  }
5633
5640
  const zones = target.zones;
5634
- if (!options?.isCutOperation) {
5641
+ if (!options.isCutOperation) {
5635
5642
  this.pasteFromCopy(sheetId, zones, content.borders);
5636
5643
  }
5637
5644
  else {
@@ -6142,6 +6149,530 @@
6142
6149
  return localizedFormula;
6143
6150
  }
6144
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
+
6145
6676
  class CellClipboardHandler extends AbstractCellClipboardHandler {
6146
6677
  isCutAllowed(data) {
6147
6678
  if (data.zones.length !== 1) {
@@ -6150,40 +6681,48 @@
6150
6681
  return "Success" /* CommandResult.Success */;
6151
6682
  }
6152
6683
  copy(data) {
6153
- if (!("zones" in data) || !data.zones.length) {
6154
- return;
6155
- }
6156
6684
  const sheetId = data.sheetId;
6157
- const zones = data.zones;
6158
- if (!zones.length) {
6159
- return {
6160
- cells: [[]],
6161
- zones: [],
6162
- sheetId,
6163
- };
6164
- }
6165
6685
  const { clippedZones, rowsIndexes, columnsIndexes } = data;
6166
6686
  const clippedCells = [];
6687
+ const isCopyingOneCell = rowsIndexes.length == 1 && columnsIndexes.length == 1;
6167
6688
  for (let row of rowsIndexes) {
6168
6689
  let cellsInRow = [];
6169
6690
  for (let col of columnsIndexes) {
6170
6691
  const position = { col, row, sheetId };
6171
- const spreader = this.getters.getArrayFormulaSpreadingOn(position);
6172
6692
  let cell = this.getters.getCell(position);
6173
6693
  const evaluatedCell = this.getters.getEvaluatedCell(position);
6174
- if (spreader && !deepEquals(spreader, position)) {
6175
- const isSpreaderCopied = rowsIndexes.includes(spreader.row) && columnsIndexes.includes(spreader.col);
6176
- const content = isSpreaderCopied
6177
- ? ""
6178
- : formatValue(evaluatedCell.value, { locale: this.getters.getLocale() });
6179
- cell = {
6180
- id: cell?.id || "",
6181
- style: cell?.style,
6182
- format: evaluatedCell.format,
6183
- content,
6184
- isFormula: false,
6185
- parsedValue: evaluatedCell.value,
6186
- };
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
+ }
6187
6726
  }
6188
6727
  cellsInRow.push({
6189
6728
  cell,
@@ -6232,12 +6771,9 @@
6232
6771
  * Paste the clipboard content in the given target
6233
6772
  */
6234
6773
  paste(target, content, options) {
6235
- if (!("cells" in content) || !("zones" in target) || !target.zones.length) {
6236
- return;
6237
- }
6238
6774
  const zones = target.zones;
6239
6775
  const sheetId = target.sheetId;
6240
- if (!options?.isCutOperation) {
6776
+ if (!options.isCutOperation) {
6241
6777
  this.pasteFromCopy(sheetId, zones, content.cells, options);
6242
6778
  }
6243
6779
  else {
@@ -6407,14 +6943,11 @@
6407
6943
  };
6408
6944
  }
6409
6945
  getPasteTarget(sheetId, target, content, options) {
6410
- if (!content?.copiedFigure || !content?.copiedChart) {
6411
- return { zones: [], sheetId };
6412
- }
6413
6946
  const newId = new UuidGenerator().uuidv4();
6414
6947
  return { zones: [], figureId: newId, sheetId };
6415
6948
  }
6416
6949
  paste(target, clippedContent, options) {
6417
- if (!clippedContent?.copiedFigure || !clippedContent?.copiedChart || !target.figureId) {
6950
+ if (!target.figureId) {
6418
6951
  return;
6419
6952
  }
6420
6953
  const { zones, figureId } = target;
@@ -6438,7 +6971,7 @@
6438
6971
  size: { height, width },
6439
6972
  definition: copy.getDefinition(),
6440
6973
  });
6441
- if (options?.isCutOperation) {
6974
+ if (options.isCutOperation) {
6442
6975
  this.dispatch("DELETE_FIGURE", {
6443
6976
  sheetId: clippedContent.copiedChart.sheetId,
6444
6977
  id: clippedContent.copiedFigure.id,
@@ -6480,15 +7013,12 @@
6480
7013
  return { cfRules };
6481
7014
  }
6482
7015
  paste(target, clippedContent, options) {
6483
- if (!clippedContent?.cfRules ||
6484
- options?.pasteOption === "asValue" ||
6485
- !("zones" in target) ||
6486
- !target.zones.length) {
7016
+ if (options.pasteOption === "asValue") {
6487
7017
  return;
6488
7018
  }
6489
7019
  const zones = target.zones;
6490
7020
  const sheetId = target.sheetId;
6491
- if (!options?.isCutOperation) {
7021
+ if (!options.isCutOperation) {
6492
7022
  this.pasteFromCopy(sheetId, zones, clippedContent.cfRules, options);
6493
7023
  }
6494
7024
  else {
@@ -6562,9 +7092,6 @@
6562
7092
  class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
6563
7093
  uuidGenerator = new UuidGenerator();
6564
7094
  copy(data) {
6565
- if (!data.zones.length) {
6566
- return;
6567
- }
6568
7095
  const { rowsIndexes, columnsIndexes } = data;
6569
7096
  const sheetId = data.sheetId;
6570
7097
  const dvRules = [];
@@ -6580,18 +7107,12 @@
6580
7107
  return { dvRules };
6581
7108
  }
6582
7109
  paste(target, clippedContent, options) {
6583
- if (!clippedContent?.dvRules) {
6584
- return;
6585
- }
6586
- if (options?.pasteOption) {
6587
- return;
6588
- }
6589
- if (!("zones" in target) || !target.zones.length) {
7110
+ if (options.pasteOption) {
6590
7111
  return;
6591
7112
  }
6592
7113
  const zones = target.zones;
6593
7114
  const sheetId = target.sheetId;
6594
- if (!options?.isCutOperation) {
7115
+ if (!options.isCutOperation) {
6595
7116
  this.pasteFromCopy(sheetId, zones, clippedContent.dvRules);
6596
7117
  }
6597
7118
  else {
@@ -6690,14 +7211,11 @@
6690
7211
  };
6691
7212
  }
6692
7213
  getPasteTarget(sheetId, target, content, options) {
6693
- if (!content?.copiedFigure || !content?.copiedImage) {
6694
- return { zones: [], sheetId };
6695
- }
6696
7214
  const newId = new UuidGenerator().uuidv4();
6697
7215
  return { sheetId, zones: [], figureId: newId };
6698
7216
  }
6699
7217
  paste(target, clippedContent, options) {
6700
- if (!clippedContent?.copiedFigure || !clippedContent?.copiedImage || !target.figureId) {
7218
+ if (!target.figureId) {
6701
7219
  return;
6702
7220
  }
6703
7221
  const { zones, figureId } = target;
@@ -6721,7 +7239,7 @@
6721
7239
  size: { height, width },
6722
7240
  definition: copy,
6723
7241
  });
6724
- if (options?.isCutOperation) {
7242
+ if (options.isCutOperation) {
6725
7243
  this.dispatch("DELETE_FIGURE", {
6726
7244
  sheetId: clippedContent.sheetId,
6727
7245
  id: clippedContent.copiedFigure.id,
@@ -6742,9 +7260,6 @@
6742
7260
 
6743
7261
  class MergeClipboardHandler extends AbstractCellClipboardHandler {
6744
7262
  copy(data) {
6745
- if (!data.zones.length) {
6746
- return;
6747
- }
6748
7263
  const sheetId = this.getters.getActiveSheetId();
6749
7264
  const { rowsIndexes, columnsIndexes } = data;
6750
7265
  const merges = [];
@@ -6762,10 +7277,7 @@
6762
7277
  * Paste the clipboard content in the given target
6763
7278
  */
6764
7279
  paste(target, content, options) {
6765
- if (!content.merges ||
6766
- options?.isCutOperation ||
6767
- !("zones" in target) ||
6768
- !target.zones.length) {
7280
+ if (options.isCutOperation) {
6769
7281
  return;
6770
7282
  }
6771
7283
  this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
@@ -6821,9 +7333,6 @@
6821
7333
  copy(data) {
6822
7334
  const sheetId = data.sheetId;
6823
7335
  const { rowsIndexes, columnsIndexes, zones } = data;
6824
- if (!zones || !rowsIndexes.length || !columnsIndexes.length) {
6825
- return { tableCells: [[]], sheetId };
6826
- }
6827
7336
  const copiedTablesIds = new Set();
6828
7337
  const tableCells = [];
6829
7338
  for (let row of rowsIndexes) {
@@ -6879,12 +7388,9 @@
6879
7388
  };
6880
7389
  }
6881
7390
  paste(target, content, options) {
6882
- if (!content || !content.tableCells) {
6883
- return;
6884
- }
6885
7391
  const zones = target.zones;
6886
7392
  const sheetId = target.sheetId;
6887
- if (!options?.isCutOperation) {
7393
+ if (!options.isCutOperation) {
6888
7394
  this.pasteFromCopy(sheetId, zones, content.tableCells, options);
6889
7395
  }
6890
7396
  else {
@@ -7777,486 +8283,6 @@
7777
8283
  };
7778
8284
  }
7779
8285
 
7780
- function boolAnd(args) {
7781
- let foundBoolean = false;
7782
- let acc = true;
7783
- conditionalVisitBoolean(args, (arg) => {
7784
- foundBoolean = true;
7785
- acc = acc && arg;
7786
- return acc;
7787
- });
7788
- return {
7789
- foundBoolean,
7790
- result: acc,
7791
- };
7792
- }
7793
- function boolOr(args) {
7794
- let foundBoolean = false;
7795
- let acc = false;
7796
- conditionalVisitBoolean(args, (arg) => {
7797
- foundBoolean = true;
7798
- acc = acc || arg;
7799
- return !acc;
7800
- });
7801
- return {
7802
- foundBoolean,
7803
- result: acc,
7804
- };
7805
- }
7806
-
7807
- function sum(values, locale) {
7808
- return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
7809
- }
7810
- function countUnique(args) {
7811
- return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
7812
- }
7813
-
7814
- function assertSameNumberOfElements(...args) {
7815
- const dims = args[0].length;
7816
- 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())));
7817
- }
7818
- function average(values, locale) {
7819
- let count = 0;
7820
- const sum = reduceNumbers(values, (acc, a) => {
7821
- count += 1;
7822
- return acc + a;
7823
- }, 0, locale);
7824
- assertNotZero(count);
7825
- return sum / count;
7826
- }
7827
- function countNumbers(values, locale) {
7828
- let count = 0;
7829
- for (let n of values) {
7830
- if (isMatrix(n)) {
7831
- for (let i of n) {
7832
- for (let j of i) {
7833
- if (typeof j.value === "number") {
7834
- count += 1;
7835
- }
7836
- }
7837
- }
7838
- }
7839
- else {
7840
- const value = n?.value;
7841
- if (!isEvaluationError(value) &&
7842
- (typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
7843
- count += 1;
7844
- }
7845
- }
7846
- }
7847
- return count;
7848
- }
7849
- function countAny(values) {
7850
- return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
7851
- }
7852
- function max(values, locale) {
7853
- const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
7854
- return result === -Infinity ? 0 : result;
7855
- }
7856
- function min(values, locale) {
7857
- const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
7858
- return result === Infinity ? 0 : result;
7859
- }
7860
-
7861
- const pivotTimeAdapterRegistry = new Registry();
7862
- function pivotTimeAdapter(granularity) {
7863
- return pivotTimeAdapterRegistry.get(granularity);
7864
- }
7865
- /**
7866
- * The Time Adapter: Managing Time Periods for Pivot Functions
7867
- *
7868
- * Overview:
7869
- * A time adapter is responsible for managing time periods associated with pivot functions.
7870
- * Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
7871
- * The adapter's primary role is to normalize period values between spreadsheet functions,
7872
- * and the pivot.
7873
- * By normalizing the period value, it can be stored consistently in the pivot.
7874
- *
7875
- * Normalization Process:
7876
- * When working with functions in the spreadsheet, the time adapter normalizes
7877
- * the provided period to facilitate accurate lookup of values in the pivot.
7878
- * For instance, if the spreadsheet function represents a day period as a number generated
7879
- * by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
7880
- *
7881
- */
7882
- /**
7883
- * Normalized value: "12/25/2023"
7884
- *
7885
- * Note: Those two format are equivalent:
7886
- * - "MM/dd/yyyy" (luxon format)
7887
- * - "mm/dd/yyyy" (spreadsheet format)
7888
- **/
7889
- const dayAdapter = {
7890
- normalizeFunctionValue(value) {
7891
- const date = toNumber(value, DEFAULT_LOCALE);
7892
- return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" });
7893
- },
7894
- getFormat(locale) {
7895
- return (locale ?? DEFAULT_LOCALE).dateFormat;
7896
- },
7897
- formatValue(normalizedValue, locale) {
7898
- locale = locale ?? DEFAULT_LOCALE;
7899
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
7900
- return formatValue(value, { locale, format: this.getFormat(locale) });
7901
- },
7902
- toCellValue(normalizedValue) {
7903
- return toNumber(normalizedValue, DEFAULT_LOCALE);
7904
- },
7905
- };
7906
- /**
7907
- * normalizes day of month number
7908
- */
7909
- const dayOfMonthAdapter = {
7910
- normalizeFunctionValue(value) {
7911
- const day = toNumber(value, DEFAULT_LOCALE);
7912
- if (day < 1 || day > 31) {
7913
- throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
7914
- }
7915
- return day;
7916
- },
7917
- getFormat() {
7918
- return "0";
7919
- },
7920
- formatValue(normalizedValue, locale) {
7921
- locale = locale ?? DEFAULT_LOCALE;
7922
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
7923
- return formatValue(value, { locale, format: this.getFormat(locale) });
7924
- },
7925
- toCellValue(normalizedValue) {
7926
- return toNumber(normalizedValue, DEFAULT_LOCALE);
7927
- },
7928
- };
7929
- /**
7930
- * Normalized value: "2/2023" for week 2 of 2023
7931
- */
7932
- const weekAdapter = {
7933
- normalizeFunctionValue(value) {
7934
- const [week, year] = value.split("/");
7935
- return `${Number(week)}/${Number(year)}`;
7936
- },
7937
- getFormat() {
7938
- return undefined;
7939
- },
7940
- formatValue(normalizedValue) {
7941
- const [week, year] = normalizedValue.split("/");
7942
- return _t("W%(week)s %(year)s", { week, year });
7943
- },
7944
- toCellValue(normalizedValue) {
7945
- return this.formatValue(normalizedValue);
7946
- },
7947
- };
7948
- /**
7949
- * normalizes iso week number
7950
- */
7951
- const isoWeekNumberAdapter = {
7952
- normalizeFunctionValue(value) {
7953
- const isoWeek = toNumber(value, DEFAULT_LOCALE);
7954
- if (isoWeek < 0 || isoWeek > 53) {
7955
- throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
7956
- }
7957
- return isoWeek;
7958
- },
7959
- getFormat() {
7960
- return "0";
7961
- },
7962
- formatValue(normalizedValue, locale) {
7963
- locale = locale ?? DEFAULT_LOCALE;
7964
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
7965
- return formatValue(value, { locale, format: this.getFormat(locale) });
7966
- },
7967
- toCellValue(normalizedValue) {
7968
- return toNumber(normalizedValue, DEFAULT_LOCALE);
7969
- },
7970
- };
7971
- /**
7972
- * normalized month value is a string formatted as "MM/yyyy" (luxon format)
7973
- * e.g. "01/2020" for January 2020
7974
- */
7975
- const monthAdapter = {
7976
- normalizeFunctionValue(value) {
7977
- const date = toNumber(value, DEFAULT_LOCALE);
7978
- return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
7979
- },
7980
- getFormat() {
7981
- return "mmmm yyyy";
7982
- },
7983
- formatValue(normalizedValue, locale) {
7984
- locale = locale ?? DEFAULT_LOCALE;
7985
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
7986
- return formatValue(value, { locale, format: this.getFormat(locale) });
7987
- },
7988
- toCellValue(normalizedValue) {
7989
- return toNumber(normalizedValue, DEFAULT_LOCALE);
7990
- },
7991
- };
7992
- /**
7993
- * normalizes month number
7994
- */
7995
- const monthNumberAdapter = {
7996
- normalizeFunctionValue(value) {
7997
- const month = toNumber(value, DEFAULT_LOCALE);
7998
- if (month < 1 || month > 12) {
7999
- throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
8000
- }
8001
- return month;
8002
- },
8003
- getFormat() {
8004
- return "0";
8005
- },
8006
- formatValue(normalizedValue, locale) {
8007
- locale = locale ?? DEFAULT_LOCALE;
8008
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
8009
- return formatValue(value, { locale, format: this.getFormat(locale) });
8010
- },
8011
- toCellValue(normalizedValue) {
8012
- return MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString();
8013
- },
8014
- };
8015
- /**
8016
- * normalized quarter value is "quarter/year"
8017
- * e.g. "1/2020" for Q1 2020
8018
- */
8019
- const quarterAdapter = {
8020
- normalizeFunctionValue(value) {
8021
- const [quarter, year] = value.split("/");
8022
- return `${quarter}/${year}`;
8023
- },
8024
- getFormat() {
8025
- return undefined;
8026
- },
8027
- formatValue(normalizedValue) {
8028
- const [quarter, year] = normalizedValue.split("/");
8029
- return _t("Q%(quarter)s %(year)s", { quarter, year });
8030
- },
8031
- toCellValue(normalizedValue) {
8032
- return this.formatValue(normalizedValue);
8033
- },
8034
- };
8035
- /**
8036
- * normalizes quarter number
8037
- */
8038
- const quarterNumberAdapter = {
8039
- normalizeFunctionValue(value) {
8040
- const quarter = toNumber(value, DEFAULT_LOCALE);
8041
- if (quarter < 1 || quarter > 4) {
8042
- throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
8043
- }
8044
- return quarter;
8045
- },
8046
- getFormat() {
8047
- return "0";
8048
- },
8049
- formatValue(normalizedValue, locale) {
8050
- locale = locale ?? DEFAULT_LOCALE;
8051
- const value = toNumber(normalizedValue, DEFAULT_LOCALE);
8052
- return formatValue(value, { locale, format: this.getFormat(locale) });
8053
- },
8054
- toCellValue(normalizedValue) {
8055
- return toNumber(normalizedValue, DEFAULT_LOCALE);
8056
- },
8057
- };
8058
- const yearAdapter = {
8059
- normalizeFunctionValue(value) {
8060
- return toNumber(value, DEFAULT_LOCALE);
8061
- },
8062
- getFormat() {
8063
- return "0";
8064
- },
8065
- formatValue(normalizedValue, locale) {
8066
- locale = locale ?? DEFAULT_LOCALE;
8067
- return formatValue(normalizedValue, { locale, format: "0" });
8068
- },
8069
- toCellValue(normalizedValue) {
8070
- return toNumber(normalizedValue, DEFAULT_LOCALE);
8071
- },
8072
- };
8073
- pivotTimeAdapterRegistry
8074
- .add("day", dayAdapter)
8075
- .add("week", weekAdapter)
8076
- .add("month", monthAdapter)
8077
- .add("quarter", quarterAdapter)
8078
- .add("year", yearAdapter)
8079
- .add("day_of_month", dayOfMonthAdapter)
8080
- .add("iso_week_number", isoWeekNumberAdapter)
8081
- .add("month_number", monthNumberAdapter)
8082
- .add("quarter_number", quarterNumberAdapter)
8083
- .add("year_number", yearAdapter);
8084
-
8085
- const AGGREGATOR_NAMES = {
8086
- count: _t("Count"),
8087
- count_distinct: _t("Count Distinct"),
8088
- bool_and: _t("Boolean And"),
8089
- bool_or: _t("Boolean Or"),
8090
- max: _t("Maximum"),
8091
- min: _t("Minimum"),
8092
- avg: _t("Average"),
8093
- sum: _t("Sum"),
8094
- };
8095
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8096
- const AGGREGATORS_BY_FIELD_TYPE = {
8097
- integer: NUMBER_CHAR_AGGREGATORS,
8098
- char: NUMBER_CHAR_AGGREGATORS,
8099
- boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8100
- };
8101
- const AGGREGATORS = {};
8102
- for (const type in AGGREGATORS_BY_FIELD_TYPE) {
8103
- AGGREGATORS[type] = {};
8104
- for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
8105
- AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
8106
- }
8107
- }
8108
- const AGGREGATORS_FN = {
8109
- count: {
8110
- fn: (args) => countAny([args]),
8111
- format: () => "0",
8112
- },
8113
- count_distinct: {
8114
- fn: (args) => countUnique([args]),
8115
- format: () => "0",
8116
- },
8117
- bool_and: {
8118
- fn: (args) => boolAnd([args]).result,
8119
- format: () => undefined,
8120
- },
8121
- bool_or: {
8122
- fn: (args) => boolOr([args]).result,
8123
- format: () => undefined,
8124
- },
8125
- max: {
8126
- fn: (args, locale) => max([args], locale),
8127
- format: inferFormat,
8128
- },
8129
- min: {
8130
- fn: (args, locale) => min([args], locale),
8131
- format: inferFormat,
8132
- },
8133
- avg: {
8134
- fn: (args, locale) => average([args], locale),
8135
- format: inferFormat,
8136
- },
8137
- sum: {
8138
- fn: (args, locale) => sum([args], locale),
8139
- format: inferFormat,
8140
- },
8141
- };
8142
- function makePivotFormulaFromPivotCell(pivotFormulaId, pivotCell) {
8143
- switch (pivotCell.type) {
8144
- case "HEADER":
8145
- return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
8146
- case "MEASURE_HEADER":
8147
- return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain), "measure", pivotCell.measure].filter(isDefined));
8148
- case "VALUE":
8149
- return makePivotFormula("PIVOT.VALUE", [pivotFormulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
8150
- case "EMPTY":
8151
- return "";
8152
- }
8153
- }
8154
- /**
8155
- * Build a pivot formula expression
8156
- */
8157
- function makePivotFormula(formula, args) {
8158
- return `=${formula}(${args
8159
- .map((arg) => {
8160
- const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
8161
- const convertToNumber = typeof arg == "number" || stringIsNumber;
8162
- return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
8163
- })
8164
- .join(",")})`;
8165
- }
8166
- /**
8167
- * Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
8168
- * in this object
8169
- * If the object has no keys, return 0
8170
- *
8171
- */
8172
- function getMaxObjectId(o) {
8173
- const keys = Object.keys(o);
8174
- if (!keys.length) {
8175
- return 0;
8176
- }
8177
- const nums = keys.map((id) => parseInt(id, 10));
8178
- const max = Math.max(...nums);
8179
- return max;
8180
- }
8181
- const ALL_PERIODS = {
8182
- year: _t("Year"),
8183
- quarter: _t("Quarter"),
8184
- month: _t("Month"),
8185
- week: _t("Week"),
8186
- day: _t("Day"),
8187
- year_number: _t("Year"),
8188
- quarter_number: _t("Quarter"),
8189
- month_number: _t("Month"),
8190
- iso_week_number: _t("Week"),
8191
- day_of_month: _t("Day of Month"),
8192
- };
8193
- const DATE_FIELDS = ["date", "datetime"];
8194
- /**
8195
- * Parse a dimension string into a pivot dimension definition.
8196
- * e.g "create_date:month" => { name: "create_date", granularity: "month" }
8197
- */
8198
- function parseDimension(dimension) {
8199
- const [name, granularity] = dimension.split(":");
8200
- if (granularity) {
8201
- return { name, granularity };
8202
- }
8203
- return { name };
8204
- }
8205
- function isDateField(field) {
8206
- return DATE_FIELDS.includes(field.type);
8207
- }
8208
- function toPivotDomain(domainStr) {
8209
- if (domainStr.length % 2 !== 0) {
8210
- throw new Error("Invalid domain: odd number of elements");
8211
- }
8212
- const domain = [];
8213
- for (let i = 0; i < domainStr.length - 1; i += 2) {
8214
- domain.push({ field: domainStr[i], value: domainStr[i + 1] });
8215
- }
8216
- return domain;
8217
- }
8218
- function flatPivotDomain(domain) {
8219
- return domain.flatMap((arg) => [arg.field, arg.value]);
8220
- }
8221
- /**
8222
- * Parses the value defining a pivot group in a PIVOT formula
8223
- * e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
8224
- * the two group values are "42" and "won".
8225
- */
8226
- function toNormalizedPivotValue(dimension, groupValue) {
8227
- if (groupValue === null || groupValue === "null") {
8228
- return null;
8229
- }
8230
- const groupValueString = typeof groupValue === "boolean"
8231
- ? toString(groupValue).toLocaleLowerCase()
8232
- : toString(groupValue);
8233
- if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
8234
- throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
8235
- field: dimension.displayName,
8236
- type: dimension.type,
8237
- }));
8238
- }
8239
- // represents a field which is not set (=False server side)
8240
- if (groupValueString === "false") {
8241
- return false;
8242
- }
8243
- const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
8244
- return normalizer(groupValueString, dimension.granularity);
8245
- }
8246
- function normalizeDateTime(value, granularity) {
8247
- if (!granularity) {
8248
- throw "";
8249
- }
8250
- return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
8251
- }
8252
- const pivotNormalizationValueRegistry = new Registry();
8253
- pivotNormalizationValueRegistry
8254
- .add("date", normalizeDateTime)
8255
- .add("datetime", normalizeDateTime)
8256
- .add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
8257
- .add("boolean", (value) => toBoolean(value))
8258
- .add("char", (value) => toString(value));
8259
-
8260
8286
  /**
8261
8287
  * Change the reference types inside the given token, if the token represent a range or a cell
8262
8288
  *
@@ -9996,7 +10022,7 @@ stores.inject(MyMetaStore, storeInstance);
9996
10022
  const cell = this.getters.getCell(position);
9997
10023
  if (pivotId && pivotCell.type !== "EMPTY" && !cell?.isFormula) {
9998
10024
  const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
9999
- const formula = makePivotFormulaFromPivotCell(formulaPivotId, pivotCell);
10025
+ const formula = createPivotFormula(formulaPivotId, pivotCell);
10000
10026
  return formula.slice(1); // strip leading =
10001
10027
  }
10002
10028
  }
@@ -19088,10 +19114,9 @@ stores.inject(MyMetaStore, storeInstance);
19088
19114
  compute: function (formulaId, measureName, ...domainArgs) {
19089
19115
  const _pivotFormulaId = toString(formulaId);
19090
19116
  const _measure = toString(measureName);
19091
- const _domainArgs = domainArgs.map(toString);
19092
19117
  const pivotId = getPivotId(_pivotFormulaId, this.getters);
19093
19118
  assertMeasureExist(pivotId, _measure, this.getters);
19094
- assertDomainLength(_domainArgs);
19119
+ assertDomainLength(domainArgs);
19095
19120
  const pivot = this.getters.getPivot(pivotId);
19096
19121
  const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
19097
19122
  addPivotDependencies(this, coreDefinition);
@@ -19099,15 +19124,14 @@ stores.inject(MyMetaStore, storeInstance);
19099
19124
  if (error) {
19100
19125
  return error;
19101
19126
  }
19102
- const domain = toPivotDomain(_domainArgs);
19103
- const { value, format } = pivot.getPivotCellValueAndFormat(_measure, domain);
19104
- if (!value && !this.getters.areDomainArgsFieldsValid(pivotId, domain)) {
19127
+ if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
19105
19128
  return {
19106
19129
  value: CellErrorType.GenericError,
19107
19130
  message: _t("Dimensions don't match the pivot definition"),
19108
19131
  };
19109
19132
  }
19110
- return { value, format };
19133
+ const domain = pivot.parseArgsToPivotDomain(domainArgs);
19134
+ return pivot.getPivotCellValueAndFormat(_measure, domain);
19111
19135
  },
19112
19136
  };
19113
19137
  const PIVOT_HEADER = {
@@ -19119,9 +19143,8 @@ stores.inject(MyMetaStore, storeInstance);
19119
19143
  ],
19120
19144
  compute: function (pivotId, ...domainArgs) {
19121
19145
  const _pivotFormulaId = toString(pivotId);
19122
- const _domainArgs = domainArgs.map(toString);
19123
19146
  const _pivotId = getPivotId(_pivotFormulaId, this.getters);
19124
- assertDomainLength(_domainArgs);
19147
+ assertDomainLength(domainArgs);
19125
19148
  const pivot = this.getters.getPivot(_pivotId);
19126
19149
  const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
19127
19150
  addPivotDependencies(this, coreDefinition);
@@ -19129,14 +19152,14 @@ stores.inject(MyMetaStore, storeInstance);
19129
19152
  if (error) {
19130
19153
  return error;
19131
19154
  }
19132
- const domain = toPivotDomain(_domainArgs);
19133
- const lastNode = domain.at(-1);
19134
- if (!this.getters.areDomainArgsFieldsValid(_pivotId, lastNode?.field === "measure" ? domain.slice(0, -1) : domain)) {
19155
+ if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
19135
19156
  return {
19136
19157
  value: CellErrorType.GenericError,
19137
19158
  message: _t("Dimensions don't match the pivot definition"),
19138
19159
  };
19139
19160
  }
19161
+ const domain = pivot.parseArgsToPivotDomain(domainArgs);
19162
+ const lastNode = domain.at(-1);
19140
19163
  if (lastNode?.field === "measure") {
19141
19164
  return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
19142
19165
  }
@@ -19153,13 +19176,21 @@ stores.inject(MyMetaStore, storeInstance);
19153
19176
  description: _t("Get a pivot table."),
19154
19177
  args: [
19155
19178
  arg("pivot_id (string)", _t("ID of the pivot.")),
19156
- arg("row_count (number, optional, default=10000)", _t("number of rows")),
19179
+ arg("row_count (number, optional)", _t("number of rows")),
19157
19180
  arg("include_total (boolean, default=TRUE)", _t("Whether to include total/sub-totals or not.")),
19158
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")),
19159
19183
  ],
19160
- 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 }) {
19161
19185
  const _pivotFormulaId = toString(pivotFormulaId);
19162
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
+ }
19163
19194
  const _includeColumnHeaders = toBoolean(includeColumnHeaders);
19164
19195
  const _includedTotal = toBoolean(includeTotal);
19165
19196
  const pivotId = getPivotId(_pivotFormulaId, this.getters);
@@ -19175,19 +19206,15 @@ stores.inject(MyMetaStore, storeInstance);
19175
19206
  const cells = table.getPivotCells(_includedTotal, _includeColumnHeaders);
19176
19207
  const headerRows = _includeColumnHeaders ? table.columns.length : 0;
19177
19208
  const pivotTitle = this.getters.getPivotDisplayName(pivotId);
19178
- if (_rowCount < 0) {
19179
- throw new EvaluationError(_t("The number of rows must be positive."));
19180
- }
19181
- const end = Math.min(headerRows + _rowCount, cells[0].length);
19182
- if (end === 0) {
19209
+ const tableHeight = Math.min(headerRows + _rowCount, cells[0].length);
19210
+ if (tableHeight === 0) {
19183
19211
  return [[{ value: pivotTitle }]];
19184
19212
  }
19185
- const tableWidth = cells.length;
19186
- const tableRows = range(0, end);
19213
+ const tableWidth = Math.min(1 + _columnCount, cells.length);
19187
19214
  const result = [];
19188
19215
  for (const col of range(0, tableWidth)) {
19189
19216
  result[col] = [];
19190
- for (const row of tableRows) {
19217
+ for (const row of range(0, tableHeight)) {
19191
19218
  const pivotCell = cells[col][row];
19192
19219
  switch (pivotCell.type) {
19193
19220
  case "EMPTY":
@@ -21877,9 +21904,6 @@ stores.inject(MyMetaStore, storeInstance);
21877
21904
  const pivot = this.getters.getPivot(pivotId);
21878
21905
  pivot.init();
21879
21906
  const fields = pivot.getFields();
21880
- if (!fields) {
21881
- return [];
21882
- }
21883
21907
  const definition = this.getters.getPivotCoreDefinition(pivotId);
21884
21908
  return definition.measures
21885
21909
  .map((measure) => {
@@ -21919,9 +21943,6 @@ stores.inject(MyMetaStore, storeInstance);
21919
21943
  const pivot = this.getters.getPivot(pivotId);
21920
21944
  pivot.init();
21921
21945
  const fields = pivot.getFields();
21922
- if (!fields) {
21923
- return;
21924
- }
21925
21946
  const { columns, rows } = pivot.definition;
21926
21947
  let args = functionContext.args;
21927
21948
  if (functionContext?.parent.toUpperCase() === "PIVOT.VALUE") {
@@ -27070,7 +27091,7 @@ stores.inject(MyMetaStore, storeInstance);
27070
27091
  if (getZoneArea(zone) === 1 && topLeftCell?.content) {
27071
27092
  return {
27072
27093
  type: "scorecard",
27073
- title: { text: "" },
27094
+ title: {},
27074
27095
  background: topLeftCell.style?.fillColor || undefined,
27075
27096
  keyValue: zoneToXc(zone),
27076
27097
  baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
@@ -27078,22 +27099,11 @@ stores.inject(MyMetaStore, storeInstance);
27078
27099
  baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
27079
27100
  };
27080
27101
  }
27081
- let title = "";
27082
27102
  const cellsInFirstRow = getters.getEvaluatedCellsInZone(sheetId, {
27083
27103
  ...dataSetZone,
27084
27104
  bottom: dataSetZone.top,
27085
27105
  });
27086
27106
  const dataSetsHaveTitle = !!cellsInFirstRow.find((cell) => cell.type !== CellValueType.empty && cell.type !== CellValueType.number);
27087
- if (dataSetsHaveTitle) {
27088
- const texts = cellsInFirstRow
27089
- .filter((cell) => cell.type !== CellValueType.error && cell.type !== CellValueType.empty)
27090
- .map((cell) => cell.formattedValue);
27091
- const lastElement = texts.splice(-1)[0];
27092
- title = texts.join(", ");
27093
- if (lastElement) {
27094
- title += (title ? " " + _t("and") + " " : "") + lastElement;
27095
- }
27096
- }
27097
27107
  let labelRangeXc;
27098
27108
  if (!singleColumn) {
27099
27109
  labelRangeXc = zoneToXc({
@@ -27106,7 +27116,7 @@ stores.inject(MyMetaStore, storeInstance);
27106
27116
  const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
27107
27117
  if (canChartParseLabels(labelRange, getters)) {
27108
27118
  return {
27109
- title: { text: title },
27119
+ title: {},
27110
27120
  dataSets,
27111
27121
  labelsAsText: false,
27112
27122
  stacked: false,
@@ -27122,7 +27132,7 @@ stores.inject(MyMetaStore, storeInstance);
27122
27132
  if (singleColumn &&
27123
27133
  getData(getters, _dataSets[0]).every((e) => typeof e === "string" && !isEvaluationError(e))) {
27124
27134
  return {
27125
- title: { text: "" },
27135
+ title: {},
27126
27136
  dataSets: [{ dataRange }],
27127
27137
  aggregated: true,
27128
27138
  labelRange: dataRange,
@@ -27132,7 +27142,7 @@ stores.inject(MyMetaStore, storeInstance);
27132
27142
  };
27133
27143
  }
27134
27144
  return {
27135
- title: { text: title },
27145
+ title: {},
27136
27146
  dataSets,
27137
27147
  labelRange: labelRangeXc,
27138
27148
  type: "bar",
@@ -34555,6 +34565,7 @@ stores.inject(MyMetaStore, storeInstance);
34555
34565
  currentSearchRegex = null;
34556
34566
  isSearchDirty = false;
34557
34567
  initialShowFormulaState;
34568
+ preserveSelectedMatchIndex = false;
34558
34569
  // fixme: why do we make selectedMatchIndex on top of a selected
34559
34570
  // property in the matches?
34560
34571
  selectedMatchIndex = null;
@@ -34627,6 +34638,10 @@ stores.inject(MyMetaStore, storeInstance);
34627
34638
  case "ACTIVATE_SHEET":
34628
34639
  this.isSearchDirty = true;
34629
34640
  break;
34641
+ case "REPLACE_SEARCH":
34642
+ for (const match of cmd.matches) {
34643
+ this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
34644
+ }
34630
34645
  }
34631
34646
  }
34632
34647
  finalize() {
@@ -34664,7 +34679,9 @@ stores.inject(MyMetaStore, storeInstance);
34664
34679
  * refresh the matches according to the current search options
34665
34680
  */
34666
34681
  refreshSearch(jumpToMatchSheet = true) {
34667
- this.selectedMatchIndex = null;
34682
+ if (!this.preserveSelectedMatchIndex) {
34683
+ this.selectedMatchIndex = null;
34684
+ }
34668
34685
  this.findMatches();
34669
34686
  this.selectNextCell(Direction.current, jumpToMatchSheet);
34670
34687
  }
@@ -34763,10 +34780,16 @@ stores.inject(MyMetaStore, storeInstance);
34763
34780
  const selectedMatch = matches[nextIndex];
34764
34781
  // Switch to the sheet where the match is located
34765
34782
  if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
34783
+ // We set `preserveSelectedMatchIndex` to true to avoid resetting the selected search
34784
+ // index in the `refreshSearch` function when a new sheet is activated. The reason being
34785
+ // that, when we automatically go back to previous sheet while performing a search, the
34786
+ // search index is reset to the first occurrence each time.
34787
+ this.preserveSelectedMatchIndex = true;
34766
34788
  this.model.dispatch("ACTIVATE_SHEET", {
34767
34789
  sheetIdFrom: this.getters.getActiveSheetId(),
34768
34790
  sheetIdTo: selectedMatch.sheetId,
34769
34791
  });
34792
+ this.preserveSelectedMatchIndex = false;
34770
34793
  // We do not want to reset the selection at finalize in this case
34771
34794
  this.isSearchDirty = false;
34772
34795
  }
@@ -34800,6 +34823,21 @@ stores.inject(MyMetaStore, storeInstance);
34800
34823
  searchOptions: this.searchOptions,
34801
34824
  });
34802
34825
  }
34826
+ replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
34827
+ const cell = this.getters.getCell(selectedMatch);
34828
+ if (!cell?.content) {
34829
+ return;
34830
+ }
34831
+ if (cell?.isFormula && !searchOptions.searchFormulas) {
34832
+ return;
34833
+ }
34834
+ const searchRegex = getSearchRegex(searchString, searchOptions);
34835
+ const replaceRegex = new RegExp(searchRegex.source, searchRegex.flags + "g");
34836
+ const toReplace = this.getters.getCellText(selectedMatch, searchOptions.searchFormulas);
34837
+ const content = toReplace.replace(replaceRegex, replaceWith);
34838
+ const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
34839
+ this.model.dispatch("UPDATE_CELL", { ...selectedMatch, content: canonicalContent });
34840
+ }
34803
34841
  getSearchableString(position) {
34804
34842
  return this.getters.getCellText(position, this.searchOptions.searchFormulas);
34805
34843
  }
@@ -35392,6 +35430,10 @@ stores.inject(MyMetaStore, storeInstance);
35392
35430
  }
35393
35431
  });
35394
35432
  }
35433
+ onClick(item) {
35434
+ item.onClick();
35435
+ this.popover.isOpen = false;
35436
+ }
35395
35437
  get popoverProps() {
35396
35438
  const { x, y, width, height } = this.buttonRef.el.getBoundingClientRect();
35397
35439
  return {
@@ -35434,16 +35476,22 @@ stores.inject(MyMetaStore, storeInstance);
35434
35476
  static components = { CogWheelMenu, Section, EditableName };
35435
35477
  static props = {
35436
35478
  pivotId: String,
35479
+ flipAxis: Function,
35437
35480
  };
35438
35481
  get cogWheelMenuItems() {
35439
35482
  return [
35440
35483
  {
35441
- name: "Duplicate",
35484
+ name: _t("Flip axes"),
35485
+ icon: "fa-exchange",
35486
+ onClick: this.props.flipAxis,
35487
+ },
35488
+ {
35489
+ name: _t("Duplicate"),
35442
35490
  icon: "fa-copy",
35443
35491
  onClick: () => this.duplicatePivot(),
35444
35492
  },
35445
35493
  {
35446
- name: "Delete",
35494
+ name: _t("Delete"),
35447
35495
  icon: "fa-trash",
35448
35496
  onClick: () => this.delete(),
35449
35497
  },
@@ -35551,7 +35599,7 @@ stores.inject(MyMetaStore, storeInstance);
35551
35599
  function createPivotDimension(fields, dimension) {
35552
35600
  const field = fields[dimension.name];
35553
35601
  const type = field?.type ?? "integer";
35554
- const granularity = field && isDateField(field) ? dimension.granularity ?? "month_number" : undefined;
35602
+ const granularity = field && isDateField(field) ? dimension.granularity : undefined;
35555
35603
  return {
35556
35604
  /**
35557
35605
  * Get the display name of the dimension
@@ -35640,9 +35688,10 @@ stores.inject(MyMetaStore, storeInstance);
35640
35688
  columns;
35641
35689
  rows;
35642
35690
  measures;
35691
+ fieldsType;
35643
35692
  maxIndent;
35644
35693
  pivotCells = {};
35645
- constructor(columns, rows, measures) {
35694
+ constructor(columns, rows, measures, fieldsType) {
35646
35695
  this.columns = columns.map((row) => {
35647
35696
  // offset in the pivot table
35648
35697
  // starts at 1 because the first column is the row title
@@ -35655,6 +35704,7 @@ stores.inject(MyMetaStore, storeInstance);
35655
35704
  });
35656
35705
  this.rows = rows;
35657
35706
  this.measures = measures;
35707
+ this.fieldsType = fieldsType;
35658
35708
  this.maxIndent = Math.max(...this.rows.map((row) => row.indent));
35659
35709
  }
35660
35710
  /**
@@ -35701,7 +35751,7 @@ stores.inject(MyMetaStore, storeInstance);
35701
35751
  if (!domain) {
35702
35752
  return EMPTY_PIVOT_CELL;
35703
35753
  }
35704
- const measure = domain.at(-1)?.value.toString() || "";
35754
+ const measure = domain.at(-1)?.value?.toString() || "";
35705
35755
  return { type: "MEASURE_HEADER", domain: domain.slice(0, -1), measure };
35706
35756
  }
35707
35757
  else if (row <= colHeadersHeight - 1) {
@@ -35733,9 +35783,13 @@ stores.inject(MyMetaStore, storeInstance);
35733
35783
  return undefined;
35734
35784
  }
35735
35785
  for (let i = 0; i < pivotCol.fields.length; i++) {
35786
+ const fieldWithGranularity = pivotCol.fields[i];
35787
+ const { name, granularity } = parseDimension(fieldWithGranularity);
35788
+ const type = this.fieldsType[name] || "char";
35736
35789
  domain.push({
35737
- field: pivotCol.fields[i],
35738
- value: pivotCol.values[i],
35790
+ type,
35791
+ field: fieldWithGranularity,
35792
+ value: toNormalizedPivotValue({ displayName: name, type, granularity }, pivotCol.values[i]),
35739
35793
  });
35740
35794
  }
35741
35795
  return domain;
@@ -35747,17 +35801,21 @@ stores.inject(MyMetaStore, storeInstance);
35747
35801
  getColMeasure(col) {
35748
35802
  const domain = this.getColHeaderDomain(col, this.columns.length - 1);
35749
35803
  const measure = domain?.at(-1)?.value;
35750
- if (measure === undefined) {
35751
- throw new Error("Measure isd missing");
35804
+ if (measure === undefined || measure === null) {
35805
+ throw new Error("Measure is missing");
35752
35806
  }
35753
35807
  return measure.toString();
35754
35808
  }
35755
35809
  getRowDomain(row) {
35756
35810
  const domain = [];
35757
35811
  for (let i = 0; i < this.rows[row].fields.length; i++) {
35812
+ const fieldWithGranularity = this.rows[row].fields[i];
35813
+ const { name, granularity } = parseDimension(fieldWithGranularity);
35814
+ const type = this.fieldsType[name] || "char";
35758
35815
  domain.push({
35759
- field: this.rows[row].fields[i],
35760
- value: this.rows[row].values[i],
35816
+ type,
35817
+ field: fieldWithGranularity,
35818
+ value: toNormalizedPivotValue({ displayName: name, type, granularity }, this.rows[row].values[i]),
35761
35819
  });
35762
35820
  }
35763
35821
  return domain;
@@ -35767,6 +35825,7 @@ stores.inject(MyMetaStore, storeInstance);
35767
35825
  cols: this.columns,
35768
35826
  rows: this.rows,
35769
35827
  measures: this.measures,
35828
+ fieldsType: this.fieldsType,
35770
35829
  };
35771
35830
  }
35772
35831
  }
@@ -35787,7 +35846,14 @@ stores.inject(MyMetaStore, storeInstance);
35787
35846
  indent: 0,
35788
35847
  });
35789
35848
  const measureNames = definition.measures.map((m) => m.name);
35790
- return new SpreadsheetPivotTable(cols, rows, measureNames);
35849
+ const fieldsType = {};
35850
+ for (const columns of definition.columns) {
35851
+ fieldsType[columns.name] = columns.type;
35852
+ }
35853
+ for (const row of definition.rows) {
35854
+ fieldsType[row.name] = row.type;
35855
+ }
35856
+ return new SpreadsheetPivotTable(cols, rows, measureNames, fieldsType);
35791
35857
  }
35792
35858
  // -----------------------------------------------------------------------------
35793
35859
  // ROWS
@@ -35963,41 +36029,42 @@ stores.inject(MyMetaStore, storeInstance);
35963
36029
  return dimension.order === "asc" ? a.localeCompare(b) : b.localeCompare(a);
35964
36030
  }
35965
36031
 
36032
+ const NULL_SYMBOL = Symbol("NULL");
35966
36033
  function createDate(dimension, value, locale) {
35967
- const granularity = dimension.granularity || "month_number";
35968
- if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
36034
+ const granularity = dimension.granularity;
36035
+ if (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
35969
36036
  throw new Error(`Unknown date granularity: ${granularity}`);
35970
36037
  }
35971
- if (value === null) {
35972
- return null;
35973
- }
36038
+ const keyInMap = typeof value === "number" || typeof value === "string" ? value : NULL_SYMBOL;
35974
36039
  if (!MAP_VALUE_DIMENSION_DATE[granularity].set.has(value)) {
35975
36040
  MAP_VALUE_DIMENSION_DATE[granularity].set.add(value);
35976
- const date = toJsDate(value, locale);
35977
- let number = 0;
35978
- switch (granularity) {
35979
- case "year_number":
35980
- number = date.getFullYear();
35981
- break;
35982
- case "quarter_number":
35983
- number = Math.floor(date.getMonth() / 3) + 1;
35984
- break;
35985
- case "month_number":
35986
- number = date.getMonth() + 1;
35987
- break;
35988
- case "iso_week_number":
35989
- number = date.getIsoWeek();
35990
- break;
35991
- case "day_of_month":
35992
- number = date.getDate();
35993
- break;
35994
- case "day":
35995
- number = Math.floor(toNumber(value, locale));
35996
- break;
36041
+ let number = null;
36042
+ if (typeof value === "number" || typeof value === "string") {
36043
+ const date = toJsDate(value, locale);
36044
+ switch (granularity) {
36045
+ case "year_number":
36046
+ number = date.getFullYear();
36047
+ break;
36048
+ case "quarter_number":
36049
+ number = Math.floor(date.getMonth() / 3) + 1;
36050
+ break;
36051
+ case "month_number":
36052
+ number = date.getMonth() + 1;
36053
+ break;
36054
+ case "iso_week_number":
36055
+ number = date.getIsoWeek();
36056
+ break;
36057
+ case "day_of_month":
36058
+ number = date.getDate();
36059
+ break;
36060
+ case "day":
36061
+ number = Math.floor(toNumber(value, locale));
36062
+ break;
36063
+ }
35997
36064
  }
35998
- MAP_VALUE_DIMENSION_DATE[granularity].values[`${value}`] = toNormalizedPivotValue(dimension, number);
36065
+ MAP_VALUE_DIMENSION_DATE[granularity].values[keyInMap] = toNormalizedPivotValue(dimension, number);
35999
36066
  }
36000
- return MAP_VALUE_DIMENSION_DATE[granularity].values[`${value}`];
36067
+ return MAP_VALUE_DIMENSION_DATE[granularity].values[keyInMap];
36001
36068
  }
36002
36069
  /**
36003
36070
  * This map is used to cache the different values of a pivot date value
@@ -36179,6 +36246,25 @@ stores.inject(MyMetaStore, storeInstance);
36179
36246
  }
36180
36247
  return undefined;
36181
36248
  }
36249
+ areDomainArgsFieldsValid(args) {
36250
+ let dimensions = args.filter((_, index) => index % 2 === 0).map(toString);
36251
+ if (dimensions.length && dimensions.at(-1) === "measure") {
36252
+ dimensions = dimensions.slice(0, -1);
36253
+ }
36254
+ return areDomainArgsFieldsValid(dimensions, this.definition);
36255
+ }
36256
+ parseArgsToPivotDomain(args) {
36257
+ const domain = [];
36258
+ for (let i = 0; i < args.length - 1; i += 2) {
36259
+ const fieldWithGranularity = toString(args[i]);
36260
+ const type = this.getTypeOfDimension(fieldWithGranularity);
36261
+ const normalizedValue = fieldWithGranularity === "measure"
36262
+ ? toString(args[i + 1])
36263
+ : toNormalizedPivotValue(this.getDimension(fieldWithGranularity), args[i + 1]);
36264
+ domain.push({ field: fieldWithGranularity, value: normalizedValue, type });
36265
+ }
36266
+ return domain;
36267
+ }
36182
36268
  markAsDirtyForEvaluation() {
36183
36269
  this.needsReevaluation = true;
36184
36270
  }
@@ -36200,18 +36286,12 @@ stores.inject(MyMetaStore, storeInstance);
36200
36286
  const finalCell = cells[0]?.[dimension.nameWithGranularity];
36201
36287
  if (dimension.type === "date") {
36202
36288
  const adapter = pivotTimeAdapter(dimension.granularity);
36203
- return {
36204
- value: lastNode.value !== "null"
36205
- ? adapter.toCellValue(toNormalizedPivotValue(dimension, lastNode.value))
36206
- : _t("(Undefined)"),
36207
- format: adapter.getFormat(this.getters.getLocale()),
36208
- };
36289
+ return adapter.toValueAndFormat(lastNode.value, this.getters.getLocale());
36209
36290
  }
36210
36291
  if (!finalCell) {
36211
36292
  return { value: "" };
36212
36293
  }
36213
- // Value can be null but stringified (e.g. an empty date, as for now every date is stringified)
36214
- if (finalCell.value === null || finalCell.value === `${null}`) {
36294
+ if (finalCell.value === null) {
36215
36295
  return { value: _t("(Undefined)") };
36216
36296
  }
36217
36297
  return {
@@ -36262,14 +36342,24 @@ stores.inject(MyMetaStore, storeInstance);
36262
36342
  getFields() {
36263
36343
  return this.fields;
36264
36344
  }
36345
+ getTypeOfDimension(fieldWithGranularity) {
36346
+ if (fieldWithGranularity === "measure") {
36347
+ return "char";
36348
+ }
36349
+ const { name } = parseDimension(fieldWithGranularity);
36350
+ const type = this.fields[name]?.type;
36351
+ if (!type) {
36352
+ throw new Error(`Field ${name} does not exist`);
36353
+ }
36354
+ return type;
36355
+ }
36265
36356
  filterDataEntriesFromDomain(dataEntries, domain) {
36266
36357
  return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
36267
36358
  }
36268
36359
  filterDataEntriesFromDomainNode(dataEntries, domain) {
36269
36360
  const { field, value } = domain;
36270
- const dimension = this.getDimension(field);
36271
- return dataEntries.filter((entry) => `${entry[dimension.nameWithGranularity]?.value}` ===
36272
- `${toNormalizedPivotValue(dimension, value)}`);
36361
+ const { nameWithGranularity } = this.getDimension(field);
36362
+ return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
36273
36363
  }
36274
36364
  getDimension(nameWithGranularity) {
36275
36365
  return this.definition.getDimension(nameWithGranularity);
@@ -36374,7 +36464,7 @@ stores.inject(MyMetaStore, storeInstance);
36374
36464
  for (const entry of dataEntries) {
36375
36465
  for (const dimension of dateDimensions) {
36376
36466
  entry[dimension.nameWithGranularity] = {
36377
- value: `${createDate(dimension, entry[dimension.name]?.value || null, this.getters.getLocale())}`,
36467
+ value: createDate(dimension, entry[dimension.name]?.value || null, this.getters.getLocale()),
36378
36468
  type: entry[dimension.name]?.type || CellValueType.empty,
36379
36469
  format: entry[dimension.name]?.format,
36380
36470
  };
@@ -36421,11 +36511,7 @@ stores.inject(MyMetaStore, storeInstance);
36421
36511
  }
36422
36512
  }
36423
36513
  get fields() {
36424
- const fields = this.pivot.getFields();
36425
- if (!fields) {
36426
- throw new Error("Fields not found");
36427
- }
36428
- return fields;
36514
+ return this.pivot.getFields();
36429
36515
  }
36430
36516
  get pivot() {
36431
36517
  return this.getters.getPivot(this.pivotId);
@@ -36659,6 +36745,13 @@ stores.inject(MyMetaStore, storeInstance);
36659
36745
  this.store.applyUpdate();
36660
36746
  }
36661
36747
  }
36748
+ flipAxis() {
36749
+ const { rows, columns } = this.definition;
36750
+ this.onDimensionsUpdated({
36751
+ rows: columns,
36752
+ columns: rows,
36753
+ });
36754
+ }
36662
36755
  onDimensionsUpdated(definition) {
36663
36756
  this.store.update(definition);
36664
36757
  }
@@ -51530,8 +51623,8 @@ stores.inject(MyMetaStore, storeInstance);
51530
51623
  case "INSERT_PIVOT": {
51531
51624
  const { sheetId, col, row, pivotId, table } = cmd;
51532
51625
  const position = { sheetId, col, row };
51533
- const { cols, rows, measures } = table;
51534
- const spTable = new SpreadsheetPivotTable(cols, rows, measures);
51626
+ const { cols, rows, measures, fieldsType } = table;
51627
+ const spTable = new SpreadsheetPivotTable(cols, rows, measures, fieldsType || {});
51535
51628
  const formulaId = this.getPivotFormulaId(pivotId);
51536
51629
  this.insertPivot(position, formulaId, spTable);
51537
51630
  break;
@@ -51612,7 +51705,7 @@ stores.inject(MyMetaStore, storeInstance);
51612
51705
  sheetId: position.sheetId,
51613
51706
  col: position.col + col,
51614
51707
  row: position.row + row,
51615
- content: makePivotFormulaFromPivotCell(formulaId, pivotCell),
51708
+ content: createPivotFormula(formulaId, pivotCell),
51616
51709
  });
51617
51710
  }
51618
51711
  }
@@ -54878,7 +54971,6 @@ stores.inject(MyMetaStore, storeInstance);
54878
54971
  "getPivotIdFromPosition",
54879
54972
  "getPivotCellFromPosition",
54880
54973
  "isPivotUnused",
54881
- "areDomainArgsFieldsValid",
54882
54974
  "isSpillPivotFormula",
54883
54975
  ];
54884
54976
  pivots = {};
@@ -55010,19 +55102,19 @@ stores.inject(MyMetaStore, storeInstance);
55010
55102
  return EMPTY_PIVOT_CELL;
55011
55103
  }
55012
55104
  const { functionName, args } = result;
55105
+ const formulaId = args[0];
55106
+ if (!formulaId) {
55107
+ return EMPTY_PIVOT_CELL;
55108
+ }
55109
+ const pivotId = this.getters.getPivotId(formulaId.toString());
55110
+ if (!pivotId) {
55111
+ return EMPTY_PIVOT_CELL;
55112
+ }
55113
+ const pivot = this.getPivot(pivotId);
55114
+ if (!pivot.isValid()) {
55115
+ return EMPTY_PIVOT_CELL;
55116
+ }
55013
55117
  if (functionName === "PIVOT") {
55014
- const formulaId = args[0];
55015
- if (!formulaId) {
55016
- return EMPTY_PIVOT_CELL;
55017
- }
55018
- const pivotId = this.getters.getPivotId(formulaId.toString());
55019
- if (!pivotId) {
55020
- return EMPTY_PIVOT_CELL;
55021
- }
55022
- const pivot = this.getPivot(pivotId);
55023
- if (!pivot.isValid()) {
55024
- return EMPTY_PIVOT_CELL;
55025
- }
55026
55118
  const includeTotal = args[2] === false ? false : undefined;
55027
55119
  const includeColumnHeaders = args[3] === false ? false : undefined;
55028
55120
  const pivotCells = pivot
@@ -55033,7 +55125,7 @@ stores.inject(MyMetaStore, storeInstance);
55033
55125
  return pivotCells[pivotCol][pivotRow];
55034
55126
  }
55035
55127
  if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
55036
- const domain = toPivotDomain(args.slice(1, -2).map((x) => `${x}`));
55128
+ const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
55037
55129
  return {
55038
55130
  type: "MEASURE_HEADER",
55039
55131
  domain,
@@ -55041,15 +55133,17 @@ stores.inject(MyMetaStore, storeInstance);
55041
55133
  };
55042
55134
  }
55043
55135
  else if (functionName === "PIVOT.HEADER") {
55136
+ const domain = pivot.parseArgsToPivotDomain(args.slice(1).map((value) => ({ value })));
55044
55137
  return {
55045
55138
  type: "HEADER",
55046
- domain: toPivotDomain(args.slice(1).map((x) => `${x}`)),
55139
+ domain,
55047
55140
  };
55048
55141
  }
55049
55142
  const [measure, ...domainArgs] = args.slice(1);
55143
+ const domain = pivot.parseArgsToPivotDomain(domainArgs.map((value) => ({ value })));
55050
55144
  return {
55051
55145
  type: "VALUE",
55052
- domain: toPivotDomain(domainArgs.map((x) => `${x}`)),
55146
+ domain,
55053
55147
  measure: measure?.toString() || "",
55054
55148
  };
55055
55149
  }
@@ -55059,32 +55153,6 @@ stores.inject(MyMetaStore, storeInstance);
55059
55153
  isPivotUnused(pivotId) {
55060
55154
  return this._getUnusedPivots().includes(pivotId);
55061
55155
  }
55062
- /**
55063
- * Check if the fields in the domain part of
55064
- * a pivot function are valid according to the pivot definition.
55065
- * e.g. =PIVOT.VALUE(1,"revenue","country_id",...,"create_date:month",...,"source_id",...)
55066
- */
55067
- areDomainArgsFieldsValid(pivotId, domain) {
55068
- const dimensions = domain
55069
- .map((node) => node.field)
55070
- .map((name) => (name.startsWith("#") ? name.slice(1) : name));
55071
- let argIndex = 0;
55072
- let definitionIndex = 0;
55073
- const pivot = this.getPivot(pivotId);
55074
- const definition = pivot.definition;
55075
- const cols = definition.columns.map((col) => col.nameWithGranularity);
55076
- const rows = definition.rows.map((row) => row.nameWithGranularity);
55077
- while (dimensions[argIndex] !== undefined && dimensions[argIndex] === rows[definitionIndex]) {
55078
- argIndex++;
55079
- definitionIndex++;
55080
- }
55081
- definitionIndex = 0;
55082
- while (dimensions[argIndex] !== undefined && dimensions[argIndex] === cols[definitionIndex]) {
55083
- argIndex++;
55084
- definitionIndex++;
55085
- }
55086
- return dimensions.length === argIndex;
55087
- }
55088
55156
  // ---------------------------------------------------------------------
55089
55157
  // Private
55090
55158
  // ---------------------------------------------------------------------
@@ -56922,43 +56990,6 @@ stores.inject(MyMetaStore, storeInstance);
56922
56990
  }
56923
56991
  }
56924
56992
 
56925
- /**
56926
- * Find and Replace Plugin
56927
- *
56928
- * This plugin is used in combination with the find_and_replace sidePanel
56929
- * It is used to 'highlight' cells that match an input string according to
56930
- * the given searchOptions. The second part of this plugin makes it possible
56931
- * (again with the find_and_replace sidePanel), to replace the values that match
56932
- * the search with a new value.
56933
- */
56934
- class FindAndReplacePlugin extends UIPlugin {
56935
- static getters = [];
56936
- handle(cmd) {
56937
- switch (cmd.type) {
56938
- case "REPLACE_SEARCH":
56939
- for (const match of cmd.matches) {
56940
- this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
56941
- }
56942
- break;
56943
- }
56944
- }
56945
- replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
56946
- const cell = this.getters.getCell(selectedMatch);
56947
- if (!cell?.content) {
56948
- return;
56949
- }
56950
- if (cell?.isFormula && !searchOptions.searchFormulas) {
56951
- return;
56952
- }
56953
- const searchRegex = getSearchRegex(searchString, searchOptions);
56954
- const replaceRegex = new RegExp(searchRegex.source, searchRegex.flags + "g");
56955
- const toReplace = this.getters.getCellText(selectedMatch, searchOptions.searchFormulas);
56956
- const content = toReplace.replace(replaceRegex, replaceWith);
56957
- const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
56958
- this.dispatch("UPDATE_CELL", { ...selectedMatch, content: canonicalContent });
56959
- }
56960
- }
56961
-
56962
56993
  class FormatPlugin extends UIPlugin {
56963
56994
  // ---------------------------------------------------------------------------
56964
56995
  // Command Handling
@@ -58604,32 +58635,32 @@ stores.inject(MyMetaStore, storeInstance);
58604
58635
  }
58605
58636
  }
58606
58637
  convertOSClipboardData(clipboardData) {
58607
- const handlers = clipboardHandlersRegistries.figureHandlers
58608
- .getAll()
58609
- .map((handler) => new handler(this.getters, this.dispatch));
58610
- clipboardHandlersRegistries.cellHandlers
58611
- .getAll()
58612
- .forEach((handler) => handlers.push(new handler(this.getters, this.dispatch)));
58638
+ const handlers = this.selectClipboardHandlers({ figureId: true }).concat(this.selectClipboardHandlers({}));
58613
58639
  let copiedData = {};
58614
- for (const handler of handlers) {
58640
+ for (const { handlerName, handler } of handlers) {
58615
58641
  const data = handler.convertOSClipboardData(clipboardData);
58616
- copiedData = { ...copiedData, ...data };
58642
+ copiedData[handlerName] = data;
58643
+ const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
58644
+ for (const key of minimalKeys) {
58645
+ if (data && key in data) {
58646
+ copiedData[key] = data[key];
58647
+ }
58648
+ }
58617
58649
  }
58618
58650
  return copiedData;
58619
58651
  }
58620
58652
  selectClipboardHandlers(data) {
58621
- if ("figureId" in data) {
58622
- return clipboardHandlersRegistries.figureHandlers
58623
- .getAll()
58624
- .map((handler) => new handler(this.getters, this.dispatch));
58625
- }
58626
- return clipboardHandlersRegistries.cellHandlers
58627
- .getAll()
58628
- .map((handler) => new handler(this.getters, this.dispatch));
58653
+ const handlersRegistry = "figureId" in data
58654
+ ? clipboardHandlersRegistries.figureHandlers
58655
+ : clipboardHandlersRegistries.cellHandlers;
58656
+ return handlersRegistry.getKeys().map((handlerName) => {
58657
+ const Handler = handlersRegistry.get(handlerName);
58658
+ return { handlerName, handler: new Handler(this.getters, this.dispatch) };
58659
+ });
58629
58660
  }
58630
58661
  isCutAllowedOn(zones) {
58631
58662
  const clipboardData = this.getClipboardData(zones);
58632
- for (const handler of this.selectClipboardHandlers(clipboardData)) {
58663
+ for (const { handler } of this.selectClipboardHandlers(clipboardData)) {
58633
58664
  const result = handler.isCutAllowed(clipboardData);
58634
58665
  if (result !== "Success" /* CommandResult.Success */) {
58635
58666
  return result;
@@ -58638,7 +58669,7 @@ stores.inject(MyMetaStore, storeInstance);
58638
58669
  return "Success" /* CommandResult.Success */;
58639
58670
  }
58640
58671
  isPasteAllowed(target, copiedData, options) {
58641
- for (const handler of this.selectClipboardHandlers(copiedData)) {
58672
+ for (const { handler } of this.selectClipboardHandlers(copiedData)) {
58642
58673
  const result = handler.isPasteAllowed(this.getters.getActiveSheetId(), target, copiedData, {
58643
58674
  ...options,
58644
58675
  });
@@ -58666,9 +58697,15 @@ stores.inject(MyMetaStore, storeInstance);
58666
58697
  copy(zones) {
58667
58698
  let copiedData = {};
58668
58699
  const clipboardData = this.getClipboardData(zones);
58669
- for (const handler of this.selectClipboardHandlers(clipboardData)) {
58700
+ for (const { handlerName, handler } of this.selectClipboardHandlers(clipboardData)) {
58670
58701
  const data = handler.copy(clipboardData);
58671
- copiedData = { ...copiedData, ...data };
58702
+ copiedData[handlerName] = data;
58703
+ const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
58704
+ for (const key of minimalKeys) {
58705
+ if (data && key in data) {
58706
+ copiedData[key] = data[key];
58707
+ }
58708
+ }
58672
58709
  }
58673
58710
  return copiedData;
58674
58711
  }
@@ -58684,8 +58721,12 @@ stores.inject(MyMetaStore, storeInstance);
58684
58721
  zones,
58685
58722
  };
58686
58723
  const handlers = this.selectClipboardHandlers(copiedData);
58687
- for (const handler of handlers) {
58688
- const currentTarget = handler.getPasteTarget(sheetId, zones, copiedData, options);
58724
+ for (const { handlerName, handler } of handlers) {
58725
+ const handlerData = copiedData[handlerName];
58726
+ if (!handlerData) {
58727
+ continue;
58728
+ }
58729
+ const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
58689
58730
  if (currentTarget.figureId) {
58690
58731
  target.figureId = currentTarget.figureId;
58691
58732
  }
@@ -58701,7 +58742,12 @@ stores.inject(MyMetaStore, storeInstance);
58701
58742
  if (zone !== undefined) {
58702
58743
  this.addMissingDimensions(this.getters.getActiveSheetId(), zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
58703
58744
  }
58704
- handlers.forEach((handler) => handler.paste(target, copiedData, options));
58745
+ handlers.forEach(({ handlerName, handler }) => {
58746
+ const handlerData = copiedData[handlerName];
58747
+ if (handlerData) {
58748
+ handler.paste(target, handlerData, options);
58749
+ }
58750
+ });
58705
58751
  if (!options?.selectTarget) {
58706
58752
  return;
58707
58753
  }
@@ -60773,7 +60819,6 @@ stores.inject(MyMetaStore, storeInstance);
60773
60819
  .add("ui_sheet", SheetUIPlugin)
60774
60820
  .add("ui_options", UIOptionsPlugin)
60775
60821
  .add("autofill", AutofillPlugin)
60776
- .add("find_and_replace", FindAndReplacePlugin)
60777
60822
  .add("sort", SortPlugin)
60778
60823
  .add("automatic_sum", AutomaticSumPlugin)
60779
60824
  .add("format", FormatPlugin)
@@ -66822,6 +66867,7 @@ stores.inject(MyMetaStore, storeInstance);
66822
66867
  const start = performance.now();
66823
66868
  console.group("Model creation");
66824
66869
  super();
66870
+ setDefaultTranslationMethod();
66825
66871
  stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
66826
66872
  const workbookData = load(data, verboseImport);
66827
66873
  this.state = new StateObserver();
@@ -67317,6 +67363,7 @@ stores.inject(MyMetaStore, storeInstance);
67317
67363
  pivotSidePanelRegistry,
67318
67364
  pivotNormalizationValueRegistry,
67319
67365
  supportedPivotPositionalFormulaRegistry,
67366
+ pivotToFunctionValueRegistry,
67320
67367
  };
67321
67368
  const helpers = {
67322
67369
  arg,
@@ -67362,7 +67409,6 @@ stores.inject(MyMetaStore, storeInstance);
67362
67409
  expandZoneOnInsertion,
67363
67410
  reduceZoneOnDeletion,
67364
67411
  unquote,
67365
- makePivotFormula,
67366
67412
  getMaxObjectId,
67367
67413
  getFunctionsFromTokens,
67368
67414
  getFirstPivotFunction,
@@ -67374,10 +67420,10 @@ stores.inject(MyMetaStore, storeInstance);
67374
67420
  insertTokenAfterLeftParenthesis,
67375
67421
  mergeContiguousZones,
67376
67422
  getPivotHighlights,
67377
- toPivotDomain,
67378
- flatPivotDomain,
67379
67423
  pivotTimeAdapter,
67380
67424
  UNDO_REDO_PIVOT_COMMANDS,
67425
+ createPivotFormula,
67426
+ areDomainArgsFieldsValid,
67381
67427
  };
67382
67428
  const links = {
67383
67429
  isMarkdownLink,
@@ -67508,9 +67554,9 @@ stores.inject(MyMetaStore, storeInstance);
67508
67554
  exports.tokenize = tokenize;
67509
67555
 
67510
67556
 
67511
- __info__.version = "17.4.0-alpha.6";
67512
- __info__.date = "2024-06-19T13:46:27.157Z";
67513
- __info__.hash = "a4f22e4";
67557
+ __info__.version = "17.4.0-alpha.8";
67558
+ __info__.date = "2024-06-24T19:51:16.144Z";
67559
+ __info__.hash = "ccd30df";
67514
67560
 
67515
67561
 
67516
67562
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);