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