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