@odoo/o-spreadsheet 18.5.0-alpha.10 → 18.5.0-alpha.12
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 +461 -158
- package/dist/o-spreadsheet.d.ts +176 -151
- package/dist/o-spreadsheet.esm.js +461 -158
- package/dist/o-spreadsheet.iife.js +461 -158
- package/dist/o-spreadsheet.iife.min.js +421 -419
- package/dist/o_spreadsheet.xml +35 -18
- package/package.json +1 -1
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 18.5.0-alpha.
|
|
6
|
-
* @date 2025-08-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.5.0-alpha.12
|
|
6
|
+
* @date 2025-08-29T08:05:09.767Z
|
|
7
|
+
* @hash 8ee7677
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -725,7 +725,7 @@ function batched(callback) {
|
|
|
725
725
|
/*
|
|
726
726
|
* Concatenate an array of strings.
|
|
727
727
|
*/
|
|
728
|
-
function concat(chars) {
|
|
728
|
+
function concat$1(chars) {
|
|
729
729
|
// ~40% faster than chars.join("")
|
|
730
730
|
let output = "";
|
|
731
731
|
for (let i = 0, len = chars.length; i < len; i++) {
|
|
@@ -1197,7 +1197,7 @@ function rgbaStringToHex(color) {
|
|
|
1197
1197
|
if (alphaHex !== 255) {
|
|
1198
1198
|
vals.push(alphaHex);
|
|
1199
1199
|
}
|
|
1200
|
-
return "#" + concat(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
|
|
1200
|
+
return "#" + concat$1(vals.map((value) => value.toString(16).padStart(2, "0"))).toUpperCase();
|
|
1201
1201
|
}
|
|
1202
1202
|
/**
|
|
1203
1203
|
* RGBA to HEX representation (#RRGGBBAA).
|
|
@@ -1347,6 +1347,18 @@ function hslaToHex(hsla) {
|
|
|
1347
1347
|
function hexToHSLA(hex) {
|
|
1348
1348
|
return rgbaToHSLA(colorToRGBA(hex));
|
|
1349
1349
|
}
|
|
1350
|
+
/**
|
|
1351
|
+
* Blend color2 on top of color1, with alpha blending.
|
|
1352
|
+
*/
|
|
1353
|
+
function blendColors(color1, color2) {
|
|
1354
|
+
const rgba2 = colorToRGBA(color2);
|
|
1355
|
+
const rgba1 = colorToRGBA(color1);
|
|
1356
|
+
const a = rgba2.a + rgba1.a * (1 - rgba2.a);
|
|
1357
|
+
const r = Math.round((rgba2.r * rgba2.a + rgba1.r * rgba1.a * (1 - rgba2.a)) / a);
|
|
1358
|
+
const g = Math.round((rgba2.g * rgba2.a + rgba1.g * rgba1.a * (1 - rgba2.a)) / a);
|
|
1359
|
+
const b = Math.round((rgba2.b * rgba2.a + rgba1.b * rgba1.a * (1 - rgba2.a)) / a);
|
|
1360
|
+
return rgbaToHex({ r, g, b, a });
|
|
1361
|
+
}
|
|
1350
1362
|
function colorOrNumberToRGBA(color) {
|
|
1351
1363
|
if (typeof color === "number") {
|
|
1352
1364
|
return colorToRGBA(colorNumberToHex(color));
|
|
@@ -2661,6 +2673,7 @@ const invalidateBordersCommands = new Set([
|
|
|
2661
2673
|
"AUTOFILL_CELL",
|
|
2662
2674
|
"SET_BORDER",
|
|
2663
2675
|
"SET_ZONE_BORDERS",
|
|
2676
|
+
"SET_BORDERS_ON_TARGET",
|
|
2664
2677
|
]);
|
|
2665
2678
|
const invalidSubtotalFormulasCommands = new Set([
|
|
2666
2679
|
"UNHIDE_COLUMNS_ROWS",
|
|
@@ -2688,6 +2701,7 @@ const readonlyAllowedCommands = new Set([
|
|
|
2688
2701
|
"UPDATE_FILTER",
|
|
2689
2702
|
"UPDATE_CHART",
|
|
2690
2703
|
"UPDATE_CAROUSEL_ACTIVE_ITEM",
|
|
2704
|
+
"UPDATE_PIVOT",
|
|
2691
2705
|
]);
|
|
2692
2706
|
const coreTypes = new Set([
|
|
2693
2707
|
/** CELLS */
|
|
@@ -5111,6 +5125,9 @@ function parseLiteral(content, locale) {
|
|
|
5111
5125
|
if (content === "") {
|
|
5112
5126
|
return null;
|
|
5113
5127
|
}
|
|
5128
|
+
if (content.includes("\n")) {
|
|
5129
|
+
return content;
|
|
5130
|
+
}
|
|
5114
5131
|
if (isNumber(content, DEFAULT_LOCALE)) {
|
|
5115
5132
|
return parseNumber(content, DEFAULT_LOCALE);
|
|
5116
5133
|
}
|
|
@@ -7319,14 +7336,11 @@ function getPasteZones(target, content) {
|
|
|
7319
7336
|
const width = content[0].length, height = content.length;
|
|
7320
7337
|
return target.map((t) => splitZoneForPaste(t, width, height)).flat();
|
|
7321
7338
|
}
|
|
7322
|
-
function parseOSClipboardContent(content
|
|
7339
|
+
function parseOSClipboardContent(content) {
|
|
7323
7340
|
let spreadsheetContent = undefined;
|
|
7324
7341
|
if (content[ClipboardMIMEType.Html]) {
|
|
7325
7342
|
const htmlDocument = new DOMParser().parseFromString(content[ClipboardMIMEType.Html], "text/html");
|
|
7326
|
-
|
|
7327
|
-
.querySelector("div")
|
|
7328
|
-
?.getAttribute("data-osheet-clipboard");
|
|
7329
|
-
spreadsheetContent = oSheetClipboardData && JSON.parse(oSheetClipboardData);
|
|
7343
|
+
spreadsheetContent = getOSheetDataFromHTML(htmlDocument);
|
|
7330
7344
|
}
|
|
7331
7345
|
const textContent = content[ClipboardMIMEType.PlainText] || "";
|
|
7332
7346
|
let imageBlob = undefined;
|
|
@@ -7345,6 +7359,17 @@ function parseOSClipboardContent(content, clipboardId) {
|
|
|
7345
7359
|
};
|
|
7346
7360
|
return osClipboardContent;
|
|
7347
7361
|
}
|
|
7362
|
+
function getOSheetDataFromHTML(htmlDocument) {
|
|
7363
|
+
const attributes = [...htmlDocument.documentElement.attributes];
|
|
7364
|
+
// Check if it's a Microsoft Office clipboard data (it will have some namespaces defined in the root element)
|
|
7365
|
+
if (attributes.some((attr) => attr.value.includes("microsoft"))) {
|
|
7366
|
+
return undefined;
|
|
7367
|
+
}
|
|
7368
|
+
const oSheetClipboardData = htmlDocument
|
|
7369
|
+
.querySelector("div")
|
|
7370
|
+
?.getAttribute("data-osheet-clipboard");
|
|
7371
|
+
return oSheetClipboardData && JSON.parse(oSheetClipboardData);
|
|
7372
|
+
}
|
|
7348
7373
|
/**
|
|
7349
7374
|
* Applies each clipboard handler to paste its corresponding data into the target.
|
|
7350
7375
|
*/
|
|
@@ -18338,7 +18363,6 @@ function rangeTokenize(formula, locale = DEFAULT_LOCALE) {
|
|
|
18338
18363
|
const functionRegex = /[a-zA-Z0-9\_]+(\.[a-zA-Z0-9\_]+)*/;
|
|
18339
18364
|
const UNARY_OPERATORS_PREFIX = ["-", "+"];
|
|
18340
18365
|
const UNARY_OPERATORS_POSTFIX = ["%"];
|
|
18341
|
-
const ASSOCIATIVE_OPERATORS = ["*", "+", "&"];
|
|
18342
18366
|
class TokenList {
|
|
18343
18367
|
tokens;
|
|
18344
18368
|
currentIndex = 0;
|
|
@@ -18359,8 +18383,8 @@ class TokenList {
|
|
|
18359
18383
|
}
|
|
18360
18384
|
}
|
|
18361
18385
|
const OP_PRIORITY = {
|
|
18386
|
+
"%": 40,
|
|
18362
18387
|
"^": 30,
|
|
18363
|
-
"%": 30,
|
|
18364
18388
|
"*": 20,
|
|
18365
18389
|
"/": 20,
|
|
18366
18390
|
"+": 15,
|
|
@@ -18641,64 +18665,6 @@ function mapAst(ast, fn) {
|
|
|
18641
18665
|
return ast;
|
|
18642
18666
|
}
|
|
18643
18667
|
}
|
|
18644
|
-
/**
|
|
18645
|
-
* Converts an ast formula to the corresponding string
|
|
18646
|
-
*/
|
|
18647
|
-
function astToFormula(ast) {
|
|
18648
|
-
switch (ast.type) {
|
|
18649
|
-
case "FUNCALL":
|
|
18650
|
-
const args = ast.args.map((arg) => astToFormula(arg));
|
|
18651
|
-
return `${ast.value}(${args.join(",")})`;
|
|
18652
|
-
case "NUMBER":
|
|
18653
|
-
return ast.value.toString();
|
|
18654
|
-
case "REFERENCE":
|
|
18655
|
-
return ast.value;
|
|
18656
|
-
case "STRING":
|
|
18657
|
-
return `"${ast.value}"`;
|
|
18658
|
-
case "BOOLEAN":
|
|
18659
|
-
return ast.value ? "TRUE" : "FALSE";
|
|
18660
|
-
case "UNARY_OPERATION":
|
|
18661
|
-
return ast.postfix
|
|
18662
|
-
? leftOperandToFormula(ast) + ast.value
|
|
18663
|
-
: ast.value + rightOperandToFormula(ast);
|
|
18664
|
-
case "BIN_OPERATION":
|
|
18665
|
-
return leftOperandToFormula(ast) + ast.value + rightOperandToFormula(ast);
|
|
18666
|
-
default:
|
|
18667
|
-
return ast.value;
|
|
18668
|
-
}
|
|
18669
|
-
}
|
|
18670
|
-
/**
|
|
18671
|
-
* Convert the left operand of a binary operation to the corresponding string
|
|
18672
|
-
* and enclose the result inside parenthesis if necessary.
|
|
18673
|
-
*/
|
|
18674
|
-
function leftOperandToFormula(operationAST) {
|
|
18675
|
-
const mainOperator = operationAST.value;
|
|
18676
|
-
const leftOperation = "left" in operationAST ? operationAST.left : operationAST.operand;
|
|
18677
|
-
const leftOperator = leftOperation.value;
|
|
18678
|
-
const needParenthesis = leftOperation.type === "BIN_OPERATION" && OP_PRIORITY[leftOperator] < OP_PRIORITY[mainOperator];
|
|
18679
|
-
return needParenthesis ? `(${astToFormula(leftOperation)})` : astToFormula(leftOperation);
|
|
18680
|
-
}
|
|
18681
|
-
/**
|
|
18682
|
-
* Convert the right operand of a binary or unary operation to the corresponding string
|
|
18683
|
-
* and enclose the result inside parenthesis if necessary.
|
|
18684
|
-
*/
|
|
18685
|
-
function rightOperandToFormula(operationAST) {
|
|
18686
|
-
const mainOperator = operationAST.value;
|
|
18687
|
-
const rightOperation = "right" in operationAST ? operationAST.right : operationAST.operand;
|
|
18688
|
-
const rightPriority = OP_PRIORITY[rightOperation.value];
|
|
18689
|
-
const mainPriority = OP_PRIORITY[mainOperator];
|
|
18690
|
-
let needParenthesis = false;
|
|
18691
|
-
if (rightOperation.type !== "BIN_OPERATION") {
|
|
18692
|
-
needParenthesis = false;
|
|
18693
|
-
}
|
|
18694
|
-
else if (rightPriority < mainPriority) {
|
|
18695
|
-
needParenthesis = true;
|
|
18696
|
-
}
|
|
18697
|
-
else if (rightPriority === mainPriority && !ASSOCIATIVE_OPERATORS.includes(mainOperator)) {
|
|
18698
|
-
needParenthesis = true;
|
|
18699
|
-
}
|
|
18700
|
-
return needParenthesis ? `(${astToFormula(rightOperation)})` : astToFormula(rightOperation);
|
|
18701
|
-
}
|
|
18702
18668
|
|
|
18703
18669
|
/**
|
|
18704
18670
|
* Add the following information on tokens:
|
|
@@ -22197,7 +22163,7 @@ function adaptFormulaStringRanges(defaultSheetId, formula, applyChange) {
|
|
|
22197
22163
|
};
|
|
22198
22164
|
}
|
|
22199
22165
|
}
|
|
22200
|
-
return concat(tokens.map((token) => token.value));
|
|
22166
|
+
return concat$1(tokens.map((token) => token.value));
|
|
22201
22167
|
}
|
|
22202
22168
|
function adaptStringRange(defaultSheetId, sheetXC, applyChange) {
|
|
22203
22169
|
const sheetName = splitReference(sheetXC).sheetName;
|
|
@@ -28282,10 +28248,6 @@ class ComboChart extends AbstractChart {
|
|
|
28282
28248
|
};
|
|
28283
28249
|
}
|
|
28284
28250
|
getDefinitionForExcel() {
|
|
28285
|
-
// Excel does not support aggregating labels
|
|
28286
|
-
if (this.aggregated) {
|
|
28287
|
-
return undefined;
|
|
28288
|
-
}
|
|
28289
28251
|
const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
28290
28252
|
const definition = this.getDefinition();
|
|
28291
28253
|
return {
|
|
@@ -29487,9 +29449,6 @@ class RadarChart extends AbstractChart {
|
|
|
29487
29449
|
};
|
|
29488
29450
|
}
|
|
29489
29451
|
getDefinitionForExcel() {
|
|
29490
|
-
if (this.aggregated) {
|
|
29491
|
-
return undefined;
|
|
29492
|
-
}
|
|
29493
29452
|
const { dataSets, labelRange } = this.getCommonDataSetAttributesForExcel(this.labelRange, this.dataSets, shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle));
|
|
29494
29453
|
const definition = this.getDefinition();
|
|
29495
29454
|
return {
|
|
@@ -29637,10 +29596,6 @@ class ScatterChart extends AbstractChart {
|
|
|
29637
29596
|
return new ScatterChart(definition, this.sheetId, this.getters);
|
|
29638
29597
|
}
|
|
29639
29598
|
getDefinitionForExcel() {
|
|
29640
|
-
// Excel does not support aggregating labels
|
|
29641
|
-
if (this.aggregated) {
|
|
29642
|
-
return undefined;
|
|
29643
|
-
}
|
|
29644
29599
|
const dataSets = this.dataSets
|
|
29645
29600
|
.map((ds) => toExcelDataset(this.getters, ds))
|
|
29646
29601
|
.filter((ds) => ds.range !== "");
|
|
@@ -30959,7 +30914,7 @@ function escapeXml(strings, ...expressions) {
|
|
|
30959
30914
|
const value = expressions[i] instanceof XMLString ? expressions[i] : xmlEscape(expressions[i]);
|
|
30960
30915
|
str.push(value + strings[i + 1]);
|
|
30961
30916
|
}
|
|
30962
|
-
return new XMLString(concat(str));
|
|
30917
|
+
return new XMLString(concat$1(str));
|
|
30963
30918
|
}
|
|
30964
30919
|
/**
|
|
30965
30920
|
* Removes the escaped namespace of all the xml tags in the string.
|
|
@@ -32045,10 +32000,10 @@ class ChartDashboardMenuStore extends SpreadsheetStore {
|
|
|
32045
32000
|
const item = chartSubtypeRegistry.get(type);
|
|
32046
32001
|
return {
|
|
32047
32002
|
id: item.chartType,
|
|
32048
|
-
label: item.displayName,
|
|
32003
|
+
label: _t("Show as %(chart_type)s chart", { chart_type: item.displayName.toLowerCase() }),
|
|
32049
32004
|
onClick: () => this.updateType(item.chartType),
|
|
32050
|
-
|
|
32051
|
-
|
|
32005
|
+
class: item.chartType === definition.type ? "active" : "",
|
|
32006
|
+
preview: item.preview,
|
|
32052
32007
|
};
|
|
32053
32008
|
});
|
|
32054
32009
|
}
|
|
@@ -32084,18 +32039,6 @@ class ChartDashboardMenuStore extends SpreadsheetStore {
|
|
|
32084
32039
|
sheetId: this.getters.getActiveSheetId(),
|
|
32085
32040
|
});
|
|
32086
32041
|
}
|
|
32087
|
-
getIconClasses(type) {
|
|
32088
|
-
if (type.includes("bar")) {
|
|
32089
|
-
return "fa fa-bar-chart";
|
|
32090
|
-
}
|
|
32091
|
-
if (type.includes("line")) {
|
|
32092
|
-
return "fa fa-line-chart";
|
|
32093
|
-
}
|
|
32094
|
-
if (type.includes("pie")) {
|
|
32095
|
-
return "fa fa-pie-chart";
|
|
32096
|
-
}
|
|
32097
|
-
return "";
|
|
32098
|
-
}
|
|
32099
32042
|
}
|
|
32100
32043
|
|
|
32101
32044
|
class ChartDashboardMenu extends owl.Component {
|
|
@@ -32134,20 +32077,11 @@ class ChartDashboardMenu extends owl.Component {
|
|
|
32134
32077
|
if (definition.type === "scorecard") {
|
|
32135
32078
|
return undefined;
|
|
32136
32079
|
}
|
|
32137
|
-
|
|
32138
|
-
return {
|
|
32139
|
-
id: "fullScreenChart",
|
|
32140
|
-
label: _t("Exit Full Screen"),
|
|
32141
|
-
iconClass: "fa fa-compress",
|
|
32142
|
-
onClick: () => {
|
|
32143
|
-
this.fullScreenFigureStore.toggleFullScreenChart(figureId);
|
|
32144
|
-
},
|
|
32145
|
-
};
|
|
32146
|
-
}
|
|
32080
|
+
const isFullScreen = this.props.chartId === this.fullScreenFigureStore.fullScreenFigure?.id;
|
|
32147
32081
|
return {
|
|
32148
32082
|
id: "fullScreenChart",
|
|
32149
|
-
label: _t("Full Screen"),
|
|
32150
|
-
|
|
32083
|
+
label: isFullScreen ? _t("Exit Full Screen") : _t("Full Screen"),
|
|
32084
|
+
class: `text-muted fa ${isFullScreen ? "fa-compress" : "fa-expand"}`,
|
|
32151
32085
|
onClick: () => {
|
|
32152
32086
|
this.fullScreenFigureStore.toggleFullScreenChart(figureId);
|
|
32153
32087
|
},
|
|
@@ -34329,7 +34263,7 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
34329
34263
|
if (isFormula(content)) {
|
|
34330
34264
|
const missing = this.getNumberOfMissingParenthesis(this.currentTokens);
|
|
34331
34265
|
if (missing > 0) {
|
|
34332
|
-
content += concat(new Array(missing).fill(")"));
|
|
34266
|
+
content += concat$1(new Array(missing).fill(")"));
|
|
34333
34267
|
}
|
|
34334
34268
|
}
|
|
34335
34269
|
}
|
|
@@ -34381,9 +34315,10 @@ class AbstractComposerStore extends SpreadsheetStore {
|
|
|
34381
34315
|
if (isNewCurrentContent || this.editionMode !== "inactive") {
|
|
34382
34316
|
const locale = this.getters.getLocale();
|
|
34383
34317
|
this.currentTokens = isFormula(text) ? composerTokenize(text, locale) : [];
|
|
34384
|
-
|
|
34318
|
+
const nonSpaceTokensCount = this.currentTokens.filter((token) => token.type !== "SPACE").length;
|
|
34319
|
+
if (nonSpaceTokensCount > 1000) {
|
|
34385
34320
|
if (raise) {
|
|
34386
|
-
this.notificationStore.raiseError(_t("This formula has over
|
|
34321
|
+
this.notificationStore.raiseError(_t("This formula has over 1000 parts. It can't be processed properly, consider splitting it into multiple cells"));
|
|
34387
34322
|
}
|
|
34388
34323
|
}
|
|
34389
34324
|
}
|
|
@@ -34804,6 +34739,8 @@ css /* scss */ `
|
|
|
34804
34739
|
padding-right: 3px;
|
|
34805
34740
|
outline: none;
|
|
34806
34741
|
|
|
34742
|
+
tab-size: 4;
|
|
34743
|
+
|
|
34807
34744
|
p {
|
|
34808
34745
|
margin-bottom: 0px;
|
|
34809
34746
|
|
|
@@ -47003,18 +46940,18 @@ const pivotProperties = {
|
|
|
47003
46940
|
};
|
|
47004
46941
|
const pivotSortingAsc = {
|
|
47005
46942
|
name: _t("Ascending"),
|
|
47006
|
-
execute: (env) => sortPivot(env, "asc"),
|
|
47007
|
-
isActive: (env) =>
|
|
46943
|
+
execute: (env) => sortPivot(env, env.model.getters.getActivePosition(), "asc"),
|
|
46944
|
+
isActive: (env) => env.model.getters.getPivotCellSortDirection(env.model.getters.getActivePosition()) === "asc",
|
|
47008
46945
|
};
|
|
47009
46946
|
const pivotSortingDesc = {
|
|
47010
46947
|
name: _t("Descending"),
|
|
47011
|
-
execute: (env) => sortPivot(env, "desc"),
|
|
47012
|
-
isActive: (env) =>
|
|
46948
|
+
execute: (env) => sortPivot(env, env.model.getters.getActivePosition(), "desc"),
|
|
46949
|
+
isActive: (env) => env.model.getters.getPivotCellSortDirection(env.model.getters.getActivePosition()) === "desc",
|
|
47013
46950
|
};
|
|
47014
46951
|
const noPivotSorting = {
|
|
47015
46952
|
name: _t("No sorting"),
|
|
47016
|
-
execute: (env) => sortPivot(env, "none"),
|
|
47017
|
-
isActive: (env) =>
|
|
46953
|
+
execute: (env) => sortPivot(env, env.model.getters.getActivePosition(), "none"),
|
|
46954
|
+
isActive: (env) => env.model.getters.getPivotCellSortDirection(env.model.getters.getActivePosition()) === "none",
|
|
47018
46955
|
};
|
|
47019
46956
|
const FIX_FORMULAS = {
|
|
47020
46957
|
name: _t("Convert to individual formulas"),
|
|
@@ -47150,23 +47087,19 @@ const ungroupPivotHeadersAction = {
|
|
|
47150
47087
|
return areFieldValuesInGroups(definition, values, field, pivot.getFields());
|
|
47151
47088
|
},
|
|
47152
47089
|
};
|
|
47153
|
-
function canSortPivot(
|
|
47154
|
-
const
|
|
47155
|
-
|
|
47156
|
-
if (!pivotId ||
|
|
47157
|
-
!env.model.getters.isExistingPivot(pivotId) ||
|
|
47158
|
-
!env.model.getters.isSpillPivotFormula(position)) {
|
|
47090
|
+
function canSortPivot(getters, position) {
|
|
47091
|
+
const pivotId = getters.getPivotIdFromPosition(position);
|
|
47092
|
+
if (!pivotId || !getters.isExistingPivot(pivotId) || !getters.isSpillPivotFormula(position)) {
|
|
47159
47093
|
return false;
|
|
47160
47094
|
}
|
|
47161
|
-
const pivot =
|
|
47095
|
+
const pivot = getters.getPivot(pivotId);
|
|
47162
47096
|
if (!pivot.isValid()) {
|
|
47163
47097
|
return false;
|
|
47164
47098
|
}
|
|
47165
|
-
const pivotCell =
|
|
47099
|
+
const pivotCell = getters.getPivotCellFromPosition(position);
|
|
47166
47100
|
return pivotCell.type === "VALUE" || pivotCell.type === "MEASURE_HEADER";
|
|
47167
47101
|
}
|
|
47168
|
-
function sortPivot(env, order) {
|
|
47169
|
-
const position = env.model.getters.getActivePosition();
|
|
47102
|
+
function sortPivot(env, position, order) {
|
|
47170
47103
|
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
47171
47104
|
const pivotCell = env.model.getters.getPivotCellFromPosition(position);
|
|
47172
47105
|
if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
|
|
@@ -47192,24 +47125,6 @@ function sortPivot(env, order) {
|
|
|
47192
47125
|
},
|
|
47193
47126
|
});
|
|
47194
47127
|
}
|
|
47195
|
-
function isPivotSortMenuItemActive(env, order) {
|
|
47196
|
-
const position = env.model.getters.getActivePosition();
|
|
47197
|
-
const pivotId = env.model.getters.getPivotIdFromPosition(position);
|
|
47198
|
-
const pivotCell = env.model.getters.getPivotCellFromPosition(position);
|
|
47199
|
-
if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
|
|
47200
|
-
return false;
|
|
47201
|
-
}
|
|
47202
|
-
const pivot = env.model.getters.getPivot(pivotId);
|
|
47203
|
-
const colDomain = domainToColRowDomain(pivot, pivotCell.domain).colDomain;
|
|
47204
|
-
const sortedColumn = pivot.definition.sortedColumn;
|
|
47205
|
-
if (order === "none") {
|
|
47206
|
-
return !sortedColumn;
|
|
47207
|
-
}
|
|
47208
|
-
if (!sortedColumn || sortedColumn.order !== order) {
|
|
47209
|
-
return false;
|
|
47210
|
-
}
|
|
47211
|
-
return sortedColumn.measure === pivotCell.measure && deepEquals(sortedColumn.domain, colDomain);
|
|
47212
|
-
}
|
|
47213
47128
|
/*
|
|
47214
47129
|
* Get the values of the pivot headers in the current selection, if all the pivot headers on the selection belong
|
|
47215
47130
|
* to the same pivot, the same field and that the pivot formula is a dynamic pivot. Otherwise return undefined.
|
|
@@ -47512,7 +47427,10 @@ cellMenuRegistry
|
|
|
47512
47427
|
name: _t("Sort pivot"),
|
|
47513
47428
|
sequence: 155,
|
|
47514
47429
|
icon: "o-spreadsheet-Icon.SORT_RANGE",
|
|
47515
|
-
isVisible:
|
|
47430
|
+
isVisible: (env) => {
|
|
47431
|
+
const position = env.model.getters.getActivePosition();
|
|
47432
|
+
return canSortPivot(env.model.getters, position);
|
|
47433
|
+
},
|
|
47516
47434
|
})
|
|
47517
47435
|
.add("pivot_fix_formulas", {
|
|
47518
47436
|
...FIX_FORMULAS,
|
|
@@ -48654,6 +48572,280 @@ class ClientTag extends owl.Component {
|
|
|
48654
48572
|
}
|
|
48655
48573
|
}
|
|
48656
48574
|
|
|
48575
|
+
const ASSOCIATIVE_OPERATORS = ["*", "+", "&"];
|
|
48576
|
+
/**
|
|
48577
|
+
* Pretty-prints formula ASTs into readable formulas.
|
|
48578
|
+
*
|
|
48579
|
+
* Implements a Wadler-inspired pretty printer:
|
|
48580
|
+
* it converts an AST into a `Doc` structure,
|
|
48581
|
+
* and then chooses between compact (flat) or expanded (with
|
|
48582
|
+
* line breaks and indentation) layouts depending on space.
|
|
48583
|
+
*
|
|
48584
|
+
* References:
|
|
48585
|
+
* - https://lik.ai/blog/how-a-pretty-printer-works/
|
|
48586
|
+
* - Wadler, "A prettier printer": https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf
|
|
48587
|
+
*/
|
|
48588
|
+
function prettify(ast, width = 60) {
|
|
48589
|
+
return "=" + print(astToDoc(ast), width - 1); // width-1 because of the leading '='
|
|
48590
|
+
}
|
|
48591
|
+
/**
|
|
48592
|
+
* A possible line break.
|
|
48593
|
+
* Printed as either space or newline.
|
|
48594
|
+
*/
|
|
48595
|
+
function line() {
|
|
48596
|
+
return { type: "insertLine" };
|
|
48597
|
+
}
|
|
48598
|
+
/**
|
|
48599
|
+
* Increase indentation for a nested block.
|
|
48600
|
+
*/
|
|
48601
|
+
function nest(indentLevel, doc) {
|
|
48602
|
+
return { type: "nest", indentLevel, doc };
|
|
48603
|
+
}
|
|
48604
|
+
/**
|
|
48605
|
+
* Combines multiple docs into a single doc, concatenating them side by side
|
|
48606
|
+
* without any line breaks or indentation.
|
|
48607
|
+
*/
|
|
48608
|
+
function concat(docs) {
|
|
48609
|
+
return { type: "concat", docs };
|
|
48610
|
+
}
|
|
48611
|
+
/**
|
|
48612
|
+
* Marks a document as a unit to be printed "flat" (all on one line)
|
|
48613
|
+
* if it fits within the width, otherwise with line breaks.
|
|
48614
|
+
*/
|
|
48615
|
+
function group(doc) {
|
|
48616
|
+
return chooseBetween(flatten(doc), doc);
|
|
48617
|
+
}
|
|
48618
|
+
/**
|
|
48619
|
+
* Creates a choice between two alternative layouts.
|
|
48620
|
+
* The formatter tries `doc1`; if it does not fit within
|
|
48621
|
+
* the line width, it falls back to `doc2`.
|
|
48622
|
+
*/
|
|
48623
|
+
function chooseBetween(doc1, doc2) {
|
|
48624
|
+
return { type: "chooseBetween", doc1, doc2 };
|
|
48625
|
+
}
|
|
48626
|
+
/**
|
|
48627
|
+
* Flattens a doc into its single-line form.
|
|
48628
|
+
*/
|
|
48629
|
+
function flatten(doc) {
|
|
48630
|
+
if (typeof doc === "string") {
|
|
48631
|
+
return doc;
|
|
48632
|
+
}
|
|
48633
|
+
if (doc.type === "chooseBetween") {
|
|
48634
|
+
// Normally should be "chooseBetween(flatten(doc.doc1), flatten(doc.doc2))",
|
|
48635
|
+
// but this is simplified for performance reasons.
|
|
48636
|
+
return flatten(doc.doc1);
|
|
48637
|
+
}
|
|
48638
|
+
if (doc.type === "concat") {
|
|
48639
|
+
return concat(doc.docs.map(flatten));
|
|
48640
|
+
}
|
|
48641
|
+
if (doc.type === "nest") {
|
|
48642
|
+
return {
|
|
48643
|
+
type: "nest",
|
|
48644
|
+
indentLevel: doc.indentLevel,
|
|
48645
|
+
doc: flatten(doc.doc),
|
|
48646
|
+
};
|
|
48647
|
+
}
|
|
48648
|
+
if (doc.type === "insertLine") {
|
|
48649
|
+
return "";
|
|
48650
|
+
}
|
|
48651
|
+
return doc;
|
|
48652
|
+
}
|
|
48653
|
+
const getIndentationString = memoize(function getIndentationString(indentLevel) {
|
|
48654
|
+
return "\n" + "\t".repeat(indentLevel);
|
|
48655
|
+
});
|
|
48656
|
+
/**
|
|
48657
|
+
* Converts a `Doc` into a string representation that fits within
|
|
48658
|
+
* the specified width.
|
|
48659
|
+
*/
|
|
48660
|
+
function print(doc, width) {
|
|
48661
|
+
return stringify(selectBestLayout(width, doc));
|
|
48662
|
+
}
|
|
48663
|
+
/**
|
|
48664
|
+
* Join all segments of a LinkedString into the final string.
|
|
48665
|
+
*/
|
|
48666
|
+
function stringify(linkedString) {
|
|
48667
|
+
let result = "";
|
|
48668
|
+
while (linkedString) {
|
|
48669
|
+
result += linkedString.subString;
|
|
48670
|
+
linkedString = linkedString.next;
|
|
48671
|
+
}
|
|
48672
|
+
return result;
|
|
48673
|
+
}
|
|
48674
|
+
/**
|
|
48675
|
+
* Layout selection for a `Doc` that fits within the given width.
|
|
48676
|
+
*/
|
|
48677
|
+
function selectBestLayout(width, doc) {
|
|
48678
|
+
const head = {
|
|
48679
|
+
indentLevel: 0,
|
|
48680
|
+
doc,
|
|
48681
|
+
next: null,
|
|
48682
|
+
};
|
|
48683
|
+
return _selectBestLayout(width, 0, head);
|
|
48684
|
+
}
|
|
48685
|
+
function _selectBestLayout(width, currentIndentLevel, head) {
|
|
48686
|
+
if (head === null) {
|
|
48687
|
+
return null;
|
|
48688
|
+
}
|
|
48689
|
+
const { indentLevel, doc, next } = head;
|
|
48690
|
+
if (typeof doc === "string") {
|
|
48691
|
+
return {
|
|
48692
|
+
subString: doc,
|
|
48693
|
+
next: _selectBestLayout(width, currentIndentLevel + doc.length, next),
|
|
48694
|
+
};
|
|
48695
|
+
}
|
|
48696
|
+
if (doc.type === "concat") {
|
|
48697
|
+
let newHead = next;
|
|
48698
|
+
for (let i = doc.docs.length - 1; i >= 0; i--) {
|
|
48699
|
+
newHead = { indentLevel, doc: doc.docs[i], next: newHead };
|
|
48700
|
+
}
|
|
48701
|
+
return _selectBestLayout(width, currentIndentLevel, newHead);
|
|
48702
|
+
}
|
|
48703
|
+
if (doc.type === "nest") {
|
|
48704
|
+
return _selectBestLayout(width, currentIndentLevel, {
|
|
48705
|
+
indentLevel: indentLevel + doc.indentLevel,
|
|
48706
|
+
doc: doc.doc,
|
|
48707
|
+
next,
|
|
48708
|
+
});
|
|
48709
|
+
}
|
|
48710
|
+
if (doc.type === "insertLine") {
|
|
48711
|
+
return {
|
|
48712
|
+
subString: getIndentationString(indentLevel),
|
|
48713
|
+
next: _selectBestLayout(width, indentLevel, next),
|
|
48714
|
+
};
|
|
48715
|
+
}
|
|
48716
|
+
if (doc.type === "chooseBetween") {
|
|
48717
|
+
const head1 = { indentLevel, doc: doc.doc1, next };
|
|
48718
|
+
const possibleLinkedString = _selectBestLayout(width, currentIndentLevel, head1);
|
|
48719
|
+
if (fits(width - currentIndentLevel, possibleLinkedString)) {
|
|
48720
|
+
return possibleLinkedString;
|
|
48721
|
+
}
|
|
48722
|
+
const head2 = { indentLevel, doc: doc.doc2, next };
|
|
48723
|
+
return _selectBestLayout(width, currentIndentLevel, head2);
|
|
48724
|
+
}
|
|
48725
|
+
return null;
|
|
48726
|
+
}
|
|
48727
|
+
/**
|
|
48728
|
+
* Check if a layout fits on a single line within width.
|
|
48729
|
+
*/
|
|
48730
|
+
function fits(width, linkedString) {
|
|
48731
|
+
while (linkedString) {
|
|
48732
|
+
if (linkedString.subString[0] === "\n") {
|
|
48733
|
+
return true;
|
|
48734
|
+
}
|
|
48735
|
+
width -= linkedString.subString.length;
|
|
48736
|
+
if (width < 0)
|
|
48737
|
+
return false;
|
|
48738
|
+
linkedString = linkedString.next;
|
|
48739
|
+
}
|
|
48740
|
+
return true;
|
|
48741
|
+
}
|
|
48742
|
+
function astToDoc(ast) {
|
|
48743
|
+
switch (ast.type) {
|
|
48744
|
+
case "NUMBER":
|
|
48745
|
+
return String(ast.value);
|
|
48746
|
+
case "STRING":
|
|
48747
|
+
return `"${ast.value}"`;
|
|
48748
|
+
case "BOOLEAN":
|
|
48749
|
+
return ast.value ? "TRUE" : "FALSE";
|
|
48750
|
+
case "REFERENCE":
|
|
48751
|
+
return ast.value;
|
|
48752
|
+
case "FUNCALL":
|
|
48753
|
+
const docs = ast.args.map(astToDoc);
|
|
48754
|
+
return wrapInParentheses(concat(docs.map((doc, i) => (i < 1 ? doc : concat([", ", line(), doc])))), ast.value);
|
|
48755
|
+
case "UNARY_OPERATION":
|
|
48756
|
+
const operandDoc = astToDoc(ast.operand);
|
|
48757
|
+
const needParenthesis = ast.postfix
|
|
48758
|
+
? leftOperandNeedsParenthesis(ast)
|
|
48759
|
+
: rightOperandNeedsParenthesis(ast);
|
|
48760
|
+
const finalOperandDoc = needParenthesis ? wrapInParentheses(operandDoc) : operandDoc;
|
|
48761
|
+
return ast.postfix
|
|
48762
|
+
? concat([finalOperandDoc, ast.value])
|
|
48763
|
+
: concat([ast.value, finalOperandDoc]);
|
|
48764
|
+
case "BIN_OPERATION": {
|
|
48765
|
+
const leftDoc = astToDoc(ast.left);
|
|
48766
|
+
const needParenthesisLeftDoc = leftOperandNeedsParenthesis(ast);
|
|
48767
|
+
const finalLeftDoc = needParenthesisLeftDoc ? wrapInParentheses(leftDoc) : leftDoc;
|
|
48768
|
+
const rightDoc = astToDoc(ast.right);
|
|
48769
|
+
const needParenthesisRightDoc = rightOperandNeedsParenthesis(ast);
|
|
48770
|
+
const finalRightDoc = needParenthesisRightDoc ? wrapInParentheses(rightDoc) : rightDoc;
|
|
48771
|
+
const operator = `${ast.value}`;
|
|
48772
|
+
return group(concat([finalLeftDoc, operator, nest(1, concat([line(), finalRightDoc]))]));
|
|
48773
|
+
}
|
|
48774
|
+
case "SYMBOL":
|
|
48775
|
+
return ast.value;
|
|
48776
|
+
case "EMPTY":
|
|
48777
|
+
return "";
|
|
48778
|
+
}
|
|
48779
|
+
}
|
|
48780
|
+
/**
|
|
48781
|
+
* Wraps a `Doc` in parentheses (with optional function name).
|
|
48782
|
+
*/
|
|
48783
|
+
function wrapInParentheses(doc, functionName = undefined) {
|
|
48784
|
+
const docToConcat = ["(", nest(1, concat([line(), doc])), line(), ")"];
|
|
48785
|
+
if (functionName) {
|
|
48786
|
+
docToConcat.unshift(functionName);
|
|
48787
|
+
}
|
|
48788
|
+
return group(concat(docToConcat));
|
|
48789
|
+
}
|
|
48790
|
+
/**
|
|
48791
|
+
* Converts an ast formula to the corresponding string
|
|
48792
|
+
*/
|
|
48793
|
+
function astToFormula(ast) {
|
|
48794
|
+
switch (ast.type) {
|
|
48795
|
+
case "FUNCALL":
|
|
48796
|
+
const args = ast.args.map((arg) => astToFormula(arg));
|
|
48797
|
+
return `${ast.value}(${args.join(",")})`;
|
|
48798
|
+
case "NUMBER":
|
|
48799
|
+
return ast.value.toString();
|
|
48800
|
+
case "REFERENCE":
|
|
48801
|
+
return ast.value;
|
|
48802
|
+
case "STRING":
|
|
48803
|
+
return `"${ast.value}"`;
|
|
48804
|
+
case "BOOLEAN":
|
|
48805
|
+
return ast.value ? "TRUE" : "FALSE";
|
|
48806
|
+
case "UNARY_OPERATION":
|
|
48807
|
+
if (ast.postfix) {
|
|
48808
|
+
const leftOperand = leftOperandNeedsParenthesis(ast)
|
|
48809
|
+
? `(${astToFormula(ast.operand)})`
|
|
48810
|
+
: astToFormula(ast.operand);
|
|
48811
|
+
return leftOperand + ast.value;
|
|
48812
|
+
}
|
|
48813
|
+
const rightOperand = rightOperandNeedsParenthesis(ast)
|
|
48814
|
+
? `(${astToFormula(ast.operand)})`
|
|
48815
|
+
: astToFormula(ast.operand);
|
|
48816
|
+
return ast.value + rightOperand;
|
|
48817
|
+
case "BIN_OPERATION":
|
|
48818
|
+
const leftOperation = leftOperandNeedsParenthesis(ast)
|
|
48819
|
+
? `(${astToFormula(ast.left)})`
|
|
48820
|
+
: astToFormula(ast.left);
|
|
48821
|
+
const rightOperation = rightOperandNeedsParenthesis(ast)
|
|
48822
|
+
? `(${astToFormula(ast.right)})`
|
|
48823
|
+
: astToFormula(ast.right);
|
|
48824
|
+
return leftOperation + ast.value + rightOperation;
|
|
48825
|
+
default:
|
|
48826
|
+
return ast.value;
|
|
48827
|
+
}
|
|
48828
|
+
}
|
|
48829
|
+
function leftOperandNeedsParenthesis(operationAST) {
|
|
48830
|
+
const mainOperator = operationAST.value;
|
|
48831
|
+
const leftOperation = "left" in operationAST ? operationAST.left : operationAST.operand;
|
|
48832
|
+
const leftOperator = leftOperation.value;
|
|
48833
|
+
return (leftOperation.type === "BIN_OPERATION" && OP_PRIORITY[leftOperator] < OP_PRIORITY[mainOperator]);
|
|
48834
|
+
}
|
|
48835
|
+
function rightOperandNeedsParenthesis(operationAST) {
|
|
48836
|
+
const mainOperator = operationAST.value;
|
|
48837
|
+
const rightOperation = "right" in operationAST ? operationAST.right : operationAST.operand;
|
|
48838
|
+
const rightPriority = OP_PRIORITY[rightOperation.value];
|
|
48839
|
+
const mainPriority = OP_PRIORITY[mainOperator];
|
|
48840
|
+
if (rightOperation.type !== "BIN_OPERATION") {
|
|
48841
|
+
return false;
|
|
48842
|
+
}
|
|
48843
|
+
if (rightPriority < mainPriority) {
|
|
48844
|
+
return true;
|
|
48845
|
+
}
|
|
48846
|
+
return rightPriority === mainPriority && !ASSOCIATIVE_OPERATORS.includes(mainOperator);
|
|
48847
|
+
}
|
|
48848
|
+
|
|
48657
48849
|
const CELL_DELETED_MESSAGE = _t("The cell you are trying to edit has been deleted.");
|
|
48658
48850
|
class CellComposerStore extends AbstractComposerStore {
|
|
48659
48851
|
canStopEdition() {
|
|
@@ -48797,7 +48989,10 @@ class CellComposerStore extends AbstractComposerStore {
|
|
|
48797
48989
|
const locale = this.getters.getLocale();
|
|
48798
48990
|
const cell = this.getters.getCell(position);
|
|
48799
48991
|
if (cell?.isFormula) {
|
|
48800
|
-
|
|
48992
|
+
const prettifiedContent = cell.compiledFormula.isBadExpression
|
|
48993
|
+
? cell.content
|
|
48994
|
+
: prettify(parseTokens(cell.compiledFormula.tokens), 80);
|
|
48995
|
+
return localizeFormula(prettifiedContent, locale);
|
|
48801
48996
|
}
|
|
48802
48997
|
const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
|
|
48803
48998
|
if (spreader) {
|
|
@@ -60501,6 +60696,42 @@ class Grid extends owl.Component {
|
|
|
60501
60696
|
const supportedPivotPositionalFormulaRegistry = new Registry();
|
|
60502
60697
|
supportedPivotPositionalFormulaRegistry.add("SPREADSHEET", false);
|
|
60503
60698
|
|
|
60699
|
+
class ClickableCellSortIcon extends owl.Component {
|
|
60700
|
+
static template = "o-spreadsheet-ClickableCellSortIcon";
|
|
60701
|
+
static props = {
|
|
60702
|
+
position: Object,
|
|
60703
|
+
sortDirection: String,
|
|
60704
|
+
};
|
|
60705
|
+
hoveredTableStore;
|
|
60706
|
+
setup() {
|
|
60707
|
+
this.hoveredTableStore = useStore(HoveredTableStore);
|
|
60708
|
+
}
|
|
60709
|
+
get style() {
|
|
60710
|
+
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.position);
|
|
60711
|
+
return cssPropertiesToCss({
|
|
60712
|
+
color: cellStyle.textColor || TEXT_BODY_MUTED,
|
|
60713
|
+
"background-color": this.getBackgroundColor(cellStyle),
|
|
60714
|
+
});
|
|
60715
|
+
}
|
|
60716
|
+
get icon() {
|
|
60717
|
+
switch (this.props.sortDirection) {
|
|
60718
|
+
case "asc":
|
|
60719
|
+
return "fa-sort-asc";
|
|
60720
|
+
case "desc":
|
|
60721
|
+
return "fa-sort-desc";
|
|
60722
|
+
default:
|
|
60723
|
+
return "fa-sort";
|
|
60724
|
+
}
|
|
60725
|
+
}
|
|
60726
|
+
getBackgroundColor(cellStyle) {
|
|
60727
|
+
const overlayColor = this.hoveredTableStore.overlayColors.get(this.props.position);
|
|
60728
|
+
if (overlayColor) {
|
|
60729
|
+
return blendColors(cellStyle.fillColor || "#FFFFFF", overlayColor);
|
|
60730
|
+
}
|
|
60731
|
+
return cellStyle.fillColor || "#FFFFFF";
|
|
60732
|
+
}
|
|
60733
|
+
}
|
|
60734
|
+
|
|
60504
60735
|
class FullScreenChart extends owl.Component {
|
|
60505
60736
|
static template = "o-spreadsheet-FullScreenChart";
|
|
60506
60737
|
static props = {};
|
|
@@ -61795,10 +62026,10 @@ class CellPlugin extends CorePlugin {
|
|
|
61795
62026
|
*/
|
|
61796
62027
|
getFormulaString(sheetId, tokens, dependencies, useBoundedReference = false) {
|
|
61797
62028
|
if (!dependencies.length) {
|
|
61798
|
-
return concat(tokens.map((token) => token.value));
|
|
62029
|
+
return concat$1(tokens.map((token) => token.value));
|
|
61799
62030
|
}
|
|
61800
62031
|
let rangeIndex = 0;
|
|
61801
|
-
return concat(tokens.map((token) => {
|
|
62032
|
+
return concat$1(tokens.map((token) => {
|
|
61802
62033
|
if (token.type === "REFERENCE") {
|
|
61803
62034
|
const range = dependencies[rangeIndex++];
|
|
61804
62035
|
return this.getters.getRangeString(range, sheetId, { useBoundedReference });
|
|
@@ -62073,11 +62304,11 @@ class FormulaCellWithDependencies {
|
|
|
62073
62304
|
};
|
|
62074
62305
|
}
|
|
62075
62306
|
get content() {
|
|
62076
|
-
return concat(this.compiledFormula.tokens.map((token) => token.value));
|
|
62307
|
+
return concat$1(this.compiledFormula.tokens.map((token) => token.value));
|
|
62077
62308
|
}
|
|
62078
62309
|
get contentWithFixedReferences() {
|
|
62079
62310
|
let rangeIndex = 0;
|
|
62080
|
-
return concat(this.compiledFormula.tokens.map((token) => {
|
|
62311
|
+
return concat$1(this.compiledFormula.tokens.map((token) => {
|
|
62081
62312
|
if (token.type === "REFERENCE") {
|
|
62082
62313
|
const index = rangeIndex++;
|
|
62083
62314
|
return this.getRangeString(this.compiledFormula.dependencies[index], this.sheetId, {
|
|
@@ -70857,6 +71088,7 @@ class PivotUIPlugin extends CoreViewPlugin {
|
|
|
70857
71088
|
static getters = [
|
|
70858
71089
|
"getPivot",
|
|
70859
71090
|
"getFirstPivotFunction",
|
|
71091
|
+
"getPivotCellSortDirection",
|
|
70860
71092
|
"getPivotIdFromPosition",
|
|
70861
71093
|
"getPivotCellFromPosition",
|
|
70862
71094
|
"generateNewCalculatedMeasureName",
|
|
@@ -71077,6 +71309,20 @@ class PivotUIPlugin extends CoreViewPlugin {
|
|
|
71077
71309
|
isPivotUnused(pivotId) {
|
|
71078
71310
|
return this._getUnusedPivots().includes(pivotId);
|
|
71079
71311
|
}
|
|
71312
|
+
getPivotCellSortDirection(position) {
|
|
71313
|
+
const pivotId = this.getters.getPivotIdFromPosition(position);
|
|
71314
|
+
const pivotCell = this.getters.getPivotCellFromPosition(position);
|
|
71315
|
+
if (pivotCell.type === "EMPTY" || pivotCell.type === "HEADER" || !pivotId) {
|
|
71316
|
+
return undefined;
|
|
71317
|
+
}
|
|
71318
|
+
const pivot = this.getters.getPivot(pivotId);
|
|
71319
|
+
const colDomain = domainToColRowDomain(pivot, pivotCell.domain).colDomain;
|
|
71320
|
+
const sortedColumn = pivot.definition.sortedColumn;
|
|
71321
|
+
if (sortedColumn?.measure === pivotCell.measure && deepEquals(sortedColumn.domain, colDomain)) {
|
|
71322
|
+
return sortedColumn.order;
|
|
71323
|
+
}
|
|
71324
|
+
return "none";
|
|
71325
|
+
}
|
|
71080
71326
|
// ---------------------------------------------------------------------
|
|
71081
71327
|
// Private
|
|
71082
71328
|
// ---------------------------------------------------------------------
|
|
@@ -78769,6 +79015,34 @@ clickableCellRegistry.add("link", {
|
|
|
78769
79015
|
},
|
|
78770
79016
|
sequence: 5,
|
|
78771
79017
|
});
|
|
79018
|
+
clickableCellRegistry.add("dashboard_pivot_sorting", {
|
|
79019
|
+
condition: (position, getters) => {
|
|
79020
|
+
if (!getters.isDashboard()) {
|
|
79021
|
+
return false;
|
|
79022
|
+
}
|
|
79023
|
+
const pivotCell = getters.getPivotCellFromPosition(position);
|
|
79024
|
+
return canSortPivot(getters, position) && pivotCell.type === "MEASURE_HEADER";
|
|
79025
|
+
},
|
|
79026
|
+
execute: (position, env) => {
|
|
79027
|
+
sortPivot(env, position, getNextSortDirection(env.model.getters, position));
|
|
79028
|
+
},
|
|
79029
|
+
component: ClickableCellSortIcon,
|
|
79030
|
+
componentProps: (position, getters) => {
|
|
79031
|
+
return {
|
|
79032
|
+
position,
|
|
79033
|
+
sortDirection: getters.getPivotCellSortDirection(position),
|
|
79034
|
+
};
|
|
79035
|
+
},
|
|
79036
|
+
sequence: 2,
|
|
79037
|
+
});
|
|
79038
|
+
const NEXT_SORT_DIRECTION = {
|
|
79039
|
+
none: "asc",
|
|
79040
|
+
asc: "desc",
|
|
79041
|
+
desc: "none",
|
|
79042
|
+
};
|
|
79043
|
+
function getNextSortDirection(getters, position) {
|
|
79044
|
+
return NEXT_SORT_DIRECTION[getters.getPivotCellSortDirection(position) ?? "none"];
|
|
79045
|
+
}
|
|
78772
79046
|
|
|
78773
79047
|
const inverseCommandRegistry = new Registry()
|
|
78774
79048
|
.add("ADD_COLUMNS_ROWS", inverseAddColumnsRows)
|
|
@@ -80485,6 +80759,8 @@ class ClickableCellsStore extends SpreadsheetStore {
|
|
|
80485
80759
|
position,
|
|
80486
80760
|
action: item.execute,
|
|
80487
80761
|
title: title || "",
|
|
80762
|
+
component: item.component,
|
|
80763
|
+
componentProps: item.componentProps?.(position, getters) ?? {},
|
|
80488
80764
|
});
|
|
80489
80765
|
}
|
|
80490
80766
|
return cells;
|
|
@@ -81237,7 +81513,7 @@ class SmallBottomBar extends owl.Component {
|
|
|
81237
81513
|
}
|
|
81238
81514
|
}
|
|
81239
81515
|
|
|
81240
|
-
const COMPOSER_MAX_HEIGHT =
|
|
81516
|
+
const COMPOSER_MAX_HEIGHT = 300;
|
|
81241
81517
|
/* svg free of use from https://uxwing.com/formula-fx-icon/ */
|
|
81242
81518
|
const FX_SVG = /*xml*/ `
|
|
81243
81519
|
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 121.8 122.9' width='16' height='16' focusable='false'>
|
|
@@ -82831,6 +83107,27 @@ class Spreadsheet extends owl.Component {
|
|
|
82831
83107
|
}
|
|
82832
83108
|
}
|
|
82833
83109
|
|
|
83110
|
+
class ReadonlyTransportFilter {
|
|
83111
|
+
transportService;
|
|
83112
|
+
constructor(transportService) {
|
|
83113
|
+
this.transportService = transportService;
|
|
83114
|
+
}
|
|
83115
|
+
async sendMessage(message) {
|
|
83116
|
+
if (message.type === "CLIENT_JOINED" ||
|
|
83117
|
+
message.type === "CLIENT_LEFT" ||
|
|
83118
|
+
message.type === "CLIENT_MOVED") {
|
|
83119
|
+
this.transportService.sendMessage(message);
|
|
83120
|
+
}
|
|
83121
|
+
// ignore all other messages
|
|
83122
|
+
}
|
|
83123
|
+
onNewMessage(id, callback) {
|
|
83124
|
+
this.transportService.onNewMessage(id, callback);
|
|
83125
|
+
}
|
|
83126
|
+
leave(id) {
|
|
83127
|
+
this.transportService.leave(id);
|
|
83128
|
+
}
|
|
83129
|
+
}
|
|
83130
|
+
|
|
82834
83131
|
function inverseCommand(cmd) {
|
|
82835
83132
|
return inverseCommandRegistry.get(cmd.type)(cmd);
|
|
82836
83133
|
}
|
|
@@ -86923,12 +87220,15 @@ class Model extends EventBus {
|
|
|
86923
87220
|
name: _t("Anonymous").toString(),
|
|
86924
87221
|
};
|
|
86925
87222
|
const transportService = config.transportService || new LocalTransportService();
|
|
87223
|
+
const isReadonly = config.mode === "readonly" || config.mode === "dashboard";
|
|
86926
87224
|
return {
|
|
86927
87225
|
...config,
|
|
86928
87226
|
mode: config.mode || "normal",
|
|
86929
87227
|
custom: config.custom || {},
|
|
86930
87228
|
external: this.setupExternalConfig(config.external || {}),
|
|
86931
|
-
transportService
|
|
87229
|
+
transportService: isReadonly
|
|
87230
|
+
? new ReadonlyTransportFilter(transportService)
|
|
87231
|
+
: transportService,
|
|
86932
87232
|
client,
|
|
86933
87233
|
moveClient: () => { },
|
|
86934
87234
|
snapshotRequested: false,
|
|
@@ -87341,6 +87641,7 @@ const components = {
|
|
|
87341
87641
|
ChartPanel,
|
|
87342
87642
|
ChartFigure,
|
|
87343
87643
|
ChartJsComponent,
|
|
87644
|
+
ClickableCellSortIcon,
|
|
87344
87645
|
ZoomableChartJsComponent,
|
|
87345
87646
|
Grid,
|
|
87346
87647
|
GridOverlay,
|
|
@@ -87426,6 +87727,8 @@ const constants = {
|
|
|
87426
87727
|
PIVOT_TABLE_CONFIG,
|
|
87427
87728
|
ChartTerms,
|
|
87428
87729
|
FIGURE_ID_SPLITTER,
|
|
87730
|
+
GRID_ICON_EDGE_LENGTH,
|
|
87731
|
+
GRID_ICON_MARGIN,
|
|
87429
87732
|
};
|
|
87430
87733
|
const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
|
|
87431
87734
|
|
|
@@ -87480,6 +87783,6 @@ exports.tokenColors = tokenColors;
|
|
|
87480
87783
|
exports.tokenize = tokenize;
|
|
87481
87784
|
|
|
87482
87785
|
|
|
87483
|
-
__info__.version = "18.5.0-alpha.
|
|
87484
|
-
__info__.date = "2025-08-
|
|
87485
|
-
__info__.hash = "
|
|
87786
|
+
__info__.version = "18.5.0-alpha.12";
|
|
87787
|
+
__info__.date = "2025-08-29T08:05:09.767Z";
|
|
87788
|
+
__info__.hash = "8ee7677";
|