@odoo/o-spreadsheet 17.4.0-alpha.7 → 17.4.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o-spreadsheet.cjs.js +811 -742
- package/dist/o-spreadsheet.d.ts +47 -31
- package/dist/o-spreadsheet.esm.js +811 -742
- package/dist/o-spreadsheet.iife.js +811 -742
- package/dist/o-spreadsheet.iife.min.js +345 -345
- package/dist/o_spreadsheet.xml +5 -5
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 17.4.0-alpha.
|
|
6
|
-
* @date 2024-06-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 17.4.0-alpha.9
|
|
6
|
+
* @date 2024-06-26T11:09:20.284Z
|
|
7
|
+
* @hash 526be20
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -1903,9 +1903,10 @@ function percentile(values, percent, isInclusive) {
|
|
|
1903
1903
|
sortedValues[indexLow] * (indexSup - percentIndex));
|
|
1904
1904
|
}
|
|
1905
1905
|
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
let
|
|
1906
|
+
const defaultTranslate = (s) => s;
|
|
1907
|
+
const defaultLoaded = () => false;
|
|
1908
|
+
let _translate = defaultTranslate;
|
|
1909
|
+
let _loaded = defaultLoaded;
|
|
1909
1910
|
function sprintf(s, ...values) {
|
|
1910
1911
|
if (values.length === 1 && typeof values[0] === "object" && !(values[0] instanceof String)) {
|
|
1911
1912
|
const valuesDict = values[0];
|
|
@@ -1917,7 +1918,8 @@ function sprintf(s, ...values) {
|
|
|
1917
1918
|
return s;
|
|
1918
1919
|
}
|
|
1919
1920
|
/***
|
|
1920
|
-
* 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.
|
|
1921
1923
|
* @param tfn the function that will do the translation
|
|
1922
1924
|
* @param loaded a function that returns true when the translation is loaded
|
|
1923
1925
|
*/
|
|
@@ -1925,6 +1927,18 @@ function setTranslationMethod(tfn, loaded = () => true) {
|
|
|
1925
1927
|
_translate = tfn;
|
|
1926
1928
|
_loaded = loaded;
|
|
1927
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
|
+
}
|
|
1928
1942
|
const _t = function (s, ...values) {
|
|
1929
1943
|
if (!_loaded()) {
|
|
1930
1944
|
return new LazyTranslatedString(s, values);
|
|
@@ -5620,18 +5634,12 @@ class BorderClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
5620
5634
|
return { borders };
|
|
5621
5635
|
}
|
|
5622
5636
|
paste(target, content, options) {
|
|
5623
|
-
if (!content) {
|
|
5624
|
-
return;
|
|
5625
|
-
}
|
|
5626
5637
|
const sheetId = target.sheetId;
|
|
5627
|
-
if (options
|
|
5628
|
-
return;
|
|
5629
|
-
}
|
|
5630
|
-
if (!("borders" in content) || !("zones" in target) || !target.zones.length) {
|
|
5638
|
+
if (options.pasteOption === "asValue") {
|
|
5631
5639
|
return;
|
|
5632
5640
|
}
|
|
5633
5641
|
const zones = target.zones;
|
|
5634
|
-
if (!options
|
|
5642
|
+
if (!options.isCutOperation) {
|
|
5635
5643
|
this.pasteFromCopy(sheetId, zones, content.borders);
|
|
5636
5644
|
}
|
|
5637
5645
|
else {
|
|
@@ -6142,6 +6150,530 @@ function _localizeFormula(formula, fromLocale, toLocale) {
|
|
|
6142
6150
|
return localizedFormula;
|
|
6143
6151
|
}
|
|
6144
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
|
+
|
|
6145
6677
|
class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
6146
6678
|
isCutAllowed(data) {
|
|
6147
6679
|
if (data.zones.length !== 1) {
|
|
@@ -6150,40 +6682,48 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6150
6682
|
return "Success" /* CommandResult.Success */;
|
|
6151
6683
|
}
|
|
6152
6684
|
copy(data) {
|
|
6153
|
-
if (!("zones" in data) || !data.zones.length) {
|
|
6154
|
-
return;
|
|
6155
|
-
}
|
|
6156
6685
|
const sheetId = data.sheetId;
|
|
6157
|
-
const zones = data.zones;
|
|
6158
|
-
if (!zones.length) {
|
|
6159
|
-
return {
|
|
6160
|
-
cells: [[]],
|
|
6161
|
-
zones: [],
|
|
6162
|
-
sheetId,
|
|
6163
|
-
};
|
|
6164
|
-
}
|
|
6165
6686
|
const { clippedZones, rowsIndexes, columnsIndexes } = data;
|
|
6166
6687
|
const clippedCells = [];
|
|
6688
|
+
const isCopyingOneCell = rowsIndexes.length == 1 && columnsIndexes.length == 1;
|
|
6167
6689
|
for (let row of rowsIndexes) {
|
|
6168
6690
|
let cellsInRow = [];
|
|
6169
6691
|
for (let col of columnsIndexes) {
|
|
6170
6692
|
const position = { col, row, sheetId };
|
|
6171
|
-
const spreader = this.getters.getArrayFormulaSpreadingOn(position);
|
|
6172
6693
|
let cell = this.getters.getCell(position);
|
|
6173
6694
|
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
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
|
+
}
|
|
6187
6727
|
}
|
|
6188
6728
|
cellsInRow.push({
|
|
6189
6729
|
cell,
|
|
@@ -6232,12 +6772,9 @@ class CellClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6232
6772
|
* Paste the clipboard content in the given target
|
|
6233
6773
|
*/
|
|
6234
6774
|
paste(target, content, options) {
|
|
6235
|
-
if (!("cells" in content) || !("zones" in target) || !target.zones.length) {
|
|
6236
|
-
return;
|
|
6237
|
-
}
|
|
6238
6775
|
const zones = target.zones;
|
|
6239
6776
|
const sheetId = target.sheetId;
|
|
6240
|
-
if (!options
|
|
6777
|
+
if (!options.isCutOperation) {
|
|
6241
6778
|
this.pasteFromCopy(sheetId, zones, content.cells, options);
|
|
6242
6779
|
}
|
|
6243
6780
|
else {
|
|
@@ -6407,14 +6944,11 @@ class ChartClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6407
6944
|
};
|
|
6408
6945
|
}
|
|
6409
6946
|
getPasteTarget(sheetId, target, content, options) {
|
|
6410
|
-
if (!content?.copiedFigure || !content?.copiedChart) {
|
|
6411
|
-
return { zones: [], sheetId };
|
|
6412
|
-
}
|
|
6413
6947
|
const newId = new UuidGenerator().uuidv4();
|
|
6414
6948
|
return { zones: [], figureId: newId, sheetId };
|
|
6415
6949
|
}
|
|
6416
6950
|
paste(target, clippedContent, options) {
|
|
6417
|
-
if (!
|
|
6951
|
+
if (!target.figureId) {
|
|
6418
6952
|
return;
|
|
6419
6953
|
}
|
|
6420
6954
|
const { zones, figureId } = target;
|
|
@@ -6438,7 +6972,7 @@ class ChartClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6438
6972
|
size: { height, width },
|
|
6439
6973
|
definition: copy.getDefinition(),
|
|
6440
6974
|
});
|
|
6441
|
-
if (options
|
|
6975
|
+
if (options.isCutOperation) {
|
|
6442
6976
|
this.dispatch("DELETE_FIGURE", {
|
|
6443
6977
|
sheetId: clippedContent.copiedChart.sheetId,
|
|
6444
6978
|
id: clippedContent.copiedFigure.id,
|
|
@@ -6480,15 +7014,12 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6480
7014
|
return { cfRules };
|
|
6481
7015
|
}
|
|
6482
7016
|
paste(target, clippedContent, options) {
|
|
6483
|
-
if (
|
|
6484
|
-
options?.pasteOption === "asValue" ||
|
|
6485
|
-
!("zones" in target) ||
|
|
6486
|
-
!target.zones.length) {
|
|
7017
|
+
if (options.pasteOption === "asValue") {
|
|
6487
7018
|
return;
|
|
6488
7019
|
}
|
|
6489
7020
|
const zones = target.zones;
|
|
6490
7021
|
const sheetId = target.sheetId;
|
|
6491
|
-
if (!options
|
|
7022
|
+
if (!options.isCutOperation) {
|
|
6492
7023
|
this.pasteFromCopy(sheetId, zones, clippedContent.cfRules, options);
|
|
6493
7024
|
}
|
|
6494
7025
|
else {
|
|
@@ -6562,9 +7093,6 @@ class ConditionalFormatClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6562
7093
|
class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
6563
7094
|
uuidGenerator = new UuidGenerator();
|
|
6564
7095
|
copy(data) {
|
|
6565
|
-
if (!data.zones.length) {
|
|
6566
|
-
return;
|
|
6567
|
-
}
|
|
6568
7096
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6569
7097
|
const sheetId = data.sheetId;
|
|
6570
7098
|
const dvRules = [];
|
|
@@ -6580,18 +7108,12 @@ class DataValidationClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6580
7108
|
return { dvRules };
|
|
6581
7109
|
}
|
|
6582
7110
|
paste(target, clippedContent, options) {
|
|
6583
|
-
if (
|
|
6584
|
-
return;
|
|
6585
|
-
}
|
|
6586
|
-
if (options?.pasteOption) {
|
|
6587
|
-
return;
|
|
6588
|
-
}
|
|
6589
|
-
if (!("zones" in target) || !target.zones.length) {
|
|
7111
|
+
if (options.pasteOption) {
|
|
6590
7112
|
return;
|
|
6591
7113
|
}
|
|
6592
7114
|
const zones = target.zones;
|
|
6593
7115
|
const sheetId = target.sheetId;
|
|
6594
|
-
if (!options
|
|
7116
|
+
if (!options.isCutOperation) {
|
|
6595
7117
|
this.pasteFromCopy(sheetId, zones, clippedContent.dvRules);
|
|
6596
7118
|
}
|
|
6597
7119
|
else {
|
|
@@ -6690,14 +7212,11 @@ class ImageClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6690
7212
|
};
|
|
6691
7213
|
}
|
|
6692
7214
|
getPasteTarget(sheetId, target, content, options) {
|
|
6693
|
-
if (!content?.copiedFigure || !content?.copiedImage) {
|
|
6694
|
-
return { zones: [], sheetId };
|
|
6695
|
-
}
|
|
6696
7215
|
const newId = new UuidGenerator().uuidv4();
|
|
6697
7216
|
return { sheetId, zones: [], figureId: newId };
|
|
6698
7217
|
}
|
|
6699
7218
|
paste(target, clippedContent, options) {
|
|
6700
|
-
if (!
|
|
7219
|
+
if (!target.figureId) {
|
|
6701
7220
|
return;
|
|
6702
7221
|
}
|
|
6703
7222
|
const { zones, figureId } = target;
|
|
@@ -6721,7 +7240,7 @@ class ImageClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6721
7240
|
size: { height, width },
|
|
6722
7241
|
definition: copy,
|
|
6723
7242
|
});
|
|
6724
|
-
if (options
|
|
7243
|
+
if (options.isCutOperation) {
|
|
6725
7244
|
this.dispatch("DELETE_FIGURE", {
|
|
6726
7245
|
sheetId: clippedContent.sheetId,
|
|
6727
7246
|
id: clippedContent.copiedFigure.id,
|
|
@@ -6742,9 +7261,6 @@ class ImageClipboardHandler extends AbstractFigureClipboardHandler {
|
|
|
6742
7261
|
|
|
6743
7262
|
class MergeClipboardHandler extends AbstractCellClipboardHandler {
|
|
6744
7263
|
copy(data) {
|
|
6745
|
-
if (!data.zones.length) {
|
|
6746
|
-
return;
|
|
6747
|
-
}
|
|
6748
7264
|
const sheetId = this.getters.getActiveSheetId();
|
|
6749
7265
|
const { rowsIndexes, columnsIndexes } = data;
|
|
6750
7266
|
const merges = [];
|
|
@@ -6762,10 +7278,7 @@ class MergeClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6762
7278
|
* Paste the clipboard content in the given target
|
|
6763
7279
|
*/
|
|
6764
7280
|
paste(target, content, options) {
|
|
6765
|
-
if (
|
|
6766
|
-
options?.isCutOperation ||
|
|
6767
|
-
!("zones" in target) ||
|
|
6768
|
-
!target.zones.length) {
|
|
7281
|
+
if (options.isCutOperation) {
|
|
6769
7282
|
return;
|
|
6770
7283
|
}
|
|
6771
7284
|
this.pasteFromCopy(target.sheetId, target.zones, content.merges, options);
|
|
@@ -6821,9 +7334,6 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6821
7334
|
copy(data) {
|
|
6822
7335
|
const sheetId = data.sheetId;
|
|
6823
7336
|
const { rowsIndexes, columnsIndexes, zones } = data;
|
|
6824
|
-
if (!zones || !rowsIndexes.length || !columnsIndexes.length) {
|
|
6825
|
-
return { tableCells: [[]], sheetId };
|
|
6826
|
-
}
|
|
6827
7337
|
const copiedTablesIds = new Set();
|
|
6828
7338
|
const tableCells = [];
|
|
6829
7339
|
for (let row of rowsIndexes) {
|
|
@@ -6879,12 +7389,9 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
|
|
|
6879
7389
|
};
|
|
6880
7390
|
}
|
|
6881
7391
|
paste(target, content, options) {
|
|
6882
|
-
if (!content || !content.tableCells) {
|
|
6883
|
-
return;
|
|
6884
|
-
}
|
|
6885
7392
|
const zones = target.zones;
|
|
6886
7393
|
const sheetId = target.sheetId;
|
|
6887
|
-
if (!options
|
|
7394
|
+
if (!options.isCutOperation) {
|
|
6888
7395
|
this.pasteFromCopy(sheetId, zones, content.tableCells, options);
|
|
6889
7396
|
}
|
|
6890
7397
|
else {
|
|
@@ -7777,486 +8284,6 @@ function errorCell(value, message) {
|
|
|
7777
8284
|
};
|
|
7778
8285
|
}
|
|
7779
8286
|
|
|
7780
|
-
function boolAnd(args) {
|
|
7781
|
-
let foundBoolean = false;
|
|
7782
|
-
let acc = true;
|
|
7783
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
7784
|
-
foundBoolean = true;
|
|
7785
|
-
acc = acc && arg;
|
|
7786
|
-
return acc;
|
|
7787
|
-
});
|
|
7788
|
-
return {
|
|
7789
|
-
foundBoolean,
|
|
7790
|
-
result: acc,
|
|
7791
|
-
};
|
|
7792
|
-
}
|
|
7793
|
-
function boolOr(args) {
|
|
7794
|
-
let foundBoolean = false;
|
|
7795
|
-
let acc = false;
|
|
7796
|
-
conditionalVisitBoolean(args, (arg) => {
|
|
7797
|
-
foundBoolean = true;
|
|
7798
|
-
acc = acc || arg;
|
|
7799
|
-
return !acc;
|
|
7800
|
-
});
|
|
7801
|
-
return {
|
|
7802
|
-
foundBoolean,
|
|
7803
|
-
result: acc,
|
|
7804
|
-
};
|
|
7805
|
-
}
|
|
7806
|
-
|
|
7807
|
-
function sum(values, locale) {
|
|
7808
|
-
return reduceNumbers(values, (acc, a) => acc + a, 0, locale);
|
|
7809
|
-
}
|
|
7810
|
-
function countUnique(args) {
|
|
7811
|
-
return reduceAny(args, (acc, a) => (isDataNonEmpty(a) ? acc.add(a?.value) : acc), new Set()).size;
|
|
7812
|
-
}
|
|
7813
|
-
|
|
7814
|
-
function assertSameNumberOfElements(...args) {
|
|
7815
|
-
const dims = args[0].length;
|
|
7816
|
-
args.forEach((arg, i) => assert(() => arg.length === dims, _t("[[FUNCTION_NAME]] has mismatched dimensions for argument %s (%s vs %s).", i.toString(), dims.toString(), arg.length.toString())));
|
|
7817
|
-
}
|
|
7818
|
-
function average(values, locale) {
|
|
7819
|
-
let count = 0;
|
|
7820
|
-
const sum = reduceNumbers(values, (acc, a) => {
|
|
7821
|
-
count += 1;
|
|
7822
|
-
return acc + a;
|
|
7823
|
-
}, 0, locale);
|
|
7824
|
-
assertNotZero(count);
|
|
7825
|
-
return sum / count;
|
|
7826
|
-
}
|
|
7827
|
-
function countNumbers(values, locale) {
|
|
7828
|
-
let count = 0;
|
|
7829
|
-
for (let n of values) {
|
|
7830
|
-
if (isMatrix(n)) {
|
|
7831
|
-
for (let i of n) {
|
|
7832
|
-
for (let j of i) {
|
|
7833
|
-
if (typeof j.value === "number") {
|
|
7834
|
-
count += 1;
|
|
7835
|
-
}
|
|
7836
|
-
}
|
|
7837
|
-
}
|
|
7838
|
-
}
|
|
7839
|
-
else {
|
|
7840
|
-
const value = n?.value;
|
|
7841
|
-
if (!isEvaluationError(value) &&
|
|
7842
|
-
(typeof value !== "string" || isNumber(value, locale) || parseDateTime(value, locale))) {
|
|
7843
|
-
count += 1;
|
|
7844
|
-
}
|
|
7845
|
-
}
|
|
7846
|
-
}
|
|
7847
|
-
return count;
|
|
7848
|
-
}
|
|
7849
|
-
function countAny(values) {
|
|
7850
|
-
return reduceAny(values, (acc, a) => (a !== undefined && a.value !== null ? acc + 1 : acc), 0);
|
|
7851
|
-
}
|
|
7852
|
-
function max(values, locale) {
|
|
7853
|
-
const result = reduceNumbers(values, (acc, a) => (acc < a ? a : acc), -Infinity, locale);
|
|
7854
|
-
return result === -Infinity ? 0 : result;
|
|
7855
|
-
}
|
|
7856
|
-
function min(values, locale) {
|
|
7857
|
-
const result = reduceNumbers(values, (acc, a) => (a < acc ? a : acc), Infinity, locale);
|
|
7858
|
-
return result === Infinity ? 0 : result;
|
|
7859
|
-
}
|
|
7860
|
-
|
|
7861
|
-
const pivotTimeAdapterRegistry = new Registry();
|
|
7862
|
-
function pivotTimeAdapter(granularity) {
|
|
7863
|
-
return pivotTimeAdapterRegistry.get(granularity);
|
|
7864
|
-
}
|
|
7865
|
-
/**
|
|
7866
|
-
* The Time Adapter: Managing Time Periods for Pivot Functions
|
|
7867
|
-
*
|
|
7868
|
-
* Overview:
|
|
7869
|
-
* A time adapter is responsible for managing time periods associated with pivot functions.
|
|
7870
|
-
* Each type of period (day, week, month, quarter, etc.) has its own dedicated adapter.
|
|
7871
|
-
* The adapter's primary role is to normalize period values between spreadsheet functions,
|
|
7872
|
-
* and the pivot.
|
|
7873
|
-
* By normalizing the period value, it can be stored consistently in the pivot.
|
|
7874
|
-
*
|
|
7875
|
-
* Normalization Process:
|
|
7876
|
-
* When working with functions in the spreadsheet, the time adapter normalizes
|
|
7877
|
-
* the provided period to facilitate accurate lookup of values in the pivot.
|
|
7878
|
-
* For instance, if the spreadsheet function represents a day period as a number generated
|
|
7879
|
-
* by the DATE function (DATE(2023, 12, 25)), the time adapter will normalize it accordingly.
|
|
7880
|
-
*
|
|
7881
|
-
*/
|
|
7882
|
-
/**
|
|
7883
|
-
* Normalized value: "12/25/2023"
|
|
7884
|
-
*
|
|
7885
|
-
* Note: Those two format are equivalent:
|
|
7886
|
-
* - "MM/dd/yyyy" (luxon format)
|
|
7887
|
-
* - "mm/dd/yyyy" (spreadsheet format)
|
|
7888
|
-
**/
|
|
7889
|
-
const dayAdapter = {
|
|
7890
|
-
normalizeFunctionValue(value) {
|
|
7891
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
7892
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/dd/yyyy" });
|
|
7893
|
-
},
|
|
7894
|
-
getFormat(locale) {
|
|
7895
|
-
return (locale ?? DEFAULT_LOCALE).dateFormat;
|
|
7896
|
-
},
|
|
7897
|
-
formatValue(normalizedValue, locale) {
|
|
7898
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
7899
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7900
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7901
|
-
},
|
|
7902
|
-
toCellValue(normalizedValue) {
|
|
7903
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7904
|
-
},
|
|
7905
|
-
};
|
|
7906
|
-
/**
|
|
7907
|
-
* normalizes day of month number
|
|
7908
|
-
*/
|
|
7909
|
-
const dayOfMonthAdapter = {
|
|
7910
|
-
normalizeFunctionValue(value) {
|
|
7911
|
-
const day = toNumber(value, DEFAULT_LOCALE);
|
|
7912
|
-
if (day < 1 || day > 31) {
|
|
7913
|
-
throw new EvaluationError(_t("%s is not a valid day of month (it should be a number between 1 and 31)", day));
|
|
7914
|
-
}
|
|
7915
|
-
return day;
|
|
7916
|
-
},
|
|
7917
|
-
getFormat() {
|
|
7918
|
-
return "0";
|
|
7919
|
-
},
|
|
7920
|
-
formatValue(normalizedValue, locale) {
|
|
7921
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
7922
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7923
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7924
|
-
},
|
|
7925
|
-
toCellValue(normalizedValue) {
|
|
7926
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7927
|
-
},
|
|
7928
|
-
};
|
|
7929
|
-
/**
|
|
7930
|
-
* Normalized value: "2/2023" for week 2 of 2023
|
|
7931
|
-
*/
|
|
7932
|
-
const weekAdapter = {
|
|
7933
|
-
normalizeFunctionValue(value) {
|
|
7934
|
-
const [week, year] = value.split("/");
|
|
7935
|
-
return `${Number(week)}/${Number(year)}`;
|
|
7936
|
-
},
|
|
7937
|
-
getFormat() {
|
|
7938
|
-
return undefined;
|
|
7939
|
-
},
|
|
7940
|
-
formatValue(normalizedValue) {
|
|
7941
|
-
const [week, year] = normalizedValue.split("/");
|
|
7942
|
-
return _t("W%(week)s %(year)s", { week, year });
|
|
7943
|
-
},
|
|
7944
|
-
toCellValue(normalizedValue) {
|
|
7945
|
-
return this.formatValue(normalizedValue);
|
|
7946
|
-
},
|
|
7947
|
-
};
|
|
7948
|
-
/**
|
|
7949
|
-
* normalizes iso week number
|
|
7950
|
-
*/
|
|
7951
|
-
const isoWeekNumberAdapter = {
|
|
7952
|
-
normalizeFunctionValue(value) {
|
|
7953
|
-
const isoWeek = toNumber(value, DEFAULT_LOCALE);
|
|
7954
|
-
if (isoWeek < 0 || isoWeek > 53) {
|
|
7955
|
-
throw new EvaluationError(_t("%s is not a valid week (it should be a number between 0 and 53)", isoWeek));
|
|
7956
|
-
}
|
|
7957
|
-
return isoWeek;
|
|
7958
|
-
},
|
|
7959
|
-
getFormat() {
|
|
7960
|
-
return "0";
|
|
7961
|
-
},
|
|
7962
|
-
formatValue(normalizedValue, locale) {
|
|
7963
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
7964
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7965
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7966
|
-
},
|
|
7967
|
-
toCellValue(normalizedValue) {
|
|
7968
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7969
|
-
},
|
|
7970
|
-
};
|
|
7971
|
-
/**
|
|
7972
|
-
* normalized month value is a string formatted as "MM/yyyy" (luxon format)
|
|
7973
|
-
* e.g. "01/2020" for January 2020
|
|
7974
|
-
*/
|
|
7975
|
-
const monthAdapter = {
|
|
7976
|
-
normalizeFunctionValue(value) {
|
|
7977
|
-
const date = toNumber(value, DEFAULT_LOCALE);
|
|
7978
|
-
return formatValue(date, { locale: DEFAULT_LOCALE, format: "mm/yyyy" });
|
|
7979
|
-
},
|
|
7980
|
-
getFormat() {
|
|
7981
|
-
return "mmmm yyyy";
|
|
7982
|
-
},
|
|
7983
|
-
formatValue(normalizedValue, locale) {
|
|
7984
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
7985
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7986
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
7987
|
-
},
|
|
7988
|
-
toCellValue(normalizedValue) {
|
|
7989
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
7990
|
-
},
|
|
7991
|
-
};
|
|
7992
|
-
/**
|
|
7993
|
-
* normalizes month number
|
|
7994
|
-
*/
|
|
7995
|
-
const monthNumberAdapter = {
|
|
7996
|
-
normalizeFunctionValue(value) {
|
|
7997
|
-
const month = toNumber(value, DEFAULT_LOCALE);
|
|
7998
|
-
if (month < 1 || month > 12) {
|
|
7999
|
-
throw new EvaluationError(_t("%s is not a valid month (it should be a number between 1 and 12)", month));
|
|
8000
|
-
}
|
|
8001
|
-
return month;
|
|
8002
|
-
},
|
|
8003
|
-
getFormat() {
|
|
8004
|
-
return "0";
|
|
8005
|
-
},
|
|
8006
|
-
formatValue(normalizedValue, locale) {
|
|
8007
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
8008
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8009
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
8010
|
-
},
|
|
8011
|
-
toCellValue(normalizedValue) {
|
|
8012
|
-
return MONTHS[toNumber(normalizedValue, DEFAULT_LOCALE) - 1].toString();
|
|
8013
|
-
},
|
|
8014
|
-
};
|
|
8015
|
-
/**
|
|
8016
|
-
* normalized quarter value is "quarter/year"
|
|
8017
|
-
* e.g. "1/2020" for Q1 2020
|
|
8018
|
-
*/
|
|
8019
|
-
const quarterAdapter = {
|
|
8020
|
-
normalizeFunctionValue(value) {
|
|
8021
|
-
const [quarter, year] = value.split("/");
|
|
8022
|
-
return `${quarter}/${year}`;
|
|
8023
|
-
},
|
|
8024
|
-
getFormat() {
|
|
8025
|
-
return undefined;
|
|
8026
|
-
},
|
|
8027
|
-
formatValue(normalizedValue) {
|
|
8028
|
-
const [quarter, year] = normalizedValue.split("/");
|
|
8029
|
-
return _t("Q%(quarter)s %(year)s", { quarter, year });
|
|
8030
|
-
},
|
|
8031
|
-
toCellValue(normalizedValue) {
|
|
8032
|
-
return this.formatValue(normalizedValue);
|
|
8033
|
-
},
|
|
8034
|
-
};
|
|
8035
|
-
/**
|
|
8036
|
-
* normalizes quarter number
|
|
8037
|
-
*/
|
|
8038
|
-
const quarterNumberAdapter = {
|
|
8039
|
-
normalizeFunctionValue(value) {
|
|
8040
|
-
const quarter = toNumber(value, DEFAULT_LOCALE);
|
|
8041
|
-
if (quarter < 1 || quarter > 4) {
|
|
8042
|
-
throw new EvaluationError(_t("%s is not a valid quarter (it should be a number between 1 and 4)", quarter));
|
|
8043
|
-
}
|
|
8044
|
-
return quarter;
|
|
8045
|
-
},
|
|
8046
|
-
getFormat() {
|
|
8047
|
-
return "0";
|
|
8048
|
-
},
|
|
8049
|
-
formatValue(normalizedValue, locale) {
|
|
8050
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
8051
|
-
const value = toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8052
|
-
return formatValue(value, { locale, format: this.getFormat(locale) });
|
|
8053
|
-
},
|
|
8054
|
-
toCellValue(normalizedValue) {
|
|
8055
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8056
|
-
},
|
|
8057
|
-
};
|
|
8058
|
-
const yearAdapter = {
|
|
8059
|
-
normalizeFunctionValue(value) {
|
|
8060
|
-
return toNumber(value, DEFAULT_LOCALE);
|
|
8061
|
-
},
|
|
8062
|
-
getFormat() {
|
|
8063
|
-
return "0";
|
|
8064
|
-
},
|
|
8065
|
-
formatValue(normalizedValue, locale) {
|
|
8066
|
-
locale = locale ?? DEFAULT_LOCALE;
|
|
8067
|
-
return formatValue(normalizedValue, { locale, format: "0" });
|
|
8068
|
-
},
|
|
8069
|
-
toCellValue(normalizedValue) {
|
|
8070
|
-
return toNumber(normalizedValue, DEFAULT_LOCALE);
|
|
8071
|
-
},
|
|
8072
|
-
};
|
|
8073
|
-
pivotTimeAdapterRegistry
|
|
8074
|
-
.add("day", dayAdapter)
|
|
8075
|
-
.add("week", weekAdapter)
|
|
8076
|
-
.add("month", monthAdapter)
|
|
8077
|
-
.add("quarter", quarterAdapter)
|
|
8078
|
-
.add("year", yearAdapter)
|
|
8079
|
-
.add("day_of_month", dayOfMonthAdapter)
|
|
8080
|
-
.add("iso_week_number", isoWeekNumberAdapter)
|
|
8081
|
-
.add("month_number", monthNumberAdapter)
|
|
8082
|
-
.add("quarter_number", quarterNumberAdapter)
|
|
8083
|
-
.add("year_number", yearAdapter);
|
|
8084
|
-
|
|
8085
|
-
const AGGREGATOR_NAMES = {
|
|
8086
|
-
count: _t("Count"),
|
|
8087
|
-
count_distinct: _t("Count Distinct"),
|
|
8088
|
-
bool_and: _t("Boolean And"),
|
|
8089
|
-
bool_or: _t("Boolean Or"),
|
|
8090
|
-
max: _t("Maximum"),
|
|
8091
|
-
min: _t("Minimum"),
|
|
8092
|
-
avg: _t("Average"),
|
|
8093
|
-
sum: _t("Sum"),
|
|
8094
|
-
};
|
|
8095
|
-
const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
|
|
8096
|
-
const AGGREGATORS_BY_FIELD_TYPE = {
|
|
8097
|
-
integer: NUMBER_CHAR_AGGREGATORS,
|
|
8098
|
-
char: NUMBER_CHAR_AGGREGATORS,
|
|
8099
|
-
boolean: ["count_distinct", "count", "bool_and", "bool_or"],
|
|
8100
|
-
};
|
|
8101
|
-
const AGGREGATORS = {};
|
|
8102
|
-
for (const type in AGGREGATORS_BY_FIELD_TYPE) {
|
|
8103
|
-
AGGREGATORS[type] = {};
|
|
8104
|
-
for (const aggregator of AGGREGATORS_BY_FIELD_TYPE[type]) {
|
|
8105
|
-
AGGREGATORS[type][aggregator] = AGGREGATOR_NAMES[aggregator];
|
|
8106
|
-
}
|
|
8107
|
-
}
|
|
8108
|
-
const AGGREGATORS_FN = {
|
|
8109
|
-
count: {
|
|
8110
|
-
fn: (args) => countAny([args]),
|
|
8111
|
-
format: () => "0",
|
|
8112
|
-
},
|
|
8113
|
-
count_distinct: {
|
|
8114
|
-
fn: (args) => countUnique([args]),
|
|
8115
|
-
format: () => "0",
|
|
8116
|
-
},
|
|
8117
|
-
bool_and: {
|
|
8118
|
-
fn: (args) => boolAnd([args]).result,
|
|
8119
|
-
format: () => undefined,
|
|
8120
|
-
},
|
|
8121
|
-
bool_or: {
|
|
8122
|
-
fn: (args) => boolOr([args]).result,
|
|
8123
|
-
format: () => undefined,
|
|
8124
|
-
},
|
|
8125
|
-
max: {
|
|
8126
|
-
fn: (args, locale) => max([args], locale),
|
|
8127
|
-
format: inferFormat,
|
|
8128
|
-
},
|
|
8129
|
-
min: {
|
|
8130
|
-
fn: (args, locale) => min([args], locale),
|
|
8131
|
-
format: inferFormat,
|
|
8132
|
-
},
|
|
8133
|
-
avg: {
|
|
8134
|
-
fn: (args, locale) => average([args], locale),
|
|
8135
|
-
format: inferFormat,
|
|
8136
|
-
},
|
|
8137
|
-
sum: {
|
|
8138
|
-
fn: (args, locale) => sum([args], locale),
|
|
8139
|
-
format: inferFormat,
|
|
8140
|
-
},
|
|
8141
|
-
};
|
|
8142
|
-
function makePivotFormulaFromPivotCell(pivotFormulaId, pivotCell) {
|
|
8143
|
-
switch (pivotCell.type) {
|
|
8144
|
-
case "HEADER":
|
|
8145
|
-
return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
8146
|
-
case "MEASURE_HEADER":
|
|
8147
|
-
return makePivotFormula("PIVOT.HEADER", [pivotFormulaId, ...flatPivotDomain(pivotCell.domain), "measure", pivotCell.measure].filter(isDefined));
|
|
8148
|
-
case "VALUE":
|
|
8149
|
-
return makePivotFormula("PIVOT.VALUE", [pivotFormulaId, pivotCell.measure, ...flatPivotDomain(pivotCell.domain)].filter(isDefined));
|
|
8150
|
-
case "EMPTY":
|
|
8151
|
-
return "";
|
|
8152
|
-
}
|
|
8153
|
-
}
|
|
8154
|
-
/**
|
|
8155
|
-
* Build a pivot formula expression
|
|
8156
|
-
*/
|
|
8157
|
-
function makePivotFormula(formula, args) {
|
|
8158
|
-
return `=${formula}(${args
|
|
8159
|
-
.map((arg) => {
|
|
8160
|
-
const stringIsNumber = typeof arg == "string" && !isNaN(Number(arg)) && Number(arg).toString() === arg;
|
|
8161
|
-
const convertToNumber = typeof arg == "number" || stringIsNumber;
|
|
8162
|
-
return convertToNumber ? `${arg}` : `"${arg.toString().replace(/"/g, '\\"')}"`;
|
|
8163
|
-
})
|
|
8164
|
-
.join(",")})`;
|
|
8165
|
-
}
|
|
8166
|
-
/**
|
|
8167
|
-
* Given an object of form {"1": {...}, "2": {...}, ...} get the maximum ID used
|
|
8168
|
-
* in this object
|
|
8169
|
-
* If the object has no keys, return 0
|
|
8170
|
-
*
|
|
8171
|
-
*/
|
|
8172
|
-
function getMaxObjectId(o) {
|
|
8173
|
-
const keys = Object.keys(o);
|
|
8174
|
-
if (!keys.length) {
|
|
8175
|
-
return 0;
|
|
8176
|
-
}
|
|
8177
|
-
const nums = keys.map((id) => parseInt(id, 10));
|
|
8178
|
-
const max = Math.max(...nums);
|
|
8179
|
-
return max;
|
|
8180
|
-
}
|
|
8181
|
-
const ALL_PERIODS = {
|
|
8182
|
-
year: _t("Year"),
|
|
8183
|
-
quarter: _t("Quarter"),
|
|
8184
|
-
month: _t("Month"),
|
|
8185
|
-
week: _t("Week"),
|
|
8186
|
-
day: _t("Day"),
|
|
8187
|
-
year_number: _t("Year"),
|
|
8188
|
-
quarter_number: _t("Quarter"),
|
|
8189
|
-
month_number: _t("Month"),
|
|
8190
|
-
iso_week_number: _t("Week"),
|
|
8191
|
-
day_of_month: _t("Day of Month"),
|
|
8192
|
-
};
|
|
8193
|
-
const DATE_FIELDS = ["date", "datetime"];
|
|
8194
|
-
/**
|
|
8195
|
-
* Parse a dimension string into a pivot dimension definition.
|
|
8196
|
-
* e.g "create_date:month" => { name: "create_date", granularity: "month" }
|
|
8197
|
-
*/
|
|
8198
|
-
function parseDimension(dimension) {
|
|
8199
|
-
const [name, granularity] = dimension.split(":");
|
|
8200
|
-
if (granularity) {
|
|
8201
|
-
return { name, granularity };
|
|
8202
|
-
}
|
|
8203
|
-
return { name };
|
|
8204
|
-
}
|
|
8205
|
-
function isDateField(field) {
|
|
8206
|
-
return DATE_FIELDS.includes(field.type);
|
|
8207
|
-
}
|
|
8208
|
-
function toPivotDomain(domainStr) {
|
|
8209
|
-
if (domainStr.length % 2 !== 0) {
|
|
8210
|
-
throw new Error("Invalid domain: odd number of elements");
|
|
8211
|
-
}
|
|
8212
|
-
const domain = [];
|
|
8213
|
-
for (let i = 0; i < domainStr.length - 1; i += 2) {
|
|
8214
|
-
domain.push({ field: domainStr[i], value: domainStr[i + 1] });
|
|
8215
|
-
}
|
|
8216
|
-
return domain;
|
|
8217
|
-
}
|
|
8218
|
-
function flatPivotDomain(domain) {
|
|
8219
|
-
return domain.flatMap((arg) => [arg.field, arg.value]);
|
|
8220
|
-
}
|
|
8221
|
-
/**
|
|
8222
|
-
* Parses the value defining a pivot group in a PIVOT formula
|
|
8223
|
-
* e.g. given the following formula PIVOT.VALUE("1", "stage_id", "42", "status", "won"),
|
|
8224
|
-
* the two group values are "42" and "won".
|
|
8225
|
-
*/
|
|
8226
|
-
function toNormalizedPivotValue(dimension, groupValue) {
|
|
8227
|
-
if (groupValue === null || groupValue === "null") {
|
|
8228
|
-
return null;
|
|
8229
|
-
}
|
|
8230
|
-
const groupValueString = typeof groupValue === "boolean"
|
|
8231
|
-
? toString(groupValue).toLocaleLowerCase()
|
|
8232
|
-
: toString(groupValue);
|
|
8233
|
-
if (!pivotNormalizationValueRegistry.contains(dimension.type)) {
|
|
8234
|
-
throw new EvaluationError(_t("Field %(field)s is not supported because of its type (%(type)s)", {
|
|
8235
|
-
field: dimension.displayName,
|
|
8236
|
-
type: dimension.type,
|
|
8237
|
-
}));
|
|
8238
|
-
}
|
|
8239
|
-
// represents a field which is not set (=False server side)
|
|
8240
|
-
if (groupValueString === "false") {
|
|
8241
|
-
return false;
|
|
8242
|
-
}
|
|
8243
|
-
const normalizer = pivotNormalizationValueRegistry.get(dimension.type);
|
|
8244
|
-
return normalizer(groupValueString, dimension.granularity);
|
|
8245
|
-
}
|
|
8246
|
-
function normalizeDateTime(value, granularity) {
|
|
8247
|
-
if (!granularity) {
|
|
8248
|
-
throw "";
|
|
8249
|
-
}
|
|
8250
|
-
return pivotTimeAdapter(granularity).normalizeFunctionValue(value);
|
|
8251
|
-
}
|
|
8252
|
-
const pivotNormalizationValueRegistry = new Registry();
|
|
8253
|
-
pivotNormalizationValueRegistry
|
|
8254
|
-
.add("date", normalizeDateTime)
|
|
8255
|
-
.add("datetime", normalizeDateTime)
|
|
8256
|
-
.add("integer", (value) => toNumber(value, DEFAULT_LOCALE))
|
|
8257
|
-
.add("boolean", (value) => toBoolean(value))
|
|
8258
|
-
.add("char", (value) => toString(value));
|
|
8259
|
-
|
|
8260
8287
|
/**
|
|
8261
8288
|
* Change the reference types inside the given token, if the token represent a range or a cell
|
|
8262
8289
|
*
|
|
@@ -9996,7 +10023,7 @@ class ComposerStore extends SpreadsheetStore {
|
|
|
9996
10023
|
const cell = this.getters.getCell(position);
|
|
9997
10024
|
if (pivotId && pivotCell.type !== "EMPTY" && !cell?.isFormula) {
|
|
9998
10025
|
const formulaPivotId = this.getters.getPivotFormulaId(pivotId);
|
|
9999
|
-
const formula =
|
|
10026
|
+
const formula = createPivotFormula(formulaPivotId, pivotCell);
|
|
10000
10027
|
return formula.slice(1); // strip leading =
|
|
10001
10028
|
}
|
|
10002
10029
|
}
|
|
@@ -19088,10 +19115,9 @@ const PIVOT_VALUE = {
|
|
|
19088
19115
|
compute: function (formulaId, measureName, ...domainArgs) {
|
|
19089
19116
|
const _pivotFormulaId = toString(formulaId);
|
|
19090
19117
|
const _measure = toString(measureName);
|
|
19091
|
-
const _domainArgs = domainArgs.map(toString);
|
|
19092
19118
|
const pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
19093
19119
|
assertMeasureExist(pivotId, _measure, this.getters);
|
|
19094
|
-
assertDomainLength(
|
|
19120
|
+
assertDomainLength(domainArgs);
|
|
19095
19121
|
const pivot = this.getters.getPivot(pivotId);
|
|
19096
19122
|
const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
|
|
19097
19123
|
addPivotDependencies(this, coreDefinition);
|
|
@@ -19099,15 +19125,14 @@ const PIVOT_VALUE = {
|
|
|
19099
19125
|
if (error) {
|
|
19100
19126
|
return error;
|
|
19101
19127
|
}
|
|
19102
|
-
|
|
19103
|
-
const { value, format } = pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
19104
|
-
if (!value && !this.getters.areDomainArgsFieldsValid(pivotId, domain)) {
|
|
19128
|
+
if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
|
|
19105
19129
|
return {
|
|
19106
19130
|
value: CellErrorType.GenericError,
|
|
19107
19131
|
message: _t("Dimensions don't match the pivot definition"),
|
|
19108
19132
|
};
|
|
19109
19133
|
}
|
|
19110
|
-
|
|
19134
|
+
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19135
|
+
return pivot.getPivotCellValueAndFormat(_measure, domain);
|
|
19111
19136
|
},
|
|
19112
19137
|
};
|
|
19113
19138
|
const PIVOT_HEADER = {
|
|
@@ -19119,9 +19144,8 @@ const PIVOT_HEADER = {
|
|
|
19119
19144
|
],
|
|
19120
19145
|
compute: function (pivotId, ...domainArgs) {
|
|
19121
19146
|
const _pivotFormulaId = toString(pivotId);
|
|
19122
|
-
const _domainArgs = domainArgs.map(toString);
|
|
19123
19147
|
const _pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
19124
|
-
assertDomainLength(
|
|
19148
|
+
assertDomainLength(domainArgs);
|
|
19125
19149
|
const pivot = this.getters.getPivot(_pivotId);
|
|
19126
19150
|
const coreDefinition = this.getters.getPivotCoreDefinition(_pivotId);
|
|
19127
19151
|
addPivotDependencies(this, coreDefinition);
|
|
@@ -19129,14 +19153,14 @@ const PIVOT_HEADER = {
|
|
|
19129
19153
|
if (error) {
|
|
19130
19154
|
return error;
|
|
19131
19155
|
}
|
|
19132
|
-
|
|
19133
|
-
const lastNode = domain.at(-1);
|
|
19134
|
-
if (!this.getters.areDomainArgsFieldsValid(_pivotId, lastNode?.field === "measure" ? domain.slice(0, -1) : domain)) {
|
|
19156
|
+
if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
|
|
19135
19157
|
return {
|
|
19136
19158
|
value: CellErrorType.GenericError,
|
|
19137
19159
|
message: _t("Dimensions don't match the pivot definition"),
|
|
19138
19160
|
};
|
|
19139
19161
|
}
|
|
19162
|
+
const domain = pivot.parseArgsToPivotDomain(domainArgs);
|
|
19163
|
+
const lastNode = domain.at(-1);
|
|
19140
19164
|
if (lastNode?.field === "measure") {
|
|
19141
19165
|
return pivot.getPivotMeasureValue(toString(lastNode.value), domain);
|
|
19142
19166
|
}
|
|
@@ -19153,13 +19177,21 @@ const PIVOT = {
|
|
|
19153
19177
|
description: _t("Get a pivot table."),
|
|
19154
19178
|
args: [
|
|
19155
19179
|
arg("pivot_id (string)", _t("ID of the pivot.")),
|
|
19156
|
-
arg("row_count (number, optional
|
|
19180
|
+
arg("row_count (number, optional)", _t("number of rows")),
|
|
19157
19181
|
arg("include_total (boolean, default=TRUE)", _t("Whether to include total/sub-totals or not.")),
|
|
19158
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")),
|
|
19159
19184
|
],
|
|
19160
|
-
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 }) {
|
|
19161
19186
|
const _pivotFormulaId = toString(pivotFormulaId);
|
|
19162
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
|
+
}
|
|
19163
19195
|
const _includeColumnHeaders = toBoolean(includeColumnHeaders);
|
|
19164
19196
|
const _includedTotal = toBoolean(includeTotal);
|
|
19165
19197
|
const pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
@@ -19175,19 +19207,15 @@ const PIVOT = {
|
|
|
19175
19207
|
const cells = table.getPivotCells(_includedTotal, _includeColumnHeaders);
|
|
19176
19208
|
const headerRows = _includeColumnHeaders ? table.columns.length : 0;
|
|
19177
19209
|
const pivotTitle = this.getters.getPivotDisplayName(pivotId);
|
|
19178
|
-
|
|
19179
|
-
|
|
19180
|
-
}
|
|
19181
|
-
const end = Math.min(headerRows + _rowCount, cells[0].length);
|
|
19182
|
-
if (end === 0) {
|
|
19210
|
+
const tableHeight = Math.min(headerRows + _rowCount, cells[0].length);
|
|
19211
|
+
if (tableHeight === 0) {
|
|
19183
19212
|
return [[{ value: pivotTitle }]];
|
|
19184
19213
|
}
|
|
19185
|
-
const tableWidth = cells.length;
|
|
19186
|
-
const tableRows = range(0, end);
|
|
19214
|
+
const tableWidth = Math.min(1 + _columnCount, cells.length);
|
|
19187
19215
|
const result = [];
|
|
19188
19216
|
for (const col of range(0, tableWidth)) {
|
|
19189
19217
|
result[col] = [];
|
|
19190
|
-
for (const row of
|
|
19218
|
+
for (const row of range(0, tableHeight)) {
|
|
19191
19219
|
const pivotCell = cells[col][row];
|
|
19192
19220
|
switch (pivotCell.type) {
|
|
19193
19221
|
case "EMPTY":
|
|
@@ -21877,9 +21905,6 @@ autoCompleteProviders.add("pivot_measures", {
|
|
|
21877
21905
|
const pivot = this.getters.getPivot(pivotId);
|
|
21878
21906
|
pivot.init();
|
|
21879
21907
|
const fields = pivot.getFields();
|
|
21880
|
-
if (!fields) {
|
|
21881
|
-
return [];
|
|
21882
|
-
}
|
|
21883
21908
|
const definition = this.getters.getPivotCoreDefinition(pivotId);
|
|
21884
21909
|
return definition.measures
|
|
21885
21910
|
.map((measure) => {
|
|
@@ -21919,9 +21944,6 @@ autoCompleteProviders.add("pivot_group_fields", {
|
|
|
21919
21944
|
const pivot = this.getters.getPivot(pivotId);
|
|
21920
21945
|
pivot.init();
|
|
21921
21946
|
const fields = pivot.getFields();
|
|
21922
|
-
if (!fields) {
|
|
21923
|
-
return;
|
|
21924
|
-
}
|
|
21925
21947
|
const { columns, rows } = pivot.definition;
|
|
21926
21948
|
let args = functionContext.args;
|
|
21927
21949
|
if (functionContext?.parent.toUpperCase() === "PIVOT.VALUE") {
|
|
@@ -27070,7 +27092,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27070
27092
|
if (getZoneArea(zone) === 1 && topLeftCell?.content) {
|
|
27071
27093
|
return {
|
|
27072
27094
|
type: "scorecard",
|
|
27073
|
-
title: {
|
|
27095
|
+
title: {},
|
|
27074
27096
|
background: topLeftCell.style?.fillColor || undefined,
|
|
27075
27097
|
keyValue: zoneToXc(zone),
|
|
27076
27098
|
baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
|
|
@@ -27078,22 +27100,11 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27078
27100
|
baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
|
|
27079
27101
|
};
|
|
27080
27102
|
}
|
|
27081
|
-
let title = "";
|
|
27082
27103
|
const cellsInFirstRow = getters.getEvaluatedCellsInZone(sheetId, {
|
|
27083
27104
|
...dataSetZone,
|
|
27084
27105
|
bottom: dataSetZone.top,
|
|
27085
27106
|
});
|
|
27086
27107
|
const dataSetsHaveTitle = !!cellsInFirstRow.find((cell) => cell.type !== CellValueType.empty && cell.type !== CellValueType.number);
|
|
27087
|
-
if (dataSetsHaveTitle) {
|
|
27088
|
-
const texts = cellsInFirstRow
|
|
27089
|
-
.filter((cell) => cell.type !== CellValueType.error && cell.type !== CellValueType.empty)
|
|
27090
|
-
.map((cell) => cell.formattedValue);
|
|
27091
|
-
const lastElement = texts.splice(-1)[0];
|
|
27092
|
-
title = texts.join(", ");
|
|
27093
|
-
if (lastElement) {
|
|
27094
|
-
title += (title ? " " + _t("and") + " " : "") + lastElement;
|
|
27095
|
-
}
|
|
27096
|
-
}
|
|
27097
27108
|
let labelRangeXc;
|
|
27098
27109
|
if (!singleColumn) {
|
|
27099
27110
|
labelRangeXc = zoneToXc({
|
|
@@ -27106,7 +27117,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27106
27117
|
const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
|
|
27107
27118
|
if (canChartParseLabels(labelRange, getters)) {
|
|
27108
27119
|
return {
|
|
27109
|
-
title: {
|
|
27120
|
+
title: {},
|
|
27110
27121
|
dataSets,
|
|
27111
27122
|
labelsAsText: false,
|
|
27112
27123
|
stacked: false,
|
|
@@ -27122,7 +27133,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27122
27133
|
if (singleColumn &&
|
|
27123
27134
|
getData(getters, _dataSets[0]).every((e) => typeof e === "string" && !isEvaluationError(e))) {
|
|
27124
27135
|
return {
|
|
27125
|
-
title: {
|
|
27136
|
+
title: {},
|
|
27126
27137
|
dataSets: [{ dataRange }],
|
|
27127
27138
|
aggregated: true,
|
|
27128
27139
|
labelRange: dataRange,
|
|
@@ -27132,7 +27143,7 @@ function getSmartChartDefinition(zone, getters) {
|
|
|
27132
27143
|
};
|
|
27133
27144
|
}
|
|
27134
27145
|
return {
|
|
27135
|
-
title: {
|
|
27146
|
+
title: {},
|
|
27136
27147
|
dataSets,
|
|
27137
27148
|
labelRange: labelRangeXc,
|
|
27138
27149
|
type: "bar",
|
|
@@ -31675,7 +31686,7 @@ class ChartTitle extends owl.Component {
|
|
|
31675
31686
|
static template = "o-spreadsheet.ChartTitle";
|
|
31676
31687
|
static components = { Section, ColorPickerWidget };
|
|
31677
31688
|
static props = {
|
|
31678
|
-
title: String,
|
|
31689
|
+
title: { type: String, optional: true },
|
|
31679
31690
|
updateTitle: Function,
|
|
31680
31691
|
name: { type: String, optional: true },
|
|
31681
31692
|
toggleItalic: { type: Function, optional: true },
|
|
@@ -31684,6 +31695,9 @@ class ChartTitle extends owl.Component {
|
|
|
31684
31695
|
updateColor: { type: Function, optional: true },
|
|
31685
31696
|
style: { type: Object, optional: true },
|
|
31686
31697
|
};
|
|
31698
|
+
static defaultProps = {
|
|
31699
|
+
title: "",
|
|
31700
|
+
};
|
|
31687
31701
|
openedEl = null;
|
|
31688
31702
|
setup() {
|
|
31689
31703
|
owl.useExternalListener(window, "click", this.onExternalClick);
|
|
@@ -34555,6 +34569,7 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34555
34569
|
currentSearchRegex = null;
|
|
34556
34570
|
isSearchDirty = false;
|
|
34557
34571
|
initialShowFormulaState;
|
|
34572
|
+
preserveSelectedMatchIndex = false;
|
|
34558
34573
|
// fixme: why do we make selectedMatchIndex on top of a selected
|
|
34559
34574
|
// property in the matches?
|
|
34560
34575
|
selectedMatchIndex = null;
|
|
@@ -34668,7 +34683,9 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34668
34683
|
* refresh the matches according to the current search options
|
|
34669
34684
|
*/
|
|
34670
34685
|
refreshSearch(jumpToMatchSheet = true) {
|
|
34671
|
-
this.
|
|
34686
|
+
if (!this.preserveSelectedMatchIndex) {
|
|
34687
|
+
this.selectedMatchIndex = null;
|
|
34688
|
+
}
|
|
34672
34689
|
this.findMatches();
|
|
34673
34690
|
this.selectNextCell(Direction.current, jumpToMatchSheet);
|
|
34674
34691
|
}
|
|
@@ -34767,10 +34784,16 @@ class FindAndReplaceStore extends SpreadsheetStore {
|
|
|
34767
34784
|
const selectedMatch = matches[nextIndex];
|
|
34768
34785
|
// Switch to the sheet where the match is located
|
|
34769
34786
|
if (jumpToMatchSheet && this.getters.getActiveSheetId() !== selectedMatch.sheetId) {
|
|
34787
|
+
// We set `preserveSelectedMatchIndex` to true to avoid resetting the selected search
|
|
34788
|
+
// index in the `refreshSearch` function when a new sheet is activated. The reason being
|
|
34789
|
+
// that, when we automatically go back to previous sheet while performing a search, the
|
|
34790
|
+
// search index is reset to the first occurrence each time.
|
|
34791
|
+
this.preserveSelectedMatchIndex = true;
|
|
34770
34792
|
this.model.dispatch("ACTIVATE_SHEET", {
|
|
34771
34793
|
sheetIdFrom: this.getters.getActiveSheetId(),
|
|
34772
34794
|
sheetIdTo: selectedMatch.sheetId,
|
|
34773
34795
|
});
|
|
34796
|
+
this.preserveSelectedMatchIndex = false;
|
|
34774
34797
|
// We do not want to reset the selection at finalize in this case
|
|
34775
34798
|
this.isSearchDirty = false;
|
|
34776
34799
|
}
|
|
@@ -35411,6 +35434,10 @@ class CogWheelMenu extends owl.Component {
|
|
|
35411
35434
|
}
|
|
35412
35435
|
});
|
|
35413
35436
|
}
|
|
35437
|
+
onClick(item) {
|
|
35438
|
+
item.onClick();
|
|
35439
|
+
this.popover.isOpen = false;
|
|
35440
|
+
}
|
|
35414
35441
|
get popoverProps() {
|
|
35415
35442
|
const { x, y, width, height } = this.buttonRef.el.getBoundingClientRect();
|
|
35416
35443
|
return {
|
|
@@ -35453,16 +35480,22 @@ class PivotTitleSection extends owl.Component {
|
|
|
35453
35480
|
static components = { CogWheelMenu, Section, EditableName };
|
|
35454
35481
|
static props = {
|
|
35455
35482
|
pivotId: String,
|
|
35483
|
+
flipAxis: Function,
|
|
35456
35484
|
};
|
|
35457
35485
|
get cogWheelMenuItems() {
|
|
35458
35486
|
return [
|
|
35459
35487
|
{
|
|
35460
|
-
name: "
|
|
35488
|
+
name: _t("Flip axes"),
|
|
35489
|
+
icon: "fa-exchange",
|
|
35490
|
+
onClick: this.props.flipAxis,
|
|
35491
|
+
},
|
|
35492
|
+
{
|
|
35493
|
+
name: _t("Duplicate"),
|
|
35461
35494
|
icon: "fa-copy",
|
|
35462
35495
|
onClick: () => this.duplicatePivot(),
|
|
35463
35496
|
},
|
|
35464
35497
|
{
|
|
35465
|
-
name: "Delete",
|
|
35498
|
+
name: _t("Delete"),
|
|
35466
35499
|
icon: "fa-trash",
|
|
35467
35500
|
onClick: () => this.delete(),
|
|
35468
35501
|
},
|
|
@@ -35659,9 +35692,10 @@ class SpreadsheetPivotTable {
|
|
|
35659
35692
|
columns;
|
|
35660
35693
|
rows;
|
|
35661
35694
|
measures;
|
|
35695
|
+
fieldsType;
|
|
35662
35696
|
maxIndent;
|
|
35663
35697
|
pivotCells = {};
|
|
35664
|
-
constructor(columns, rows, measures) {
|
|
35698
|
+
constructor(columns, rows, measures, fieldsType) {
|
|
35665
35699
|
this.columns = columns.map((row) => {
|
|
35666
35700
|
// offset in the pivot table
|
|
35667
35701
|
// starts at 1 because the first column is the row title
|
|
@@ -35674,6 +35708,7 @@ class SpreadsheetPivotTable {
|
|
|
35674
35708
|
});
|
|
35675
35709
|
this.rows = rows;
|
|
35676
35710
|
this.measures = measures;
|
|
35711
|
+
this.fieldsType = fieldsType;
|
|
35677
35712
|
this.maxIndent = Math.max(...this.rows.map((row) => row.indent));
|
|
35678
35713
|
}
|
|
35679
35714
|
/**
|
|
@@ -35720,7 +35755,7 @@ class SpreadsheetPivotTable {
|
|
|
35720
35755
|
if (!domain) {
|
|
35721
35756
|
return EMPTY_PIVOT_CELL;
|
|
35722
35757
|
}
|
|
35723
|
-
const measure = domain.at(-1)?.value
|
|
35758
|
+
const measure = domain.at(-1)?.value?.toString() || "";
|
|
35724
35759
|
return { type: "MEASURE_HEADER", domain: domain.slice(0, -1), measure };
|
|
35725
35760
|
}
|
|
35726
35761
|
else if (row <= colHeadersHeight - 1) {
|
|
@@ -35752,9 +35787,13 @@ class SpreadsheetPivotTable {
|
|
|
35752
35787
|
return undefined;
|
|
35753
35788
|
}
|
|
35754
35789
|
for (let i = 0; i < pivotCol.fields.length; i++) {
|
|
35790
|
+
const fieldWithGranularity = pivotCol.fields[i];
|
|
35791
|
+
const { name, granularity } = parseDimension(fieldWithGranularity);
|
|
35792
|
+
const type = this.fieldsType[name] || "char";
|
|
35755
35793
|
domain.push({
|
|
35756
|
-
|
|
35757
|
-
|
|
35794
|
+
type,
|
|
35795
|
+
field: fieldWithGranularity,
|
|
35796
|
+
value: toNormalizedPivotValue({ displayName: name, type, granularity }, pivotCol.values[i]),
|
|
35758
35797
|
});
|
|
35759
35798
|
}
|
|
35760
35799
|
return domain;
|
|
@@ -35766,17 +35805,21 @@ class SpreadsheetPivotTable {
|
|
|
35766
35805
|
getColMeasure(col) {
|
|
35767
35806
|
const domain = this.getColHeaderDomain(col, this.columns.length - 1);
|
|
35768
35807
|
const measure = domain?.at(-1)?.value;
|
|
35769
|
-
if (measure === undefined) {
|
|
35770
|
-
throw new Error("Measure
|
|
35808
|
+
if (measure === undefined || measure === null) {
|
|
35809
|
+
throw new Error("Measure is missing");
|
|
35771
35810
|
}
|
|
35772
35811
|
return measure.toString();
|
|
35773
35812
|
}
|
|
35774
35813
|
getRowDomain(row) {
|
|
35775
35814
|
const domain = [];
|
|
35776
35815
|
for (let i = 0; i < this.rows[row].fields.length; i++) {
|
|
35816
|
+
const fieldWithGranularity = this.rows[row].fields[i];
|
|
35817
|
+
const { name, granularity } = parseDimension(fieldWithGranularity);
|
|
35818
|
+
const type = this.fieldsType[name] || "char";
|
|
35777
35819
|
domain.push({
|
|
35778
|
-
|
|
35779
|
-
|
|
35820
|
+
type,
|
|
35821
|
+
field: fieldWithGranularity,
|
|
35822
|
+
value: toNormalizedPivotValue({ displayName: name, type, granularity }, this.rows[row].values[i]),
|
|
35780
35823
|
});
|
|
35781
35824
|
}
|
|
35782
35825
|
return domain;
|
|
@@ -35786,6 +35829,7 @@ class SpreadsheetPivotTable {
|
|
|
35786
35829
|
cols: this.columns,
|
|
35787
35830
|
rows: this.rows,
|
|
35788
35831
|
measures: this.measures,
|
|
35832
|
+
fieldsType: this.fieldsType,
|
|
35789
35833
|
};
|
|
35790
35834
|
}
|
|
35791
35835
|
}
|
|
@@ -35806,7 +35850,14 @@ function dataEntriesToSpreadsheetPivotTable(dataEntries, definition) {
|
|
|
35806
35850
|
indent: 0,
|
|
35807
35851
|
});
|
|
35808
35852
|
const measureNames = definition.measures.map((m) => m.name);
|
|
35809
|
-
|
|
35853
|
+
const fieldsType = {};
|
|
35854
|
+
for (const columns of definition.columns) {
|
|
35855
|
+
fieldsType[columns.name] = columns.type;
|
|
35856
|
+
}
|
|
35857
|
+
for (const row of definition.rows) {
|
|
35858
|
+
fieldsType[row.name] = row.type;
|
|
35859
|
+
}
|
|
35860
|
+
return new SpreadsheetPivotTable(cols, rows, measureNames, fieldsType);
|
|
35810
35861
|
}
|
|
35811
35862
|
// -----------------------------------------------------------------------------
|
|
35812
35863
|
// ROWS
|
|
@@ -35982,41 +36033,42 @@ function compareDimensionValues(dimension, a, b) {
|
|
|
35982
36033
|
return dimension.order === "asc" ? a.localeCompare(b) : b.localeCompare(a);
|
|
35983
36034
|
}
|
|
35984
36035
|
|
|
36036
|
+
const NULL_SYMBOL = Symbol("NULL");
|
|
35985
36037
|
function createDate(dimension, value, locale) {
|
|
35986
36038
|
const granularity = dimension.granularity;
|
|
35987
36039
|
if (!granularity || !(granularity in MAP_VALUE_DIMENSION_DATE)) {
|
|
35988
36040
|
throw new Error(`Unknown date granularity: ${granularity}`);
|
|
35989
36041
|
}
|
|
35990
|
-
|
|
35991
|
-
return null;
|
|
35992
|
-
}
|
|
36042
|
+
const keyInMap = typeof value === "number" || typeof value === "string" ? value : NULL_SYMBOL;
|
|
35993
36043
|
if (!MAP_VALUE_DIMENSION_DATE[granularity].set.has(value)) {
|
|
35994
36044
|
MAP_VALUE_DIMENSION_DATE[granularity].set.add(value);
|
|
35995
|
-
|
|
35996
|
-
|
|
35997
|
-
|
|
35998
|
-
|
|
35999
|
-
|
|
36000
|
-
|
|
36001
|
-
|
|
36002
|
-
|
|
36003
|
-
|
|
36004
|
-
|
|
36005
|
-
|
|
36006
|
-
|
|
36007
|
-
|
|
36008
|
-
|
|
36009
|
-
|
|
36010
|
-
|
|
36011
|
-
|
|
36012
|
-
|
|
36013
|
-
|
|
36014
|
-
|
|
36015
|
-
|
|
36045
|
+
let number = null;
|
|
36046
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
36047
|
+
const date = toJsDate(value, locale);
|
|
36048
|
+
switch (granularity) {
|
|
36049
|
+
case "year_number":
|
|
36050
|
+
number = date.getFullYear();
|
|
36051
|
+
break;
|
|
36052
|
+
case "quarter_number":
|
|
36053
|
+
number = Math.floor(date.getMonth() / 3) + 1;
|
|
36054
|
+
break;
|
|
36055
|
+
case "month_number":
|
|
36056
|
+
number = date.getMonth() + 1;
|
|
36057
|
+
break;
|
|
36058
|
+
case "iso_week_number":
|
|
36059
|
+
number = date.getIsoWeek();
|
|
36060
|
+
break;
|
|
36061
|
+
case "day_of_month":
|
|
36062
|
+
number = date.getDate();
|
|
36063
|
+
break;
|
|
36064
|
+
case "day":
|
|
36065
|
+
number = Math.floor(toNumber(value, locale));
|
|
36066
|
+
break;
|
|
36067
|
+
}
|
|
36016
36068
|
}
|
|
36017
|
-
MAP_VALUE_DIMENSION_DATE[granularity].values[
|
|
36069
|
+
MAP_VALUE_DIMENSION_DATE[granularity].values[keyInMap] = toNormalizedPivotValue(dimension, number);
|
|
36018
36070
|
}
|
|
36019
|
-
return MAP_VALUE_DIMENSION_DATE[granularity].values[
|
|
36071
|
+
return MAP_VALUE_DIMENSION_DATE[granularity].values[keyInMap];
|
|
36020
36072
|
}
|
|
36021
36073
|
/**
|
|
36022
36074
|
* This map is used to cache the different values of a pivot date value
|
|
@@ -36198,6 +36250,25 @@ class SpreadsheetPivot {
|
|
|
36198
36250
|
}
|
|
36199
36251
|
return undefined;
|
|
36200
36252
|
}
|
|
36253
|
+
areDomainArgsFieldsValid(args) {
|
|
36254
|
+
let dimensions = args.filter((_, index) => index % 2 === 0).map(toString);
|
|
36255
|
+
if (dimensions.length && dimensions.at(-1) === "measure") {
|
|
36256
|
+
dimensions = dimensions.slice(0, -1);
|
|
36257
|
+
}
|
|
36258
|
+
return areDomainArgsFieldsValid(dimensions, this.definition);
|
|
36259
|
+
}
|
|
36260
|
+
parseArgsToPivotDomain(args) {
|
|
36261
|
+
const domain = [];
|
|
36262
|
+
for (let i = 0; i < args.length - 1; i += 2) {
|
|
36263
|
+
const fieldWithGranularity = toString(args[i]);
|
|
36264
|
+
const type = this.getTypeOfDimension(fieldWithGranularity);
|
|
36265
|
+
const normalizedValue = fieldWithGranularity === "measure"
|
|
36266
|
+
? toString(args[i + 1])
|
|
36267
|
+
: toNormalizedPivotValue(this.getDimension(fieldWithGranularity), args[i + 1]);
|
|
36268
|
+
domain.push({ field: fieldWithGranularity, value: normalizedValue, type });
|
|
36269
|
+
}
|
|
36270
|
+
return domain;
|
|
36271
|
+
}
|
|
36201
36272
|
markAsDirtyForEvaluation() {
|
|
36202
36273
|
this.needsReevaluation = true;
|
|
36203
36274
|
}
|
|
@@ -36219,18 +36290,12 @@ class SpreadsheetPivot {
|
|
|
36219
36290
|
const finalCell = cells[0]?.[dimension.nameWithGranularity];
|
|
36220
36291
|
if (dimension.type === "date") {
|
|
36221
36292
|
const adapter = pivotTimeAdapter(dimension.granularity);
|
|
36222
|
-
return
|
|
36223
|
-
value: lastNode.value !== "null"
|
|
36224
|
-
? adapter.toCellValue(toNormalizedPivotValue(dimension, lastNode.value))
|
|
36225
|
-
: _t("(Undefined)"),
|
|
36226
|
-
format: adapter.getFormat(this.getters.getLocale()),
|
|
36227
|
-
};
|
|
36293
|
+
return adapter.toValueAndFormat(lastNode.value, this.getters.getLocale());
|
|
36228
36294
|
}
|
|
36229
36295
|
if (!finalCell) {
|
|
36230
36296
|
return { value: "" };
|
|
36231
36297
|
}
|
|
36232
|
-
|
|
36233
|
-
if (finalCell.value === null || finalCell.value === `${null}`) {
|
|
36298
|
+
if (finalCell.value === null) {
|
|
36234
36299
|
return { value: _t("(Undefined)") };
|
|
36235
36300
|
}
|
|
36236
36301
|
return {
|
|
@@ -36281,14 +36346,24 @@ class SpreadsheetPivot {
|
|
|
36281
36346
|
getFields() {
|
|
36282
36347
|
return this.fields;
|
|
36283
36348
|
}
|
|
36349
|
+
getTypeOfDimension(fieldWithGranularity) {
|
|
36350
|
+
if (fieldWithGranularity === "measure") {
|
|
36351
|
+
return "char";
|
|
36352
|
+
}
|
|
36353
|
+
const { name } = parseDimension(fieldWithGranularity);
|
|
36354
|
+
const type = this.fields[name]?.type;
|
|
36355
|
+
if (!type) {
|
|
36356
|
+
throw new Error(`Field ${name} does not exist`);
|
|
36357
|
+
}
|
|
36358
|
+
return type;
|
|
36359
|
+
}
|
|
36284
36360
|
filterDataEntriesFromDomain(dataEntries, domain) {
|
|
36285
36361
|
return domain.reduce((current, acc) => this.filterDataEntriesFromDomainNode(current, acc), dataEntries);
|
|
36286
36362
|
}
|
|
36287
36363
|
filterDataEntriesFromDomainNode(dataEntries, domain) {
|
|
36288
36364
|
const { field, value } = domain;
|
|
36289
|
-
const
|
|
36290
|
-
return dataEntries.filter((entry) =>
|
|
36291
|
-
`${toNormalizedPivotValue(dimension, value)}`);
|
|
36365
|
+
const { nameWithGranularity } = this.getDimension(field);
|
|
36366
|
+
return dataEntries.filter((entry) => entry[nameWithGranularity]?.value === value);
|
|
36292
36367
|
}
|
|
36293
36368
|
getDimension(nameWithGranularity) {
|
|
36294
36369
|
return this.definition.getDimension(nameWithGranularity);
|
|
@@ -36393,7 +36468,7 @@ class SpreadsheetPivot {
|
|
|
36393
36468
|
for (const entry of dataEntries) {
|
|
36394
36469
|
for (const dimension of dateDimensions) {
|
|
36395
36470
|
entry[dimension.nameWithGranularity] = {
|
|
36396
|
-
value:
|
|
36471
|
+
value: createDate(dimension, entry[dimension.name]?.value || null, this.getters.getLocale()),
|
|
36397
36472
|
type: entry[dimension.name]?.type || CellValueType.empty,
|
|
36398
36473
|
format: entry[dimension.name]?.format,
|
|
36399
36474
|
};
|
|
@@ -36440,11 +36515,7 @@ class PivotSidePanelStore extends SpreadsheetStore {
|
|
|
36440
36515
|
}
|
|
36441
36516
|
}
|
|
36442
36517
|
get fields() {
|
|
36443
|
-
|
|
36444
|
-
if (!fields) {
|
|
36445
|
-
throw new Error("Fields not found");
|
|
36446
|
-
}
|
|
36447
|
-
return fields;
|
|
36518
|
+
return this.pivot.getFields();
|
|
36448
36519
|
}
|
|
36449
36520
|
get pivot() {
|
|
36450
36521
|
return this.getters.getPivot(this.pivotId);
|
|
@@ -36678,6 +36749,13 @@ class PivotSpreadsheetSidePanel extends owl.Component {
|
|
|
36678
36749
|
this.store.applyUpdate();
|
|
36679
36750
|
}
|
|
36680
36751
|
}
|
|
36752
|
+
flipAxis() {
|
|
36753
|
+
const { rows, columns } = this.definition;
|
|
36754
|
+
this.onDimensionsUpdated({
|
|
36755
|
+
rows: columns,
|
|
36756
|
+
columns: rows,
|
|
36757
|
+
});
|
|
36758
|
+
}
|
|
36681
36759
|
onDimensionsUpdated(definition) {
|
|
36682
36760
|
this.store.update(definition);
|
|
36683
36761
|
}
|
|
@@ -51549,8 +51627,8 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
51549
51627
|
case "INSERT_PIVOT": {
|
|
51550
51628
|
const { sheetId, col, row, pivotId, table } = cmd;
|
|
51551
51629
|
const position = { sheetId, col, row };
|
|
51552
|
-
const { cols, rows, measures } = table;
|
|
51553
|
-
const spTable = new SpreadsheetPivotTable(cols, rows, measures);
|
|
51630
|
+
const { cols, rows, measures, fieldsType } = table;
|
|
51631
|
+
const spTable = new SpreadsheetPivotTable(cols, rows, measures, fieldsType || {});
|
|
51554
51632
|
const formulaId = this.getPivotFormulaId(pivotId);
|
|
51555
51633
|
this.insertPivot(position, formulaId, spTable);
|
|
51556
51634
|
break;
|
|
@@ -51631,7 +51709,7 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
51631
51709
|
sheetId: position.sheetId,
|
|
51632
51710
|
col: position.col + col,
|
|
51633
51711
|
row: position.row + row,
|
|
51634
|
-
content:
|
|
51712
|
+
content: createPivotFormula(formulaId, pivotCell),
|
|
51635
51713
|
});
|
|
51636
51714
|
}
|
|
51637
51715
|
}
|
|
@@ -54897,7 +54975,6 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54897
54975
|
"getPivotIdFromPosition",
|
|
54898
54976
|
"getPivotCellFromPosition",
|
|
54899
54977
|
"isPivotUnused",
|
|
54900
|
-
"areDomainArgsFieldsValid",
|
|
54901
54978
|
"isSpillPivotFormula",
|
|
54902
54979
|
];
|
|
54903
54980
|
pivots = {};
|
|
@@ -54969,7 +55046,7 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54969
55046
|
getPivotIdFromPosition(position) {
|
|
54970
55047
|
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
54971
55048
|
if (cell && cell.isFormula) {
|
|
54972
|
-
const pivotFunction = this.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
55049
|
+
const pivotFunction = this.getFirstPivotFunction(position.sheetId, cell.compiledFormula.tokens);
|
|
54973
55050
|
if (pivotFunction) {
|
|
54974
55051
|
const pivotId = pivotFunction.args[0]?.toString();
|
|
54975
55052
|
return pivotId && this.getters.getPivotId(pivotId);
|
|
@@ -54980,12 +55057,12 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
54980
55057
|
isSpillPivotFormula(position) {
|
|
54981
55058
|
const cell = this.getters.getCorrespondingFormulaCell(position);
|
|
54982
55059
|
if (cell && cell.isFormula) {
|
|
54983
|
-
const pivotFunction = this.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
55060
|
+
const pivotFunction = this.getFirstPivotFunction(position.sheetId, cell.compiledFormula.tokens);
|
|
54984
55061
|
return pivotFunction?.functionName === "PIVOT";
|
|
54985
55062
|
}
|
|
54986
55063
|
return false;
|
|
54987
55064
|
}
|
|
54988
|
-
getFirstPivotFunction(tokens) {
|
|
55065
|
+
getFirstPivotFunction(sheetId, tokens) {
|
|
54989
55066
|
const pivotFunction = getFirstPivotFunction(tokens);
|
|
54990
55067
|
if (!pivotFunction) {
|
|
54991
55068
|
return undefined;
|
|
@@ -55001,7 +55078,7 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55001
55078
|
return argAst.value;
|
|
55002
55079
|
}
|
|
55003
55080
|
const argsString = astToFormula(argAst);
|
|
55004
|
-
return this.getters.evaluateFormula(
|
|
55081
|
+
return this.getters.evaluateFormula(sheetId, argsString);
|
|
55005
55082
|
});
|
|
55006
55083
|
return { functionName, args: evaluatedArgs };
|
|
55007
55084
|
}
|
|
@@ -55024,24 +55101,24 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55024
55101
|
return EMPTY_PIVOT_CELL;
|
|
55025
55102
|
}
|
|
55026
55103
|
const mainPosition = this.getters.getCellPosition(cell.id);
|
|
55027
|
-
const result = this.getters.getFirstPivotFunction(cell.compiledFormula.tokens);
|
|
55104
|
+
const result = this.getters.getFirstPivotFunction(position.sheetId, cell.compiledFormula.tokens);
|
|
55028
55105
|
if (!result) {
|
|
55029
55106
|
return EMPTY_PIVOT_CELL;
|
|
55030
55107
|
}
|
|
55031
55108
|
const { functionName, args } = result;
|
|
55109
|
+
const formulaId = args[0];
|
|
55110
|
+
if (!formulaId) {
|
|
55111
|
+
return EMPTY_PIVOT_CELL;
|
|
55112
|
+
}
|
|
55113
|
+
const pivotId = this.getters.getPivotId(formulaId.toString());
|
|
55114
|
+
if (!pivotId) {
|
|
55115
|
+
return EMPTY_PIVOT_CELL;
|
|
55116
|
+
}
|
|
55117
|
+
const pivot = this.getPivot(pivotId);
|
|
55118
|
+
if (!pivot.isValid()) {
|
|
55119
|
+
return EMPTY_PIVOT_CELL;
|
|
55120
|
+
}
|
|
55032
55121
|
if (functionName === "PIVOT") {
|
|
55033
|
-
const formulaId = args[0];
|
|
55034
|
-
if (!formulaId) {
|
|
55035
|
-
return EMPTY_PIVOT_CELL;
|
|
55036
|
-
}
|
|
55037
|
-
const pivotId = this.getters.getPivotId(formulaId.toString());
|
|
55038
|
-
if (!pivotId) {
|
|
55039
|
-
return EMPTY_PIVOT_CELL;
|
|
55040
|
-
}
|
|
55041
|
-
const pivot = this.getPivot(pivotId);
|
|
55042
|
-
if (!pivot.isValid()) {
|
|
55043
|
-
return EMPTY_PIVOT_CELL;
|
|
55044
|
-
}
|
|
55045
55122
|
const includeTotal = args[2] === false ? false : undefined;
|
|
55046
55123
|
const includeColumnHeaders = args[3] === false ? false : undefined;
|
|
55047
55124
|
const pivotCells = pivot
|
|
@@ -55052,7 +55129,7 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55052
55129
|
return pivotCells[pivotCol][pivotRow];
|
|
55053
55130
|
}
|
|
55054
55131
|
if (functionName === "PIVOT.HEADER" && args.at(-2) === "measure") {
|
|
55055
|
-
const domain =
|
|
55132
|
+
const domain = pivot.parseArgsToPivotDomain(args.slice(1, -2).map((value) => ({ value })));
|
|
55056
55133
|
return {
|
|
55057
55134
|
type: "MEASURE_HEADER",
|
|
55058
55135
|
domain,
|
|
@@ -55060,15 +55137,17 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55060
55137
|
};
|
|
55061
55138
|
}
|
|
55062
55139
|
else if (functionName === "PIVOT.HEADER") {
|
|
55140
|
+
const domain = pivot.parseArgsToPivotDomain(args.slice(1).map((value) => ({ value })));
|
|
55063
55141
|
return {
|
|
55064
55142
|
type: "HEADER",
|
|
55065
|
-
domain
|
|
55143
|
+
domain,
|
|
55066
55144
|
};
|
|
55067
55145
|
}
|
|
55068
55146
|
const [measure, ...domainArgs] = args.slice(1);
|
|
55147
|
+
const domain = pivot.parseArgsToPivotDomain(domainArgs.map((value) => ({ value })));
|
|
55069
55148
|
return {
|
|
55070
55149
|
type: "VALUE",
|
|
55071
|
-
domain
|
|
55150
|
+
domain,
|
|
55072
55151
|
measure: measure?.toString() || "",
|
|
55073
55152
|
};
|
|
55074
55153
|
}
|
|
@@ -55078,32 +55157,6 @@ class PivotUIPlugin extends UIPlugin {
|
|
|
55078
55157
|
isPivotUnused(pivotId) {
|
|
55079
55158
|
return this._getUnusedPivots().includes(pivotId);
|
|
55080
55159
|
}
|
|
55081
|
-
/**
|
|
55082
|
-
* Check if the fields in the domain part of
|
|
55083
|
-
* a pivot function are valid according to the pivot definition.
|
|
55084
|
-
* e.g. =PIVOT.VALUE(1,"revenue","country_id",...,"create_date:month",...,"source_id",...)
|
|
55085
|
-
*/
|
|
55086
|
-
areDomainArgsFieldsValid(pivotId, domain) {
|
|
55087
|
-
const dimensions = domain
|
|
55088
|
-
.map((node) => node.field)
|
|
55089
|
-
.map((name) => (name.startsWith("#") ? name.slice(1) : name));
|
|
55090
|
-
let argIndex = 0;
|
|
55091
|
-
let definitionIndex = 0;
|
|
55092
|
-
const pivot = this.getPivot(pivotId);
|
|
55093
|
-
const definition = pivot.definition;
|
|
55094
|
-
const cols = definition.columns.map((col) => col.nameWithGranularity);
|
|
55095
|
-
const rows = definition.rows.map((row) => row.nameWithGranularity);
|
|
55096
|
-
while (dimensions[argIndex] !== undefined && dimensions[argIndex] === rows[definitionIndex]) {
|
|
55097
|
-
argIndex++;
|
|
55098
|
-
definitionIndex++;
|
|
55099
|
-
}
|
|
55100
|
-
definitionIndex = 0;
|
|
55101
|
-
while (dimensions[argIndex] !== undefined && dimensions[argIndex] === cols[definitionIndex]) {
|
|
55102
|
-
argIndex++;
|
|
55103
|
-
definitionIndex++;
|
|
55104
|
-
}
|
|
55105
|
-
return dimensions.length === argIndex;
|
|
55106
|
-
}
|
|
55107
55160
|
// ---------------------------------------------------------------------
|
|
55108
55161
|
// Private
|
|
55109
55162
|
// ---------------------------------------------------------------------
|
|
@@ -58586,32 +58639,32 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58586
58639
|
}
|
|
58587
58640
|
}
|
|
58588
58641
|
convertOSClipboardData(clipboardData) {
|
|
58589
|
-
const handlers =
|
|
58590
|
-
.getAll()
|
|
58591
|
-
.map((handler) => new handler(this.getters, this.dispatch));
|
|
58592
|
-
clipboardHandlersRegistries.cellHandlers
|
|
58593
|
-
.getAll()
|
|
58594
|
-
.forEach((handler) => handlers.push(new handler(this.getters, this.dispatch)));
|
|
58642
|
+
const handlers = this.selectClipboardHandlers({ figureId: true }).concat(this.selectClipboardHandlers({}));
|
|
58595
58643
|
let copiedData = {};
|
|
58596
|
-
for (const handler of handlers) {
|
|
58644
|
+
for (const { handlerName, handler } of handlers) {
|
|
58597
58645
|
const data = handler.convertOSClipboardData(clipboardData);
|
|
58598
|
-
copiedData =
|
|
58646
|
+
copiedData[handlerName] = data;
|
|
58647
|
+
const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
|
|
58648
|
+
for (const key of minimalKeys) {
|
|
58649
|
+
if (data && key in data) {
|
|
58650
|
+
copiedData[key] = data[key];
|
|
58651
|
+
}
|
|
58652
|
+
}
|
|
58599
58653
|
}
|
|
58600
58654
|
return copiedData;
|
|
58601
58655
|
}
|
|
58602
58656
|
selectClipboardHandlers(data) {
|
|
58603
|
-
|
|
58604
|
-
|
|
58605
|
-
|
|
58606
|
-
|
|
58607
|
-
|
|
58608
|
-
|
|
58609
|
-
|
|
58610
|
-
.map((handler) => new handler(this.getters, this.dispatch));
|
|
58657
|
+
const handlersRegistry = "figureId" in data
|
|
58658
|
+
? clipboardHandlersRegistries.figureHandlers
|
|
58659
|
+
: clipboardHandlersRegistries.cellHandlers;
|
|
58660
|
+
return handlersRegistry.getKeys().map((handlerName) => {
|
|
58661
|
+
const Handler = handlersRegistry.get(handlerName);
|
|
58662
|
+
return { handlerName, handler: new Handler(this.getters, this.dispatch) };
|
|
58663
|
+
});
|
|
58611
58664
|
}
|
|
58612
58665
|
isCutAllowedOn(zones) {
|
|
58613
58666
|
const clipboardData = this.getClipboardData(zones);
|
|
58614
|
-
for (const handler of this.selectClipboardHandlers(clipboardData)) {
|
|
58667
|
+
for (const { handler } of this.selectClipboardHandlers(clipboardData)) {
|
|
58615
58668
|
const result = handler.isCutAllowed(clipboardData);
|
|
58616
58669
|
if (result !== "Success" /* CommandResult.Success */) {
|
|
58617
58670
|
return result;
|
|
@@ -58620,7 +58673,7 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58620
58673
|
return "Success" /* CommandResult.Success */;
|
|
58621
58674
|
}
|
|
58622
58675
|
isPasteAllowed(target, copiedData, options) {
|
|
58623
|
-
for (const handler of this.selectClipboardHandlers(copiedData)) {
|
|
58676
|
+
for (const { handler } of this.selectClipboardHandlers(copiedData)) {
|
|
58624
58677
|
const result = handler.isPasteAllowed(this.getters.getActiveSheetId(), target, copiedData, {
|
|
58625
58678
|
...options,
|
|
58626
58679
|
});
|
|
@@ -58648,9 +58701,15 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58648
58701
|
copy(zones) {
|
|
58649
58702
|
let copiedData = {};
|
|
58650
58703
|
const clipboardData = this.getClipboardData(zones);
|
|
58651
|
-
for (const handler of this.selectClipboardHandlers(clipboardData)) {
|
|
58704
|
+
for (const { handlerName, handler } of this.selectClipboardHandlers(clipboardData)) {
|
|
58652
58705
|
const data = handler.copy(clipboardData);
|
|
58653
|
-
copiedData =
|
|
58706
|
+
copiedData[handlerName] = data;
|
|
58707
|
+
const minimalKeys = ["sheetId", "cells", "zones", "figureId"];
|
|
58708
|
+
for (const key of minimalKeys) {
|
|
58709
|
+
if (data && key in data) {
|
|
58710
|
+
copiedData[key] = data[key];
|
|
58711
|
+
}
|
|
58712
|
+
}
|
|
58654
58713
|
}
|
|
58655
58714
|
return copiedData;
|
|
58656
58715
|
}
|
|
@@ -58666,8 +58725,12 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58666
58725
|
zones,
|
|
58667
58726
|
};
|
|
58668
58727
|
const handlers = this.selectClipboardHandlers(copiedData);
|
|
58669
|
-
for (const handler of handlers) {
|
|
58670
|
-
const
|
|
58728
|
+
for (const { handlerName, handler } of handlers) {
|
|
58729
|
+
const handlerData = copiedData[handlerName];
|
|
58730
|
+
if (!handlerData) {
|
|
58731
|
+
continue;
|
|
58732
|
+
}
|
|
58733
|
+
const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
|
|
58671
58734
|
if (currentTarget.figureId) {
|
|
58672
58735
|
target.figureId = currentTarget.figureId;
|
|
58673
58736
|
}
|
|
@@ -58683,7 +58746,12 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
58683
58746
|
if (zone !== undefined) {
|
|
58684
58747
|
this.addMissingDimensions(this.getters.getActiveSheetId(), zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
|
|
58685
58748
|
}
|
|
58686
|
-
handlers.forEach((handler) =>
|
|
58749
|
+
handlers.forEach(({ handlerName, handler }) => {
|
|
58750
|
+
const handlerData = copiedData[handlerName];
|
|
58751
|
+
if (handlerData) {
|
|
58752
|
+
handler.paste(target, handlerData, options);
|
|
58753
|
+
}
|
|
58754
|
+
});
|
|
58687
58755
|
if (!options?.selectTarget) {
|
|
58688
58756
|
return;
|
|
58689
58757
|
}
|
|
@@ -66803,6 +66871,7 @@ class Model extends EventBus {
|
|
|
66803
66871
|
const start = performance.now();
|
|
66804
66872
|
console.group("Model creation");
|
|
66805
66873
|
super();
|
|
66874
|
+
setDefaultTranslationMethod();
|
|
66806
66875
|
stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
|
|
66807
66876
|
const workbookData = load(data, verboseImport);
|
|
66808
66877
|
this.state = new StateObserver();
|
|
@@ -67298,6 +67367,7 @@ const registries = {
|
|
|
67298
67367
|
pivotSidePanelRegistry,
|
|
67299
67368
|
pivotNormalizationValueRegistry,
|
|
67300
67369
|
supportedPivotPositionalFormulaRegistry,
|
|
67370
|
+
pivotToFunctionValueRegistry,
|
|
67301
67371
|
};
|
|
67302
67372
|
const helpers = {
|
|
67303
67373
|
arg,
|
|
@@ -67343,7 +67413,6 @@ const helpers = {
|
|
|
67343
67413
|
expandZoneOnInsertion,
|
|
67344
67414
|
reduceZoneOnDeletion,
|
|
67345
67415
|
unquote,
|
|
67346
|
-
makePivotFormula,
|
|
67347
67416
|
getMaxObjectId,
|
|
67348
67417
|
getFunctionsFromTokens,
|
|
67349
67418
|
getFirstPivotFunction,
|
|
@@ -67355,10 +67424,10 @@ const helpers = {
|
|
|
67355
67424
|
insertTokenAfterLeftParenthesis,
|
|
67356
67425
|
mergeContiguousZones,
|
|
67357
67426
|
getPivotHighlights,
|
|
67358
|
-
toPivotDomain,
|
|
67359
|
-
flatPivotDomain,
|
|
67360
67427
|
pivotTimeAdapter,
|
|
67361
67428
|
UNDO_REDO_PIVOT_COMMANDS,
|
|
67429
|
+
createPivotFormula,
|
|
67430
|
+
areDomainArgsFieldsValid,
|
|
67362
67431
|
};
|
|
67363
67432
|
const links = {
|
|
67364
67433
|
isMarkdownLink,
|
|
@@ -67489,6 +67558,6 @@ exports.tokenColors = tokenColors;
|
|
|
67489
67558
|
exports.tokenize = tokenize;
|
|
67490
67559
|
|
|
67491
67560
|
|
|
67492
|
-
__info__.version = "17.4.0-alpha.
|
|
67493
|
-
__info__.date = "2024-06-
|
|
67494
|
-
__info__.hash = "
|
|
67561
|
+
__info__.version = "17.4.0-alpha.9";
|
|
67562
|
+
__info__.date = "2024-06-26T11:09:20.284Z";
|
|
67563
|
+
__info__.hash = "526be20";
|