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