@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.
- package/dist/o-spreadsheet.cjs.js +824 -778
- package/dist/o-spreadsheet.d.ts +40 -44
- package/dist/o-spreadsheet.esm.js +824 -778
- package/dist/o-spreadsheet.iife.js +824 -778
- package/dist/o-spreadsheet.iife.min.js +345 -345
- package/dist/o_spreadsheet.xml +5 -5
- package/package.json +6 -4
|
@@ -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.
|
|
7
|
-
* @date 2024-06-
|
|
8
|
-
* @hash
|
|
5
|
+
* @version 17.4.0-alpha.8
|
|
6
|
+
* @date 2024-06-24T19:51:16.144Z
|
|
7
|
+
* @hash ccd30df
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
'use strict';
|
|
@@ -1904,9 +1903,10 @@ function percentile(values, percent, isInclusive) {
|
|
|
1904
1903
|
sortedValues[indexLow] * (indexSup - percentIndex));
|
|
1905
1904
|
}
|
|
1906
1905
|
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
let
|
|
1906
|
+
const defaultTranslate = (s) => s;
|
|
1907
|
+
const defaultLoaded = () => false;
|
|
1908
|
+
let _translate = defaultTranslate;
|
|
1909
|
+
let _loaded = defaultLoaded;
|
|
1910
1910
|
function sprintf(s, ...values) {
|
|
1911
1911
|
if (values.length === 1 && typeof values[0] === "object" && !(values[0] instanceof String)) {
|
|
1912
1912
|
const valuesDict = values[0];
|
|
@@ -1918,7 +1918,8 @@ function sprintf(s, ...values) {
|
|
|
1918
1918
|
return s;
|
|
1919
1919
|
}
|
|
1920
1920
|
/***
|
|
1921
|
-
* Allow to inject a translation function from outside o-spreadsheet.
|
|
1921
|
+
* Allow to inject a translation function from outside o-spreadsheet. This should be called before instantiating
|
|
1922
|
+
* a model.
|
|
1922
1923
|
* @param tfn the function that will do the translation
|
|
1923
1924
|
* @param loaded a function that returns true when the translation is loaded
|
|
1924
1925
|
*/
|
|
@@ -1926,6 +1927,18 @@ function setTranslationMethod(tfn, loaded = () => true) {
|
|
|
1926
1927
|
_translate = tfn;
|
|
1927
1928
|
_loaded = loaded;
|
|
1928
1929
|
}
|
|
1930
|
+
/**
|
|
1931
|
+
* If no translation function has been set, this will mark the translation are loaded.
|
|
1932
|
+
*
|
|
1933
|
+
* By default, the translations should not be set as loaded, otherwise top-level translated constants will never be
|
|
1934
|
+
* translated. But if by the time the model is instantiated no custom translation function has been set, we can set
|
|
1935
|
+
* the default translation function as loaded so o-spreadsheet can be run in standalone with no translations.
|
|
1936
|
+
*/
|
|
1937
|
+
function setDefaultTranslationMethod() {
|
|
1938
|
+
if (_translate === defaultTranslate && _loaded === defaultLoaded) {
|
|
1939
|
+
_loaded = () => true;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1929
1942
|
const _t = function (s, ...values) {
|
|
1930
1943
|
if (!_loaded()) {
|
|
1931
1944
|
return new LazyTranslatedString(s, values);
|
|
@@ -5621,18 +5634,12 @@ class BorderClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
5621
5634
|
return { borders };
|
|
5622
5635
|
}
|
|
5623
5636
|
paste(target, content, options) {
|
|
5624
|
-
if (!content) {
|
|
5625
|
-
return;
|
|
5626
|
-
}
|
|
5627
5637
|
const sheetId = target.sheetId;
|
|
5628
|
-
if (options
|
|
5629
|
-
return;
|
|
5630
|
-
}
|
|
5631
|
-
if (!("borders" in content) || !("zones" in target) || !target.zones.length) {
|
|
5638
|
+
if (options.pasteOption === "asValue") {
|
|
5632
5639
|
return;
|
|
5633
5640
|
}
|
|
5634
5641
|
const zones = target.zones;
|
|
5635
|
-
if (!options
|
|
5642
|
+
if (!options.isCutOperation) {
|
|
5636
5643
|
this.pasteFromCopy(sheetId, zones, content.borders);
|
|
5637
5644
|
}
|
|
5638
5645
|
else {
|
|
@@ -6143,6 +6150,530 @@ function _localizeFormula(formula, fromLocale, toLocale) {
|
|
|
6143
6150
|
return localizedFormula;
|
|
6144
6151
|
}
|
|
6145
6152
|
|
|
6153
|
+
function boolAnd(args) {
|
|
6154
|
+
let foundBoolean = false;
|
|
6155
|
+
let acc = true;
|
|
6156
|
+
conditionalVisitBoolean(args, (arg) => {
|
|
6157
|
+
foundBoolean = true;
|
|
6158
|
+
acc = acc && arg;
|
|
6159
|
+
return acc;
|
|
6160
|
+
});
|
|
6161
|
+
return {
|
|
6162
|
+
foundBoolean,
|
|
6163
|
+
result: acc,
|
|
6164
|
+
};
|
|
6165
|
+
}
|
|
6166
|
+
function boolOr(args) {
|
|
6167
|
+
let foundBoolean = false;
|
|
6168
|
+
let acc = false;
|
|
6169
|
+
conditionalVisitBoolean(args, (arg) => {
|
|
6170
|
+
foundBoolean = true;
|
|
6171
|
+
acc = acc || arg;
|
|
6172
|
+
return !acc;
|
|
6173
|
+
});
|
|
6174
|
+
return {
|
|
6175
|
+
foundBoolean,
|
|
6176
|
+
result: acc,
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
6179
|
+
|
|
6180
|
+
function sum(values, locale) {
|
|
6181
|
+
return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
|
|
6182
|
+
}
|
|
6183
|
+
function countUnique(args) {
|
|
6184
|
+
return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
|
|
6185
|
+
}
|
|
6186
|
+
|
|
6187
|
+
function assertSameNumberOfElements(...args) {
|
|
6188
|
+
const dims = args[0].length;
|
|
6189
|
+
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())));
|
|
6190
|
+
}
|
|
6191
|
+
function average(values, locale) {
|
|
6192
|
+
let count = 0;
|
|
6193
|
+
const sum = reduceNumbers(values, (acc, a) => {
|
|
6194
|
+
count += 1;
|
|
6195
|
+
return acc + a;
|
|
6196
|
+
}, 0, locale);
|
|
6197
|
+
assertNotZero(count);
|
|
6198
|
+
return sum / count;
|
|
6199
|
+
}
|
|
6200
|
+
function countNumbers(values, locale) {
|
|
6201
|
+
let count = 0;
|
|
6202
|
+
for (let n of values) {
|
|
6203
|
+
if (isMatrix(n)) {
|
|
6204
|
+
for (let i of n) {
|
|
6205
|
+
for (let j of i) {
|
|
6206
|
+
if (typeof j.value === "number") {
|
|
6207
|
+
count += 1;
|
|
6208
|
+
}
|
|
6209
|
+
}
|
|
6210
|
+
}
|
|
6211
|
+
}
|
|
6212
|
+
else {
|
|
6213
|
+
const value = n?.value;
|
|
6214
|
+
if (!isEvaluationError(value) &&
|
|
6215
|
+
(typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
|
|
6216
|
+
count += 1;
|
|
6217
|
+
}
|
|
6218
|
+
}
|
|
6219
|
+
}
|
|
6220
|
+
return count;
|
|
6221
|
+
}
|
|
6222
|
+
function countAny(values) {
|
|
6223
|
+
return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
|
|
6224
|
+
}
|
|
6225
|
+
function max(values, locale) {
|
|
6226
|
+
const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
|
|
6227
|
+
return result === -Infinity ? 0 : result;
|
|
6228
|
+
}
|
|
6229
|
+
function min(values, locale) {
|
|
6230
|
+
const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
|
|
6231
|
+
return result === Infinity ? 0 : result;
|
|
6232
|
+
}
|
|
6233
|
+
|
|
6234
|
+
const pivotTimeAdapterRegistry = new Registry();
|
|
6235
|
+
function pivotTimeAdapter(granularity) {
|
|
6236
|
+
return pivotTimeAdapterRegistry.get(granularity);
|
|
6237
|
+
}
|
|
6238
|
+
/**
|
|
6239
|
+
* The Time Adapter: Managing Time Periods for Pivot Functions
|
|
6240
|
+
*
|
|
6241
|
+
* Overview:
|
|
6242
|
+
* A time adapter is responsible for managing time periods associated with pivot functions.
|
|
6243
|
+
* Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
|
|
6244
|
+
* The adapter's primary role is to normalize period values between spreadsheet functions,
|
|
6245
|
+
* and the pivot.
|
|
6246
|
+
* By normalizing the period value, it can be stored consistently in the pivot.
|
|
6247
|
+
*
|
|
6248
|
+
* Normalization Process:
|
|
6249
|
+
* When working with functions in the spreadsheet, the time adapter normalizes
|
|
6250
|
+
* the provided period to facilitate accurate lookup of values in the pivot.
|
|
6251
|
+
* For instance, if the spreadsheet function represents a day period as a number generated
|
|
6252
|
+
* by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
|
|
6253
|
+
*
|
|
6254
|
+
*/
|
|
6255
|
+
/**
|
|
6256
|
+
* Normalized value: "12/25/2023"
|
|
6257
|
+
*
|
|
6258
|
+
* Note: Those two format are equivalent:
|
|
6259
|
+
* - "MM/dd/yyyy" (luxon format)
|
|
6260
|
+
* - "mm/dd/yyyy" (spreadsheet format)
|
|
6261
|
+
**/
|
|
6262
|
+
const dayAdapter = {
|
|
6263
|
+
normalizeFunctionValue(value) {
|
|
6264
|
+
return toNumber(value, DEFAULT_LOCALE);
|
|
6265
|
+
},
|
|
6266
|
+
toValueAndFormat(normalizedValue, locale) {
|
|
6267
|
+
return {
|
|
6268
|
+
value: toNumber(normalizedValue, DEFAULT_LOCALE),
|
|
6269
|
+
format: (locale ?? DEFAULT_LOCALE).dateFormat,
|
|
6270
|
+
};
|
|
6271
|
+
},
|
|
6272
|
+
toFunctionValue(normalizedValue) {
|
|
6273
|
+
const date = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
6274
|
+
return `"${formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" })}"`;
|
|
6275
|
+
},
|
|
6276
|
+
};
|
|
6277
|
+
/**
|
|
6278
|
+
* normalizes day of month number
|
|
6279
|
+
*/
|
|
6280
|
+
const dayOfMonthAdapter = {
|
|
6281
|
+
normalizeFunctionValue(value) {
|
|
6282
|
+
const day = toNumber(value, DEFAULT_LOCALE);
|
|
6283
|
+
if (day < 1 || day > 31) {
|
|
6284
|
+
throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
|
|
6285
|
+
}
|
|
6286
|
+
return day;
|
|
6287
|
+
},
|
|
6288
|
+
toValueAndFormat(normalizedValue) {
|
|
6289
|
+
return {
|
|
6290
|
+
value: toNumber(normalizedValue, DEFAULT_LOCALE),
|
|
6291
|
+
format: "0",
|
|
6292
|
+
};
|
|
6293
|
+
},
|
|
6294
|
+
toFunctionValue(normalizedValue) {
|
|
6295
|
+
return `${normalizedValue}`;
|
|
6296
|
+
},
|
|
6297
|
+
};
|
|
6298
|
+
/**
|
|
6299
|
+
* Normalized value: "2/2023" for week 2 of 2023
|
|
6300
|
+
*/
|
|
6301
|
+
const weekAdapter = {
|
|
6302
|
+
normalizeFunctionValue(value) {
|
|
6303
|
+
const [week, year] = toString(value).split("/");
|
|
6304
|
+
return `${Number(week)}/${Number(year)}`;
|
|
6305
|
+
},
|
|
6306
|
+
toValueAndFormat(normalizedValue, locale) {
|
|
6307
|
+
const [week, year] = normalizedValue.split("/");
|
|
6308
|
+
return {
|
|
6309
|
+
value: _t("W%(week)s %(year)s", { week, year }),
|
|
6310
|
+
};
|
|
6311
|
+
},
|
|
6312
|
+
toFunctionValue(normalizedValue) {
|
|
6313
|
+
return `"${normalizedValue}"`;
|
|
6314
|
+
},
|
|
6315
|
+
};
|
|
6316
|
+
/**
|
|
6317
|
+
* normalizes iso week number
|
|
6318
|
+
*/
|
|
6319
|
+
const isoWeekNumberAdapter = {
|
|
6320
|
+
normalizeFunctionValue(value) {
|
|
6321
|
+
const isoWeek = toNumber(value, DEFAULT_LOCALE);
|
|
6322
|
+
if (isoWeek < 0 || isoWeek > 53) {
|
|
6323
|
+
throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
|
|
6324
|
+
}
|
|
6325
|
+
return isoWeek;
|
|
6326
|
+
},
|
|
6327
|
+
toValueAndFormat(normalizedValue) {
|
|
6328
|
+
return {
|
|
6329
|
+
value: toNumber(normalizedValue, DEFAULT_LOCALE),
|
|
6330
|
+
format: "0",
|
|
6331
|
+
};
|
|
6332
|
+
},
|
|
6333
|
+
toFunctionValue(normalizedValue) {
|
|
6334
|
+
return `${normalizedValue}`;
|
|
6335
|
+
},
|
|
6336
|
+
};
|
|
6337
|
+
/**
|
|
6338
|
+
* normalized month value is a string formatted as "MM/yyyy" (luxon format)
|
|
6339
|
+
* e.g. "01/2020" for January 2020
|
|
6340
|
+
*/
|
|
6341
|
+
const monthAdapter = {
|
|
6342
|
+
normalizeFunctionValue(value) {
|
|
6343
|
+
const date = toNumber(value, DEFAULT_LOCALE);
|
|
6344
|
+
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
6345
|
+
},
|
|
6346
|
+
toValueAndFormat(normalizedValue) {
|
|
6347
|
+
return {
|
|
6348
|
+
value: toNumber(normalizedValue, DEFAULT_LOCALE),
|
|
6349
|
+
format: "mmmm yyyy",
|
|
6350
|
+
};
|
|
6351
|
+
},
|
|
6352
|
+
toFunctionValue(normalizedValue) {
|
|
6353
|
+
return `"${normalizedValue}"`;
|
|
6354
|
+
},
|
|
6355
|
+
};
|
|
6356
|
+
/**
|
|
6357
|
+
* normalizes month number
|
|
6358
|
+
*/
|
|
6359
|
+
const monthNumberAdapter = {
|
|
6360
|
+
normalizeFunctionValue(value) {
|
|
6361
|
+
const month = toNumber(value, DEFAULT_LOCALE);
|
|
6362
|
+
if (month < 1 || month > 12) {
|
|
6363
|
+
throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
|
|
6364
|
+
}
|
|
6365
|
+
return month;
|
|
6366
|
+
},
|
|
6367
|
+
toValueAndFormat(normalizedValue) {
|
|
6368
|
+
return {
|
|
6369
|
+
value: MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString(),
|
|
6370
|
+
format: "0",
|
|
6371
|
+
};
|
|
6372
|
+
},
|
|
6373
|
+
toFunctionValue(normalizedValue) {
|
|
6374
|
+
return `${normalizedValue}`;
|
|
6375
|
+
},
|
|
6376
|
+
};
|
|
6377
|
+
/**
|
|
6378
|
+
* normalized quarter value is "quarter/year"
|
|
6379
|
+
* e.g. "1/2020" for Q1 2020
|
|
6380
|
+
*/
|
|
6381
|
+
const quarterAdapter = {
|
|
6382
|
+
normalizeFunctionValue(value) {
|
|
6383
|
+
const [quarter, year] = toString(value).split("/");
|
|
6384
|
+
return `${quarter}/${year}`;
|
|
6385
|
+
},
|
|
6386
|
+
toValueAndFormat(normalizedValue) {
|
|
6387
|
+
const [quarter, year] = normalizedValue.split("/");
|
|
6388
|
+
return {
|
|
6389
|
+
value: _t("Q%(quarter)s %(year)s", { quarter, year }),
|
|
6390
|
+
};
|
|
6391
|
+
},
|
|
6392
|
+
toFunctionValue(normalizedValue) {
|
|
6393
|
+
return `"${normalizedValue}"`;
|
|
6394
|
+
},
|
|
6395
|
+
};
|
|
6396
|
+
/**
|
|
6397
|
+
* normalizes quarter number
|
|
6398
|
+
*/
|
|
6399
|
+
const quarterNumberAdapter = {
|
|
6400
|
+
normalizeFunctionValue(value) {
|
|
6401
|
+
const quarter = toNumber(value, DEFAULT_LOCALE);
|
|
6402
|
+
if (quarter < 1 || quarter > 4) {
|
|
6403
|
+
throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
|
|
6404
|
+
}
|
|
6405
|
+
return quarter;
|
|
6406
|
+
},
|
|
6407
|
+
toValueAndFormat(normalizedValue) {
|
|
6408
|
+
return {
|
|
6409
|
+
value: toNumber(normalizedValue, DEFAULT_LOCALE),
|
|
6410
|
+
format: "0",
|
|
6411
|
+
};
|
|
6412
|
+
},
|
|
6413
|
+
toFunctionValue(normalizedValue) {
|
|
6414
|
+
return `${normalizedValue}`;
|
|
6415
|
+
},
|
|
6416
|
+
};
|
|
6417
|
+
const yearAdapter = {
|
|
6418
|
+
normalizeFunctionValue(value) {
|
|
6419
|
+
return toNumber(value, DEFAULT_LOCALE);
|
|
6420
|
+
},
|
|
6421
|
+
toValueAndFormat(normalizedValue) {
|
|
6422
|
+
return {
|
|
6423
|
+
value: toNumber(normalizedValue, DEFAULT_LOCALE),
|
|
6424
|
+
format: "0",
|
|
6425
|
+
};
|
|
6426
|
+
},
|
|
6427
|
+
toFunctionValue(normalizedValue) {
|
|
6428
|
+
return `${normalizedValue}`;
|
|
6429
|
+
},
|
|
6430
|
+
};
|
|
6431
|
+
/**
|
|
6432
|
+
* This function takes an adapter and wraps it with a null handler.
|
|
6433
|
+
* null value means that the value is not set.
|
|
6434
|
+
*/
|
|
6435
|
+
function nullHandlerDecorator(adapter) {
|
|
6436
|
+
return {
|
|
6437
|
+
normalizeFunctionValue(value) {
|
|
6438
|
+
if (value === null) {
|
|
6439
|
+
return null;
|
|
6440
|
+
}
|
|
6441
|
+
return adapter.normalizeFunctionValue(value);
|
|
6442
|
+
},
|
|
6443
|
+
toValueAndFormat(normalizedValue, locale) {
|
|
6444
|
+
if (normalizedValue === null) {
|
|
6445
|
+
return { value: _t("(Undefined)") }; //TODO Return NA ?
|
|
6446
|
+
}
|
|
6447
|
+
return adapter.toValueAndFormat(normalizedValue, locale);
|
|
6448
|
+
},
|
|
6449
|
+
toFunctionValue(normalizedValue) {
|
|
6450
|
+
if (normalizedValue === null) {
|
|
6451
|
+
return "false"; //TODO Return NA ?
|
|
6452
|
+
}
|
|
6453
|
+
return adapter.toFunctionValue(normalizedValue);
|
|
6454
|
+
},
|
|
6455
|
+
};
|
|
6456
|
+
}
|
|
6457
|
+
pivotTimeAdapterRegistry
|
|
6458
|
+
.add("day", nullHandlerDecorator(dayAdapter))
|
|
6459
|
+
.add("week", nullHandlerDecorator(weekAdapter))
|
|
6460
|
+
.add("month", nullHandlerDecorator(monthAdapter))
|
|
6461
|
+
.add("quarter", nullHandlerDecorator(quarterAdapter))
|
|
6462
|
+
.add("year", nullHandlerDecorator(yearAdapter))
|
|
6463
|
+
.add("day_of_month", nullHandlerDecorator(dayOfMonthAdapter))
|
|
6464
|
+
.add("iso_week_number", nullHandlerDecorator(isoWeekNumberAdapter))
|
|
6465
|
+
.add("month_number", nullHandlerDecorator(monthNumberAdapter))
|
|
6466
|
+
.add("quarter_number", nullHandlerDecorator(quarterNumberAdapter))
|
|
6467
|
+
.add("year_number", nullHandlerDecorator(yearAdapter));
|
|
6468
|
+
|
|
6469
|
+
const AGGREGATOR_NAMES = {
|
|
6470
|
+
count: _t("Count"),
|
|
6471
|
+
count_distinct: _t("Count Distinct"),
|
|
6472
|
+
bool_and: _t("Boolean And"),
|
|
6473
|
+
bool_or: _t("Boolean Or"),
|
|
6474
|
+
max: _t("Maximum"),
|
|
6475
|
+
min: _t("Minimum"),
|
|
6476
|
+
avg: _t("Average"),
|
|
6477
|
+
sum: _t("Sum"),
|
|
6478
|
+
};
|
|
6479
|
+
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
6480
|
+
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
6481
|
+
integer: NUMBER_CHAR_AGGREGATORS,
|
|
6482
|
+
char: NUMBER_CHAR_AGGREGATORS,
|
|
6483
|
+
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
6484
|
+
};
|
|
6485
|
+
const AGGREGATORS = {};
|
|
6486
|
+
for (const type in AGGREGATORS_BY_FIELD_TYPE) {
|
|
6487
|
+
AGGREGATORS[type] = {};
|
|
6488
|
+
for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
|
|
6489
|
+
AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
const AGGREGATORS_FN = {
|
|
6493
|
+
count: {
|
|
6494
|
+
fn: (args) => countAny([args]),
|
|
6495
|
+
format: () => "0",
|
|
6496
|
+
},
|
|
6497
|
+
count_distinct: {
|
|
6498
|
+
fn: (args) => countUnique([args]),
|
|
6499
|
+
format: () => "0",
|
|
6500
|
+
},
|
|
6501
|
+
bool_and: {
|
|
6502
|
+
fn: (args) => boolAnd([args]).result,
|
|
6503
|
+
format: () => undefined,
|
|
6504
|
+
},
|
|
6505
|
+
bool_or: {
|
|
6506
|
+
fn: (args) => boolOr([args]).result,
|
|
6507
|
+
format: () => undefined,
|
|
6508
|
+
},
|
|
6509
|
+
max: {
|
|
6510
|
+
fn: (args, locale) => max([args], locale),
|
|
6511
|
+
format: inferFormat,
|
|
6512
|
+
},
|
|
6513
|
+
min: {
|
|
6514
|
+
fn: (args, locale) => min([args], locale),
|
|
6515
|
+
format: inferFormat,
|
|
6516
|
+
},
|
|
6517
|
+
avg: {
|
|
6518
|
+
fn: (args, locale) => average([args], locale),
|
|
6519
|
+
format: inferFormat,
|
|
6520
|
+
},
|
|
6521
|
+
sum: {
|
|
6522
|
+
fn: (args, locale) => sum([args], locale),
|
|
6523
|
+
format: inferFormat,
|
|
6524
|
+
},
|
|
6525
|
+
};
|
|
6526
|
+
/**
|
|
6527
|
+
* Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
|
|
6528
|
+
* in this object
|
|
6529
|
+
* If the object has no keys, return 0
|
|
6530
|
+
*
|
|
6531
|
+
*/
|
|
6532
|
+
function getMaxObjectId(o) {
|
|
6533
|
+
const keys = Object.keys(o);
|
|
6534
|
+
if (!keys.length) {
|
|
6535
|
+
return 0;
|
|
6536
|
+
}
|
|
6537
|
+
const nums = keys.map((id) => parseInt(id, 10));
|
|
6538
|
+
const max = Math.max(...nums);
|
|
6539
|
+
return max;
|
|
6540
|
+
}
|
|
6541
|
+
const ALL_PERIODS = {
|
|
6542
|
+
year: _t("Year"),
|
|
6543
|
+
quarter: _t("Quarter"),
|
|
6544
|
+
month: _t("Month"),
|
|
6545
|
+
week: _t("Week"),
|
|
6546
|
+
day: _t("Day"),
|
|
6547
|
+
year_number: _t("Year"),
|
|
6548
|
+
quarter_number: _t("Quarter"),
|
|
6549
|
+
month_number: _t("Month"),
|
|
6550
|
+
iso_week_number: _t("Week"),
|
|
6551
|
+
day_of_month: _t("Day of Month"),
|
|
6552
|
+
};
|
|
6553
|
+
const DATE_FIELDS = ["date", "datetime"];
|
|
6554
|
+
/**
|
|
6555
|
+
* Parse a dimension string into a pivot dimension definition.
|
|
6556
|
+
* e.g "create_date:month" => { name: "create_date", granularity: "month" }
|
|
6557
|
+
*/
|
|
6558
|
+
function parseDimension(dimension) {
|
|
6559
|
+
const [name, granularity] = dimension.split(":");
|
|
6560
|
+
if (granularity) {
|
|
6561
|
+
return { name, granularity };
|
|
6562
|
+
}
|
|
6563
|
+
return { name };
|
|
6564
|
+
}
|
|
6565
|
+
function isDateField(field) {
|
|
6566
|
+
return DATE_FIELDS.includes(field.type);
|
|
6567
|
+
}
|
|
6568
|
+
function generatePivotArgs(formulaId, domain, measure) {
|
|
6569
|
+
const args = [formulaId];
|
|
6570
|
+
if (measure) {
|
|
6571
|
+
args.push(`"${measure}"`);
|
|
6572
|
+
}
|
|
6573
|
+
for (const { field, value, type } of domain) {
|
|
6574
|
+
if (field === "measure") {
|
|
6575
|
+
args.push(`"measure"`, `"${value}"`);
|
|
6576
|
+
continue;
|
|
6577
|
+
}
|
|
6578
|
+
const { granularity } = parseDimension(field);
|
|
6579
|
+
const formattedValue = toFunctionPivotValue(value, { type, granularity });
|
|
6580
|
+
args.push(`"${field}"`, formattedValue);
|
|
6581
|
+
}
|
|
6582
|
+
return args;
|
|
6583
|
+
}
|
|
6584
|
+
/**
|
|
6585
|
+
* Check if the fields in the domain part of
|
|
6586
|
+
* a pivot function are valid according to the pivot definition.
|
|
6587
|
+
* e.g. =PIVOT.VALUE(1,"revenue","country_id",...,"create_date:month",...,"source_id",...)
|
|
6588
|
+
*/
|
|
6589
|
+
function areDomainArgsFieldsValid(dimensions, definition) {
|
|
6590
|
+
let argIndex = 0;
|
|
6591
|
+
let definitionIndex = 0;
|
|
6592
|
+
const cols = definition.columns.map((col) => col.nameWithGranularity);
|
|
6593
|
+
const rows = definition.rows.map((row) => row.nameWithGranularity);
|
|
6594
|
+
while (dimensions[argIndex] !== undefined && dimensions[argIndex] === rows[definitionIndex]) {
|
|
6595
|
+
argIndex++;
|
|
6596
|
+
definitionIndex++;
|
|
6597
|
+
}
|
|
6598
|
+
definitionIndex = 0;
|
|
6599
|
+
while (dimensions[argIndex] !== undefined && dimensions[argIndex] === cols[definitionIndex]) {
|
|
6600
|
+
argIndex++;
|
|
6601
|
+
definitionIndex++;
|
|
6602
|
+
}
|
|
6603
|
+
return dimensions.length === argIndex;
|
|
6604
|
+
}
|
|
6605
|
+
function createPivotFormula(formulaId, cell) {
|
|
6606
|
+
switch (cell.type) {
|
|
6607
|
+
case "HEADER":
|
|
6608
|
+
return `=PIVOT.HEADER(${generatePivotArgs(formulaId, cell.domain).join(",")})`;
|
|
6609
|
+
case "VALUE":
|
|
6610
|
+
return `=PIVOT.VALUE(${generatePivotArgs(formulaId, cell.domain, cell.measure).join(",")})`;
|
|
6611
|
+
case "MEASURE_HEADER":
|
|
6612
|
+
return `=PIVOT.HEADER(${generatePivotArgs(formulaId, [
|
|
6613
|
+
...cell.domain,
|
|
6614
|
+
{ field: "measure", value: cell.measure, type: "char" },
|
|
6615
|
+
]).join(",")})`;
|
|
6616
|
+
}
|
|
6617
|
+
return "";
|
|
6618
|
+
}
|
|
6619
|
+
/**
|
|
6620
|
+
* Parses the value defining a pivot group in a PIVOT formula
|
|
6621
|
+
* e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
|
|
6622
|
+
* the two group values are "42" and "won".
|
|
6623
|
+
*/
|
|
6624
|
+
function toNormalizedPivotValue(dimension, groupValue) {
|
|
6625
|
+
if (groupValue === null || groupValue === "null") {
|
|
6626
|
+
return null;
|
|
6627
|
+
}
|
|
6628
|
+
const groupValueString = typeof groupValue === "boolean"
|
|
6629
|
+
? toString(groupValue).toLocaleLowerCase()
|
|
6630
|
+
: toString(groupValue);
|
|
6631
|
+
if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
|
|
6632
|
+
throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
|
|
6633
|
+
field: dimension.displayName,
|
|
6634
|
+
type: dimension.type,
|
|
6635
|
+
}));
|
|
6636
|
+
}
|
|
6637
|
+
// represents a field which is not set (=False server side)
|
|
6638
|
+
if (groupValueString.toLowerCase() === "false") {
|
|
6639
|
+
return false;
|
|
6640
|
+
}
|
|
6641
|
+
const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
|
|
6642
|
+
return normalizer(groupValueString, dimension.granularity);
|
|
6643
|
+
}
|
|
6644
|
+
function normalizeDateTime(value, granularity) {
|
|
6645
|
+
if (!granularity) {
|
|
6646
|
+
throw new Error("Missing granularity");
|
|
6647
|
+
}
|
|
6648
|
+
return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
|
|
6649
|
+
}
|
|
6650
|
+
function toFunctionPivotValue(value, dimension) {
|
|
6651
|
+
if (!pivotToFunctionValueRegistry.contains(dimension.type)) {
|
|
6652
|
+
return `"${value}"`;
|
|
6653
|
+
}
|
|
6654
|
+
return pivotToFunctionValueRegistry.get(dimension.type)(value, dimension.granularity);
|
|
6655
|
+
}
|
|
6656
|
+
function toFunctionValueDateTime(value, granularity) {
|
|
6657
|
+
if (!granularity) {
|
|
6658
|
+
throw new Error("Missing granularity");
|
|
6659
|
+
}
|
|
6660
|
+
return pivotTimeAdapter(granularity).toFunctionValue(value);
|
|
6661
|
+
}
|
|
6662
|
+
const pivotNormalizationValueRegistry = new Registry();
|
|
6663
|
+
pivotNormalizationValueRegistry
|
|
6664
|
+
.add("date", normalizeDateTime)
|
|
6665
|
+
.add("datetime", normalizeDateTime)
|
|
6666
|
+
.add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
|
|
6667
|
+
.add("boolean", (value) => toBoolean(value))
|
|
6668
|
+
.add("char", (value) => toString(value));
|
|
6669
|
+
const pivotToFunctionValueRegistry = new Registry();
|
|
6670
|
+
pivotToFunctionValueRegistry
|
|
6671
|
+
.add("date", toFunctionValueDateTime)
|
|
6672
|
+
.add("datetime", toFunctionValueDateTime)
|
|
6673
|
+
.add("integer", (value) => `${toNumber(value, DEFAULT_LOCALE)}`)
|
|
6674
|
+
.add("boolean", (value) => (toBoolean(value) ? "TRUE" : "FALSE"))
|
|
6675
|
+
.add("char", (value) => `"${toString(value).replace(/"/g, '\\"')}"`);
|
|
6676
|
+
|
|
6146
6677
|
class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
6147
6678
|
isCutAllowed(data) {
|
|
6148
6679
|
if (data.zones.length !== 1) {
|
|
@@ -6151,40 +6682,48 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6151
6682
|
return "Success" /* CommandResult.Success */;
|
|
6152
6683
|
}
|
|
6153
6684
|
copy(data) {
|
|
6154
|
-
if (!("zones" in data) || !data.zones.length) {
|
|
6155
|
-
return;
|
|
6156
|
-
}
|
|
6157
6685
|
const sheetId = data.sheetId;
|
|
6158
|
-
const zones = data.zones;
|
|
6159
|
-
if (!zones.length) {
|
|
6160
|
-
return {
|
|
6161
|
-
cells: [[]],
|
|
6162
|
-
zones: [],
|
|
6163
|
-
sheetId,
|
|
6164
|
-
};
|
|
6165
|
-
}
|
|
6166
6686
|
const { clippedZones, rowsIndexes, columnsIndexes } = data;
|
|
6167
6687
|
const clippedCells = [];
|
|
6688
|
+
const isCopyingOneCell = rowsIndexes.length == 1 && columnsIndexes.length == 1;
|
|
6168
6689
|
for (let row of rowsIndexes) {
|
|
6169
6690
|
let cellsInRow = [];
|
|
6170
6691
|
for (let col of columnsIndexes) {
|
|
6171
6692
|
const position = { col, row, sheetId };
|
|
6172
|
-
const spreader = this.getters.getArrayFormulaSpreadingOn(position);
|
|
6173
6693
|
let cell = this.getters.getCell(position);
|
|
6174
6694
|
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6695
|
+
const pivotId = this.getters.getPivotIdFromPosition(position);
|
|
6696
|
+
const spreader = this.getters.getArrayFormulaSpreadingOn(position);
|
|
6697
|
+
if (pivotId) {
|
|
6698
|
+
if (!deepEquals(spreader, position) || !isCopyingOneCell) {
|
|
6699
|
+
const pivotCell = this.getters.getPivotCellFromPosition(position);
|
|
6700
|
+
const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
|
|
6701
|
+
const pivotFormula = createPivotFormula(formulaPivotId, pivotCell);
|
|
6702
|
+
cell = {
|
|
6703
|
+
id: cell?.id || "",
|
|
6704
|
+
style: cell?.style,
|
|
6705
|
+
format: evaluatedCell.format,
|
|
6706
|
+
content: pivotFormula,
|
|
6707
|
+
isFormula: false,
|
|
6708
|
+
parsedValue: evaluatedCell.value,
|
|
6709
|
+
};
|
|
6710
|
+
}
|
|
6711
|
+
}
|
|
6712
|
+
else {
|
|
6713
|
+
if (spreader && !deepEquals(spreader, position)) {
|
|
6714
|
+
const isSpreaderCopied = rowsIndexes.includes(spreader.row) && columnsIndexes.includes(spreader.col);
|
|
6715
|
+
const content = isSpreaderCopied
|
|
6716
|
+
? ""
|
|
6717
|
+
: formatValue(evaluatedCell.value, { locale: this.getters.getLocale() });
|
|
6718
|
+
cell = {
|
|
6719
|
+
id: cell?.id || "",
|
|
6720
|
+
style: cell?.style,
|
|
6721
|
+
format: evaluatedCell.format,
|
|
6722
|
+
content,
|
|
6723
|
+
isFormula: false,
|
|
6724
|
+
parsedValue: evaluatedCell.value,
|
|
6725
|
+
};
|
|
6726
|
+
}
|
|
6188
6727
|
}
|
|
6189
6728
|
cellsInRow.push({
|
|
6190
6729
|
cell,
|
|
@@ -6233,12 +6772,9 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6233
6772
|
* Paste the clipboard content in the given target
|
|
6234
6773
|
*/
|
|
6235
6774
|
paste(target, content, options) {
|
|
6236
|
-
if (!("cells" in content) || !("zones" in target) || !target.zones.length) {
|
|
6237
|
-
return;
|
|
6238
|
-
}
|
|
6239
6775
|
const zones = target.zones;
|
|
6240
6776
|
const sheetId = target.sheetId;
|
|
6241
|
-
if (!options
|
|
6777
|
+
if (!options.isCutOperation) {
|
|
6242
6778
|
this.pasteFromCopy(sheetId, zones, content.cells, options);
|
|
6243
6779
|
}
|
|
6244
6780
|
else {
|
|
@@ -6408,14 +6944,11 @@ class ChartClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6408
6944
|
};
|
|
6409
6945
|
}
|
|
6410
6946
|
getPasteTarget(sheetId, target, content, options) {
|
|
6411
|
-
if (!content?.copiedFigure || !content?.copiedChart) {
|
|
6412
|
-
return { zones: [], sheetId };
|
|
6413
|
-
}
|
|
6414
6947
|
const newId = new UuidGenerator().uuidv4();
|
|
6415
6948
|
return { zones: [], figureId: newId, sheetId };
|
|
6416
6949
|
}
|
|
6417
6950
|
paste(target, clippedContent, options) {
|
|
6418
|
-
if (!
|
|
6951
|
+
if (!target.figureId) {
|
|
6419
6952
|
return;
|
|
6420
6953
|
}
|
|
6421
6954
|
const { zones, figureId } = target;
|
|
@@ -6439,7 +6972,7 @@ class ChartClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6439
6972
|
size: { height, width },
|
|
6440
6973
|
definition: copy.getDefinition(),
|
|
6441
6974
|
});
|
|
6442
|
-
if (options
|
|
6975
|
+
if (options.isCutOperation) {
|
|
6443
6976
|
this.dispatch("DELETE_FIGURE", {
|
|
6444
6977
|
sheetId: clippedContent.copiedChart.sheetId,
|
|
6445
6978
|
id: clippedContent.copiedFigure.id,
|
|
@@ -6481,15 +7014,12 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6481
7014
|
return { cfRules };
|
|
6482
7015
|
}
|
|
6483
7016
|
paste(target, clippedContent, options) {
|
|
6484
|
-
if (
|
|
6485
|
-
options?.pasteOption === "asValue" ||
|
|
6486
|
-
!("zones" in target) ||
|
|
6487
|
-
!target.zones.length) {
|
|
7017
|
+
if (options.pasteOption === "asValue") {
|
|
6488
7018
|
return;
|
|
6489
7019
|
}
|
|
6490
7020
|
const zones = target.zones;
|
|
6491
7021
|
const sheetId = target.sheetId;
|
|
6492
|
-
if (!options
|
|
7022
|
+
if (!options.isCutOperation) {
|
|
6493
7023
|
this.pasteFromCopy(sheetId, zones, clippedContent.cfRules, options);
|
|
6494
7024
|
}
|
|
6495
7025
|
else {
|
|
@@ -6563,9 +7093,6 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6563
7093
|
class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
6564
7094
|
uuidGenerator = new UuidGenerator();
|
|
6565
7095
|
copy(data) {
|
|
6566
|
-
if (!data.zones.length) {
|
|
6567
|
-
return;
|
|
6568
|
-
}
|
|
6569
7096
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6570
7097
|
const sheetId = data.sheetId;
|
|
6571
7098
|
const dvRules = [];
|
|
@@ -6581,18 +7108,12 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6581
7108
|
return { dvRules };
|
|
6582
7109
|
}
|
|
6583
7110
|
paste(target, clippedContent, options) {
|
|
6584
|
-
if (
|
|
6585
|
-
return;
|
|
6586
|
-
}
|
|
6587
|
-
if (options?.pasteOption) {
|
|
6588
|
-
return;
|
|
6589
|
-
}
|
|
6590
|
-
if (!("zones" in target) || !target.zones.length) {
|
|
7111
|
+
if (options.pasteOption) {
|
|
6591
7112
|
return;
|
|
6592
7113
|
}
|
|
6593
7114
|
const zones = target.zones;
|
|
6594
7115
|
const sheetId = target.sheetId;
|
|
6595
|
-
if (!options
|
|
7116
|
+
if (!options.isCutOperation) {
|
|
6596
7117
|
this.pasteFromCopy(sheetId, zones, clippedContent.dvRules);
|
|
6597
7118
|
}
|
|
6598
7119
|
else {
|
|
@@ -6691,14 +7212,11 @@ class ImageClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6691
7212
|
};
|
|
6692
7213
|
}
|
|
6693
7214
|
getPasteTarget(sheetId, target, content, options) {
|
|
6694
|
-
if (!content?.copiedFigure || !content?.copiedImage) {
|
|
6695
|
-
return { zones: [], sheetId };
|
|
6696
|
-
}
|
|
6697
7215
|
const newId = new UuidGenerator().uuidv4();
|
|
6698
7216
|
return { sheetId, zones: [], figureId: newId };
|
|
6699
7217
|
}
|
|
6700
7218
|
paste(target, clippedContent, options) {
|
|
6701
|
-
if (!
|
|
7219
|
+
if (!target.figureId) {
|
|
6702
7220
|
return;
|
|
6703
7221
|
}
|
|
6704
7222
|
const { zones, figureId } = target;
|
|
@@ -6722,7 +7240,7 @@ class ImageClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6722
7240
|
size: { height, width },
|
|
6723
7241
|
definition: copy,
|
|
6724
7242
|
});
|
|
6725
|
-
if (options
|
|
7243
|
+
if (options.isCutOperation) {
|
|
6726
7244
|
this.dispatch("DELETE_FIGURE", {
|
|
6727
7245
|
sheetId: clippedContent.sheetId,
|
|
6728
7246
|
id: clippedContent.copiedFigure.id,
|
|
@@ -6743,9 +7261,6 @@ class ImageClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6743
7261
|
|
|
6744
7262
|
class MergeClipboardHandler extends AbstractCellClipboardHandler {
|
|
6745
7263
|
copy(data) {
|
|
6746
|
-
if (!data.zones.length) {
|
|
6747
|
-
return;
|
|
6748
|
-
}
|
|
6749
7264
|
const sheetId = this.getters.getActiveSheetId();
|
|
6750
7265
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6751
7266
|
const merges = [];
|
|
@@ -6763,10 +7278,7 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6763
7278
|
* Paste the clipboard content in the given target
|
|
6764
7279
|
*/
|
|
6765
7280
|
paste(target, content, options) {
|
|
6766
|
-
if (
|
|
6767
|
-
options?.isCutOperation ||
|
|
6768
|
-
!("zones" in target) ||
|
|
6769
|
-
!target.zones.length) {
|
|
7281
|
+
if (options.isCutOperation) {
|
|
6770
7282
|
return;
|
|
6771
7283
|
}
|
|
6772
7284
|
this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
|
|
@@ -6822,9 +7334,6 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6822
7334
|
copy(data) {
|
|
6823
7335
|
const sheetId = data.sheetId;
|
|
6824
7336
|
const { rowsIndexes, columnsIndexes, zones } = data;
|
|
6825
|
-
if (!zones || !rowsIndexes.length || !columnsIndexes.length) {
|
|
6826
|
-
return { tableCells: [[]], sheetId };
|
|
6827
|
-
}
|
|
6828
7337
|
const copiedTablesIds = new Set();
|
|
6829
7338
|
const tableCells = [];
|
|
6830
7339
|
for (let row of rowsIndexes) {
|
|
@@ -6880,12 +7389,9 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6880
7389
|
};
|
|
6881
7390
|
}
|
|
6882
7391
|
paste(target, content, options) {
|
|
6883
|
-
if (!content || !content.tableCells) {
|
|
6884
|
-
return;
|
|
6885
|
-
}
|
|
6886
7392
|
const zones = target.zones;
|
|
6887
7393
|
const sheetId = target.sheetId;
|
|
6888
|
-
if (!options
|
|
7394
|
+
if (!options.isCutOperation) {
|
|
6889
7395
|
this.pasteFromCopy(sheetId, zones, content.tableCells, options);
|
|
6890
7396
|
}
|
|
6891
7397
|
else {
|
|
@@ -7778,486 +8284,6 @@ function errorCell(value, message) {
|
|
|
7778
8284
|
};
|
|
7779
8285
|
}
|
|
7780
8286
|
|
|
7781
|
-
function boolAnd(args) {
|
|
7782
|
-
let foundBoolean = false;
|
|
7783
|
-
let acc = true;
|
|
7784
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
7785
|
-
foundBoolean = true;
|
|
7786
|
-
acc = acc && arg;
|
|
7787
|
-
return acc;
|
|
7788
|
-
});
|
|
7789
|
-
return {
|
|
7790
|
-
foundBoolean,
|
|
7791
|
-
result: acc,
|
|
7792
|
-
};
|
|
7793
|
-
}
|
|
7794
|
-
function boolOr(args) {
|
|
7795
|
-
let foundBoolean = false;
|
|
7796
|
-
let acc = false;
|
|
7797
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
7798
|
-
foundBoolean = true;
|
|
7799
|
-
acc = acc || arg;
|
|
7800
|
-
return !acc;
|
|
7801
|
-
});
|
|
7802
|
-
return {
|
|
7803
|
-
foundBoolean,
|
|
7804
|
-
result: acc,
|
|
7805
|
-
};
|
|
7806
|
-
}
|
|
7807
|
-
|
|
7808
|
-
function sum(values, locale) {
|
|
7809
|
-
return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
|
|
7810
|
-
}
|
|
7811
|
-
function countUnique(args) {
|
|
7812
|
-
return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
|
|
7813
|
-
}
|
|
7814
|
-
|
|
7815
|
-
function assertSameNumberOfElements(...args) {
|
|
7816
|
-
const dims = args[0].length;
|
|
7817
|
-
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())));
|
|
7818
|
-
}
|
|
7819
|
-
function average(values, locale) {
|
|
7820
|
-
let count = 0;
|
|
7821
|
-
const sum = reduceNumbers(values, (acc, a) => {
|
|
7822
|
-
count += 1;
|
|
7823
|
-
return acc + a;
|
|
7824
|
-
}, 0, locale);
|
|
7825
|
-
assertNotZero(count);
|
|
7826
|
-
return sum / count;
|
|
7827
|
-
}
|
|
7828
|
-
function countNumbers(values, locale) {
|
|
7829
|
-
let count = 0;
|
|
7830
|
-
for (let n of values) {
|
|
7831
|
-
if (isMatrix(n)) {
|
|
7832
|
-
for (let i of n) {
|
|
7833
|
-
for (let j of i) {
|
|
7834
|
-
if (typeof j.value === "number") {
|
|
7835
|
-
count += 1;
|
|
7836
|
-
}
|
|
7837
|
-
}
|
|
7838
|
-
}
|
|
7839
|
-
}
|
|
7840
|
-
else {
|
|
7841
|
-
const value = n?.value;
|
|
7842
|
-
if (!isEvaluationError(value) &&
|
|
7843
|
-
(typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
|
|
7844
|
-
count += 1;
|
|
7845
|
-
}
|
|
7846
|
-
}
|
|
7847
|
-
}
|
|
7848
|
-
return count;
|
|
7849
|
-
}
|
|
7850
|
-
function countAny(values) {
|
|
7851
|
-
return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
|
|
7852
|
-
}
|
|
7853
|
-
function max(values, locale) {
|
|
7854
|
-
const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
|
|
7855
|
-
return result === -Infinity ? 0 : result;
|
|
7856
|
-
}
|
|
7857
|
-
function min(values, locale) {
|
|
7858
|
-
const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
|
|
7859
|
-
return result === Infinity ? 0 : result;
|
|
7860
|
-
}
|
|
7861
|
-
|
|
7862
|
-
const pivotTimeAdapterRegistry = new Registry();
|
|
7863
|
-
function pivotTimeAdapter(granularity) {
|
|
7864
|
-
return pivotTimeAdapterRegistry.get(granularity);
|
|
7865
|
-
}
|
|
7866
|
-
/**
|
|
7867
|
-
* The Time Adapter: Managing Time Periods for Pivot Functions
|
|
7868
|
-
*
|
|
7869
|
-
* Overview:
|
|
7870
|
-
* A time adapter is responsible for managing time periods associated with pivot functions.
|
|
7871
|
-
* Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
|
|
7872
|
-
* The adapter's primary role is to normalize period values between spreadsheet functions,
|
|
7873
|
-
* and the pivot.
|
|
7874
|
-
* By normalizing the period value, it can be stored consistently in the pivot.
|
|
7875
|
-
*
|
|
7876
|
-
* Normalization Process:
|
|
7877
|
-
* When working with functions in the spreadsheet, the time adapter normalizes
|
|
7878
|
-
* the provided period to facilitate accurate lookup of values in the pivot.
|
|
7879
|
-
* For instance, if the spreadsheet function represents a day period as a number generated
|
|
7880
|
-
* by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
|
|
7881
|
-
*
|
|
7882
|
-
*/
|
|
7883
|
-
/**
|
|
7884
|
-
* Normalized value: "12/25/2023"
|
|
7885
|
-
*
|
|
7886
|
-
* Note: Those two format are equivalent:
|
|
7887
|
-
* - "MM/dd/yyyy" (luxon format)
|
|
7888
|
-
* - "mm/dd/yyyy" (spreadsheet format)
|
|
7889
|
-
**/
|
|
7890
|
-
const dayAdapter = {
|
|
7891
|
-
normalizeFunctionValue(value) {
|
|
7892
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
7893
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" });
|
|
7894
|
-
},
|
|
7895
|
-
getFormat(locale) {
|
|
7896
|
-
return (locale ?? DEFAULT_LOCALE).dateFormat;
|
|
7897
|
-
},
|
|
7898
|
-
formatValue(normalizedValue, locale) {
|
|
7899
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
7900
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7901
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7902
|
-
},
|
|
7903
|
-
toCellValue(normalizedValue) {
|
|
7904
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7905
|
-
},
|
|
7906
|
-
};
|
|
7907
|
-
/**
|
|
7908
|
-
* normalizes day of month number
|
|
7909
|
-
*/
|
|
7910
|
-
const dayOfMonthAdapter = {
|
|
7911
|
-
normalizeFunctionValue(value) {
|
|
7912
|
-
const day = toNumber(value, DEFAULT_LOCALE);
|
|
7913
|
-
if (day < 1 || day > 31) {
|
|
7914
|
-
throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
|
|
7915
|
-
}
|
|
7916
|
-
return day;
|
|
7917
|
-
},
|
|
7918
|
-
getFormat() {
|
|
7919
|
-
return "0";
|
|
7920
|
-
},
|
|
7921
|
-
formatValue(normalizedValue, locale) {
|
|
7922
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
7923
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7924
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7925
|
-
},
|
|
7926
|
-
toCellValue(normalizedValue) {
|
|
7927
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7928
|
-
},
|
|
7929
|
-
};
|
|
7930
|
-
/**
|
|
7931
|
-
* Normalized value: "2/2023" for week 2 of 2023
|
|
7932
|
-
*/
|
|
7933
|
-
const weekAdapter = {
|
|
7934
|
-
normalizeFunctionValue(value) {
|
|
7935
|
-
const [week, year] = value.split("/");
|
|
7936
|
-
return `${Number(week)}/${Number(year)}`;
|
|
7937
|
-
},
|
|
7938
|
-
getFormat() {
|
|
7939
|
-
return undefined;
|
|
7940
|
-
},
|
|
7941
|
-
formatValue(normalizedValue) {
|
|
7942
|
-
const [week, year] = normalizedValue.split("/");
|
|
7943
|
-
return _t("W%(week)s %(year)s", { week, year });
|
|
7944
|
-
},
|
|
7945
|
-
toCellValue(normalizedValue) {
|
|
7946
|
-
return this.formatValue(normalizedValue);
|
|
7947
|
-
},
|
|
7948
|
-
};
|
|
7949
|
-
/**
|
|
7950
|
-
* normalizes iso week number
|
|
7951
|
-
*/
|
|
7952
|
-
const isoWeekNumberAdapter = {
|
|
7953
|
-
normalizeFunctionValue(value) {
|
|
7954
|
-
const isoWeek = toNumber(value, DEFAULT_LOCALE);
|
|
7955
|
-
if (isoWeek < 0 || isoWeek > 53) {
|
|
7956
|
-
throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
|
|
7957
|
-
}
|
|
7958
|
-
return isoWeek;
|
|
7959
|
-
},
|
|
7960
|
-
getFormat() {
|
|
7961
|
-
return "0";
|
|
7962
|
-
},
|
|
7963
|
-
formatValue(normalizedValue, locale) {
|
|
7964
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
7965
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7966
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7967
|
-
},
|
|
7968
|
-
toCellValue(normalizedValue) {
|
|
7969
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7970
|
-
},
|
|
7971
|
-
};
|
|
7972
|
-
/**
|
|
7973
|
-
* normalized month value is a string formatted as "MM/yyyy" (luxon format)
|
|
7974
|
-
* e.g. "01/2020" for January 2020
|
|
7975
|
-
*/
|
|
7976
|
-
const monthAdapter = {
|
|
7977
|
-
normalizeFunctionValue(value) {
|
|
7978
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
7979
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
7980
|
-
},
|
|
7981
|
-
getFormat() {
|
|
7982
|
-
return "mmmm yyyy";
|
|
7983
|
-
},
|
|
7984
|
-
formatValue(normalizedValue, locale) {
|
|
7985
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
7986
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7987
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7988
|
-
},
|
|
7989
|
-
toCellValue(normalizedValue) {
|
|
7990
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7991
|
-
},
|
|
7992
|
-
};
|
|
7993
|
-
/**
|
|
7994
|
-
* normalizes month number
|
|
7995
|
-
*/
|
|
7996
|
-
const monthNumberAdapter = {
|
|
7997
|
-
normalizeFunctionValue(value) {
|
|
7998
|
-
const month = toNumber(value, DEFAULT_LOCALE);
|
|
7999
|
-
if (month < 1 || month > 12) {
|
|
8000
|
-
throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
|
|
8001
|
-
}
|
|
8002
|
-
return month;
|
|
8003
|
-
},
|
|
8004
|
-
getFormat() {
|
|
8005
|
-
return "0";
|
|
8006
|
-
},
|
|
8007
|
-
formatValue(normalizedValue, locale) {
|
|
8008
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
8009
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8010
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
8011
|
-
},
|
|
8012
|
-
toCellValue(normalizedValue) {
|
|
8013
|
-
return MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString();
|
|
8014
|
-
},
|
|
8015
|
-
};
|
|
8016
|
-
/**
|
|
8017
|
-
* normalized quarter value is "quarter/year"
|
|
8018
|
-
* e.g. "1/2020" for Q1 2020
|
|
8019
|
-
*/
|
|
8020
|
-
const quarterAdapter = {
|
|
8021
|
-
normalizeFunctionValue(value) {
|
|
8022
|
-
const [quarter, year] = value.split("/");
|
|
8023
|
-
return `${quarter}/${year}`;
|
|
8024
|
-
},
|
|
8025
|
-
getFormat() {
|
|
8026
|
-
return undefined;
|
|
8027
|
-
},
|
|
8028
|
-
formatValue(normalizedValue) {
|
|
8029
|
-
const [quarter, year] = normalizedValue.split("/");
|
|
8030
|
-
return _t("Q%(quarter)s %(year)s", { quarter, year });
|
|
8031
|
-
},
|
|
8032
|
-
toCellValue(normalizedValue) {
|
|
8033
|
-
return this.formatValue(normalizedValue);
|
|
8034
|
-
},
|
|
8035
|
-
};
|
|
8036
|
-
/**
|
|
8037
|
-
* normalizes quarter number
|
|
8038
|
-
*/
|
|
8039
|
-
const quarterNumberAdapter = {
|
|
8040
|
-
normalizeFunctionValue(value) {
|
|
8041
|
-
const quarter = toNumber(value, DEFAULT_LOCALE);
|
|
8042
|
-
if (quarter < 1 || quarter > 4) {
|
|
8043
|
-
throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
|
|
8044
|
-
}
|
|
8045
|
-
return quarter;
|
|
8046
|
-
},
|
|
8047
|
-
getFormat() {
|
|
8048
|
-
return "0";
|
|
8049
|
-
},
|
|
8050
|
-
formatValue(normalizedValue, locale) {
|
|
8051
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
8052
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8053
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
8054
|
-
},
|
|
8055
|
-
toCellValue(normalizedValue) {
|
|
8056
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8057
|
-
},
|
|
8058
|
-
};
|
|
8059
|
-
const yearAdapter = {
|
|
8060
|
-
normalizeFunctionValue(value) {
|
|
8061
|
-
return toNumber(value, DEFAULT_LOCALE);
|
|
8062
|
-
},
|
|
8063
|
-
getFormat() {
|
|
8064
|
-
return "0";
|
|
8065
|
-
},
|
|
8066
|
-
formatValue(normalizedValue, locale) {
|
|
8067
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
8068
|
-
return formatValue(normalizedValue, { locale, format: "0" });
|
|
8069
|
-
},
|
|
8070
|
-
toCellValue(normalizedValue) {
|
|
8071
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8072
|
-
},
|
|
8073
|
-
};
|
|
8074
|
-
pivotTimeAdapterRegistry
|
|
8075
|
-
.add("day", dayAdapter)
|
|
8076
|
-
.add("week", weekAdapter)
|
|
8077
|
-
.add("month", monthAdapter)
|
|
8078
|
-
.add("quarter", quarterAdapter)
|
|
8079
|
-
.add("year", yearAdapter)
|
|
8080
|
-
.add("day_of_month", dayOfMonthAdapter)
|
|
8081
|
-
.add("iso_week_number", isoWeekNumberAdapter)
|
|
8082
|
-
.add("month_number", monthNumberAdapter)
|
|
8083
|
-
.add("quarter_number", quarterNumberAdapter)
|
|
8084
|
-
.add("year_number", yearAdapter);
|
|
8085
|
-
|
|
8086
|
-
const AGGREGATOR_NAMES = {
|
|
8087
|
-
count: _t("Count"),
|
|
8088
|
-
count_distinct: _t("Count Distinct"),
|
|
8089
|
-
bool_and: _t("Boolean And"),
|
|
8090
|
-
bool_or: _t("Boolean Or"),
|
|
8091
|
-
max: _t("Maximum"),
|
|
8092
|
-
min: _t("Minimum"),
|
|
8093
|
-
avg: _t("Average"),
|
|
8094
|
-
sum: _t("Sum"),
|
|
8095
|
-
};
|
|
8096
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
8097
|
-
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
8098
|
-
integer: NUMBER_CHAR_AGGREGATORS,
|
|
8099
|
-
char: NUMBER_CHAR_AGGREGATORS,
|
|
8100
|
-
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
8101
|
-
};
|
|
8102
|
-
const AGGREGATORS = {};
|
|
8103
|
-
for (const type in AGGREGATORS_BY_FIELD_TYPE) {
|
|
8104
|
-
AGGREGATORS[type] = {};
|
|
8105
|
-
for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
|
|
8106
|
-
AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
|
|
8107
|
-
}
|
|
8108
|
-
}
|
|
8109
|
-
const AGGREGATORS_FN = {
|
|
8110
|
-
count: {
|
|
8111
|
-
fn: (args) => countAny([args]),
|
|
8112
|
-
format: () => "0",
|
|
8113
|
-
},
|
|
8114
|
-
count_distinct: {
|
|
8115
|
-
fn: (args) => countUnique([args]),
|
|
8116
|
-
format: () => "0",
|
|
8117
|
-
},
|
|
8118
|
-
bool_and: {
|
|
8119
|
-
fn: (args) => boolAnd([args]).result,
|
|
8120
|
-
format: () => undefined,
|
|
8121
|
-
},
|
|
8122
|
-
bool_or: {
|
|
8123
|
-
fn: (args) => boolOr([args]).result,
|
|
8124
|
-
format: () => undefined,
|
|
8125
|
-
},
|
|
8126
|
-
max: {
|
|
8127
|
-
fn: (args, locale) => max([args], locale),
|
|
8128
|
-
format: inferFormat,
|
|
8129
|
-
},
|
|
8130
|
-
min: {
|
|
8131
|
-
fn: (args, locale) => min([args], locale),
|
|
8132
|
-
format: inferFormat,
|
|
8133
|
-
},
|
|
8134
|
-
avg: {
|
|
8135
|
-
fn: (args, locale) => average([args], locale),
|
|
8136
|
-
format: inferFormat,
|
|
8137
|
-
},
|
|
8138
|
-
sum: {
|
|
8139
|
-
fn: (args, locale) => sum([args], locale),
|
|
8140
|
-
format: inferFormat,
|
|
8141
|
-
},
|
|
8142
|
-
};
|
|
8143
|
-
function makePivotFormulaFromPivotCell(pivotFormulaId, pivotCell) {
|
|
8144
|
-
switch (pivotCell.type) {
|
|
8145
|
-
case "HEADER":
|
|
8146
|
-
return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
8147
|
-
case "MEASURE_HEADER":
|
|
8148
|
-
return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain), "measure", pivotCell.measure].filter(isDefined));
|
|
8149
|
-
case "VALUE":
|
|
8150
|
-
return makePivotFormula("PIVOT.VALUE", [pivotFormulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
8151
|
-
case "EMPTY":
|
|
8152
|
-
return "";
|
|
8153
|
-
}
|
|
8154
|
-
}
|
|
8155
|
-
/**
|
|
8156
|
-
* Build a pivot formula expression
|
|
8157
|
-
*/
|
|
8158
|
-
function makePivotFormula(formula, args) {
|
|
8159
|
-
return `=${formula}(${args
|
|
8160
|
-
.map((arg) => {
|
|
8161
|
-
const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
|
|
8162
|
-
const convertToNumber = typeof arg == "number" || stringIsNumber;
|
|
8163
|
-
return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
|
|
8164
|
-
})
|
|
8165
|
-
.join(",")})`;
|
|
8166
|
-
}
|
|
8167
|
-
/**
|
|
8168
|
-
* Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
|
|
8169
|
-
* in this object
|
|
8170
|
-
* If the object has no keys, return 0
|
|
8171
|
-
*
|
|
8172
|
-
*/
|
|
8173
|
-
function getMaxObjectId(o) {
|
|
8174
|
-
const keys = Object.keys(o);
|
|
8175
|
-
if (!keys.length) {
|
|
8176
|
-
return 0;
|
|
8177
|
-
}
|
|
8178
|
-
const nums = keys.map((id) => parseInt(id, 10));
|
|
8179
|
-
const max = Math.max(...nums);
|
|
8180
|
-
return max;
|
|
8181
|
-
}
|
|
8182
|
-
const ALL_PERIODS = {
|
|
8183
|
-
year: _t("Year"),
|
|
8184
|
-
quarter: _t("Quarter"),
|
|
8185
|
-
month: _t("Month"),
|
|
8186
|
-
week: _t("Week"),
|
|
8187
|
-
day: _t("Day"),
|
|
8188
|
-
year_number: _t("Year"),
|
|
8189
|
-
quarter_number: _t("Quarter"),
|
|
8190
|
-
month_number: _t("Month"),
|
|
8191
|
-
iso_week_number: _t("Week"),
|
|
8192
|
-
day_of_month: _t("Day of Month"),
|
|
8193
|
-
};
|
|
8194
|
-
const DATE_FIELDS = ["date", "datetime"];
|
|
8195
|
-
/**
|
|
8196
|
-
* Parse a dimension string into a pivot dimension definition.
|
|
8197
|
-
* e.g "create_date:month" => { name: "create_date", granularity: "month" }
|
|
8198
|
-
*/
|
|
8199
|
-
function parseDimension(dimension) {
|
|
8200
|
-
const [name, granularity] = dimension.split(":");
|
|
8201
|
-
if (granularity) {
|
|
8202
|
-
return { name, granularity };
|
|
8203
|
-
}
|
|
8204
|
-
return { name };
|
|
8205
|
-
}
|
|
8206
|
-
function isDateField(field) {
|
|
8207
|
-
return DATE_FIELDS.includes(field.type);
|
|
8208
|
-
}
|
|
8209
|
-
function toPivotDomain(domainStr) {
|
|
8210
|
-
if (domainStr.length % 2 !== 0) {
|
|
8211
|
-
throw new Error("Invalid domain: odd number of elements");
|
|
8212
|
-
}
|
|
8213
|
-
const domain = [];
|
|
8214
|
-
for (let i = 0; i < domainStr.length - 1; i += 2) {
|
|
8215
|
-
domain.push({ field: domainStr[i], value: domainStr[i + 1] });
|
|
8216
|
-
}
|
|
8217
|
-
return domain;
|
|
8218
|
-
}
|
|
8219
|
-
function flatPivotDomain(domain) {
|
|
8220
|
-
return domain.flatMap((arg) => [arg.field, arg.value]);
|
|
8221
|
-
}
|
|
8222
|
-
/**
|
|
8223
|
-
* Parses the value defining a pivot group in a PIVOT formula
|
|
8224
|
-
* e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
|
|
8225
|
-
* the two group values are "42" and "won".
|
|
8226
|
-
*/
|
|
8227
|
-
function toNormalizedPivotValue(dimension, groupValue) {
|
|
8228
|
-
if (groupValue === null || groupValue === "null") {
|
|
8229
|
-
return null;
|
|
8230
|
-
}
|
|
8231
|
-
const groupValueString = typeof groupValue === "boolean"
|
|
8232
|
-
? toString(groupValue).toLocaleLowerCase()
|
|
8233
|
-
: toString(groupValue);
|
|
8234
|
-
if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
|
|
8235
|
-
throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
|
|
8236
|
-
field: dimension.displayName,
|
|
8237
|
-
type: dimension.type,
|
|
8238
|
-
}));
|
|
8239
|
-
}
|
|
8240
|
-
// represents a field which is not set (=False server side)
|
|
8241
|
-
if (groupValueString === "false") {
|
|
8242
|
-
return false;
|
|
8243
|
-
}
|
|
8244
|
-
const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
|
|
8245
|
-
return normalizer(groupValueString, dimension.granularity);
|
|
8246
|
-
}
|
|
8247
|
-
function normalizeDateTime(value, granularity) {
|
|
8248
|
-
if (!granularity) {
|
|
8249
|
-
throw "";
|
|
8250
|
-
}
|
|
8251
|
-
return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
|
|
8252
|
-
}
|
|
8253
|
-
const pivotNormalizationValueRegistry = new Registry();
|
|
8254
|
-
pivotNormalizationValueRegistry
|
|
8255
|
-
.add("date", normalizeDateTime)
|
|
8256
|
-
.add("datetime", normalizeDateTime)
|
|
8257
|
-
.add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
|
|
8258
|
-
.add("boolean", (value) => toBoolean(value))
|
|
8259
|
-
.add("char", (value) => toString(value));
|
|
8260
|
-
|
|
8261
8287
|
/**
|
|
8262
8288
|
* Change the reference types inside the given token, if the token represent a range or a cell
|
|
8263
8289
|
*
|
|
@@ -9997,7 +10023,7 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9997
10023
|
const cell = this.getters.getCell(position);
|
|
9998
10024
|
if (pivotId && pivotCell.type !== "EMPTY" && !cell?.isFormula) {
|
|
9999
10025
|
const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
|
|
10000
|
-
const formula =
|
|
10026
|
+
const formula = createPivotFormula(formulaPivotId, pivotCell);
|
|
10001
10027
|
return formula.slice(1); // strip leading =
|
|
10002
10028
|
}
|
|
10003
10029
|
}
|
|
@@ -19089,10 +19115,9 @@ const PIVOT_VALUE = {
|
|
|
19089
19115
|
compute: function (formulaId, measureName, ...domainArgs) {
|
|
19090
19116
|
const _pivotFormulaId = toString(formulaId);
|
|
19091
19117
|
const _measure = toString(measureName);
|
|
19092
|
-
const _domainArgs = domainArgs.map(toString);
|
|
19093
19118
|
const pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
19094
19119
|
assertMeasureExist(pivotId, _measure, this.getters);
|
|
19095
|
-
assertDomainLength(
|
|
19120
|
+
assertDomainLength(domainArgs);
|
|
19096
19121
|
const pivot = this.getters.getPivot(pivotId);
|
|
19097
19122
|
const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
|
|
19098
19123
|
addPivotDependencies(this, coreDefinition);
|
|
@@ -19100,15 +19125,14 @@ const PIVOT_VALUE = {
|
|
|
19100
19125
|
if (error) {
|
|
19101
19126
|
return error;
|
|
19102
19127
|
}
|
|
19103
|
-
|
|
19104
|
-
const { value, format } = pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
19105
|
-
if (!value && !this.getters.areDomainArgsFieldsValid(pivotId, domain)) {
|
|
19128
|
+
if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
|
|
19106
19129
|
return {
|
|
19107
19130
|
value: CellErrorType.GenericError,
|
|
19108
19131
|
message: _t("Dimensions don't match the pivot definition"),
|
|
19109
19132
|
};
|
|
19110
19133
|
}
|
|
19111
|
-
|
|
19134
|
+
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19135
|
+
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
19112
19136
|
},
|
|
19113
19137
|
};
|
|
19114
19138
|
const PIVOT_HEADER = {
|
|
@@ -19120,9 +19144,8 @@ const PIVOT_HEADER = {
|
|
|
19120
19144
|
],
|
|
19121
19145
|
compute: function (pivotId, ...domainArgs) {
|
|
19122
19146
|
const _pivotFormulaId = toString(pivotId);
|
|
19123
|
-
const _domainArgs = domainArgs.map(toString);
|
|
19124
19147
|
const _pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
19125
|
-
assertDomainLength(
|
|
19148
|
+
assertDomainLength(domainArgs);
|
|
19126
19149
|
const pivot = this.getters.getPivot(_pivotId);
|
|
19127
19150
|
const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
|
|
19128
19151
|
addPivotDependencies(this, coreDefinition);
|
|
@@ -19130,14 +19153,14 @@ const PIVOT_HEADER = {
|
|
|
19130
19153
|
if (error) {
|
|
19131
19154
|
return error;
|
|
19132
19155
|
}
|
|
19133
|
-
|
|
19134
|
-
const lastNode = domain.at(-1);
|
|
19135
|
-
if (!this.getters.areDomainArgsFieldsValid(_pivotId, lastNode?.field === "measure" ? domain.slice(0, -1) : domain)) {
|
|
19156
|
+
if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
|
|
19136
19157
|
return {
|
|
19137
19158
|
value: CellErrorType.GenericError,
|
|
19138
19159
|
message: _t("Dimensions don't match the pivot definition"),
|
|
19139
19160
|
};
|
|
19140
19161
|
}
|
|
19162
|
+
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19163
|
+
const lastNode = domain.at(-1);
|
|
19141
19164
|
if (lastNode?.field === "measure") {
|
|
19142
19165
|
return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
|
|
19143
19166
|
}
|
|
@@ -19154,13 +19177,21 @@ const PIVOT = {
|
|
|
19154
19177
|
description: _t("Get a pivot table."),
|
|
19155
19178
|
args: [
|
|
19156
19179
|
arg("pivot_id (string)", _t("ID of the pivot.")),
|
|
19157
|
-
arg("row_count (number, optional
|
|
19180
|
+
arg("row_count (number, optional)", _t("number of rows")),
|
|
19158
19181
|
arg("include_total (boolean, default=TRUE)", _t("Whether to include total/sub-totals or not.")),
|
|
19159
19182
|
arg("include_column_titles (boolean, default=TRUE)", _t("Whether to include the column titles or not.")),
|
|
19183
|
+
arg("column_count (number, optional)", _t("number of columns")),
|
|
19160
19184
|
],
|
|
19161
|
-
compute: function (pivotFormulaId, rowCount = { value:
|
|
19185
|
+
compute: function (pivotFormulaId, rowCount = { value: Number.MAX_VALUE }, includeTotal = { value: true }, includeColumnHeaders = { value: true }, columnCount = { value: Number.MAX_VALUE }) {
|
|
19162
19186
|
const _pivotFormulaId = toString(pivotFormulaId);
|
|
19163
19187
|
const _rowCount = toNumber(rowCount, this.locale);
|
|
19188
|
+
if (_rowCount < 0) {
|
|
19189
|
+
throw new EvaluationError(_t("The number of rows must be positive."));
|
|
19190
|
+
}
|
|
19191
|
+
const _columnCount = toNumber(columnCount, this.locale);
|
|
19192
|
+
if (_columnCount < 0) {
|
|
19193
|
+
throw new EvaluationError(_t("The number of columns must be positive."));
|
|
19194
|
+
}
|
|
19164
19195
|
const _includeColumnHeaders = toBoolean(includeColumnHeaders);
|
|
19165
19196
|
const _includedTotal = toBoolean(includeTotal);
|
|
19166
19197
|
const pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
@@ -19176,19 +19207,15 @@ const PIVOT = {
|
|
|
19176
19207
|
const cells = table.getPivotCells(_includedTotal, _includeColumnHeaders);
|
|
19177
19208
|
const headerRows = _includeColumnHeaders ? table.columns.length : 0;
|
|
19178
19209
|
const pivotTitle = this.getters.getPivotDisplayName(pivotId);
|
|
19179
|
-
|
|
19180
|
-
|
|
19181
|
-
}
|
|
19182
|
-
const end = Math.min(headerRows + _rowCount, cells[0].length);
|
|
19183
|
-
if (end === 0) {
|
|
19210
|
+
const tableHeight = Math.min(headerRows + _rowCount, cells[0].length);
|
|
19211
|
+
if (tableHeight === 0) {
|
|
19184
19212
|
return [[{ value: pivotTitle }]];
|
|
19185
19213
|
}
|
|
19186
|
-
const tableWidth = cells.length;
|
|
19187
|
-
const tableRows = range(0, end);
|
|
19214
|
+
const tableWidth = Math.min(1 + _columnCount, cells.length);
|
|
19188
19215
|
const result = [];
|
|
19189
19216
|
for (const col of range(0, tableWidth)) {
|
|
19190
19217
|
result[col] = [];
|
|
19191
|
-
for (const row of
|
|
19218
|
+
for (const row of range(0, tableHeight)) {
|
|
19192
19219
|
const pivotCell = cells[col][row];
|
|
19193
19220
|
switch (pivotCell.type) {
|
|
19194
19221
|
case "EMPTY":
|
|
@@ -21878,9 +21905,6 @@ autoCompleteProviders.add("pivot_measures", {
|
|
|
21878
21905
|
const pivot = this.getters.getPivot(pivotId);
|
|
21879
21906
|
pivot.init();
|
|
21880
21907
|
const fields = pivot.getFields();
|
|
21881
|
-
if (!fields) {
|
|
21882
|
-
return [];
|
|
21883
|
-
}
|
|
21884
21908
|
const definition = this.getters.getPivotCoreDefinition(pivotId);
|
|
21885
21909
|
return definition.measures
|
|
21886
21910
|
.map((measure) => {
|
|
@@ -21920,9 +21944,6 @@ autoCompleteProviders.add("pivot_group_fields", {
|
|
|
21920
21944
|
const pivot = this.getters.getPivot(pivotId);
|
|
21921
21945
|
pivot.init();
|
|
21922
21946
|
const fields = pivot.getFields();
|
|
21923
|
-
if (!fields) {
|
|
21924
|
-
return;
|
|
21925
|
-
}
|
|
21926
21947
|
const { columns, rows } = pivot.definition;
|
|
21927
21948
|
let args = functionContext.args;
|
|
21928
21949
|
if (functionContext?.parent.toUpperCase() === "PIVOT.VALUE") {
|
|
@@ -27071,7 +27092,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27071
27092
|
if (getZoneArea(zone) === 1 && topLeftCell?.content) {
|
|
27072
27093
|
return {
|
|
27073
27094
|
type: "scorecard",
|
|
27074
|
-
title: {
|
|
27095
|
+
title: {},
|
|
27075
27096
|
background: topLeftCell.style?.fillColor || undefined,
|
|
27076
27097
|
keyValue: zoneToXc(zone),
|
|
27077
27098
|
baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
|
|
@@ -27079,22 +27100,11 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27079
27100
|
baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
|
|
27080
27101
|
};
|
|
27081
27102
|
}
|
|
27082
|
-
let title = "";
|
|
27083
27103
|
const cellsInFirstRow = getters.getEvaluatedCellsInZone(sheetId, {
|
|
27084
27104
|
...dataSetZone,
|
|
27085
27105
|
bottom: dataSetZone.top,
|
|
27086
27106
|
});
|
|
27087
27107
|
const dataSetsHaveTitle = !!cellsInFirstRow.find((cell) => cell.type !== CellValueType.empty && cell.type !== CellValueType.number);
|
|
27088
|
-
if (dataSetsHaveTitle) {
|
|
27089
|
-
const texts = cellsInFirstRow
|
|
27090
|
-
.filter((cell) => cell.type !== CellValueType.error && cell.type !== CellValueType.empty)
|
|
27091
|
-
.map((cell) => cell.formattedValue);
|
|
27092
|
-
const lastElement = texts.splice(-1)[0];
|
|
27093
|
-
title = texts.join(", ");
|
|
27094
|
-
if (lastElement) {
|
|
27095
|
-
title += (title ? " " + _t("and") + " " : "") + lastElement;
|
|
27096
|
-
}
|
|
27097
|
-
}
|
|
27098
27108
|
let labelRangeXc;
|
|
27099
27109
|
if (!singleColumn) {
|
|
27100
27110
|
labelRangeXc = zoneToXc({
|
|
@@ -27107,7 +27117,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27107
27117
|
const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
|
|
27108
27118
|
if (canChartParseLabels(labelRange, getters)) {
|
|
27109
27119
|
return {
|
|
27110
|
-
title: {
|
|
27120
|
+
title: {},
|
|
27111
27121
|
dataSets,
|
|
27112
27122
|
labelsAsText: false,
|
|
27113
27123
|
stacked: false,
|
|
@@ -27123,7 +27133,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27123
27133
|
if (singleColumn &&
|
|
27124
27134
|
getData(getters, _dataSets[0]).every((e) => typeof e === "string" && !isEvaluationError(e))) {
|
|
27125
27135
|
return {
|
|
27126
|
-
title: {
|
|
27136
|
+
title: {},
|
|
27127
27137
|
dataSets: [{ dataRange }],
|
|
27128
27138
|
aggregated: true,
|
|
27129
27139
|
labelRange: dataRange,
|
|
@@ -27133,7 +27143,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27133
27143
|
};
|
|
27134
27144
|
}
|
|
27135
27145
|
return {
|
|
27136
|
-
title: {
|
|
27146
|
+
title: {},
|
|
27137
27147
|
dataSets,
|
|
27138
27148
|
labelRange: labelRangeXc,
|
|
27139
27149
|
type: "bar",
|
|
@@ -34556,6 +34566,7 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34556
34566
|
currentSearchRegex = null;
|
|
34557
34567
|
isSearchDirty = false;
|
|
34558
34568
|
initialShowFormulaState;
|
|
34569
|
+
preserveSelectedMatchIndex = false;
|
|
34559
34570
|
// fixme: why do we make selectedMatchIndex on top of a selected
|
|
34560
34571
|
// property in the matches?
|
|
34561
34572
|
selectedMatchIndex = null;
|
|
@@ -34628,6 +34639,10 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34628
34639
|
case "ACTIVATE_SHEET":
|
|
34629
34640
|
this.isSearchDirty = true;
|
|
34630
34641
|
break;
|
|
34642
|
+
case "REPLACE_SEARCH":
|
|
34643
|
+
for (const match of cmd.matches) {
|
|
34644
|
+
this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
|
|
34645
|
+
}
|
|
34631
34646
|
}
|
|
34632
34647
|
}
|
|
34633
34648
|
finalize() {
|
|
@@ -34665,7 +34680,9 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34665
34680
|
* refresh the matches according to the current search options
|
|
34666
34681
|
*/
|
|
34667
34682
|
refreshSearch(jumpToMatchSheet = true) {
|
|
34668
|
-
this.
|
|
34683
|
+
if (!this.preserveSelectedMatchIndex) {
|
|
34684
|
+
this.selectedMatchIndex = null;
|
|
34685
|
+
}
|
|
34669
34686
|
this.findMatches();
|
|
34670
34687
|
this.selectNextCell(Direction.current, jumpToMatchSheet);
|
|
34671
34688
|
}
|
|
@@ -34764,10 +34781,16 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34764
34781
|
const selectedMatch = matches[nextIndex];
|
|
34765
34782
|
// Switch to the sheet where the match is located
|
|
34766
34783
|
if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
|
|
34784
|
+
// We set `preserveSelectedMatchIndex` to true to avoid resetting the selected search
|
|
34785
|
+
// index in the `refreshSearch` function when a new sheet is activated. The reason being
|
|
34786
|
+
// that, when we automatically go back to previous sheet while performing a search, the
|
|
34787
|
+
// search index is reset to the first occurrence each time.
|
|
34788
|
+
this.preserveSelectedMatchIndex = true;
|
|
34767
34789
|
this.model.dispatch("ACTIVATE_SHEET", {
|
|
34768
34790
|
sheetIdFrom: this.getters.getActiveSheetId(),
|
|
34769
34791
|
sheetIdTo: selectedMatch.sheetId,
|
|
34770
34792
|
});
|
|
34793
|
+
this.preserveSelectedMatchIndex = false;
|
|
34771
34794
|
// We do not want to reset the selection at finalize in this case
|
|
34772
34795
|
this.isSearchDirty = false;
|
|
34773
34796
|
}
|
|
@@ -34801,6 +34824,21 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34801
34824
|
searchOptions: this.searchOptions,
|
|
34802
34825
|
});
|
|
34803
34826
|
}
|
|
34827
|
+
replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
|
|
34828
|
+
const cell = this.getters.getCell(selectedMatch);
|
|
34829
|
+
if (!cell?.content) {
|
|
34830
|
+
return;
|
|
34831
|
+
}
|
|
34832
|
+
if (cell?.isFormula && !searchOptions.searchFormulas) {
|
|
34833
|
+
return;
|
|
34834
|
+
}
|
|
34835
|
+
const searchRegex = getSearchRegex(searchString, searchOptions);
|
|
34836
|
+
const replaceRegex = new RegExp(searchRegex.source, searchRegex.flags + "g");
|
|
34837
|
+
const toReplace = this.getters.getCellText(selectedMatch, searchOptions.searchFormulas);
|
|
34838
|
+
const content = toReplace.replace(replaceRegex, replaceWith);
|
|
34839
|
+
const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
|
|
34840
|
+
this.model.dispatch("UPDATE_CELL", { ...selectedMatch, content: canonicalContent });
|
|
34841
|
+
}
|
|
34804
34842
|
getSearchableString(position) {
|
|
34805
34843
|
return this.getters.getCellText(position, this.searchOptions.searchFormulas);
|
|
34806
34844
|
}
|
|
@@ -35393,6 +35431,10 @@ class CogWheelMenu extends owl.Component {
|
|
|
35393
35431
|
}
|
|
35394
35432
|
});
|
|
35395
35433
|
}
|
|
35434
|
+
onClick(item) {
|
|
35435
|
+
item.onClick();
|
|
35436
|
+
this.popover.isOpen = false;
|
|
35437
|
+
}
|
|
35396
35438
|
get popoverProps() {
|
|
35397
35439
|
const { x, y, width, height } = this.buttonRef.el.getBoundingClientRect();
|
|
35398
35440
|
return {
|
|
@@ -35435,16 +35477,22 @@ class PivotTitleSection extends owl.Component {
|
|
|
35435
35477
|
static components = { CogWheelMenu, Section, EditableName };
|
|
35436
35478
|
static props = {
|
|
35437
35479
|
pivotId: String,
|
|
35480
|
+
flipAxis: Function,
|
|
35438
35481
|
};
|
|
35439
35482
|
get cogWheelMenuItems() {
|
|
35440
35483
|
return [
|
|
35441
35484
|
{
|
|
35442
|
-
name: "
|
|
35485
|
+
name: _t("Flip axes"),
|
|
35486
|
+
icon: "fa-exchange",
|
|
35487
|
+
onClick: this.props.flipAxis,
|
|
35488
|
+
},
|
|
35489
|
+
{
|
|
35490
|
+
name: _t("Duplicate"),
|
|
35443
35491
|
icon: "fa-copy",
|
|
35444
35492
|
onClick: () => this.duplicatePivot(),
|
|
35445
35493
|
},
|
|
35446
35494
|
{
|
|
35447
|
-
name: "Delete",
|
|
35495
|
+
name: _t("Delete"),
|
|
35448
35496
|
icon: "fa-trash",
|
|
35449
35497
|
onClick: () => this.delete(),
|
|
35450
35498
|
},
|
|
@@ -35552,7 +35600,7 @@ function createMeasure(fields, measure) {
|
|
|
35552
35600
|
function createPivotDimension(fields, dimension) {
|
|
35553
35601
|
const field = fields[dimension.name];
|
|
35554
35602
|
const type = field?.type ?? "integer";
|
|
35555
|
-
const granularity = field && isDateField(field) ? dimension.granularity
|
|
35603
|
+
const granularity = field && isDateField(field) ? dimension.granularity : undefined;
|
|
35556
35604
|
return {
|
|
35557
35605
|
/**
|
|
35558
35606
|
* Get the display name of the dimension
|
|
@@ -35641,9 +35689,10 @@ class SpreadsheetPivotTable {
|
|
|
35641
35689
|
columns;
|
|
35642
35690
|
rows;
|
|
35643
35691
|
measures;
|
|
35692
|
+
fieldsType;
|
|
35644
35693
|
maxIndent;
|
|
35645
35694
|
pivotCells = {};
|
|
35646
|
-
constructor(columns, rows, measures) {
|
|
35695
|
+
constructor(columns, rows, measures, fieldsType) {
|
|
35647
35696
|
this.columns = columns.map((row) => {
|
|
35648
35697
|
// offset in the pivot table
|
|
35649
35698
|
// starts at 1 because the first column is the row title
|
|
@@ -35656,6 +35705,7 @@ class SpreadsheetPivotTable {
|
|
|
35656
35705
|
});
|
|
35657
35706
|
this.rows = rows;
|
|
35658
35707
|
this.measures = measures;
|
|
35708
|
+
this.fieldsType = fieldsType;
|
|
35659
35709
|
this.maxIndent = Math.max(...this.rows.map((row) => row.indent));
|
|
35660
35710
|
}
|
|
35661
35711
|
/**
|
|
@@ -35702,7 +35752,7 @@ class SpreadsheetPivotTable {
|
|
|
35702
35752
|
if (!domain) {
|
|
35703
35753
|
return EMPTY_PIVOT_CELL;
|
|
35704
35754
|
}
|
|
35705
|
-
const measure = domain.at(-1)?.value
|
|
35755
|
+
const measure = domain.at(-1)?.value?.toString() || "";
|
|
35706
35756
|
return { type: "MEASURE_HEADER", domain: domain.slice(0, -1), measure };
|
|
35707
35757
|
}
|
|
35708
35758
|
else if (row <= colHeadersHeight - 1) {
|
|
@@ -35734,9 +35784,13 @@ class SpreadsheetPivotTable {
|
|
|
35734
35784
|
return undefined;
|
|
35735
35785
|
}
|
|
35736
35786
|
for (let i = 0; i < pivotCol.fields.length; i++) {
|
|
35787
|
+
const fieldWithGranularity = pivotCol.fields[i];
|
|
35788
|
+
const { name, granularity } = parseDimension(fieldWithGranularity);
|
|
35789
|
+
const type = this.fieldsType[name] || "char";
|
|
35737
35790
|
domain.push({
|
|
35738
|
-
|
|
35739
|
-
|
|
35791
|
+
type,
|
|
35792
|
+
field: fieldWithGranularity,
|
|
35793
|
+
value: toNormalizedPivotValue({ displayName: name, type, granularity }, pivotCol.values[i]),
|
|
35740
35794
|
});
|
|
35741
35795
|
}
|
|
35742
35796
|
return domain;
|
|
@@ -35748,17 +35802,21 @@ class SpreadsheetPivotTable {
|
|
|
35748
35802
|
getColMeasure(col) {
|
|
35749
35803
|
const domain = this.getColHeaderDomain(col, this.columns.length - 1);
|
|
35750
35804
|
const measure = domain?.at(-1)?.value;
|
|
35751
|
-
if (measure === undefined) {
|
|
35752
|
-
throw new Error("Measure
|
|
35805
|
+
if (measure === undefined || measure === null) {
|
|
35806
|
+
throw new Error("Measure is missing");
|
|
35753
35807
|
}
|
|
35754
35808
|
return measure.toString();
|
|
35755
35809
|
}
|
|
35756
35810
|
getRowDomain(row) {
|
|
35757
35811
|
const domain = [];
|
|
35758
35812
|
for (let i = 0; i < this.rows[row].fields.length; i++) {
|
|
35813
|
+
const fieldWithGranularity = this.rows[row].fields[i];
|
|
35814
|
+
const { name, granularity } = parseDimension(fieldWithGranularity);
|
|
35815
|
+
const type = this.fieldsType[name] || "char";
|
|
35759
35816
|
domain.push({
|
|
35760
|
-
|
|
35761
|
-
|
|
35817
|
+
type,
|
|
35818
|
+
field: fieldWithGranularity,
|
|
35819
|
+
value: toNormalizedPivotValue({ displayName: name, type, granularity }, this.rows[row].values[i]),
|
|
35762
35820
|
});
|
|
35763
35821
|
}
|
|
35764
35822
|
return domain;
|
|
@@ -35768,6 +35826,7 @@ class SpreadsheetPivotTable {
|
|
|
35768
35826
|
cols: this.columns,
|
|
35769
35827
|
rows: this.rows,
|
|
35770
35828
|
measures: this.measures,
|
|
35829
|
+
fieldsType: this.fieldsType,
|
|
35771
35830
|
};
|
|
35772
35831
|
}
|
|
35773
35832
|
}
|
|
@@ -35788,7 +35847,14 @@ function dataEntriesToSpreadsheetPivotTable(dataEntries, definition) {
|
|
|
35788
35847
|
indent: 0,
|
|
35789
35848
|
});
|
|
35790
35849
|
const measureNames = definition.measures.map((m) => m.name);
|
|
35791
|
-
|
|
35850
|
+
const fieldsType = {};
|
|
35851
|
+
for (const columns of definition.columns) {
|
|
35852
|
+
fieldsType[columns.name] = columns.type;
|
|
35853
|
+
}
|
|
35854
|
+
for (const row of definition.rows) {
|
|
35855
|
+
fieldsType[row.name] = row.type;
|
|
35856
|
+
}
|
|
35857
|
+
return new SpreadsheetPivotTable(cols, rows, measureNames, fieldsType);
|
|
35792
35858
|
}
|
|
35793
35859
|
// -----------------------------------------------------------------------------
|
|
35794
35860
|
// ROWS
|
|
@@ -35964,41 +36030,42 @@ function compareDimensionValues(dimension, a, b) {
|
|
|
35964
36030
|
return dimension.order === "asc" ? a.localeCompare(b) : b.localeCompare(a);
|
|
35965
36031
|
}
|
|
35966
36032
|
|
|
36033
|
+
const NULL_SYMBOL = Symbol("NULL");
|
|
35967
36034
|
function createDate(dimension, value, locale) {
|
|
35968
|
-
const granularity = dimension.granularity
|
|
35969
|
-
if (!(granularity in MAP_VALUE_DIMENSION_DATE)) {
|
|
36035
|
+
const granularity = dimension.granularity;
|
|
36036
|
+
if (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
|
|
35970
36037
|
throw new Error(`Unknown date granularity: ${granularity}`);
|
|
35971
36038
|
}
|
|
35972
|
-
|
|
35973
|
-
return null;
|
|
35974
|
-
}
|
|
36039
|
+
const keyInMap = typeof value === "number" || typeof value === "string" ? value : NULL_SYMBOL;
|
|
35975
36040
|
if (!MAP_VALUE_DIMENSION_DATE[granularity].set.has(value)) {
|
|
35976
36041
|
MAP_VALUE_DIMENSION_DATE[granularity].set.add(value);
|
|
35977
|
-
|
|
35978
|
-
|
|
35979
|
-
|
|
35980
|
-
|
|
35981
|
-
|
|
35982
|
-
|
|
35983
|
-
|
|
35984
|
-
|
|
35985
|
-
|
|
35986
|
-
|
|
35987
|
-
|
|
35988
|
-
|
|
35989
|
-
|
|
35990
|
-
|
|
35991
|
-
|
|
35992
|
-
|
|
35993
|
-
|
|
35994
|
-
|
|
35995
|
-
|
|
35996
|
-
|
|
35997
|
-
|
|
36042
|
+
let number = null;
|
|
36043
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
36044
|
+
const date = toJsDate(value, locale);
|
|
36045
|
+
switch (granularity) {
|
|
36046
|
+
case "year_number":
|
|
36047
|
+
number = date.getFullYear();
|
|
36048
|
+
break;
|
|
36049
|
+
case "quarter_number":
|
|
36050
|
+
number = Math.floor(date.getMonth() / 3) + 1;
|
|
36051
|
+
break;
|
|
36052
|
+
case "month_number":
|
|
36053
|
+
number = date.getMonth() + 1;
|
|
36054
|
+
break;
|
|
36055
|
+
case "iso_week_number":
|
|
36056
|
+
number = date.getIsoWeek();
|
|
36057
|
+
break;
|
|
36058
|
+
case "day_of_month":
|
|
36059
|
+
number = date.getDate();
|
|
36060
|
+
break;
|
|
36061
|
+
case "day":
|
|
36062
|
+
number = Math.floor(toNumber(value, locale));
|
|
36063
|
+
break;
|
|
36064
|
+
}
|
|
35998
36065
|
}
|
|
35999
|
-
MAP_VALUE_DIMENSION_DATE[granularity].values[
|
|
36066
|
+
MAP_VALUE_DIMENSION_DATE[granularity].values[keyInMap] = toNormalizedPivotValue(dimension, number);
|
|
36000
36067
|
}
|
|
36001
|
-
return MAP_VALUE_DIMENSION_DATE[granularity].values[
|
|
36068
|
+
return MAP_VALUE_DIMENSION_DATE[granularity].values[keyInMap];
|
|
36002
36069
|
}
|
|
36003
36070
|
/**
|
|
36004
36071
|
* This map is used to cache the different values of a pivot date value
|
|
@@ -36180,6 +36247,25 @@ class SpreadsheetPivot {
|
|
|
36180
36247
|
}
|
|
36181
36248
|
return undefined;
|
|
36182
36249
|
}
|
|
36250
|
+
areDomainArgsFieldsValid(args) {
|
|
36251
|
+
let dimensions = args.filter((_, index) => index % 2 === 0).map(toString);
|
|
36252
|
+
if (dimensions.length && dimensions.at(-1) === "measure") {
|
|
36253
|
+
dimensions = dimensions.slice(0, -1);
|
|
36254
|
+
}
|
|
36255
|
+
return areDomainArgsFieldsValid(dimensions, this.definition);
|
|
36256
|
+
}
|
|
36257
|
+
parseArgsToPivotDomain(args) {
|
|
36258
|
+
const domain = [];
|
|
36259
|
+
for (let i = 0; i < args.length - 1; i += 2) {
|
|
36260
|
+
const fieldWithGranularity = toString(args[i]);
|
|
36261
|
+
const type = this.getTypeOfDimension(fieldWithGranularity);
|
|
36262
|
+
const normalizedValue = fieldWithGranularity === "measure"
|
|
36263
|
+
? toString(args[i + 1])
|
|
36264
|
+
: toNormalizedPivotValue(this.getDimension(fieldWithGranularity), args[i + 1]);
|
|
36265
|
+
domain.push({ field: fieldWithGranularity, value: normalizedValue, type });
|
|
36266
|
+
}
|
|
36267
|
+
return domain;
|
|
36268
|
+
}
|
|
36183
36269
|
markAsDirtyForEvaluation() {
|
|
36184
36270
|
this.needsReevaluation = true;
|
|
36185
36271
|
}
|
|
@@ -36201,18 +36287,12 @@ class SpreadsheetPivot {
|
|
|
36201
36287
|
const finalCell = cells[0]?.[dimension.nameWithGranularity];
|
|
36202
36288
|
if (dimension.type === "date") {
|
|
36203
36289
|
const adapter = pivotTimeAdapter(dimension.granularity);
|
|
36204
|
-
return
|
|
36205
|
-
value: lastNode.value !== "null"
|
|
36206
|
-
? adapter.toCellValue(toNormalizedPivotValue(dimension, lastNode.value))
|
|
36207
|
-
: _t("(Undefined)"),
|
|
36208
|
-
format: adapter.getFormat(this.getters.getLocale()),
|
|
36209
|
-
};
|
|
36290
|
+
return adapter.toValueAndFormat(lastNode.value, this.getters.getLocale());
|
|
36210
36291
|
}
|
|
36211
36292
|
if (!finalCell) {
|
|
36212
36293
|
return { value: "" };
|
|
36213
36294
|
}
|
|
36214
|
-
|
|
36215
|
-
if (finalCell.value === null || finalCell.value === `${null}`) {
|
|
36295
|
+
if (finalCell.value === null) {
|
|
36216
36296
|
return { value: _t("(Undefined)") };
|
|
36217
36297
|
}
|
|
36218
36298
|
return {
|
|
@@ -36263,14 +36343,24 @@ class SpreadsheetPivot {
|
|
|
36263
36343
|
getFields() {
|
|
36264
36344
|
return this.fields;
|
|
36265
36345
|
}
|
|
36346
|
+
getTypeOfDimension(fieldWithGranularity) {
|
|
36347
|
+
if (fieldWithGranularity === "measure") {
|
|
36348
|
+
return "char";
|
|
36349
|
+
}
|
|
36350
|
+
const { name } = parseDimension(fieldWithGranularity);
|
|
36351
|
+
const type = this.fields[name]?.type;
|
|
36352
|
+
if (!type) {
|
|
36353
|
+
throw new Error(`Field ${name} does not exist`);
|
|
36354
|
+
}
|
|
36355
|
+
return type;
|
|
36356
|
+
}
|
|
36266
36357
|
filterDataEntriesFromDomain(dataEntries, domain) {
|
|
36267
36358
|
return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
|
|
36268
36359
|
}
|
|
36269
36360
|
filterDataEntriesFromDomainNode(dataEntries, domain) {
|
|
36270
36361
|
const { field, value } = domain;
|
|
36271
|
-
const
|
|
36272
|
-
return dataEntries.filter((entry) =>
|
|
36273
|
-
`${toNormalizedPivotValue(dimension, value)}`);
|
|
36362
|
+
const { nameWithGranularity } = this.getDimension(field);
|
|
36363
|
+
return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
|
|
36274
36364
|
}
|
|
36275
36365
|
getDimension(nameWithGranularity) {
|
|
36276
36366
|
return this.definition.getDimension(nameWithGranularity);
|
|
@@ -36375,7 +36465,7 @@ class SpreadsheetPivot {
|
|
|
36375
36465
|
for (const entry of dataEntries) {
|
|
36376
36466
|
for (const dimension of dateDimensions) {
|
|
36377
36467
|
entry[dimension.nameWithGranularity] = {
|
|
36378
|
-
value:
|
|
36468
|
+
value: createDate(dimension, entry[dimension.name]?.value || null, this.getters.getLocale()),
|
|
36379
36469
|
type: entry[dimension.name]?.type || CellValueType.empty,
|
|
36380
36470
|
format: entry[dimension.name]?.format,
|
|
36381
36471
|
};
|
|
@@ -36422,11 +36512,7 @@ class PivotSidePanelStore extends SpreadsheetStore {
|
|
|
36422
36512
|
}
|
|
36423
36513
|
}
|
|
36424
36514
|
get fields() {
|
|
36425
|
-
|
|
36426
|
-
if (!fields) {
|
|
36427
|
-
throw new Error("Fields not found");
|
|
36428
|
-
}
|
|
36429
|
-
return fields;
|
|
36515
|
+
return this.pivot.getFields();
|
|
36430
36516
|
}
|
|
36431
36517
|
get pivot() {
|
|
36432
36518
|
return this.getters.getPivot(this.pivotId);
|
|
@@ -36660,6 +36746,13 @@ class PivotSpreadsheetSidePanel extends owl.Component {
|
|
|
36660
36746
|
this.store.applyUpdate();
|
|
36661
36747
|
}
|
|
36662
36748
|
}
|
|
36749
|
+
flipAxis() {
|
|
36750
|
+
const { rows, columns } = this.definition;
|
|
36751
|
+
this.onDimensionsUpdated({
|
|
36752
|
+
rows: columns,
|
|
36753
|
+
columns: rows,
|
|
36754
|
+
});
|
|
36755
|
+
}
|
|
36663
36756
|
onDimensionsUpdated(definition) {
|
|
36664
36757
|
this.store.update(definition);
|
|
36665
36758
|
}
|
|
@@ -51531,8 +51624,8 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
51531
51624
|
case "INSERT_PIVOT": {
|
|
51532
51625
|
const { sheetId, col, row, pivotId, table } = cmd;
|
|
51533
51626
|
const position = { sheetId, col, row };
|
|
51534
|
-
const { cols, rows, measures } = table;
|
|
51535
|
-
const spTable = new SpreadsheetPivotTable(cols, rows, measures);
|
|
51627
|
+
const { cols, rows, measures, fieldsType } = table;
|
|
51628
|
+
const spTable = new SpreadsheetPivotTable(cols, rows, measures, fieldsType || {});
|
|
51536
51629
|
const formulaId = this.getPivotFormulaId(pivotId);
|
|
51537
51630
|
this.insertPivot(position, formulaId, spTable);
|
|
51538
51631
|
break;
|
|
@@ -51613,7 +51706,7 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
51613
51706
|
sheetId: position.sheetId,
|
|
51614
51707
|
col: position.col + col,
|
|
51615
51708
|
row: position.row + row,
|
|
51616
|
-
content:
|
|
51709
|
+
content: createPivotFormula(formulaId, pivotCell),
|
|
51617
51710
|
});
|
|
51618
51711
|
}
|
|
51619
51712
|
}
|
|
@@ -54879,7 +54972,6 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54879
54972
|
"getPivotIdFromPosition",
|
|
54880
54973
|
"getPivotCellFromPosition",
|
|
54881
54974
|
"isPivotUnused",
|
|
54882
|
-
"areDomainArgsFieldsValid",
|
|
54883
54975
|
"isSpillPivotFormula",
|
|
54884
54976
|
];
|
|
54885
54977
|
pivots = {};
|
|
@@ -55011,19 +55103,19 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55011
55103
|
return EMPTY_PIVOT_CELL;
|
|
55012
55104
|
}
|
|
55013
55105
|
const { functionName, args } = result;
|
|
55106
|
+
const formulaId = args[0];
|
|
55107
|
+
if (!formulaId) {
|
|
55108
|
+
return EMPTY_PIVOT_CELL;
|
|
55109
|
+
}
|
|
55110
|
+
const pivotId = this.getters.getPivotId(formulaId.toString());
|
|
55111
|
+
if (!pivotId) {
|
|
55112
|
+
return EMPTY_PIVOT_CELL;
|
|
55113
|
+
}
|
|
55114
|
+
const pivot = this.getPivot(pivotId);
|
|
55115
|
+
if (!pivot.isValid()) {
|
|
55116
|
+
return EMPTY_PIVOT_CELL;
|
|
55117
|
+
}
|
|
55014
55118
|
if (functionName === "PIVOT") {
|
|
55015
|
-
const formulaId = args[0];
|
|
55016
|
-
if (!formulaId) {
|
|
55017
|
-
return EMPTY_PIVOT_CELL;
|
|
55018
|
-
}
|
|
55019
|
-
const pivotId = this.getters.getPivotId(formulaId.toString());
|
|
55020
|
-
if (!pivotId) {
|
|
55021
|
-
return EMPTY_PIVOT_CELL;
|
|
55022
|
-
}
|
|
55023
|
-
const pivot = this.getPivot(pivotId);
|
|
55024
|
-
if (!pivot.isValid()) {
|
|
55025
|
-
return EMPTY_PIVOT_CELL;
|
|
55026
|
-
}
|
|
55027
55119
|
const includeTotal = args[2] === false ? false : undefined;
|
|
55028
55120
|
const includeColumnHeaders = args[3] === false ? false : undefined;
|
|
55029
55121
|
const pivotCells = pivot
|
|
@@ -55034,7 +55126,7 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55034
55126
|
return pivotCells[pivotCol][pivotRow];
|
|
55035
55127
|
}
|
|
55036
55128
|
if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
|
|
55037
|
-
const domain =
|
|
55129
|
+
const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
|
|
55038
55130
|
return {
|
|
55039
55131
|
type: "MEASURE_HEADER",
|
|
55040
55132
|
domain,
|
|
@@ -55042,15 +55134,17 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55042
55134
|
};
|
|
55043
55135
|
}
|
|
55044
55136
|
else if (functionName === "PIVOT.HEADER") {
|
|
55137
|
+
const domain = pivot.parseArgsToPivotDomain(args.slice(1).map((value) => ({ value })));
|
|
55045
55138
|
return {
|
|
55046
55139
|
type: "HEADER",
|
|
55047
|
-
domain
|
|
55140
|
+
domain,
|
|
55048
55141
|
};
|
|
55049
55142
|
}
|
|
55050
55143
|
const [measure, ...domainArgs] = args.slice(1);
|
|
55144
|
+
const domain = pivot.parseArgsToPivotDomain(domainArgs.map((value) => ({ value })));
|
|
55051
55145
|
return {
|
|
55052
55146
|
type: "VALUE",
|
|
55053
|
-
domain
|
|
55147
|
+
domain,
|
|
55054
55148
|
measure: measure?.toString() || "",
|
|
55055
55149
|
};
|
|
55056
55150
|
}
|
|
@@ -55060,32 +55154,6 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55060
55154
|
isPivotUnused(pivotId) {
|
|
55061
55155
|
return this._getUnusedPivots().includes(pivotId);
|
|
55062
55156
|
}
|
|
55063
|
-
/**
|
|
55064
|
-
* Check if the fields in the domain part of
|
|
55065
|
-
* a pivot function are valid according to the pivot definition.
|
|
55066
|
-
* e.g. =PIVOT.VALUE(1,"revenue","country_id",...,"create_date:month",...,"source_id",...)
|
|
55067
|
-
*/
|
|
55068
|
-
areDomainArgsFieldsValid(pivotId, domain) {
|
|
55069
|
-
const dimensions = domain
|
|
55070
|
-
.map((node) => node.field)
|
|
55071
|
-
.map((name) => (name.startsWith("#") ? name.slice(1) : name));
|
|
55072
|
-
let argIndex = 0;
|
|
55073
|
-
let definitionIndex = 0;
|
|
55074
|
-
const pivot = this.getPivot(pivotId);
|
|
55075
|
-
const definition = pivot.definition;
|
|
55076
|
-
const cols = definition.columns.map((col) => col.nameWithGranularity);
|
|
55077
|
-
const rows = definition.rows.map((row) => row.nameWithGranularity);
|
|
55078
|
-
while (dimensions[argIndex] !== undefined && dimensions[argIndex] === rows[definitionIndex]) {
|
|
55079
|
-
argIndex++;
|
|
55080
|
-
definitionIndex++;
|
|
55081
|
-
}
|
|
55082
|
-
definitionIndex = 0;
|
|
55083
|
-
while (dimensions[argIndex] !== undefined && dimensions[argIndex] === cols[definitionIndex]) {
|
|
55084
|
-
argIndex++;
|
|
55085
|
-
definitionIndex++;
|
|
55086
|
-
}
|
|
55087
|
-
return dimensions.length === argIndex;
|
|
55088
|
-
}
|
|
55089
55157
|
// ---------------------------------------------------------------------
|
|
55090
55158
|
// Private
|
|
55091
55159
|
// ---------------------------------------------------------------------
|
|
@@ -56923,43 +56991,6 @@ class DataCleanupPlugin extends UIPlugin {
|
|
|
56923
56991
|
}
|
|
56924
56992
|
}
|
|
56925
56993
|
|
|
56926
|
-
/**
|
|
56927
|
-
* Find and Replace Plugin
|
|
56928
|
-
*
|
|
56929
|
-
* This plugin is used in combination with the find_and_replace sidePanel
|
|
56930
|
-
* It is used to 'highlight' cells that match an input string according to
|
|
56931
|
-
* the given searchOptions. The second part of this plugin makes it possible
|
|
56932
|
-
* (again with the find_and_replace sidePanel), to replace the values that match
|
|
56933
|
-
* the search with a new value.
|
|
56934
|
-
*/
|
|
56935
|
-
class FindAndReplacePlugin extends UIPlugin {
|
|
56936
|
-
static getters = [];
|
|
56937
|
-
handle(cmd) {
|
|
56938
|
-
switch (cmd.type) {
|
|
56939
|
-
case "REPLACE_SEARCH":
|
|
56940
|
-
for (const match of cmd.matches) {
|
|
56941
|
-
this.replaceMatch(match, cmd.searchString, cmd.replaceWith, cmd.searchOptions);
|
|
56942
|
-
}
|
|
56943
|
-
break;
|
|
56944
|
-
}
|
|
56945
|
-
}
|
|
56946
|
-
replaceMatch(selectedMatch, searchString, replaceWith, searchOptions) {
|
|
56947
|
-
const cell = this.getters.getCell(selectedMatch);
|
|
56948
|
-
if (!cell?.content) {
|
|
56949
|
-
return;
|
|
56950
|
-
}
|
|
56951
|
-
if (cell?.isFormula && !searchOptions.searchFormulas) {
|
|
56952
|
-
return;
|
|
56953
|
-
}
|
|
56954
|
-
const searchRegex = getSearchRegex(searchString, searchOptions);
|
|
56955
|
-
const replaceRegex = new RegExp(searchRegex.source, searchRegex.flags + "g");
|
|
56956
|
-
const toReplace = this.getters.getCellText(selectedMatch, searchOptions.searchFormulas);
|
|
56957
|
-
const content = toReplace.replace(replaceRegex, replaceWith);
|
|
56958
|
-
const canonicalContent = canonicalizeNumberContent(content, this.getters.getLocale());
|
|
56959
|
-
this.dispatch("UPDATE_CELL", { ...selectedMatch, content: canonicalContent });
|
|
56960
|
-
}
|
|
56961
|
-
}
|
|
56962
|
-
|
|
56963
56994
|
class FormatPlugin extends UIPlugin {
|
|
56964
56995
|
// ---------------------------------------------------------------------------
|
|
56965
56996
|
// Command Handling
|
|
@@ -58605,32 +58636,32 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58605
58636
|
}
|
|
58606
58637
|
}
|
|
58607
58638
|
convertOSClipboardData(clipboardData) {
|
|
58608
|
-
const handlers =
|
|
58609
|
-
.getAll()
|
|
58610
|
-
.map((handler) => new handler(this.getters, this.dispatch));
|
|
58611
|
-
clipboardHandlersRegistries.cellHandlers
|
|
58612
|
-
.getAll()
|
|
58613
|
-
.forEach((handler) => handlers.push(new handler(this.getters, this.dispatch)));
|
|
58639
|
+
const handlers = this.selectClipboardHandlers({ figureId: true }).concat(this.selectClipboardHandlers({}));
|
|
58614
58640
|
let copiedData = {};
|
|
58615
|
-
for (const handler of handlers) {
|
|
58641
|
+
for (const { handlerName, handler } of handlers) {
|
|
58616
58642
|
const data = handler.convertOSClipboardData(clipboardData);
|
|
58617
|
-
copiedData =
|
|
58643
|
+
copiedData[handlerName] = data;
|
|
58644
|
+
const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
|
|
58645
|
+
for (const key of minimalKeys) {
|
|
58646
|
+
if (data && key in data) {
|
|
58647
|
+
copiedData[key] = data[key];
|
|
58648
|
+
}
|
|
58649
|
+
}
|
|
58618
58650
|
}
|
|
58619
58651
|
return copiedData;
|
|
58620
58652
|
}
|
|
58621
58653
|
selectClipboardHandlers(data) {
|
|
58622
|
-
|
|
58623
|
-
|
|
58624
|
-
|
|
58625
|
-
|
|
58626
|
-
|
|
58627
|
-
|
|
58628
|
-
|
|
58629
|
-
.map((handler) => new handler(this.getters, this.dispatch));
|
|
58654
|
+
const handlersRegistry = "figureId" in data
|
|
58655
|
+
? clipboardHandlersRegistries.figureHandlers
|
|
58656
|
+
: clipboardHandlersRegistries.cellHandlers;
|
|
58657
|
+
return handlersRegistry.getKeys().map((handlerName) => {
|
|
58658
|
+
const Handler = handlersRegistry.get(handlerName);
|
|
58659
|
+
return { handlerName, handler: new Handler(this.getters, this.dispatch) };
|
|
58660
|
+
});
|
|
58630
58661
|
}
|
|
58631
58662
|
isCutAllowedOn(zones) {
|
|
58632
58663
|
const clipboardData = this.getClipboardData(zones);
|
|
58633
|
-
for (const handler of this.selectClipboardHandlers(clipboardData)) {
|
|
58664
|
+
for (const { handler } of this.selectClipboardHandlers(clipboardData)) {
|
|
58634
58665
|
const result = handler.isCutAllowed(clipboardData);
|
|
58635
58666
|
if (result !== "Success" /* CommandResult.Success */) {
|
|
58636
58667
|
return result;
|
|
@@ -58639,7 +58670,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58639
58670
|
return "Success" /* CommandResult.Success */;
|
|
58640
58671
|
}
|
|
58641
58672
|
isPasteAllowed(target, copiedData, options) {
|
|
58642
|
-
for (const handler of this.selectClipboardHandlers(copiedData)) {
|
|
58673
|
+
for (const { handler } of this.selectClipboardHandlers(copiedData)) {
|
|
58643
58674
|
const result = handler.isPasteAllowed(this.getters.getActiveSheetId(), target, copiedData, {
|
|
58644
58675
|
...options,
|
|
58645
58676
|
});
|
|
@@ -58667,9 +58698,15 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58667
58698
|
copy(zones) {
|
|
58668
58699
|
let copiedData = {};
|
|
58669
58700
|
const clipboardData = this.getClipboardData(zones);
|
|
58670
|
-
for (const handler of this.selectClipboardHandlers(clipboardData)) {
|
|
58701
|
+
for (const { handlerName, handler } of this.selectClipboardHandlers(clipboardData)) {
|
|
58671
58702
|
const data = handler.copy(clipboardData);
|
|
58672
|
-
copiedData =
|
|
58703
|
+
copiedData[handlerName] = data;
|
|
58704
|
+
const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
|
|
58705
|
+
for (const key of minimalKeys) {
|
|
58706
|
+
if (data && key in data) {
|
|
58707
|
+
copiedData[key] = data[key];
|
|
58708
|
+
}
|
|
58709
|
+
}
|
|
58673
58710
|
}
|
|
58674
58711
|
return copiedData;
|
|
58675
58712
|
}
|
|
@@ -58685,8 +58722,12 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58685
58722
|
zones,
|
|
58686
58723
|
};
|
|
58687
58724
|
const handlers = this.selectClipboardHandlers(copiedData);
|
|
58688
|
-
for (const handler of handlers) {
|
|
58689
|
-
const
|
|
58725
|
+
for (const { handlerName, handler } of handlers) {
|
|
58726
|
+
const handlerData = copiedData[handlerName];
|
|
58727
|
+
if (!handlerData) {
|
|
58728
|
+
continue;
|
|
58729
|
+
}
|
|
58730
|
+
const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
|
|
58690
58731
|
if (currentTarget.figureId) {
|
|
58691
58732
|
target.figureId = currentTarget.figureId;
|
|
58692
58733
|
}
|
|
@@ -58702,7 +58743,12 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58702
58743
|
if (zone !== undefined) {
|
|
58703
58744
|
this.addMissingDimensions(this.getters.getActiveSheetId(), zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
|
|
58704
58745
|
}
|
|
58705
|
-
handlers.forEach((handler) =>
|
|
58746
|
+
handlers.forEach(({ handlerName, handler }) => {
|
|
58747
|
+
const handlerData = copiedData[handlerName];
|
|
58748
|
+
if (handlerData) {
|
|
58749
|
+
handler.paste(target, handlerData, options);
|
|
58750
|
+
}
|
|
58751
|
+
});
|
|
58706
58752
|
if (!options?.selectTarget) {
|
|
58707
58753
|
return;
|
|
58708
58754
|
}
|
|
@@ -60774,7 +60820,6 @@ const featurePluginRegistry = new Registry()
|
|
|
60774
60820
|
.add("ui_sheet", SheetUIPlugin)
|
|
60775
60821
|
.add("ui_options", UIOptionsPlugin)
|
|
60776
60822
|
.add("autofill", AutofillPlugin)
|
|
60777
|
-
.add("find_and_replace", FindAndReplacePlugin)
|
|
60778
60823
|
.add("sort", SortPlugin)
|
|
60779
60824
|
.add("automatic_sum", AutomaticSumPlugin)
|
|
60780
60825
|
.add("format", FormatPlugin)
|
|
@@ -66823,6 +66868,7 @@ class Model extends EventBus {
|
|
|
66823
66868
|
const start = performance.now();
|
|
66824
66869
|
console.group("Model creation");
|
|
66825
66870
|
super();
|
|
66871
|
+
setDefaultTranslationMethod();
|
|
66826
66872
|
stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
|
|
66827
66873
|
const workbookData = load(data, verboseImport);
|
|
66828
66874
|
this.state = new StateObserver();
|
|
@@ -67318,6 +67364,7 @@ const registries = {
|
|
|
67318
67364
|
pivotSidePanelRegistry,
|
|
67319
67365
|
pivotNormalizationValueRegistry,
|
|
67320
67366
|
supportedPivotPositionalFormulaRegistry,
|
|
67367
|
+
pivotToFunctionValueRegistry,
|
|
67321
67368
|
};
|
|
67322
67369
|
const helpers = {
|
|
67323
67370
|
arg,
|
|
@@ -67363,7 +67410,6 @@ const helpers = {
|
|
|
67363
67410
|
expandZoneOnInsertion,
|
|
67364
67411
|
reduceZoneOnDeletion,
|
|
67365
67412
|
unquote,
|
|
67366
|
-
makePivotFormula,
|
|
67367
67413
|
getMaxObjectId,
|
|
67368
67414
|
getFunctionsFromTokens,
|
|
67369
67415
|
getFirstPivotFunction,
|
|
@@ -67375,10 +67421,10 @@ const helpers = {
|
|
|
67375
67421
|
insertTokenAfterLeftParenthesis,
|
|
67376
67422
|
mergeContiguousZones,
|
|
67377
67423
|
getPivotHighlights,
|
|
67378
|
-
toPivotDomain,
|
|
67379
|
-
flatPivotDomain,
|
|
67380
67424
|
pivotTimeAdapter,
|
|
67381
67425
|
UNDO_REDO_PIVOT_COMMANDS,
|
|
67426
|
+
createPivotFormula,
|
|
67427
|
+
areDomainArgsFieldsValid,
|
|
67382
67428
|
};
|
|
67383
67429
|
const links = {
|
|
67384
67430
|
isMarkdownLink,
|
|
@@ -67509,6 +67555,6 @@ exports.tokenColors = tokenColors;
|
|
|
67509
67555
|
exports.tokenize = tokenize;
|
|
67510
67556
|
|
|
67511
67557
|
|
|
67512
|
-
__info__.version = "17.4.0-alpha.
|
|
67513
|
-
__info__.date = "2024-06-
|
|
67514
|
-
__info__.hash = "
|
|
67558
|
+
__info__.version = "17.4.0-alpha.8";
|
|
67559
|
+
__info__.date = "2024-06-24T19:51:16.144Z";
|
|
67560
|
+
__info__.hash = "ccd30df";
|