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