@odoo/o-spreadsheet 19.5.0-alpha.11 → 19.5.0-alpha.13
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/material_symbols_outlined.css +9 -0
- package/dist/o_spreadsheet.cjs +2578 -900
- package/dist/o_spreadsheet.css +182 -41
- package/dist/o_spreadsheet.esm.js +2570 -892
- package/dist/o_spreadsheet.iife.js +2578 -900
- package/dist/o_spreadsheet.min.iife.js +344 -344
- package/dist/o_spreadsheet.xml +238 -150
- package/dist/types/actions/data_actions.d.ts +1 -0
- package/dist/types/components/bottom_bar/bottom_bar_statistic/bottom_bar_statistic.d.ts +3 -0
- package/dist/types/components/dashboard/clickable_cell_sort_icon/clickable_cell_sort_icon.d.ts +1 -1
- package/dist/types/components/figures/figure/figure.d.ts +1 -0
- package/dist/types/components/figures/figure_carousel/figure_carousel.d.ts +10 -0
- package/dist/types/components/figures/figure_container/figure_container.d.ts +1 -2
- package/dist/types/components/grid/grid.d.ts +1 -0
- package/dist/types/components/helpers/chart_drag_and_drop.d.ts +15 -0
- package/dist/types/components/helpers/dom_helpers.d.ts +3 -2
- package/dist/types/components/link/link_editor/link_editor.d.ts +1 -1
- package/dist/types/components/side_panel/carousel_panel/carousel_panel.d.ts +1 -1
- package/dist/types/components/side_panel/chart/building_blocks/color_scale/color_scale_picker.d.ts +1 -1
- package/dist/types/components/side_panel/chart/scorecard_chart_panel/scorecard_chart_config_panel.d.ts +6 -10
- package/dist/types/components/side_panel/data_analysis/chart_suggestion_preview.d.ts +19 -0
- package/dist/types/components/side_panel/data_analysis/data_analysis_panel.d.ts +17 -0
- package/dist/types/components/side_panel/data_analysis/data_analysis_store.d.ts +15 -0
- package/dist/types/components/side_panel/pivot/pivot_layout_configurator/pivot_layout_configurator.d.ts +1 -6
- package/dist/types/components/side_panel/side_panel/side_panel_store.d.ts +2 -1
- package/dist/types/components/standalone_grid_canvas/carousel_data_view_print.d.ts +32 -0
- package/dist/types/components/standalone_grid_canvas/standalone_grid_canvas.d.ts +6 -0
- package/dist/types/components/standalone_viewport/standalone_viewport.d.ts +6 -3
- package/dist/types/components/standalone_viewport/standalone_viewport_store.d.ts +2 -1
- package/dist/types/components/tables/hovered_table_store.d.ts +6 -7
- package/dist/types/constants.d.ts +13 -1
- package/dist/types/functions/helpers.d.ts +4 -0
- package/dist/types/functions/module_operators.d.ts +1 -1
- package/dist/types/functions/module_text.d.ts +11 -11
- package/dist/types/functions/module_web.d.ts +1 -1
- package/dist/types/helpers/carousel_helpers.d.ts +8 -1
- package/dist/types/helpers/data_analysis.d.ts +16 -0
- package/dist/types/helpers/figures/chart.d.ts +1 -1
- package/dist/types/helpers/figures/charts/chart_data_source_helpers.d.ts +10 -0
- package/dist/types/helpers/figures/charts/chart_suggestion_engine.d.ts +8 -0
- package/dist/types/helpers/figures/charts/chart_ui_common.d.ts +8 -1
- package/dist/types/helpers/figures/charts/charts_suggestions_constants.d.ts +37 -0
- package/dist/types/helpers/figures/charts/gauge_chart_rendering.d.ts +6 -2
- package/dist/types/helpers/text_helper.d.ts +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/registries/figures_registry.d.ts +1 -0
- package/dist/types/stores/cell_hover_overlay_store.d.ts +15 -0
- package/dist/types/stores/chart_drag_store.d.ts +10 -0
- package/dist/types/stores/grid_renderer_store.d.ts +3 -2
- package/dist/types/stores/main_viewport_store.d.ts +3 -0
- package/dist/types/stores/viewports_store.d.ts +0 -1
- package/dist/types/types/chart/chart.d.ts +3 -1
- package/dist/types/types/chart/chart_suggestion.d.ts +136 -0
- package/dist/types/types/chart/scorecard_chart.d.ts +3 -4
- package/dist/types/types/commands.d.ts +16 -11
- package/package.json +6 -5
|
@@ -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 19.5.0-alpha.
|
|
6
|
-
* @date 2026-08-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 19.5.0-alpha.13
|
|
6
|
+
* @date 2026-08-21T15:33:44.046Z
|
|
7
|
+
* @hash a73559e
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import * as owl from "@odoo/owl";
|
|
@@ -206,6 +206,16 @@ function getBoundingRectAsPOJO(el) {
|
|
|
206
206
|
height: rect.height
|
|
207
207
|
};
|
|
208
208
|
}
|
|
209
|
+
function getBoundingRectWithMargins(el) {
|
|
210
|
+
const style = getComputedStyle(el);
|
|
211
|
+
const rect = el.getBoundingClientRect();
|
|
212
|
+
return {
|
|
213
|
+
x: rect.x,
|
|
214
|
+
y: rect.y,
|
|
215
|
+
width: rect.width + parseInt(style.marginLeft || "0") + parseInt(style.marginRight || "0"),
|
|
216
|
+
height: rect.height + parseInt(style.marginTop || "0") + parseInt(style.marginBottom || "0")
|
|
217
|
+
};
|
|
218
|
+
}
|
|
209
219
|
/**
|
|
210
220
|
* Iterate over all the children of `el` in the dom tree starting at `el`, depth first.
|
|
211
221
|
*/
|
|
@@ -391,7 +401,13 @@ function createAction(item) {
|
|
|
391
401
|
if (isEnabled(env)) return item.execute(env, isMiddleClick);
|
|
392
402
|
} : void 0,
|
|
393
403
|
children: children ? (env) => {
|
|
394
|
-
|
|
404
|
+
const uniqueChildren = {};
|
|
405
|
+
children.flatMap((child) => typeof child === "function" ? child(env) : child).forEach((childSpec) => {
|
|
406
|
+
const childAction = createAction(childSpec);
|
|
407
|
+
if (uniqueChildren[childAction.id]) throw new Error(`Duplicate child action id "${childAction.id}" in action "${itemId}".`);
|
|
408
|
+
uniqueChildren[childAction.id] = childAction;
|
|
409
|
+
});
|
|
410
|
+
return Object.values(uniqueChildren).sort((a, b) => a.sequence - b.sequence);
|
|
395
411
|
} : () => [],
|
|
396
412
|
isReadonlyAllowed: item.isReadonlyAllowed || false,
|
|
397
413
|
isEnabledOnLockedSheet: item.isEnabledOnLockedSheet || false,
|
|
@@ -649,6 +665,14 @@ const DEFAULT_CAROUSEL_TITLE_STYLE = {
|
|
|
649
665
|
fontSize: 18,
|
|
650
666
|
color: TEXT_BODY
|
|
651
667
|
};
|
|
668
|
+
const CAROUSEL_LAYOUT = {
|
|
669
|
+
paddingX: 24,
|
|
670
|
+
paddingY: 8,
|
|
671
|
+
headerPaddingTop: 4,
|
|
672
|
+
separatorWidth: 1,
|
|
673
|
+
headerLineHeight: 1.5,
|
|
674
|
+
minHeaderHeight: 25
|
|
675
|
+
};
|
|
652
676
|
const DEFAULT_TOKEN_COLOR = "light-dark(#000000, #ffffff)";
|
|
653
677
|
const functionColor = DEFAULT_TOKEN_COLOR;
|
|
654
678
|
const operatorColor = "#3da4ab";
|
|
@@ -4071,6 +4095,18 @@ function toString(data) {
|
|
|
4071
4095
|
default: return "";
|
|
4072
4096
|
}
|
|
4073
4097
|
}
|
|
4098
|
+
/**
|
|
4099
|
+
* Only converts the decimal separator, ignore number format such as % or dates
|
|
4100
|
+
*/
|
|
4101
|
+
function toLocaleString(data, locale) {
|
|
4102
|
+
const value = toValue(data);
|
|
4103
|
+
switch (typeof value) {
|
|
4104
|
+
case "string": return value;
|
|
4105
|
+
case "number": return value.toString().replace(".", locale.decimalSeparator);
|
|
4106
|
+
case "boolean": return value ? "TRUE" : "FALSE";
|
|
4107
|
+
default: return "";
|
|
4108
|
+
}
|
|
4109
|
+
}
|
|
4074
4110
|
/** Normalize string by setting it to lowercase and replacing accent letters with plain letters */
|
|
4075
4111
|
const normalizeString = memoize(function normalizeString(str) {
|
|
4076
4112
|
return str.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
@@ -5377,7 +5413,7 @@ function _roundFormat(internalFormat) {
|
|
|
5377
5413
|
}
|
|
5378
5414
|
function humanizeNumber({ value, format }, locale) {
|
|
5379
5415
|
const numberValue = tryToNumber(value, locale);
|
|
5380
|
-
if (numberValue === void 0) return "";
|
|
5416
|
+
if (numberValue === void 0) return value?.toString() || "";
|
|
5381
5417
|
let numberFormat = format;
|
|
5382
5418
|
if (Math.abs(numberValue) < 1e3) {
|
|
5383
5419
|
const hasDecimal = numberValue % 1 !== 0;
|
|
@@ -8092,8 +8128,6 @@ let CommandResult = /* @__PURE__ */ function(CommandResult) {
|
|
|
8092
8128
|
CommandResult["InvalidXRange"] = "InvalidXRange";
|
|
8093
8129
|
CommandResult["InvalidLabelRange"] = "InvalidLabelRange";
|
|
8094
8130
|
CommandResult["InvalidBubbleSizeRange"] = "InvalidBubbleSizeRange";
|
|
8095
|
-
CommandResult["InvalidScorecardKeyValue"] = "InvalidScorecardKeyValue";
|
|
8096
|
-
CommandResult["InvalidScorecardBaseline"] = "InvalidScorecardBaseline";
|
|
8097
8131
|
CommandResult["InvalidGaugeDataRange"] = "InvalidGaugeDataRange";
|
|
8098
8132
|
CommandResult["EmptyGaugeRangeMin"] = "EmptyGaugeRangeMin";
|
|
8099
8133
|
CommandResult["GaugeRangeMinNaN"] = "GaugeRangeMinNaN";
|
|
@@ -9247,8 +9281,8 @@ function computeCachedTextDimension(context, text, font) {
|
|
|
9247
9281
|
function fontSizeInPixels(fontSize) {
|
|
9248
9282
|
return Math.round(fontSize * 96 / 72);
|
|
9249
9283
|
}
|
|
9250
|
-
function computeTextFont(style, fontUnit = "pt") {
|
|
9251
|
-
return `${style.italic ? "italic " : ""}${style.bold ? "bold" :
|
|
9284
|
+
function computeTextFont(style, fontUnit = "pt", fontWeight = 400) {
|
|
9285
|
+
return `${style.italic ? "italic " : ""}${style.bold ? "bold" : fontWeight} ${(fontUnit === "pt" ? computeTextFontSizeInPixels(style) : style.fontSize) ?? DEFAULT_FONT_SIZE}px ${DEFAULT_FONT}`;
|
|
9252
9286
|
}
|
|
9253
9287
|
function computeTextFontSizeInPixels(style) {
|
|
9254
9288
|
return fontSizeInPixels(style?.fontSize || DEFAULT_FONT_SIZE);
|
|
@@ -11882,7 +11916,7 @@ var ChartJsComponent = class extends Component {
|
|
|
11882
11916
|
chartId: types$1.string(),
|
|
11883
11917
|
isFullScreen: types$1.boolean().optional()
|
|
11884
11918
|
});
|
|
11885
|
-
canvas = signal(
|
|
11919
|
+
canvas = signal.ref(HTMLCanvasElement);
|
|
11886
11920
|
chart;
|
|
11887
11921
|
currentRuntime;
|
|
11888
11922
|
animationStore;
|
|
@@ -11980,6 +12014,110 @@ var ChartJsComponent = class extends Component {
|
|
|
11980
12014
|
}
|
|
11981
12015
|
};
|
|
11982
12016
|
|
|
12017
|
+
//#endregion
|
|
12018
|
+
//#region src/functions/helper_matrices.ts
|
|
12019
|
+
function getUnitMatrix(n) {
|
|
12020
|
+
const matrix = Array(n);
|
|
12021
|
+
for (let i = 0; i < n; i++) {
|
|
12022
|
+
matrix[i] = Array(n).fill(0);
|
|
12023
|
+
matrix[i][i] = 1;
|
|
12024
|
+
}
|
|
12025
|
+
return matrix;
|
|
12026
|
+
}
|
|
12027
|
+
/**
|
|
12028
|
+
* Invert a matrix and compute its determinant using Gaussian Elimination.
|
|
12029
|
+
*
|
|
12030
|
+
* The Matrix should be a square matrix, and should be indexed [col][row] instead of the
|
|
12031
|
+
* standard mathematical indexing [row][col].
|
|
12032
|
+
*/
|
|
12033
|
+
function invertMatrix(M) {
|
|
12034
|
+
if (M.length < 1 || M[0].length < 1) throw new Error("invertMatrix: an empty matrix cannot be inverted.");
|
|
12035
|
+
if (M.length !== M[0].length) throw new Error("invertMatrix: only square matrices are invertible");
|
|
12036
|
+
let determinant = 1;
|
|
12037
|
+
const dim = M.length;
|
|
12038
|
+
const I = getUnitMatrix(dim);
|
|
12039
|
+
const C = M.map((row) => row.slice());
|
|
12040
|
+
for (let pivot = 0; pivot < dim; pivot++) {
|
|
12041
|
+
let diagonalElement = C[pivot][pivot];
|
|
12042
|
+
if (diagonalElement === 0) {
|
|
12043
|
+
for (let row = pivot + 1; row < dim; row++) if (C[pivot][row] !== 0) {
|
|
12044
|
+
swapMatrixRows(C, pivot, row);
|
|
12045
|
+
swapMatrixRows(I, pivot, row);
|
|
12046
|
+
determinant *= -1;
|
|
12047
|
+
break;
|
|
12048
|
+
}
|
|
12049
|
+
diagonalElement = C[pivot][pivot];
|
|
12050
|
+
if (diagonalElement === 0) return { determinant: 0 };
|
|
12051
|
+
}
|
|
12052
|
+
for (let col = 0; col < dim; col++) {
|
|
12053
|
+
C[col][pivot] = C[col][pivot] / diagonalElement;
|
|
12054
|
+
I[col][pivot] = I[col][pivot] / diagonalElement;
|
|
12055
|
+
}
|
|
12056
|
+
determinant *= diagonalElement;
|
|
12057
|
+
for (let row = 0; row < dim; row++) {
|
|
12058
|
+
if (row === pivot) continue;
|
|
12059
|
+
const e = C[pivot][row];
|
|
12060
|
+
for (let col = 0; col < dim; col++) {
|
|
12061
|
+
C[col][row] -= e * C[col][pivot];
|
|
12062
|
+
I[col][row] -= e * I[col][pivot];
|
|
12063
|
+
}
|
|
12064
|
+
}
|
|
12065
|
+
}
|
|
12066
|
+
return {
|
|
12067
|
+
inverted: I,
|
|
12068
|
+
determinant
|
|
12069
|
+
};
|
|
12070
|
+
}
|
|
12071
|
+
function swapMatrixRows(matrix, row1, row2) {
|
|
12072
|
+
for (let i = 0; i < matrix.length; i++) {
|
|
12073
|
+
const tmp = matrix[i][row1];
|
|
12074
|
+
matrix[i][row1] = matrix[i][row2];
|
|
12075
|
+
matrix[i][row2] = tmp;
|
|
12076
|
+
}
|
|
12077
|
+
}
|
|
12078
|
+
/**
|
|
12079
|
+
* Matrix multiplication of 2 matrices.
|
|
12080
|
+
* ex: matrix1 : n x l, matrix2 : m x n => result : m x l
|
|
12081
|
+
*
|
|
12082
|
+
* Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
|
|
12083
|
+
*/
|
|
12084
|
+
function multiplyMatrices(matrix1, matrix2) {
|
|
12085
|
+
if (matrix1.length < 1 || matrix2.length < 1) throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
|
|
12086
|
+
if (matrix1.length !== matrix2[0].length) throw new Error("multiplyMatrices: incompatible matrices size.");
|
|
12087
|
+
const rowsM1 = matrix1[0].length;
|
|
12088
|
+
const colsM2 = matrix2.length;
|
|
12089
|
+
const n = matrix1.length;
|
|
12090
|
+
const result = Array(colsM2);
|
|
12091
|
+
for (let col = 0; col < colsM2; col++) {
|
|
12092
|
+
result[col] = Array(rowsM1);
|
|
12093
|
+
for (let row = 0; row < rowsM1; row++) {
|
|
12094
|
+
let sum = 0;
|
|
12095
|
+
for (let k = 0; k < n; k++) sum += matrix1[k][row] * matrix2[col][k];
|
|
12096
|
+
result[col][row] = sum;
|
|
12097
|
+
}
|
|
12098
|
+
}
|
|
12099
|
+
return result;
|
|
12100
|
+
}
|
|
12101
|
+
/**
|
|
12102
|
+
* Return the input if it's a scalar or the first element of the input if it's a matrix.
|
|
12103
|
+
*/
|
|
12104
|
+
function toScalar(arg) {
|
|
12105
|
+
if (!isMatrix(arg)) return arg;
|
|
12106
|
+
if (!isSingleElementMatrix(arg)) throw new Error("The value should be a scalar or a 1x1 matrix");
|
|
12107
|
+
return arg[0][0];
|
|
12108
|
+
}
|
|
12109
|
+
function isSingleElementMatrix(matrix) {
|
|
12110
|
+
return matrix.length === 1 && matrix[0].length === 1;
|
|
12111
|
+
}
|
|
12112
|
+
function isMultipleElementMatrix(arg) {
|
|
12113
|
+
return isMatrix(arg) && !isSingleElementMatrix(arg);
|
|
12114
|
+
}
|
|
12115
|
+
function getMatrixArgIndices(args) {
|
|
12116
|
+
const indices = [];
|
|
12117
|
+
for (let i = 0; i < args.length; i++) if (isMultipleElementMatrix(args[i])) indices.push(i);
|
|
12118
|
+
return indices;
|
|
12119
|
+
}
|
|
12120
|
+
|
|
11983
12121
|
//#endregion
|
|
11984
12122
|
//#region src/helpers/figures/charts/abstract_chart.ts
|
|
11985
12123
|
var AbstractChart = class {
|
|
@@ -11995,11 +12133,42 @@ var AbstractChart = class {
|
|
|
11995
12133
|
|
|
11996
12134
|
//#endregion
|
|
11997
12135
|
//#region src/helpers/figures/charts/scorecard_chart.ts
|
|
12136
|
+
function getData$1(value, getters, sheetId) {
|
|
12137
|
+
if (!value) return {
|
|
12138
|
+
scalar: void 0,
|
|
12139
|
+
range: void 0
|
|
12140
|
+
};
|
|
12141
|
+
if (!isFormula(value)) return {
|
|
12142
|
+
scalar: { value },
|
|
12143
|
+
range: void 0
|
|
12144
|
+
};
|
|
12145
|
+
const result = getters.evaluateFormulaResult(sheetId, value);
|
|
12146
|
+
let scalar = isMultipleElementMatrix(result) ? result[0][0] : toScalar(result);
|
|
12147
|
+
let range = void 0;
|
|
12148
|
+
const xc = getFormulaRangeXc(value);
|
|
12149
|
+
if (xc) {
|
|
12150
|
+
range = createValidRange(getters, sheetId, xc);
|
|
12151
|
+
if (range) {
|
|
12152
|
+
if (getters.getEvaluatedCell({
|
|
12153
|
+
sheetId: range.sheetId,
|
|
12154
|
+
col: range.zone.left,
|
|
12155
|
+
row: range.zone.top
|
|
12156
|
+
}).type === "empty") scalar = void 0;
|
|
12157
|
+
}
|
|
12158
|
+
}
|
|
12159
|
+
return {
|
|
12160
|
+
scalar,
|
|
12161
|
+
range
|
|
12162
|
+
};
|
|
12163
|
+
}
|
|
11998
12164
|
function getBaselineText(baseline, keyValue, baselineMode, humanizeNumbers, locale) {
|
|
11999
12165
|
if (!baseline) return "";
|
|
12000
|
-
else if (baselineMode === "text" || keyValue?.
|
|
12166
|
+
else if (baselineMode === "text" || typeof keyValue?.value !== "number" || typeof baseline.value !== "number") {
|
|
12001
12167
|
if (humanizeNumbers) return humanizeNumber(baseline, locale);
|
|
12002
|
-
return baseline.
|
|
12168
|
+
return formatValue(baseline.value, {
|
|
12169
|
+
format: baseline.format,
|
|
12170
|
+
locale
|
|
12171
|
+
});
|
|
12003
12172
|
}
|
|
12004
12173
|
let { value, format } = baseline;
|
|
12005
12174
|
if (baselineMode === "progress") {
|
|
@@ -12020,29 +12189,31 @@ function getBaselineText(baseline, keyValue, baselineMode, humanizeNumbers, loca
|
|
|
12020
12189
|
locale
|
|
12021
12190
|
});
|
|
12022
12191
|
}
|
|
12023
|
-
function getKeyValueText(
|
|
12024
|
-
if (
|
|
12025
|
-
if (humanizeNumbers) return humanizeNumber(
|
|
12026
|
-
return
|
|
12192
|
+
function getKeyValueText(keyValue, humanizeNumbers, locale) {
|
|
12193
|
+
if (keyValue?.value === void 0 || keyValue?.value === null) return "";
|
|
12194
|
+
if (humanizeNumbers) return humanizeNumber(keyValue, locale);
|
|
12195
|
+
return keyValue.format ? formatValue(keyValue.value, {
|
|
12196
|
+
format: keyValue.format,
|
|
12197
|
+
locale
|
|
12198
|
+
}) : String(keyValue.value ?? "");
|
|
12027
12199
|
}
|
|
12028
12200
|
function getBaselineColor(baseline, baselineMode, keyValue, colorUp, colorDown) {
|
|
12029
|
-
if (baselineMode === "text" || baselineMode === "progress" || baseline?.
|
|
12201
|
+
if (baselineMode === "text" || baselineMode === "progress" || typeof baseline?.value !== "number" || typeof keyValue?.value !== "number") return;
|
|
12030
12202
|
const diff = keyValue.value - baseline.value;
|
|
12031
12203
|
if (diff > 0) return colorUp;
|
|
12032
12204
|
else if (diff < 0) return colorDown;
|
|
12033
12205
|
}
|
|
12034
12206
|
function getBaselineArrowDirection(baseline, keyValue, baselineMode) {
|
|
12035
|
-
if (baselineMode === "text" || baseline?.
|
|
12207
|
+
if (baselineMode === "text" || typeof baseline?.value !== "number" || typeof keyValue?.value !== "number") return "neutral";
|
|
12036
12208
|
const diff = keyValue.value - baseline.value;
|
|
12037
12209
|
if (diff > 0) return "up";
|
|
12038
12210
|
else if (diff < 0) return "down";
|
|
12039
12211
|
return "neutral";
|
|
12040
12212
|
}
|
|
12041
|
-
function
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
|
|
12045
|
-
return definition.baseline && !rangeReference.test(definition.baseline) ? "InvalidScorecardBaseline" : "Success";
|
|
12213
|
+
function getFormulaRangeXc(formula) {
|
|
12214
|
+
if (!formula || !isFormula(formula)) return;
|
|
12215
|
+
const content = formula.slice(1);
|
|
12216
|
+
return rangeReference.test(content) ? content : void 0;
|
|
12046
12217
|
}
|
|
12047
12218
|
const Path2DConstructor = globalThis.Path2D;
|
|
12048
12219
|
const arrowDownPath = Path2DConstructor && new Path2DConstructor("M8.6 4.8a.5.5 0 0 1 0 .75l-3.9 3.9a.5 .5 0 0 1 -.75 0l-3.8 -3.9a.5 .5 0 0 1 0 -.75l.4-.4a.5.5 0 0 1 .75 0l2.3 2.4v-5.7c0-.25.25-.5.5-.5h.6c.25 0 .5.25.5.5v5.8l2.3 -2.4a.5.5 0 0 1 .75 0z");
|
|
@@ -12059,84 +12230,76 @@ const ScorecardChart$1 = {
|
|
|
12059
12230
|
"baselineColorUp",
|
|
12060
12231
|
"baselineColorDown"
|
|
12061
12232
|
],
|
|
12062
|
-
fromStrDefinition(definition
|
|
12063
|
-
|
|
12064
|
-
|
|
12233
|
+
fromStrDefinition: (definition) => definition,
|
|
12234
|
+
validateDefinition(validator, definition) {
|
|
12235
|
+
return "Success";
|
|
12236
|
+
},
|
|
12237
|
+
copyInSheetId: (definition, sheetIdFrom, sheetIdTo, getters) => {
|
|
12238
|
+
const adaptFormula = (formula) => getters.copyFormulaStringForSheet(sheetIdFrom, sheetIdTo, formula, "keepSameReference");
|
|
12065
12239
|
return {
|
|
12066
12240
|
...definition,
|
|
12067
|
-
|
|
12068
|
-
|
|
12241
|
+
keyValue: definition.keyValue ? adaptFormula(definition.keyValue) : definition.keyValue,
|
|
12242
|
+
baseline: definition.baseline ? adaptFormula(definition.baseline) : definition.baseline
|
|
12069
12243
|
};
|
|
12070
12244
|
},
|
|
12071
|
-
validateDefinition(validator, definition) {
|
|
12072
|
-
return validator.checkValidations(definition, checkKeyValue, checkBaseline);
|
|
12073
|
-
},
|
|
12074
|
-
copyInSheetId: (definition) => definition,
|
|
12075
12245
|
getDefinitionFromContextCreation(context, dataSourceBuilder) {
|
|
12246
|
+
const dataRange = context.dataSource?.type === "range" ? context.dataSource?.dataSets?.[0]?.dataRange : void 0;
|
|
12247
|
+
const keyValue = (context.scorecardKeyValueFormula === void 0 || getFormulaRangeXc(context.scorecardKeyValueFormula)) && dataRange ? `=${dataRange}` : context.scorecardKeyValueFormula;
|
|
12248
|
+
const baseline = (context.scorecardBaselineFormula === void 0 || getFormulaRangeXc(context.scorecardBaselineFormula)) && context.auxiliaryRange ? `=${context.auxiliaryRange}` : context.scorecardBaselineFormula;
|
|
12076
12249
|
return {
|
|
12077
12250
|
background: context.background,
|
|
12078
12251
|
type: "scorecard",
|
|
12079
|
-
keyValue
|
|
12252
|
+
keyValue,
|
|
12080
12253
|
title: context.title || { text: "" },
|
|
12081
12254
|
baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
|
|
12082
12255
|
baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP,
|
|
12083
12256
|
baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
|
|
12084
|
-
baseline
|
|
12257
|
+
baseline,
|
|
12085
12258
|
humanize: context.humanize,
|
|
12086
12259
|
annotationLink: context.annotationLink,
|
|
12087
12260
|
annotationText: context.annotationText
|
|
12088
12261
|
};
|
|
12089
12262
|
},
|
|
12090
|
-
transformDefinition(definition, chartSheetId, {
|
|
12263
|
+
transformDefinition(definition, chartSheetId, { adaptFormulaString }) {
|
|
12091
12264
|
let baseline;
|
|
12092
12265
|
let keyValue;
|
|
12093
|
-
if (definition.baseline)
|
|
12094
|
-
|
|
12095
|
-
if (changeType !== "REMOVE") baseline = adaptedRange;
|
|
12096
|
-
}
|
|
12097
|
-
if (definition.keyValue) {
|
|
12098
|
-
const { changeType, range: adaptedRange } = adaptRangeString(chartSheetId, definition.keyValue);
|
|
12099
|
-
if (changeType !== "REMOVE") keyValue = adaptedRange;
|
|
12100
|
-
}
|
|
12266
|
+
if (definition.baseline) baseline = adaptFormulaString(chartSheetId, definition.baseline);
|
|
12267
|
+
if (definition.keyValue) keyValue = adaptFormulaString(chartSheetId, definition.keyValue);
|
|
12101
12268
|
return {
|
|
12102
12269
|
...definition,
|
|
12103
12270
|
baseline,
|
|
12104
12271
|
keyValue
|
|
12105
12272
|
};
|
|
12106
12273
|
},
|
|
12107
|
-
duplicateInDuplicatedSheet(definition, sheetIdFrom, sheetIdTo) {
|
|
12108
|
-
const
|
|
12109
|
-
const keyValue = duplicateLabelRangeInDuplicatedSheet(sheetIdFrom, sheetIdTo, definition.keyValue);
|
|
12274
|
+
duplicateInDuplicatedSheet(definition, sheetIdFrom, sheetIdTo, getters) {
|
|
12275
|
+
const adaptFormula = (formula) => getters.copyFormulaStringForSheet(sheetIdFrom, sheetIdTo, formula, "moveReference");
|
|
12110
12276
|
return {
|
|
12111
12277
|
...definition,
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
};
|
|
12115
|
-
},
|
|
12116
|
-
toStrDefinition(definition, sheetId, getters) {
|
|
12117
|
-
return {
|
|
12118
|
-
...definition,
|
|
12119
|
-
keyValue: definition.keyValue ? getters.getRangeString(definition.keyValue, sheetId) : void 0,
|
|
12120
|
-
baseline: definition.baseline ? getters.getRangeString(definition.baseline, sheetId) : void 0
|
|
12278
|
+
keyValue: definition.keyValue ? adaptFormula(definition.keyValue) : definition.keyValue,
|
|
12279
|
+
baseline: definition.baseline ? adaptFormula(definition.baseline) : definition.baseline
|
|
12121
12280
|
};
|
|
12122
12281
|
},
|
|
12282
|
+
toStrDefinition: (definition) => definition,
|
|
12123
12283
|
getContextCreation(definition, dataSource) {
|
|
12284
|
+
const keyValueXc = getFormulaRangeXc(definition.keyValue);
|
|
12124
12285
|
return {
|
|
12125
12286
|
...definition,
|
|
12126
12287
|
dataSource: {
|
|
12127
12288
|
type: "range",
|
|
12128
|
-
dataSets:
|
|
12129
|
-
dataRange:
|
|
12289
|
+
dataSets: keyValueXc ? [{
|
|
12290
|
+
dataRange: keyValueXc,
|
|
12130
12291
|
dataSetId: "0"
|
|
12131
12292
|
}] : []
|
|
12132
12293
|
},
|
|
12133
|
-
auxiliaryRange: definition.baseline
|
|
12294
|
+
auxiliaryRange: getFormulaRangeXc(definition.baseline),
|
|
12295
|
+
scorecardKeyValueFormula: definition.keyValue,
|
|
12296
|
+
scorecardBaselineFormula: definition.baseline
|
|
12134
12297
|
};
|
|
12135
12298
|
},
|
|
12136
12299
|
getDefinitionForExcel: () => void 0,
|
|
12137
|
-
updateRanges(definition, adapterFunctions) {
|
|
12138
|
-
const baseline =
|
|
12139
|
-
const keyValue =
|
|
12300
|
+
updateRanges(definition, adapterFunctions, sheetId) {
|
|
12301
|
+
const baseline = definition.baseline ? adapterFunctions.adaptFormulaString(sheetId, definition.baseline) : definition.baseline;
|
|
12302
|
+
const keyValue = definition.keyValue ? adapterFunctions.adaptFormulaString(sheetId, definition.keyValue) : definition.keyValue;
|
|
12140
12303
|
if (definition.baseline === baseline && definition.keyValue === keyValue) return definition;
|
|
12141
12304
|
return {
|
|
12142
12305
|
...definition,
|
|
@@ -12144,32 +12307,21 @@ const ScorecardChart$1 = {
|
|
|
12144
12307
|
keyValue
|
|
12145
12308
|
};
|
|
12146
12309
|
},
|
|
12147
|
-
getFormulas
|
|
12148
|
-
|
|
12310
|
+
getFormulas(getters, sheetId, definition) {
|
|
12311
|
+
const formulas = [];
|
|
12312
|
+
if (definition.keyValue && isFormula(definition.keyValue)) formulas.push(CompiledFormula.Compile(definition.keyValue, sheetId, getters));
|
|
12313
|
+
if (definition.baseline && isFormula(definition.baseline)) formulas.push(CompiledFormula.Compile(definition.baseline, sheetId, getters));
|
|
12314
|
+
return formulas;
|
|
12315
|
+
},
|
|
12316
|
+
getRuntime(getters, definition, _dataExtractor, sheetId) {
|
|
12149
12317
|
let formattedKeyValue = "";
|
|
12150
|
-
|
|
12318
|
+
const { scalar: keyValue, range: keyValueRange } = getData$1(definition.keyValue, getters, sheetId);
|
|
12151
12319
|
const locale = getters.getLocale();
|
|
12152
|
-
if (definition.
|
|
12153
|
-
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
|
|
12157
|
-
};
|
|
12158
|
-
keyValueCell = getters.getEvaluatedCell(keyValuePosition);
|
|
12159
|
-
formattedKeyValue = getKeyValueText(keyValueCell, definition.humanize ?? true, locale);
|
|
12160
|
-
}
|
|
12161
|
-
let baselineCell;
|
|
12162
|
-
const baseline = definition.baseline;
|
|
12163
|
-
if (baseline) {
|
|
12164
|
-
const baselinePosition = {
|
|
12165
|
-
sheetId: baseline.sheetId,
|
|
12166
|
-
col: baseline.zone.left,
|
|
12167
|
-
row: baseline.zone.top
|
|
12168
|
-
};
|
|
12169
|
-
baselineCell = getters.getEvaluatedCell(baselinePosition);
|
|
12170
|
-
}
|
|
12171
|
-
const { background, fontColor } = getters.getStyleOfSingleCellChart(definition.background, definition.keyValue);
|
|
12172
|
-
const baselineDisplay = getBaselineText(baselineCell, keyValueCell, definition.baselineMode, definition.humanize ?? true, locale);
|
|
12320
|
+
if (keyValue !== null && keyValue !== void 0) formattedKeyValue = getKeyValueText(keyValue, definition.humanize ?? true, locale);
|
|
12321
|
+
else formattedKeyValue = "";
|
|
12322
|
+
const { scalar: baseline, range: baselineRange } = getData$1(definition.baseline, getters, sheetId);
|
|
12323
|
+
const { background, fontColor } = getters.getStyleOfSingleCellChart(definition.background, keyValueRange);
|
|
12324
|
+
const baselineDisplay = getBaselineText(baseline, keyValue, definition.baselineMode, definition.humanize ?? true, locale);
|
|
12173
12325
|
const baselineValue = definition.baselineMode === "progress" && isNumber(baselineDisplay, locale) ? toNumber(baselineDisplay, locale) : 0;
|
|
12174
12326
|
const title = definition.title;
|
|
12175
12327
|
return {
|
|
@@ -12180,16 +12332,16 @@ const ScorecardChart$1 = {
|
|
|
12180
12332
|
keyValue: formattedKeyValue,
|
|
12181
12333
|
keyDescr: definition.keyDescr?.text ? getters.dynamicTranslate(definition.keyDescr.text) : "",
|
|
12182
12334
|
baselineDisplay,
|
|
12183
|
-
baselineArrow: getBaselineArrowDirection(
|
|
12184
|
-
baselineColor: getBaselineColor(
|
|
12335
|
+
baselineArrow: getBaselineArrowDirection(baseline, keyValue, definition.baselineMode),
|
|
12336
|
+
baselineColor: getBaselineColor(baseline, definition.baselineMode, keyValue, definition.baselineColorUp, definition.baselineColorDown),
|
|
12185
12337
|
baselineDescr: definition.baselineMode !== "progress" && definition.baselineDescr?.text ? getters.dynamicTranslate(definition.baselineDescr.text) : "",
|
|
12186
12338
|
fontColor,
|
|
12187
12339
|
background,
|
|
12188
12340
|
baselineStyle: {
|
|
12189
|
-
...definition.baselineMode !== "percentage" && definition.baselineMode !== "progress" &&
|
|
12190
|
-
sheetId:
|
|
12191
|
-
col:
|
|
12192
|
-
row:
|
|
12341
|
+
...definition.baselineMode !== "percentage" && definition.baselineMode !== "progress" && baselineRange ? getters.getCellComputedStyle({
|
|
12342
|
+
sheetId: baselineRange.sheetId,
|
|
12343
|
+
col: baselineRange.zone.left,
|
|
12344
|
+
row: baselineRange.zone.top
|
|
12193
12345
|
}) : void 0,
|
|
12194
12346
|
fontSize: definition.baselineDescr?.fontSize,
|
|
12195
12347
|
align: definition.baselineDescr?.align
|
|
@@ -12199,10 +12351,10 @@ const ScorecardChart$1 = {
|
|
|
12199
12351
|
...definition.baselineDescr
|
|
12200
12352
|
},
|
|
12201
12353
|
keyValueStyle: {
|
|
12202
|
-
...
|
|
12203
|
-
sheetId:
|
|
12204
|
-
col:
|
|
12205
|
-
row:
|
|
12354
|
+
...keyValueRange ? getters.getCellComputedStyle({
|
|
12355
|
+
sheetId: keyValueRange.sheetId,
|
|
12356
|
+
col: keyValueRange.zone.left,
|
|
12357
|
+
row: keyValueRange.zone.top
|
|
12206
12358
|
}) : void 0,
|
|
12207
12359
|
fontSize: definition.keyDescr?.fontSize,
|
|
12208
12360
|
align: definition.keyDescr?.align
|
|
@@ -12605,7 +12757,7 @@ var ScorecardChart = class extends Component {
|
|
|
12605
12757
|
chartId: types$1.string(),
|
|
12606
12758
|
isFullScreen: types$1.boolean().optional()
|
|
12607
12759
|
});
|
|
12608
|
-
canvas = signal(
|
|
12760
|
+
canvas = signal.ref(HTMLCanvasElement);
|
|
12609
12761
|
zoomStore;
|
|
12610
12762
|
get runtime() {
|
|
12611
12763
|
return this.env.model.getters.getChartRuntime(this.props.chartId);
|
|
@@ -13704,33 +13856,8 @@ var ViewportsStore = class extends SpreadsheetStore {
|
|
|
13704
13856
|
displayedSheetId = this.model.getters.getActiveSheetId();
|
|
13705
13857
|
constructor(get) {
|
|
13706
13858
|
super(get);
|
|
13707
|
-
this.model.selection.observe(this, { handleEvent: this.handleEvent.bind(this) });
|
|
13708
|
-
this.onDispose(() => {
|
|
13709
|
-
this.model.selection.unobserve(this);
|
|
13710
|
-
});
|
|
13711
13859
|
this.viewports.resetViewports(this.displayedSheetId);
|
|
13712
13860
|
}
|
|
13713
|
-
handleEvent(event) {
|
|
13714
|
-
const eventSheetId = this.getters.getActiveSheetId();
|
|
13715
|
-
if (event.options.scrollIntoView) {
|
|
13716
|
-
const oldZone = event.previousAnchor.zone;
|
|
13717
|
-
const newZone = event.anchor.zone;
|
|
13718
|
-
const isUpdateAnchorEvent = event.mode === "updateAnchor";
|
|
13719
|
-
const sameZone = isEqual(oldZone, newZone);
|
|
13720
|
-
let { col, row } = isUpdateAnchorEvent && sameZone ? event.anchor.cell : findCellInNewZone(oldZone, newZone);
|
|
13721
|
-
if (isUpdateAnchorEvent && !sameZone) {
|
|
13722
|
-
const { top, bottom, left, right } = this.viewports.getMainInternalViewport(eventSheetId);
|
|
13723
|
-
if (oldZone.left === newZone.left && oldZone.right === newZone.right) col = left > col || col > right ? left : col;
|
|
13724
|
-
if (oldZone.top === newZone.top && oldZone.bottom === newZone.bottom) row = top > row || row > bottom ? top : row;
|
|
13725
|
-
}
|
|
13726
|
-
col = Math.min(col, this.getters.getNumberCols(eventSheetId) - 1);
|
|
13727
|
-
row = Math.min(row, this.getters.getNumberRows(eventSheetId) - 1);
|
|
13728
|
-
if (!this.sheetsWithDirtyViewports.has(eventSheetId)) this.viewports.refreshViewport(eventSheetId, {
|
|
13729
|
-
col,
|
|
13730
|
-
row
|
|
13731
|
-
});
|
|
13732
|
-
}
|
|
13733
|
-
}
|
|
13734
13861
|
handle(cmd) {
|
|
13735
13862
|
if (invalidateEvaluationCommands.has(cmd.type)) for (const sheetId of this.getters.getSheetIds()) this.sheetsWithDirtyViewports.add(sheetId);
|
|
13736
13863
|
switch (cmd.type) {
|
|
@@ -13830,6 +13957,7 @@ var ViewportsStore = class extends SpreadsheetStore {
|
|
|
13830
13957
|
this.shiftVertically(topRowDims.end - boundaryTopY - viewportHeight);
|
|
13831
13958
|
}
|
|
13832
13959
|
scrollToCell(sheetId, col, row) {
|
|
13960
|
+
if (this.sheetsWithDirtyViewports.has(sheetId)) return;
|
|
13833
13961
|
this.viewports.refreshViewport(sheetId, {
|
|
13834
13962
|
col,
|
|
13835
13963
|
row
|
|
@@ -13990,6 +14118,44 @@ function getCarouselItemTitle(getters, item) {
|
|
|
13990
14118
|
const definition = getters.getChartDefinition(item.chartId);
|
|
13991
14119
|
return (chartSubtypeRegistry.getAll().find((c) => c.matcher?.(definition)) || chartSubtypeRegistry.get(definition.type)).displayName;
|
|
13992
14120
|
}
|
|
14121
|
+
function getCarouselLayout(figureRect, carousel, selectedItem) {
|
|
14122
|
+
const layout = CAROUSEL_LAYOUT;
|
|
14123
|
+
const title = {
|
|
14124
|
+
...DEFAULT_CAROUSEL_TITLE_STYLE,
|
|
14125
|
+
...carousel.title
|
|
14126
|
+
};
|
|
14127
|
+
const x = figureRect.x + layout.paddingX;
|
|
14128
|
+
const width = Math.max(0, figureRect.width - 2 * layout.paddingX);
|
|
14129
|
+
const titleHeight = title.text ? title.fontSize * layout.headerLineHeight : 0;
|
|
14130
|
+
const headerRect = {
|
|
14131
|
+
x,
|
|
14132
|
+
y: figureRect.y + layout.paddingY + layout.headerPaddingTop,
|
|
14133
|
+
width,
|
|
14134
|
+
height: Math.max(layout.minHeaderHeight, titleHeight)
|
|
14135
|
+
};
|
|
14136
|
+
const headerBottom = headerRect.y + headerRect.height;
|
|
14137
|
+
const separatorRect = title.text ? {
|
|
14138
|
+
x,
|
|
14139
|
+
y: headerBottom + layout.paddingY,
|
|
14140
|
+
width,
|
|
14141
|
+
height: layout.separatorWidth
|
|
14142
|
+
} : void 0;
|
|
14143
|
+
const contentPaddingTop = selectedItem?.type === "carouselDataView" ? layout.paddingY : 0;
|
|
14144
|
+
const rectAboveContent = separatorRect || headerRect;
|
|
14145
|
+
const contentY = rectAboveContent.y + rectAboveContent.height + contentPaddingTop;
|
|
14146
|
+
const contentBottom = figureRect.y + figureRect.height - layout.paddingY;
|
|
14147
|
+
return {
|
|
14148
|
+
headerRect,
|
|
14149
|
+
separatorRect,
|
|
14150
|
+
contentRect: {
|
|
14151
|
+
x,
|
|
14152
|
+
y: contentY,
|
|
14153
|
+
width,
|
|
14154
|
+
height: Math.max(0, contentBottom - contentY)
|
|
14155
|
+
},
|
|
14156
|
+
headerLineHeight: layout.headerLineHeight
|
|
14157
|
+
};
|
|
14158
|
+
}
|
|
13993
14159
|
|
|
13994
14160
|
//#endregion
|
|
13995
14161
|
//#region src/helpers/figures/charts/gauge_chart_rendering.ts
|
|
@@ -14001,7 +14167,7 @@ const GAUGE_DEFAULT_VALUE_FONT_SIZE = 80;
|
|
|
14001
14167
|
const GAUGE_BACKGROUND_COLOR = "#F3F2F1";
|
|
14002
14168
|
const GAUGE_INFLECTION_LABEL_BOTTOM_MARGIN = 6;
|
|
14003
14169
|
const GAUGE_TITLE_SECTION_HEIGHT = 25;
|
|
14004
|
-
function drawGaugeChart(canvas, runtime, zoom = 1, dimensions) {
|
|
14170
|
+
function drawGaugeChart(canvas, runtime, zoom = 1, dimensions, options) {
|
|
14005
14171
|
const size = dimensions ?? getCanvasSize(canvas);
|
|
14006
14172
|
const dpr = typeof globalThis.devicePixelRatio === "number" ? globalThis.devicePixelRatio : 1;
|
|
14007
14173
|
canvas.width = size.width * dpr;
|
|
@@ -14014,7 +14180,7 @@ function drawGaugeChart(canvas, runtime, zoom = 1, dimensions) {
|
|
|
14014
14180
|
height: size.height / zoom,
|
|
14015
14181
|
x: 0,
|
|
14016
14182
|
y: 0
|
|
14017
|
-
}, runtime, ctx);
|
|
14183
|
+
}, runtime, ctx, options);
|
|
14018
14184
|
drawBackground(ctx, config);
|
|
14019
14185
|
drawGauge(ctx, config);
|
|
14020
14186
|
drawInflectionValues(ctx, config);
|
|
@@ -14102,7 +14268,8 @@ function drawTitle(ctx, config) {
|
|
|
14102
14268
|
ctx.fillText(title.label, title.textPosition.x, title.textPosition.y);
|
|
14103
14269
|
ctx.restore();
|
|
14104
14270
|
}
|
|
14105
|
-
function getGaugeRenderingConfig(boundingRect, runtime, ctx) {
|
|
14271
|
+
function getGaugeRenderingConfig(boundingRect, runtime, ctx, options) {
|
|
14272
|
+
const labelFontSize = options?.labelFontSize ?? 12;
|
|
14106
14273
|
const maxValue = runtime.maxValue;
|
|
14107
14274
|
const minValue = runtime.minValue;
|
|
14108
14275
|
const gaugeValue = getGaugeValue(runtime, "animated");
|
|
@@ -14120,14 +14287,14 @@ function getGaugeRenderingConfig(boundingRect, runtime, ctx) {
|
|
|
14120
14287
|
if (computeTextWidth(ctx, gaugeLabel, { fontSize: gaugeValueFontSize }, "px") > maxTextWidth) gaugeValueFontSize = getFontSizeMatchingWidth(maxTextWidth, gaugeValueFontSize, (fontSize) => computeTextWidth(ctx, gaugeLabel, { fontSize }, "px"));
|
|
14121
14288
|
const minLabelPosition = {
|
|
14122
14289
|
x: gaugeRect.x + gaugeArcWidth / 2,
|
|
14123
|
-
y: gaugeRect.y + gaugeRect.height +
|
|
14290
|
+
y: gaugeRect.y + gaugeRect.height + labelFontSize
|
|
14124
14291
|
};
|
|
14125
14292
|
const maxLabelPosition = {
|
|
14126
14293
|
x: gaugeRect.x + gaugeRect.width - gaugeArcWidth / 2,
|
|
14127
|
-
y: gaugeRect.y + gaugeRect.height +
|
|
14294
|
+
y: gaugeRect.y + gaugeRect.height + labelFontSize
|
|
14128
14295
|
};
|
|
14129
14296
|
const textColor = chartMutedFontColor(runtime.background);
|
|
14130
|
-
const inflectionValues = getInflectionValues(runtime, gaugeRect, textColor, ctx);
|
|
14297
|
+
const inflectionValues = getInflectionValues(runtime, gaugeRect, textColor, ctx, labelFontSize);
|
|
14131
14298
|
let x = 0, titleWidth = 0, titleHeight = 0;
|
|
14132
14299
|
if (runtime.title.text) ({width: titleWidth, height: titleHeight} = computeTextDimension(ctx, runtime.title.text, {
|
|
14133
14300
|
fontSize: 16,
|
|
@@ -14175,13 +14342,13 @@ function getGaugeRenderingConfig(boundingRect, runtime, ctx) {
|
|
|
14175
14342
|
minLabel: {
|
|
14176
14343
|
label: runtime.minValue.label,
|
|
14177
14344
|
textPosition: minLabelPosition,
|
|
14178
|
-
fontSize:
|
|
14345
|
+
fontSize: labelFontSize,
|
|
14179
14346
|
color: textColor
|
|
14180
14347
|
},
|
|
14181
14348
|
maxLabel: {
|
|
14182
14349
|
label: runtime.maxValue.label,
|
|
14183
14350
|
textPosition: maxLabelPosition,
|
|
14184
|
-
fontSize:
|
|
14351
|
+
fontSize: labelFontSize,
|
|
14185
14352
|
color: textColor
|
|
14186
14353
|
}
|
|
14187
14354
|
};
|
|
@@ -14215,27 +14382,27 @@ function getGaugeRect(boundingRect, title) {
|
|
|
14215
14382
|
*
|
|
14216
14383
|
* Also compute an offset for the text so that it doesn't overlap with other text.
|
|
14217
14384
|
*/
|
|
14218
|
-
function getInflectionValues(runtime, gaugeRect, textColor, ctx) {
|
|
14385
|
+
function getInflectionValues(runtime, gaugeRect, textColor, ctx, labelFontSize = 12) {
|
|
14219
14386
|
const maxValue = runtime.maxValue;
|
|
14220
14387
|
const minValue = runtime.minValue;
|
|
14221
14388
|
const gaugeCircleCenter = {
|
|
14222
14389
|
x: gaugeRect.x + gaugeRect.width / 2,
|
|
14223
14390
|
y: gaugeRect.y + gaugeRect.height
|
|
14224
14391
|
};
|
|
14225
|
-
const textStyle = { fontSize:
|
|
14392
|
+
const textStyle = { fontSize: labelFontSize };
|
|
14226
14393
|
const inflectionValues = [];
|
|
14227
14394
|
const inflectionValuesTextRects = [];
|
|
14228
14395
|
for (const inflectionValue of runtime.inflectionValues) {
|
|
14229
14396
|
const percentage = (inflectionValue.value - minValue.value) / (maxValue.value - minValue.value);
|
|
14230
14397
|
const labelWidth = computeTextWidth(ctx, inflectionValue.label, textStyle, "px");
|
|
14231
14398
|
const angle = Math.PI - Math.PI * percentage;
|
|
14232
|
-
const textRect = getRectangleTangentToCircle(angle, gaugeRect.height + GAUGE_INFLECTION_LABEL_BOTTOM_MARGIN, gaugeCircleCenter.x, gaugeCircleCenter.y, labelWidth + 2,
|
|
14233
|
-
const offset = inflectionValuesTextRects.some((rect) => doRectanglesIntersect(rect, textRect)) ?
|
|
14399
|
+
const textRect = getRectangleTangentToCircle(angle, gaugeRect.height + GAUGE_INFLECTION_LABEL_BOTTOM_MARGIN, gaugeCircleCenter.x, gaugeCircleCenter.y, labelWidth + 2, labelFontSize);
|
|
14400
|
+
const offset = inflectionValuesTextRects.some((rect) => doRectanglesIntersect(rect, textRect)) ? labelFontSize : 0;
|
|
14234
14401
|
inflectionValuesTextRects.push(textRect);
|
|
14235
14402
|
inflectionValues.push({
|
|
14236
14403
|
rotation: angle,
|
|
14237
14404
|
label: inflectionValue.label,
|
|
14238
|
-
fontSize:
|
|
14405
|
+
fontSize: labelFontSize,
|
|
14239
14406
|
color: textColor,
|
|
14240
14407
|
offset
|
|
14241
14408
|
});
|
|
@@ -14355,6 +14522,44 @@ const CHART_COMMON_OPTIONS = {
|
|
|
14355
14522
|
"mouseup"
|
|
14356
14523
|
]
|
|
14357
14524
|
};
|
|
14525
|
+
/**
|
|
14526
|
+
* Sample down a Chart.js configuration's data points to at most `maxPoints`, evenly spaced.
|
|
14527
|
+
* Used for lightweight previews (drag preview, chart suggestions) where rendering every point
|
|
14528
|
+
* of a large dataset would make Chart.js slow without any visible benefit on a small canvas.
|
|
14529
|
+
*/
|
|
14530
|
+
function limitChartConfigDataPoints(config, maxPoints = 150) {
|
|
14531
|
+
const data = config.data;
|
|
14532
|
+
if (!data) return config;
|
|
14533
|
+
return {
|
|
14534
|
+
...config,
|
|
14535
|
+
data: {
|
|
14536
|
+
...data,
|
|
14537
|
+
labels: limitArrayDataPoints(data.labels, maxPoints),
|
|
14538
|
+
datasets: data.datasets?.map((dataset) => limitDatasetDataPoints(dataset, maxPoints))
|
|
14539
|
+
}
|
|
14540
|
+
};
|
|
14541
|
+
}
|
|
14542
|
+
function limitDatasetDataPoints(dataset, maxPoints) {
|
|
14543
|
+
const values = dataset.data;
|
|
14544
|
+
if (!Array.isArray(values) || values.length <= maxPoints) return dataset;
|
|
14545
|
+
const length = values.length;
|
|
14546
|
+
const indices = sampleIndices(length, maxPoints);
|
|
14547
|
+
const limited = { ...dataset };
|
|
14548
|
+
for (const key of Object.keys(limited)) {
|
|
14549
|
+
const value = limited[key];
|
|
14550
|
+
if (Array.isArray(value) && value.length === length) limited[key] = indices.map((i) => value[i]);
|
|
14551
|
+
}
|
|
14552
|
+
return limited;
|
|
14553
|
+
}
|
|
14554
|
+
function limitArrayDataPoints(arr, maxPoints) {
|
|
14555
|
+
if (!Array.isArray(arr) || arr.length <= maxPoints) return arr;
|
|
14556
|
+
return sampleIndices(arr.length, maxPoints).map((i) => arr[i]);
|
|
14557
|
+
}
|
|
14558
|
+
/** Evenly spaced indices sampled from [0, length). */
|
|
14559
|
+
function sampleIndices(length, maxPoints) {
|
|
14560
|
+
const step = length / maxPoints;
|
|
14561
|
+
return Array.from({ length: maxPoints }, (_, i) => Math.min(length - 1, Math.floor(i * step)));
|
|
14562
|
+
}
|
|
14358
14563
|
async function chartToImageUrl(runtime, figure, type) {
|
|
14359
14564
|
try {
|
|
14360
14565
|
const canvas = createRenderingSurface(figure.width, figure.height);
|
|
@@ -14402,7 +14607,7 @@ async function canvasToObjectUrl(canvas) {
|
|
|
14402
14607
|
*
|
|
14403
14608
|
* @returns a cleanup function to be called after the drawing is no longer needed (to free Chart.js resources)
|
|
14404
14609
|
*/
|
|
14405
|
-
function drawChartOnCanvas(canvas, runtime, size, type) {
|
|
14610
|
+
function drawChartOnCanvas(canvas, runtime, size, type, zoom = 1) {
|
|
14406
14611
|
if ("chartJsConfig" in runtime) {
|
|
14407
14612
|
if (!globalThis.Chart) throw new Error("Chart.js library is not loaded");
|
|
14408
14613
|
const extensionsLoaded = areChartJSExtensionsLoaded();
|
|
@@ -14418,8 +14623,8 @@ function drawChartOnCanvas(canvas, runtime, size, type) {
|
|
|
14418
14623
|
chart.destroy();
|
|
14419
14624
|
if (!extensionsLoaded) unregisterChartJsExtensions();
|
|
14420
14625
|
};
|
|
14421
|
-
} else if (type === "scorecard") drawScoreChart(getScorecardConfiguration(size, runtime), canvas);
|
|
14422
|
-
else if (type === "gauge") drawGaugeChart(canvas, runtime,
|
|
14626
|
+
} else if (type === "scorecard") drawScoreChart(getScorecardConfiguration(size, runtime), canvas, zoom);
|
|
14627
|
+
else if (type === "gauge") drawGaugeChart(canvas, runtime, zoom, size);
|
|
14423
14628
|
return () => {};
|
|
14424
14629
|
}
|
|
14425
14630
|
|
|
@@ -14957,7 +15162,7 @@ var ZoomableChartStore = class extends SpreadsheetStore {
|
|
|
14957
15162
|
var ZoomableChartJsComponent = class extends ChartJsComponent {
|
|
14958
15163
|
static template = "o-spreadsheet-ZoomableChartJsComponent";
|
|
14959
15164
|
store;
|
|
14960
|
-
masterChartCanvas = signal(
|
|
15165
|
+
masterChartCanvas = signal.ref(HTMLCanvasElement);
|
|
14961
15166
|
masterChart;
|
|
14962
15167
|
mode;
|
|
14963
15168
|
hasLinearScale;
|
|
@@ -15711,7 +15916,7 @@ var GaugeChartComponent = class extends Component {
|
|
|
15711
15916
|
chartId: types$1.string(),
|
|
15712
15917
|
isFullScreen: types$1.boolean().optional()
|
|
15713
15918
|
});
|
|
15714
|
-
canvas = signal(
|
|
15919
|
+
canvas = signal.ref(HTMLCanvasElement);
|
|
15715
15920
|
animationStore;
|
|
15716
15921
|
zoomStore;
|
|
15717
15922
|
get runtime() {
|
|
@@ -15948,7 +16153,7 @@ var Menu = class extends Component {
|
|
|
15948
16153
|
onKeyDown: types$1.function().optional(),
|
|
15949
16154
|
disableKeyboardNavigation: types$1.boolean().optional()
|
|
15950
16155
|
});
|
|
15951
|
-
menuRef = signal(
|
|
16156
|
+
menuRef = signal.ref();
|
|
15952
16157
|
setup() {
|
|
15953
16158
|
useLayoutEffect(() => {
|
|
15954
16159
|
const menuEl = this.menuRef();
|
|
@@ -16068,8 +16273,8 @@ var Popover = class extends Component {
|
|
|
16068
16273
|
zIndex: types$1.number().optional(),
|
|
16069
16274
|
class: types$1.string().optional()
|
|
16070
16275
|
});
|
|
16071
|
-
popoverRef = signal(
|
|
16072
|
-
popoverContentRef = signal(
|
|
16276
|
+
popoverRef = signal.ref();
|
|
16277
|
+
popoverContentRef = signal.ref();
|
|
16073
16278
|
currentPosition = void 0;
|
|
16074
16279
|
currentDisplayValue = void 0;
|
|
16075
16280
|
spreadsheetRect = useSpreadsheetRect();
|
|
@@ -16257,7 +16462,7 @@ var MenuPopover = class MenuPopover extends Component {
|
|
|
16257
16462
|
isHoveringChild: false
|
|
16258
16463
|
});
|
|
16259
16464
|
state = proxy({ hoveredMenu: this.props.autoSelectFirstItem ? this.getNextEnabledMenuItem() : void 0 });
|
|
16260
|
-
menuRef = signal(
|
|
16465
|
+
menuRef = signal.ref();
|
|
16261
16466
|
openingTimeOut = useTimeOut();
|
|
16262
16467
|
setup() {
|
|
16263
16468
|
const domFocusableElementStore = useStore(DOMFocusableElementStore);
|
|
@@ -16546,43 +16751,29 @@ var PositionMap = class {
|
|
|
16546
16751
|
};
|
|
16547
16752
|
|
|
16548
16753
|
//#endregion
|
|
16549
|
-
//#region src/
|
|
16550
|
-
var
|
|
16551
|
-
mutators = [
|
|
16552
|
-
|
|
16754
|
+
//#region src/stores/cell_hover_overlay_store.ts
|
|
16755
|
+
var CellHoverOverlayStore = class extends SpreadsheetStore {
|
|
16756
|
+
mutators = [
|
|
16757
|
+
"hover",
|
|
16758
|
+
"register",
|
|
16759
|
+
"unRegister"
|
|
16760
|
+
];
|
|
16761
|
+
storeGetters = ["getCellHoverOverlayColor"];
|
|
16762
|
+
providers = [];
|
|
16553
16763
|
overlayColors = new PositionMap();
|
|
16554
|
-
hover(
|
|
16555
|
-
|
|
16556
|
-
|
|
16557
|
-
this.
|
|
16764
|
+
hover(hoveredPosition) {
|
|
16765
|
+
this.overlayColors = new PositionMap();
|
|
16766
|
+
if (!hoveredPosition) return;
|
|
16767
|
+
for (const provider of this.providers) {
|
|
16768
|
+
const positions = provider.getHighlightedPositions(hoveredPosition);
|
|
16769
|
+
for (const position of positions) this.overlayColors.set(position, TABLE_HOVER_BACKGROUND_COLOR);
|
|
16770
|
+
}
|
|
16558
16771
|
}
|
|
16559
|
-
|
|
16560
|
-
this.
|
|
16772
|
+
register(highlightProvider) {
|
|
16773
|
+
this.providers.push(highlightProvider);
|
|
16561
16774
|
}
|
|
16562
|
-
|
|
16563
|
-
this.
|
|
16564
|
-
if (!this.position) return;
|
|
16565
|
-
const { sheetId, col, row } = this.position;
|
|
16566
|
-
const table = this.getters.getTable({
|
|
16567
|
-
sheetId,
|
|
16568
|
-
col,
|
|
16569
|
-
row
|
|
16570
|
-
});
|
|
16571
|
-
if (!table) return;
|
|
16572
|
-
const { left, right, top } = table.range.zone;
|
|
16573
|
-
const isTableHeader = row < top + table.config.numberOfHeaders;
|
|
16574
|
-
const doesTableRowHaveContent = range(left, right + 1).some((col) => {
|
|
16575
|
-
return !this.getters.isColHidden(sheetId, col) && this.getters.getEvaluatedCell({
|
|
16576
|
-
sheetId,
|
|
16577
|
-
col,
|
|
16578
|
-
row
|
|
16579
|
-
}).formattedValue;
|
|
16580
|
-
});
|
|
16581
|
-
if (!isTableHeader && doesTableRowHaveContent) for (let col = left; col <= right; col++) this.overlayColors.set({
|
|
16582
|
-
sheetId,
|
|
16583
|
-
col,
|
|
16584
|
-
row
|
|
16585
|
-
}, TABLE_HOVER_BACKGROUND_COLOR);
|
|
16775
|
+
unRegister(highlightProvider) {
|
|
16776
|
+
this.providers = this.providers.filter((h) => h !== highlightProvider);
|
|
16586
16777
|
}
|
|
16587
16778
|
};
|
|
16588
16779
|
|
|
@@ -16594,9 +16785,9 @@ var ClickableCellSortIcon = class extends Component {
|
|
|
16594
16785
|
position: types$1.CellPosition(),
|
|
16595
16786
|
sortDirection: types$1.or([types$1.SortDirection, types$1.literal("none")])
|
|
16596
16787
|
});
|
|
16597
|
-
|
|
16788
|
+
hoveredCellOverlayStore;
|
|
16598
16789
|
setup() {
|
|
16599
|
-
this.
|
|
16790
|
+
this.hoveredCellOverlayStore = useStore(CellHoverOverlayStore);
|
|
16600
16791
|
}
|
|
16601
16792
|
get style() {
|
|
16602
16793
|
const cellStyle = this.env.model.getters.getCellComputedStyle(this.props.position);
|
|
@@ -16616,7 +16807,7 @@ var ClickableCellSortIcon = class extends Component {
|
|
|
16616
16807
|
}
|
|
16617
16808
|
}
|
|
16618
16809
|
getBackgroundColor(cellStyle) {
|
|
16619
|
-
const overlayColor = this.
|
|
16810
|
+
const overlayColor = this.hoveredCellOverlayStore.overlayColors.get(this.props.position);
|
|
16620
16811
|
if (overlayColor) return blendColors(cellStyle.fillColor || "#FFFFFF", overlayColor);
|
|
16621
16812
|
return cellStyle.fillColor || "#FFFFFF";
|
|
16622
16813
|
}
|
|
@@ -16859,110 +17050,6 @@ function isSquareMatrix(arg) {
|
|
|
16859
17050
|
}
|
|
16860
17051
|
const expectNumberGreaterThanOrEqualToOne = (value) => _t("The function [[FUNCTION_NAME]] expects a number value to be greater than or equal to 1, but receives %s.", value);
|
|
16861
17052
|
|
|
16862
|
-
//#endregion
|
|
16863
|
-
//#region src/functions/helper_matrices.ts
|
|
16864
|
-
function getUnitMatrix(n) {
|
|
16865
|
-
const matrix = Array(n);
|
|
16866
|
-
for (let i = 0; i < n; i++) {
|
|
16867
|
-
matrix[i] = Array(n).fill(0);
|
|
16868
|
-
matrix[i][i] = 1;
|
|
16869
|
-
}
|
|
16870
|
-
return matrix;
|
|
16871
|
-
}
|
|
16872
|
-
/**
|
|
16873
|
-
* Invert a matrix and compute its determinant using Gaussian Elimination.
|
|
16874
|
-
*
|
|
16875
|
-
* The Matrix should be a square matrix, and should be indexed [col][row] instead of the
|
|
16876
|
-
* standard mathematical indexing [row][col].
|
|
16877
|
-
*/
|
|
16878
|
-
function invertMatrix(M) {
|
|
16879
|
-
if (M.length < 1 || M[0].length < 1) throw new Error("invertMatrix: an empty matrix cannot be inverted.");
|
|
16880
|
-
if (M.length !== M[0].length) throw new Error("invertMatrix: only square matrices are invertible");
|
|
16881
|
-
let determinant = 1;
|
|
16882
|
-
const dim = M.length;
|
|
16883
|
-
const I = getUnitMatrix(dim);
|
|
16884
|
-
const C = M.map((row) => row.slice());
|
|
16885
|
-
for (let pivot = 0; pivot < dim; pivot++) {
|
|
16886
|
-
let diagonalElement = C[pivot][pivot];
|
|
16887
|
-
if (diagonalElement === 0) {
|
|
16888
|
-
for (let row = pivot + 1; row < dim; row++) if (C[pivot][row] !== 0) {
|
|
16889
|
-
swapMatrixRows(C, pivot, row);
|
|
16890
|
-
swapMatrixRows(I, pivot, row);
|
|
16891
|
-
determinant *= -1;
|
|
16892
|
-
break;
|
|
16893
|
-
}
|
|
16894
|
-
diagonalElement = C[pivot][pivot];
|
|
16895
|
-
if (diagonalElement === 0) return { determinant: 0 };
|
|
16896
|
-
}
|
|
16897
|
-
for (let col = 0; col < dim; col++) {
|
|
16898
|
-
C[col][pivot] = C[col][pivot] / diagonalElement;
|
|
16899
|
-
I[col][pivot] = I[col][pivot] / diagonalElement;
|
|
16900
|
-
}
|
|
16901
|
-
determinant *= diagonalElement;
|
|
16902
|
-
for (let row = 0; row < dim; row++) {
|
|
16903
|
-
if (row === pivot) continue;
|
|
16904
|
-
const e = C[pivot][row];
|
|
16905
|
-
for (let col = 0; col < dim; col++) {
|
|
16906
|
-
C[col][row] -= e * C[col][pivot];
|
|
16907
|
-
I[col][row] -= e * I[col][pivot];
|
|
16908
|
-
}
|
|
16909
|
-
}
|
|
16910
|
-
}
|
|
16911
|
-
return {
|
|
16912
|
-
inverted: I,
|
|
16913
|
-
determinant
|
|
16914
|
-
};
|
|
16915
|
-
}
|
|
16916
|
-
function swapMatrixRows(matrix, row1, row2) {
|
|
16917
|
-
for (let i = 0; i < matrix.length; i++) {
|
|
16918
|
-
const tmp = matrix[i][row1];
|
|
16919
|
-
matrix[i][row1] = matrix[i][row2];
|
|
16920
|
-
matrix[i][row2] = tmp;
|
|
16921
|
-
}
|
|
16922
|
-
}
|
|
16923
|
-
/**
|
|
16924
|
-
* Matrix multiplication of 2 matrices.
|
|
16925
|
-
* ex: matrix1 : n x l, matrix2 : m x n => result : m x l
|
|
16926
|
-
*
|
|
16927
|
-
* Note: we use indexing [col][row] instead of the standard mathematical notation [row][col]
|
|
16928
|
-
*/
|
|
16929
|
-
function multiplyMatrices(matrix1, matrix2) {
|
|
16930
|
-
if (matrix1.length < 1 || matrix2.length < 1) throw new Error("multiplyMatrices: empty matrices cannot be multiplied.");
|
|
16931
|
-
if (matrix1.length !== matrix2[0].length) throw new Error("multiplyMatrices: incompatible matrices size.");
|
|
16932
|
-
const rowsM1 = matrix1[0].length;
|
|
16933
|
-
const colsM2 = matrix2.length;
|
|
16934
|
-
const n = matrix1.length;
|
|
16935
|
-
const result = Array(colsM2);
|
|
16936
|
-
for (let col = 0; col < colsM2; col++) {
|
|
16937
|
-
result[col] = Array(rowsM1);
|
|
16938
|
-
for (let row = 0; row < rowsM1; row++) {
|
|
16939
|
-
let sum = 0;
|
|
16940
|
-
for (let k = 0; k < n; k++) sum += matrix1[k][row] * matrix2[col][k];
|
|
16941
|
-
result[col][row] = sum;
|
|
16942
|
-
}
|
|
16943
|
-
}
|
|
16944
|
-
return result;
|
|
16945
|
-
}
|
|
16946
|
-
/**
|
|
16947
|
-
* Return the input if it's a scalar or the first element of the input if it's a matrix.
|
|
16948
|
-
*/
|
|
16949
|
-
function toScalar(arg) {
|
|
16950
|
-
if (!isMatrix(arg)) return arg;
|
|
16951
|
-
if (!isSingleElementMatrix(arg)) throw new Error("The value should be a scalar or a 1x1 matrix");
|
|
16952
|
-
return arg[0][0];
|
|
16953
|
-
}
|
|
16954
|
-
function isSingleElementMatrix(matrix) {
|
|
16955
|
-
return matrix.length === 1 && matrix[0].length === 1;
|
|
16956
|
-
}
|
|
16957
|
-
function isMultipleElementMatrix(arg) {
|
|
16958
|
-
return isMatrix(arg) && !isSingleElementMatrix(arg);
|
|
16959
|
-
}
|
|
16960
|
-
function getMatrixArgIndices(args) {
|
|
16961
|
-
const indices = [];
|
|
16962
|
-
for (let i = 0; i < args.length; i++) if (isMultipleElementMatrix(args[i])) indices.push(i);
|
|
16963
|
-
return indices;
|
|
16964
|
-
}
|
|
16965
|
-
|
|
16966
17053
|
//#endregion
|
|
16967
17054
|
//#region src/functions/helper_statistical.ts
|
|
16968
17055
|
function assertSameNumberOfElements(...args) {
|
|
@@ -18113,8 +18200,6 @@ const ChartTerms = {
|
|
|
18113
18200
|
Unexpected: _t("The chart definition is invalid for an unknown reason"),
|
|
18114
18201
|
["InvalidDataSet"]: _t("The dataset is invalid"),
|
|
18115
18202
|
["InvalidLabelRange"]: _t("Labels are invalid"),
|
|
18116
|
-
["InvalidScorecardKeyValue"]: _t("The key value is invalid"),
|
|
18117
|
-
["InvalidScorecardBaseline"]: _t("The baseline value is invalid"),
|
|
18118
18203
|
["InvalidGaugeDataRange"]: _t("The data range is invalid"),
|
|
18119
18204
|
["EmptyGaugeRangeMin"]: _t("A minimum range limit value is needed"),
|
|
18120
18205
|
["GaugeRangeMinNaN"]: _t("The minimum range limit value must be a number"),
|
|
@@ -21355,7 +21440,7 @@ var HoveredIconStore = class extends SpreadsheetStore {
|
|
|
21355
21440
|
//#region src/components/grid_overlay/grid_overlay.ts
|
|
21356
21441
|
function useCellHovered(env, gridRef) {
|
|
21357
21442
|
const delayedHoveredCell = useStore(DelayedHoveredCellStore);
|
|
21358
|
-
const
|
|
21443
|
+
const cellHoverOverlay = useStore(CellHoverOverlayStore);
|
|
21359
21444
|
const viewStore = useStore(ViewportsStore);
|
|
21360
21445
|
const hoveredPosition = {
|
|
21361
21446
|
col: void 0,
|
|
@@ -21399,7 +21484,9 @@ function useCellHovered(env, gridRef) {
|
|
|
21399
21484
|
if (isChildEvent(gridRef(), zoomedMouseEvent.ev)) {
|
|
21400
21485
|
({x, y} = getOffsetRelativeToOverlay(zoomedMouseEvent));
|
|
21401
21486
|
lastMoved = Date.now();
|
|
21402
|
-
|
|
21487
|
+
const position = getPosition();
|
|
21488
|
+
if (position.col < 0 || position.row < 0) cellHoverOverlay.hover(void 0);
|
|
21489
|
+
else cellHoverOverlay.hover(getPosition());
|
|
21403
21490
|
}
|
|
21404
21491
|
}
|
|
21405
21492
|
function recompute() {
|
|
@@ -21410,7 +21497,7 @@ function useCellHovered(env, gridRef) {
|
|
|
21410
21497
|
const zoomedMouseEvent = withZoom(env, e);
|
|
21411
21498
|
const { x, y } = getOffsetRelativeToOverlay(zoomedMouseEvent);
|
|
21412
21499
|
const gridRect = getElBoundingRect(gridRef());
|
|
21413
|
-
if (y
|
|
21500
|
+
if (y <= 0 || y >= gridRect.height || x <= 0 || x >= gridRect.width) return updateMousePosition(zoomedMouseEvent);
|
|
21414
21501
|
else return pause();
|
|
21415
21502
|
}
|
|
21416
21503
|
useListener(gridRef, "pointermove", (ev) => !env.isMobile() && updateMousePosition(withZoom(env, ev)));
|
|
@@ -21447,7 +21534,7 @@ var GridOverlay = class extends Component {
|
|
|
21447
21534
|
gridOverlayDimensions: types$1.string(),
|
|
21448
21535
|
hasFooter: types$1.boolean().optional(() => true)
|
|
21449
21536
|
});
|
|
21450
|
-
gridOverlayRef = signal(
|
|
21537
|
+
gridOverlayRef = signal.ref();
|
|
21451
21538
|
cellPopovers;
|
|
21452
21539
|
paintFormatStore;
|
|
21453
21540
|
hoveredIconStore;
|
|
@@ -21474,7 +21561,7 @@ var GridOverlay = class extends Component {
|
|
|
21474
21561
|
return el;
|
|
21475
21562
|
}
|
|
21476
21563
|
get style() {
|
|
21477
|
-
return this.props.gridOverlayDimensions + cssPropertiesToCss({ cursor: this.hoveredIconStore.hoveredIcon ? "pointer" : "
|
|
21564
|
+
return this.props.gridOverlayDimensions + cssPropertiesToCss({ cursor: this.hoveredIconStore.hoveredIcon ? "pointer" : "inherit" });
|
|
21478
21565
|
}
|
|
21479
21566
|
get isPaintingFormat() {
|
|
21480
21567
|
return this.paintFormatStore.isActive;
|
|
@@ -21789,7 +21876,7 @@ function getPivotIconSvg(isCollapsed, isHovered) {
|
|
|
21789
21876
|
};
|
|
21790
21877
|
}
|
|
21791
21878
|
function getDataFilterIcon(isActive, isHighContrast, isHovered) {
|
|
21792
|
-
const symbolPath = isActive ? "
|
|
21879
|
+
const symbolPath = isActive ? "M 11 20 Q 10.575 20 10.2875 19.7125 T 10 19 L 10 13 L 4.2 5.6 Q 3.825 5.1 4.0875 4.55 T 5 4 L 19 4 Q 19.65 4 19.9125 4.55 T 19.8 5.6 L 14 13 L 14 19 Q 14 19.425 13.7125 19.7125 T 13 20 L 11 20 Z" : "M 339.667 681 L 510.333 681 L 510.333 595.667 L 339.667 595.667 L 339.667 681 Z M 41 169 L 41 254.333 L 809 254.333 L 809 169 L 41 169 Z M 169 467.667 L 681 467.667 L 681 382.333 L 169 382.333 L 169 467.667 Z";
|
|
21793
21880
|
const hoverBackgroundPath = isActive ? "M0,0 h24 v24 h-24" : "M0,0 h850 v850 h-850";
|
|
21794
21881
|
const colors = {
|
|
21795
21882
|
iconColor: FILTERS_COLOR,
|
|
@@ -22114,8 +22201,9 @@ var FormulaFingerprintStore = class extends SpreadsheetStore {
|
|
|
22114
22201
|
const CELL_ANIMATION_DURATION = 200;
|
|
22115
22202
|
var GridRenderer = class extends DisposableStore {
|
|
22116
22203
|
renderer;
|
|
22204
|
+
storeGetters = ["drawLayer"];
|
|
22117
22205
|
fingerprints;
|
|
22118
|
-
|
|
22206
|
+
cellHoverOverlayStore = this.get(CellHoverOverlayStore);
|
|
22119
22207
|
hoveredIcon;
|
|
22120
22208
|
lastRenderSheetId = void 0;
|
|
22121
22209
|
lastRenderBoxes = /* @__PURE__ */ new Map();
|
|
@@ -22129,15 +22217,14 @@ var GridRenderer = class extends DisposableStore {
|
|
|
22129
22217
|
const model = get(ModelStore);
|
|
22130
22218
|
this.getters = model.getters;
|
|
22131
22219
|
this.fingerprints = get(FormulaFingerprintStore);
|
|
22132
|
-
this.hoveredTables = get(HoveredTableStore);
|
|
22133
22220
|
this.hoveredIcon = get(HoveredIconStore);
|
|
22134
22221
|
model.on("command-dispatched", this, this.handle);
|
|
22135
22222
|
model.on("command-finalized", this, this.finalize);
|
|
22136
|
-
this.renderer.register(this);
|
|
22223
|
+
if (this.renderer) this.renderer.register(this);
|
|
22137
22224
|
this.onDispose(() => {
|
|
22138
22225
|
model.off("command-dispatched", this);
|
|
22139
22226
|
model.off("command-finalized", this);
|
|
22140
|
-
this.renderer.unRegister(this);
|
|
22227
|
+
if (this.renderer) this.renderer.unRegister(this);
|
|
22141
22228
|
});
|
|
22142
22229
|
}
|
|
22143
22230
|
handle(cmd) {
|
|
@@ -22681,7 +22768,7 @@ var GridRenderer = class extends DisposableStore {
|
|
|
22681
22768
|
border: this.getters.getCellComputedBorder(position) || void 0,
|
|
22682
22769
|
style,
|
|
22683
22770
|
dataBarFill,
|
|
22684
|
-
overlayColor: this.
|
|
22771
|
+
overlayColor: this.cellHoverOverlayStore.overlayColors.get(position),
|
|
22685
22772
|
isError: cell.type === "error" && !!cell.message || this.getters.isDataValidationInvalid(position),
|
|
22686
22773
|
icons: cellIcons,
|
|
22687
22774
|
disabledAnimation: this.zonesWithPreventedAnimationsInNextFrame[sheetId]?.some((z) => isZoneInside(zone, z) || overlap(zone, z))
|
|
@@ -22857,6 +22944,7 @@ var GridRenderer = class extends DisposableStore {
|
|
|
22857
22944
|
return boxes;
|
|
22858
22945
|
}
|
|
22859
22946
|
getBoxesWithAnimations(boxes, oldBoxes, timeStamp) {
|
|
22947
|
+
if (!this.renderer) return boxes;
|
|
22860
22948
|
this.updateAnimationsProgress(timeStamp);
|
|
22861
22949
|
this.addNewAnimations(boxes, oldBoxes, timeStamp);
|
|
22862
22950
|
if (this.animations.size > 0) {
|
|
@@ -23001,7 +23089,7 @@ var ScrollBar = class extends Component {
|
|
|
23001
23089
|
offset: types$1.Pixel(),
|
|
23002
23090
|
onScroll: types$1.function()
|
|
23003
23091
|
});
|
|
23004
|
-
scrollbarRef = signal(
|
|
23092
|
+
scrollbarRef = signal.ref();
|
|
23005
23093
|
scrollbar;
|
|
23006
23094
|
setup() {
|
|
23007
23095
|
this.scrollbar = new ScrollBar$1(this.scrollbarRef(), this.props.direction);
|
|
@@ -23103,6 +23191,9 @@ var StandaloneViewportStore = class extends SpreadsheetStore {
|
|
|
23103
23191
|
zoneToDisplay: this.range.zone,
|
|
23104
23192
|
getFooterSize: () => 0
|
|
23105
23193
|
});
|
|
23194
|
+
const cellHoverOverlayStore = this.get(CellHoverOverlayStore);
|
|
23195
|
+
cellHoverOverlayStore.register(this);
|
|
23196
|
+
this.onDispose(() => cellHoverOverlayStore.unRegister(this));
|
|
23106
23197
|
}
|
|
23107
23198
|
handle(cmd) {
|
|
23108
23199
|
this.cachedColDimensions = void 0;
|
|
@@ -23251,6 +23342,16 @@ var StandaloneViewportStore = class extends SpreadsheetStore {
|
|
|
23251
23342
|
}
|
|
23252
23343
|
return normalizedWeights;
|
|
23253
23344
|
}
|
|
23345
|
+
getHighlightedPositions(position) {
|
|
23346
|
+
const highlightedPositions = [];
|
|
23347
|
+
const { col, row, sheetId } = position;
|
|
23348
|
+
if (sheetId !== this.range.sheetId || !isInside(col, row, this.range.zone)) return highlightedPositions;
|
|
23349
|
+
for (let col = this.range.zone.left; col <= this.range.zone.right; col++) highlightedPositions.push({
|
|
23350
|
+
...position,
|
|
23351
|
+
col
|
|
23352
|
+
});
|
|
23353
|
+
return highlightedPositions;
|
|
23354
|
+
}
|
|
23254
23355
|
};
|
|
23255
23356
|
|
|
23256
23357
|
//#endregion
|
|
@@ -23267,10 +23368,11 @@ var StandaloneViewport = class extends Component {
|
|
|
23267
23368
|
range: types$1.Range(),
|
|
23268
23369
|
canResizeColumns: types$1.boolean().optional(true),
|
|
23269
23370
|
onResizeColumns: types$1.function().optional(),
|
|
23270
|
-
columnWeights: types$1.array().optional()
|
|
23371
|
+
columnWeights: types$1.array().optional(),
|
|
23372
|
+
size: types$1.object()
|
|
23271
23373
|
});
|
|
23272
|
-
canvasRef = signal(
|
|
23273
|
-
containerRef = signal(
|
|
23374
|
+
canvasRef = signal.ref(HTMLCanvasElement);
|
|
23375
|
+
containerRef = signal.ref();
|
|
23274
23376
|
store;
|
|
23275
23377
|
dndState = proxy({ col: void 0 });
|
|
23276
23378
|
onMouseWheel;
|
|
@@ -23282,8 +23384,8 @@ var StandaloneViewport = class extends Component {
|
|
|
23282
23384
|
useChildStoreProvider([
|
|
23283
23385
|
ViewportsStore,
|
|
23284
23386
|
HoveredIconStore,
|
|
23285
|
-
HoveredTableStore,
|
|
23286
23387
|
ClickableCellsStore,
|
|
23388
|
+
CellHoverOverlayStore,
|
|
23287
23389
|
DelayedHoveredCellStore,
|
|
23288
23390
|
CellPopoverStore
|
|
23289
23391
|
]);
|
|
@@ -23292,18 +23394,10 @@ var StandaloneViewport = class extends Component {
|
|
|
23292
23394
|
this.zoomStore = useStore(ZoomStore);
|
|
23293
23395
|
this.cellPopoverStore = useStore(CellPopoverStore);
|
|
23294
23396
|
this.rendererStore = useLocalStore(RendererStore, ["Background", "Chart"]);
|
|
23295
|
-
const resizeObserver = new ResizeObserver(() => {
|
|
23296
|
-
this.store.setContainerSize(this.containerWidth, this.containerHeight);
|
|
23297
|
-
});
|
|
23298
|
-
useEffect(() => {
|
|
23299
|
-
const el = this.containerRef();
|
|
23300
|
-
if (el) resizeObserver.observe(el);
|
|
23301
|
-
return () => resizeObserver.disconnect();
|
|
23302
|
-
});
|
|
23303
23397
|
useEffect(() => {
|
|
23304
23398
|
this.store.setRange(this.props.range);
|
|
23305
23399
|
if (this.dndState.col === void 0) this.store.setCustomColWeights(this.props.columnWeights);
|
|
23306
|
-
this.store.setContainerSize(this.
|
|
23400
|
+
this.store.setContainerSize(this.contentWidth, this.props.size.height);
|
|
23307
23401
|
});
|
|
23308
23402
|
useGridDrawing({
|
|
23309
23403
|
canvasRef: this.canvasRef,
|
|
@@ -23322,14 +23416,11 @@ var StandaloneViewport = class extends Component {
|
|
|
23322
23416
|
}
|
|
23323
23417
|
});
|
|
23324
23418
|
}
|
|
23325
|
-
get
|
|
23326
|
-
return
|
|
23327
|
-
}
|
|
23328
|
-
get containerHeight() {
|
|
23329
|
-
return Math.floor(getElBoundingRect(this.containerRef()).height / this.zoomStore.zoomLevel);
|
|
23419
|
+
get contentWidth() {
|
|
23420
|
+
return this.hasVerticalScrollBar ? this.props.size.width - this.zoomStore.scrollBarWidth : this.props.size.width;
|
|
23330
23421
|
}
|
|
23331
23422
|
get hasVerticalScrollBar() {
|
|
23332
|
-
return this.
|
|
23423
|
+
return this.viewStore.mainViewportCoordinates.y + this.viewStore.mainViewportRect.height > this.props.size.height;
|
|
23333
23424
|
}
|
|
23334
23425
|
get scrollBarContainerStyle() {
|
|
23335
23426
|
return cssPropertiesToCss({ width: `${this.zoomStore.scrollBarWidth}px` });
|
|
@@ -23381,7 +23472,7 @@ var StandaloneViewport = class extends Component {
|
|
|
23381
23472
|
};
|
|
23382
23473
|
const onMouseMove = (ev) => {
|
|
23383
23474
|
deltaX = withZoom(this.env, ev).clientX - initialX;
|
|
23384
|
-
const weightDelta = deltaX / this.
|
|
23475
|
+
const weightDelta = deltaX / this.props.size.width * totalWeight;
|
|
23385
23476
|
this.store.resizeColumn(resizer.col, weightDelta, startingColWeights);
|
|
23386
23477
|
};
|
|
23387
23478
|
startDnd(onMouseMove, onMouseUp);
|
|
@@ -23411,8 +23502,8 @@ var Select = class extends Component {
|
|
|
23411
23502
|
name: types$1.string().optional(),
|
|
23412
23503
|
title: types$1.string().optional()
|
|
23413
23504
|
});
|
|
23414
|
-
selectRef = signal(
|
|
23415
|
-
dropdownRef = signal(
|
|
23505
|
+
selectRef = signal.ref();
|
|
23506
|
+
dropdownRef = signal.ref();
|
|
23416
23507
|
state = proxy({
|
|
23417
23508
|
isPopoverOpen: false,
|
|
23418
23509
|
hoveredValue: void 0
|
|
@@ -23522,7 +23613,7 @@ var InfoPopover = class extends Component {
|
|
|
23522
23613
|
setup() {
|
|
23523
23614
|
useExternalListener(window, "click", this.onExternalClick, { capture: true });
|
|
23524
23615
|
}
|
|
23525
|
-
infoRef = signal(
|
|
23616
|
+
infoRef = signal.ref();
|
|
23526
23617
|
get popoverProps() {
|
|
23527
23618
|
return {
|
|
23528
23619
|
anchorRect: this.props.anchorRect,
|
|
@@ -23667,8 +23758,8 @@ var CarouselFigure = class extends Component {
|
|
|
23667
23758
|
isFullScreen: types$1.boolean().optional(),
|
|
23668
23759
|
openContextMenu: types$1.function().optional()
|
|
23669
23760
|
});
|
|
23670
|
-
carouselTabsRef = signal(
|
|
23671
|
-
carouselTabsDropdownRef = signal(
|
|
23761
|
+
carouselTabsRef = signal.ref();
|
|
23762
|
+
carouselTabsDropdownRef = signal.ref();
|
|
23672
23763
|
menuState = proxy({
|
|
23673
23764
|
isOpen: false,
|
|
23674
23765
|
anchorRect: null,
|
|
@@ -23736,6 +23827,19 @@ var CarouselFigure = class extends Component {
|
|
|
23736
23827
|
} else cssProperties["background-color"] = backgroundColor;
|
|
23737
23828
|
return cssPropertiesToCss(cssProperties);
|
|
23738
23829
|
}
|
|
23830
|
+
get contentStyle() {
|
|
23831
|
+
return cssPropertiesToCss(this.rectToCss(this.carouselLayout.contentRect));
|
|
23832
|
+
}
|
|
23833
|
+
get headerStyle() {
|
|
23834
|
+
return cssPropertiesToCss({
|
|
23835
|
+
...this.rectToCss(this.carouselLayout.headerRect),
|
|
23836
|
+
"line-height": String(this.carouselLayout.headerLineHeight)
|
|
23837
|
+
});
|
|
23838
|
+
}
|
|
23839
|
+
get separatorStyle() {
|
|
23840
|
+
const separatorRect = this.carouselLayout.separatorRect;
|
|
23841
|
+
return separatorRect ? cssPropertiesToCss(this.rectToCss(separatorRect)) : "";
|
|
23842
|
+
}
|
|
23739
23843
|
get title() {
|
|
23740
23844
|
return this.env.model.getters.dynamicTranslate(this.carousel.title?.text ?? "");
|
|
23741
23845
|
}
|
|
@@ -23745,6 +23849,14 @@ var CarouselFigure = class extends Component {
|
|
|
23745
23849
|
...this.carousel.title
|
|
23746
23850
|
})));
|
|
23747
23851
|
}
|
|
23852
|
+
rectToCss(rect) {
|
|
23853
|
+
return {
|
|
23854
|
+
top: `${rect.y}px`,
|
|
23855
|
+
left: `${rect.x}px`,
|
|
23856
|
+
width: `${rect.width}px`,
|
|
23857
|
+
height: `${rect.height}px`
|
|
23858
|
+
};
|
|
23859
|
+
}
|
|
23748
23860
|
updateTabsVisibility() {
|
|
23749
23861
|
const tabsContainerEl = this.carouselTabsRef();
|
|
23750
23862
|
const dropDownEl = this.carouselTabsDropdownRef();
|
|
@@ -23836,6 +23948,13 @@ var CarouselFigure = class extends Component {
|
|
|
23836
23948
|
get canResizeDataViewColumns() {
|
|
23837
23949
|
return !this.env.model.getters.isReadonly() && !this.env.model.getters.isSheetLocked(this.env.model.getters.getActiveSheetId());
|
|
23838
23950
|
}
|
|
23951
|
+
get carouselLayout() {
|
|
23952
|
+
return getCarouselLayout({
|
|
23953
|
+
...this.props.figureUI,
|
|
23954
|
+
x: 0,
|
|
23955
|
+
y: 0
|
|
23956
|
+
}, this.carousel, this.selectedCarouselItem);
|
|
23957
|
+
}
|
|
23839
23958
|
};
|
|
23840
23959
|
|
|
23841
23960
|
//#endregion
|
|
@@ -23876,7 +23995,7 @@ var ImageFigure = class extends Component {
|
|
|
23876
23995
|
figureUI: types$1.FigureUI(),
|
|
23877
23996
|
openContextMenu: types$1.function()
|
|
23878
23997
|
});
|
|
23879
|
-
menuButtonRef = signal(
|
|
23998
|
+
menuButtonRef = signal.ref();
|
|
23880
23999
|
showMenu(ev) {
|
|
23881
24000
|
if (!this.env.model.getters.getSelectedFigureIds().includes(this.props.figureUI.id)) this.env.model.dispatch("SELECT_FIGURE", {
|
|
23882
24001
|
figureId: this.props.figureUI.id,
|
|
@@ -23904,7 +24023,8 @@ figureRegistry.add("chart", {
|
|
|
23904
24023
|
menuBuilder: getChartMenuActions,
|
|
23905
24024
|
borderWidth: (getters) => getters.isDashboard() ? 0 : 1,
|
|
23906
24025
|
hasShadow: (getters) => getters.isDashboard(),
|
|
23907
|
-
isRounded: (getters) => getters.isDashboard()
|
|
24026
|
+
isRounded: (getters) => getters.isDashboard(),
|
|
24027
|
+
isThemeDependant: true
|
|
23908
24028
|
});
|
|
23909
24029
|
figureRegistry.add("image", {
|
|
23910
24030
|
Component: ImageFigure,
|
|
@@ -23913,14 +24033,16 @@ figureRegistry.add("image", {
|
|
|
23913
24033
|
borderWidth: () => 0,
|
|
23914
24034
|
hasShadow: () => false,
|
|
23915
24035
|
isRounded: () => false,
|
|
23916
|
-
menuBuilder: getImageMenuActions
|
|
24036
|
+
menuBuilder: getImageMenuActions,
|
|
24037
|
+
isThemeDependant: false
|
|
23917
24038
|
});
|
|
23918
24039
|
figureRegistry.add("carousel", {
|
|
23919
24040
|
Component: CarouselFigure,
|
|
23920
24041
|
menuBuilder: getCarouselMenuActions,
|
|
23921
24042
|
borderWidth: (getters) => getters.isDashboard() ? 0 : 1,
|
|
23922
24043
|
hasShadow: (getters) => getters.isDashboard(),
|
|
23923
|
-
isRounded: (getters) => getters.isDashboard()
|
|
24044
|
+
isRounded: (getters) => getters.isDashboard(),
|
|
24045
|
+
isThemeDependant: true
|
|
23924
24046
|
});
|
|
23925
24047
|
|
|
23926
24048
|
//#endregion
|
|
@@ -23942,10 +24064,13 @@ var FigureComponent = class extends Component {
|
|
|
23942
24064
|
anchorRect: null,
|
|
23943
24065
|
menuItems: []
|
|
23944
24066
|
});
|
|
23945
|
-
figureRef = signal(
|
|
24067
|
+
figureRef = signal.ref();
|
|
23946
24068
|
get isSelected() {
|
|
23947
24069
|
return !this.env.model.getters.isDashboard() && this.env.model.getters.getSelectedFigureIds().includes(this.props.figureUI.id);
|
|
23948
24070
|
}
|
|
24071
|
+
get isThemeDependant() {
|
|
24072
|
+
return figureRegistry.get(this.props.figureUI.tag).isThemeDependant;
|
|
24073
|
+
}
|
|
23949
24074
|
get figureRegistry() {
|
|
23950
24075
|
return figureRegistry;
|
|
23951
24076
|
}
|
|
@@ -29144,6 +29269,20 @@ migrationStepRegistry.add("0.1", { migrate(data) {
|
|
|
29144
29269
|
figure.data.chartDefinitions[chartId] = upgrade(definition);
|
|
29145
29270
|
}
|
|
29146
29271
|
return data;
|
|
29272
|
+
} }).add("19.5.1", { migrate(data) {
|
|
29273
|
+
function upgrade(definition) {
|
|
29274
|
+
if (definition.type !== "scorecard") return definition;
|
|
29275
|
+
definition = { ...definition };
|
|
29276
|
+
if (definition.keyValue) definition.keyValue = `=${definition.keyValue}`;
|
|
29277
|
+
if (definition.baseline) definition.baseline = `=${definition.baseline}`;
|
|
29278
|
+
return definition;
|
|
29279
|
+
}
|
|
29280
|
+
for (const sheet of data.sheets || []) for (const figure of sheet.figures || []) if (figure.tag === "chart") figure.data = upgrade(figure.data);
|
|
29281
|
+
else if (figure.tag === "carousel") for (const chartId in figure.data.chartDefinitions) {
|
|
29282
|
+
const definition = figure.data.chartDefinitions[chartId];
|
|
29283
|
+
figure.data.chartDefinitions[chartId] = upgrade(definition);
|
|
29284
|
+
}
|
|
29285
|
+
return data;
|
|
29147
29286
|
} });
|
|
29148
29287
|
function fixOverlappingFilters(data) {
|
|
29149
29288
|
for (const sheet of data.sheets || []) {
|
|
@@ -29544,6 +29683,35 @@ async function interactivePasteFromOS(env, target, parsedClipboardContent, paste
|
|
|
29544
29683
|
}));
|
|
29545
29684
|
}
|
|
29546
29685
|
|
|
29686
|
+
//#endregion
|
|
29687
|
+
//#region src/helpers/figures/charts/chart_data_source_helpers.ts
|
|
29688
|
+
function getUnboundRange(getters, zone) {
|
|
29689
|
+
return zoneToXc(getters.getUnboundedZone(getters.getActiveSheetId(), zone));
|
|
29690
|
+
}
|
|
29691
|
+
function isDatasetTitled(getters, zone) {
|
|
29692
|
+
const sheetId = getters.getActiveSheetId();
|
|
29693
|
+
const cell = getters.getEvaluatedCell({
|
|
29694
|
+
sheetId,
|
|
29695
|
+
col: zone.left,
|
|
29696
|
+
row: zone.top
|
|
29697
|
+
});
|
|
29698
|
+
return !["number", "empty"].includes(cell.type);
|
|
29699
|
+
}
|
|
29700
|
+
function dataset(zone, getters, id = "0") {
|
|
29701
|
+
return {
|
|
29702
|
+
dataRange: getUnboundRange(getters, zone),
|
|
29703
|
+
dataSetId: id
|
|
29704
|
+
};
|
|
29705
|
+
}
|
|
29706
|
+
function rangeSource(dataSets, dataSetsHaveTitle, labelRange) {
|
|
29707
|
+
return {
|
|
29708
|
+
type: "range",
|
|
29709
|
+
dataSets,
|
|
29710
|
+
dataSetsHaveTitle,
|
|
29711
|
+
labelRange
|
|
29712
|
+
};
|
|
29713
|
+
}
|
|
29714
|
+
|
|
29547
29715
|
//#endregion
|
|
29548
29716
|
//#region src/helpers/figures/charts/smart_chart_engine.ts
|
|
29549
29717
|
const DEFAULT_BAR_CHART_CONFIG = {
|
|
@@ -29574,9 +29742,6 @@ const DEFAULT_LINE_CHART_CONFIG = {
|
|
|
29574
29742
|
labelsAsText: false,
|
|
29575
29743
|
humanize: true
|
|
29576
29744
|
};
|
|
29577
|
-
function getUnboundRange(getters, zone) {
|
|
29578
|
-
return zoneToXc(getters.getUnboundedZone(getters.getActiveSheetId(), zone));
|
|
29579
|
-
}
|
|
29580
29745
|
function detectColumnType(cells) {
|
|
29581
29746
|
if (!cells.length) return "empty";
|
|
29582
29747
|
const counts = {
|
|
@@ -29623,14 +29788,6 @@ function getCellStats(getters, zone) {
|
|
|
29623
29788
|
totalCount: values.length
|
|
29624
29789
|
};
|
|
29625
29790
|
}
|
|
29626
|
-
function isDatasetTitled(getters, column) {
|
|
29627
|
-
const titleCell = getters.getEvaluatedCell({
|
|
29628
|
-
sheetId: getters.getActiveSheetId(),
|
|
29629
|
-
col: column.zone.left,
|
|
29630
|
-
row: column.zone.top
|
|
29631
|
-
});
|
|
29632
|
-
return !["number", "empty"].includes(titleCell.type);
|
|
29633
|
-
}
|
|
29634
29791
|
/**
|
|
29635
29792
|
* Builds a chart definition for a single column selection. The logic to detect the chart type is as follows:
|
|
29636
29793
|
* - If the column contains a single cell, create a scorecard.
|
|
@@ -29642,8 +29799,7 @@ function isDatasetTitled(getters, column) {
|
|
|
29642
29799
|
function buildSingleColumnChart(column, getters) {
|
|
29643
29800
|
const { type, zone } = column;
|
|
29644
29801
|
const sheetId = getters.getActiveSheetId();
|
|
29645
|
-
const dataSetsHaveTitle = isDatasetTitled(getters, column);
|
|
29646
|
-
const dataRange = getUnboundRange(getters, zone);
|
|
29802
|
+
const dataSetsHaveTitle = isDatasetTitled(getters, column.zone);
|
|
29647
29803
|
const titleCell = getters.getEvaluatedCell({
|
|
29648
29804
|
sheetId,
|
|
29649
29805
|
col: zone.left,
|
|
@@ -29654,14 +29810,7 @@ function buildSingleColumnChart(column, getters) {
|
|
|
29654
29810
|
case "percentage": return {
|
|
29655
29811
|
type: "pie",
|
|
29656
29812
|
title: dataSetsHaveTitle ? { text: String(titleCell.value) } : {},
|
|
29657
|
-
dataSource:
|
|
29658
|
-
type: "range",
|
|
29659
|
-
dataSets: [{
|
|
29660
|
-
dataRange,
|
|
29661
|
-
dataSetId: "0"
|
|
29662
|
-
}],
|
|
29663
|
-
dataSetsHaveTitle
|
|
29664
|
-
},
|
|
29813
|
+
dataSource: rangeSource([dataset(zone, getters)], dataSetsHaveTitle),
|
|
29665
29814
|
dataSetStyles: {},
|
|
29666
29815
|
legendPosition: "none"
|
|
29667
29816
|
};
|
|
@@ -29672,45 +29821,28 @@ function buildSingleColumnChart(column, getters) {
|
|
|
29672
29821
|
return {
|
|
29673
29822
|
type: "pie",
|
|
29674
29823
|
title: hasUniqueTitle ? { text: String(titleCell.value) } : {},
|
|
29675
|
-
dataSource:
|
|
29676
|
-
type: "range",
|
|
29677
|
-
dataSets: [{
|
|
29678
|
-
dataRange,
|
|
29679
|
-
dataSetId: "0"
|
|
29680
|
-
}],
|
|
29681
|
-
labelRange: dataRange,
|
|
29682
|
-
dataSetsHaveTitle: hasUniqueTitle
|
|
29683
|
-
},
|
|
29824
|
+
dataSource: rangeSource([dataset(zone, getters)], hasUniqueTitle, getUnboundRange(getters, zone)),
|
|
29684
29825
|
dataSetStyles: {},
|
|
29685
29826
|
aggregated: true,
|
|
29686
29827
|
legendPosition: "top"
|
|
29687
29828
|
};
|
|
29688
|
-
case "date":
|
|
29689
|
-
|
|
29690
|
-
|
|
29691
|
-
|
|
29692
|
-
|
|
29693
|
-
|
|
29694
|
-
|
|
29695
|
-
|
|
29696
|
-
|
|
29697
|
-
|
|
29698
|
-
|
|
29699
|
-
|
|
29700
|
-
verticalGroupBy: "month_number"
|
|
29701
|
-
};
|
|
29829
|
+
case "date": {
|
|
29830
|
+
const dataRange = getUnboundRange(getters, zone);
|
|
29831
|
+
return {
|
|
29832
|
+
type: "calendar",
|
|
29833
|
+
title: dataSetsHaveTitle ? { text: String(titleCell.value) } : {},
|
|
29834
|
+
dataSource: rangeSource([], dataSetsHaveTitle, dataRange),
|
|
29835
|
+
dataSetStyles: {},
|
|
29836
|
+
legendPosition: "left",
|
|
29837
|
+
horizontalGroupBy: "day_of_week",
|
|
29838
|
+
verticalGroupBy: "month_number"
|
|
29839
|
+
};
|
|
29840
|
+
}
|
|
29702
29841
|
}
|
|
29703
29842
|
return {
|
|
29704
29843
|
...DEFAULT_BAR_CHART_CONFIG,
|
|
29705
29844
|
title: dataSetsHaveTitle ? { text: String(titleCell.value) } : {},
|
|
29706
|
-
dataSource:
|
|
29707
|
-
type: "range",
|
|
29708
|
-
dataSets: [{
|
|
29709
|
-
dataRange,
|
|
29710
|
-
dataSetId: "0"
|
|
29711
|
-
}],
|
|
29712
|
-
dataSetsHaveTitle
|
|
29713
|
-
},
|
|
29845
|
+
dataSource: rangeSource([dataset(zone, getters)], dataSetsHaveTitle),
|
|
29714
29846
|
dataSetStyles: {}
|
|
29715
29847
|
};
|
|
29716
29848
|
}
|
|
@@ -29728,15 +29860,7 @@ function buildTwoColumnChart(columns, getters) {
|
|
|
29728
29860
|
if (columns[1].type === "percentage") return {
|
|
29729
29861
|
type: "pie",
|
|
29730
29862
|
title: {},
|
|
29731
|
-
dataSource:
|
|
29732
|
-
type: "range",
|
|
29733
|
-
dataSets: [{
|
|
29734
|
-
dataRange: getUnboundRange(getters, columns[1].zone),
|
|
29735
|
-
dataSetId: "0"
|
|
29736
|
-
}],
|
|
29737
|
-
labelRange: getUnboundRange(getters, columns[0].zone),
|
|
29738
|
-
dataSetsHaveTitle: isDatasetTitled(getters, columns[1])
|
|
29739
|
-
},
|
|
29863
|
+
dataSource: rangeSource([dataset(columns[1].zone, getters)], isDatasetTitled(getters, columns[1].zone), getUnboundRange(getters, columns[0].zone)),
|
|
29740
29864
|
dataSetStyles: {},
|
|
29741
29865
|
aggregated: true,
|
|
29742
29866
|
legendPosition: "none"
|
|
@@ -29744,15 +29868,7 @@ function buildTwoColumnChart(columns, getters) {
|
|
|
29744
29868
|
if (columns[0].type === "number" && columns[1].type === "number") return {
|
|
29745
29869
|
type: "scatter",
|
|
29746
29870
|
title: {},
|
|
29747
|
-
dataSource:
|
|
29748
|
-
type: "range",
|
|
29749
|
-
dataSets: [{
|
|
29750
|
-
dataRange: getUnboundRange(getters, columns[1].zone),
|
|
29751
|
-
dataSetId: "0"
|
|
29752
|
-
}],
|
|
29753
|
-
labelRange: getUnboundRange(getters, columns[0].zone),
|
|
29754
|
-
dataSetsHaveTitle: isDatasetTitled(getters, columns[1])
|
|
29755
|
-
},
|
|
29871
|
+
dataSource: rangeSource([dataset(columns[1].zone, getters)], isDatasetTitled(getters, columns[1].zone), getUnboundRange(getters, columns[0].zone)),
|
|
29756
29872
|
dataSetStyles: {},
|
|
29757
29873
|
labelsAsText: false,
|
|
29758
29874
|
legendPosition: "none"
|
|
@@ -29760,49 +29876,25 @@ function buildTwoColumnChart(columns, getters) {
|
|
|
29760
29876
|
if (columns[0].type === "date" && columns[1].type === "number") return {
|
|
29761
29877
|
...DEFAULT_LINE_CHART_CONFIG,
|
|
29762
29878
|
type: "line",
|
|
29763
|
-
dataSource:
|
|
29764
|
-
type: "range",
|
|
29765
|
-
dataSets: [{
|
|
29766
|
-
dataRange: getUnboundRange(getters, columns[1].zone),
|
|
29767
|
-
dataSetId: "0"
|
|
29768
|
-
}],
|
|
29769
|
-
labelRange: getUnboundRange(getters, columns[0].zone),
|
|
29770
|
-
dataSetsHaveTitle: isDatasetTitled(getters, columns[0])
|
|
29771
|
-
},
|
|
29879
|
+
dataSource: rangeSource([dataset(columns[1].zone, getters)], isDatasetTitled(getters, columns[0].zone), getUnboundRange(getters, columns[0].zone)),
|
|
29772
29880
|
dataSetStyles: {}
|
|
29773
29881
|
};
|
|
29774
29882
|
if (columns[0].type === "text" && columns[1].type === "number") {
|
|
29775
29883
|
const textColumn = columns[0];
|
|
29776
29884
|
const numberColumn = columns[1];
|
|
29777
29885
|
const { uniqueCount, totalCount } = getCellStats(getters, textColumn.zone);
|
|
29778
|
-
const dataSetsHaveTitle = isDatasetTitled(getters, numberColumn);
|
|
29886
|
+
const dataSetsHaveTitle = isDatasetTitled(getters, numberColumn.zone);
|
|
29779
29887
|
if (uniqueCount !== totalCount) return {
|
|
29780
29888
|
type: "treemap",
|
|
29781
29889
|
title: {},
|
|
29782
|
-
dataSource:
|
|
29783
|
-
type: "range",
|
|
29784
|
-
dataSets: [{
|
|
29785
|
-
dataRange: getUnboundRange(getters, textColumn.zone),
|
|
29786
|
-
dataSetId: "0"
|
|
29787
|
-
}],
|
|
29788
|
-
labelRange: getUnboundRange(getters, numberColumn.zone),
|
|
29789
|
-
dataSetsHaveTitle
|
|
29790
|
-
},
|
|
29890
|
+
dataSource: rangeSource([dataset(textColumn.zone, getters)], dataSetsHaveTitle, getUnboundRange(getters, numberColumn.zone)),
|
|
29791
29891
|
dataSetStyles: {},
|
|
29792
29892
|
legendPosition: "none"
|
|
29793
29893
|
};
|
|
29794
29894
|
}
|
|
29795
29895
|
return {
|
|
29796
29896
|
...DEFAULT_BAR_CHART_CONFIG,
|
|
29797
|
-
dataSource:
|
|
29798
|
-
type: "range",
|
|
29799
|
-
dataSets: [{
|
|
29800
|
-
dataRange: getUnboundRange(getters, columns[1].zone),
|
|
29801
|
-
dataSetId: "0"
|
|
29802
|
-
}],
|
|
29803
|
-
labelRange: getUnboundRange(getters, columns[0].zone),
|
|
29804
|
-
dataSetsHaveTitle: isDatasetTitled(getters, columns[1])
|
|
29805
|
-
},
|
|
29897
|
+
dataSource: rangeSource([dataset(columns[1].zone, getters)], isDatasetTitled(getters, columns[1].zone), getUnboundRange(getters, columns[0].zone)),
|
|
29806
29898
|
dataSetStyles: {}
|
|
29807
29899
|
};
|
|
29808
29900
|
}
|
|
@@ -29816,42 +29908,26 @@ function buildTwoColumnChart(columns, getters) {
|
|
|
29816
29908
|
*/
|
|
29817
29909
|
function buildMultiColumnChart(columns, getters) {
|
|
29818
29910
|
if (columns.length < 3) throw new Error("buildMultiColumnChart expects at least three columns");
|
|
29819
|
-
const dataSetsHaveTitle = columns.some((col) => col.type !== "text" && isDatasetTitled(getters, col));
|
|
29911
|
+
const dataSetsHaveTitle = columns.some((col) => col.type !== "text" && isDatasetTitled(getters, col.zone));
|
|
29820
29912
|
const lastColumn = columns[columns.length - 1];
|
|
29821
29913
|
const columnsExceptLast = columns.slice(0, columns.length - 1);
|
|
29822
29914
|
if ((lastColumn.type === "percentage" || lastColumn.type === "number") && columnsExceptLast.every((col) => col.type === "text")) {
|
|
29823
|
-
const dataSets = columnsExceptLast.map(({ zone }, i) => (
|
|
29824
|
-
dataRange: getUnboundRange(getters, zone),
|
|
29825
|
-
dataSetId: i.toString()
|
|
29826
|
-
}));
|
|
29915
|
+
const dataSets = columnsExceptLast.map(({ zone }, i) => dataset(zone, getters, String(i)));
|
|
29827
29916
|
return {
|
|
29828
29917
|
type: columnsExceptLast.length >= 3 ? "sunburst" : "treemap",
|
|
29829
29918
|
title: {},
|
|
29830
|
-
dataSource:
|
|
29831
|
-
type: "range",
|
|
29832
|
-
dataSets,
|
|
29833
|
-
dataSetsHaveTitle,
|
|
29834
|
-
labelRange: getUnboundRange(getters, lastColumn.zone)
|
|
29835
|
-
},
|
|
29919
|
+
dataSource: rangeSource(dataSets, dataSetsHaveTitle, getUnboundRange(getters, lastColumn.zone)),
|
|
29836
29920
|
dataSetStyles: {},
|
|
29837
29921
|
legendPosition: "none"
|
|
29838
29922
|
};
|
|
29839
29923
|
}
|
|
29840
29924
|
const firstColumn = columns[0];
|
|
29841
29925
|
const columnsExceptFirst = columns.slice(1);
|
|
29842
|
-
const rangesOfColumnsExceptFirst = columnsExceptFirst.map(({ zone }, i) => (
|
|
29843
|
-
dataRange: getUnboundRange(getters, zone),
|
|
29844
|
-
dataSetId: i.toString()
|
|
29845
|
-
}));
|
|
29926
|
+
const rangesOfColumnsExceptFirst = columnsExceptFirst.map(({ zone }, i) => dataset(zone, getters, String(i)));
|
|
29846
29927
|
if (columnsExceptFirst.every((col) => col.type === "percentage")) return {
|
|
29847
29928
|
type: "pie",
|
|
29848
29929
|
title: {},
|
|
29849
|
-
dataSource:
|
|
29850
|
-
type: "range",
|
|
29851
|
-
dataSets: rangesOfColumnsExceptFirst,
|
|
29852
|
-
labelRange: getUnboundRange(getters, firstColumn.zone),
|
|
29853
|
-
dataSetsHaveTitle
|
|
29854
|
-
},
|
|
29930
|
+
dataSource: rangeSource(rangesOfColumnsExceptFirst, dataSetsHaveTitle, getUnboundRange(getters, firstColumn.zone)),
|
|
29855
29931
|
dataSetStyles: {},
|
|
29856
29932
|
aggregated: false,
|
|
29857
29933
|
legendPosition: "top"
|
|
@@ -29859,23 +29935,13 @@ function buildMultiColumnChart(columns, getters) {
|
|
|
29859
29935
|
if (firstColumn.type === "date" && columnsExceptFirst.every((col) => col.type === "number")) return {
|
|
29860
29936
|
...DEFAULT_LINE_CHART_CONFIG,
|
|
29861
29937
|
type: "line",
|
|
29862
|
-
dataSource:
|
|
29863
|
-
type: "range",
|
|
29864
|
-
dataSets: rangesOfColumnsExceptFirst,
|
|
29865
|
-
labelRange: getUnboundRange(getters, firstColumn.zone),
|
|
29866
|
-
dataSetsHaveTitle
|
|
29867
|
-
},
|
|
29938
|
+
dataSource: rangeSource(rangesOfColumnsExceptFirst, dataSetsHaveTitle, getUnboundRange(getters, firstColumn.zone)),
|
|
29868
29939
|
dataSetStyles: {},
|
|
29869
29940
|
legendPosition: "top"
|
|
29870
29941
|
};
|
|
29871
29942
|
return {
|
|
29872
29943
|
...DEFAULT_BAR_CHART_CONFIG,
|
|
29873
|
-
dataSource:
|
|
29874
|
-
type: "range",
|
|
29875
|
-
dataSets: rangesOfColumnsExceptFirst,
|
|
29876
|
-
labelRange: getUnboundRange(getters, firstColumn.zone),
|
|
29877
|
-
dataSetsHaveTitle
|
|
29878
|
-
},
|
|
29944
|
+
dataSource: rangeSource(rangesOfColumnsExceptFirst, dataSetsHaveTitle, getUnboundRange(getters, firstColumn.zone)),
|
|
29879
29945
|
dataSetStyles: {},
|
|
29880
29946
|
legendPosition: "top"
|
|
29881
29947
|
};
|
|
@@ -29889,7 +29955,7 @@ function buildScorecard(zone, getters) {
|
|
|
29889
29955
|
return {
|
|
29890
29956
|
type: "scorecard",
|
|
29891
29957
|
title: {},
|
|
29892
|
-
keyValue: getUnboundRange(getters, zone)
|
|
29958
|
+
keyValue: `=${getUnboundRange(getters, zone)}`,
|
|
29893
29959
|
background: cell?.style?.fillColor,
|
|
29894
29960
|
baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
|
|
29895
29961
|
baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP,
|
|
@@ -29902,17 +29968,10 @@ function buildScorecard(zone, getters) {
|
|
|
29902
29968
|
function getSmartChartDefinition(zones, getters) {
|
|
29903
29969
|
const columns = categorizeColumns(zones, getters);
|
|
29904
29970
|
if (columns.length === 0 || columns.every((col) => col.type === "empty")) {
|
|
29905
|
-
const dataSets = columns.map(({ zone }, i) => (
|
|
29906
|
-
dataRange: getUnboundRange(getters, zone),
|
|
29907
|
-
dataSetId: i.toString()
|
|
29908
|
-
}));
|
|
29971
|
+
const dataSets = columns.map(({ zone }, i) => dataset(zone, getters, String(i)));
|
|
29909
29972
|
return {
|
|
29910
29973
|
...DEFAULT_BAR_CHART_CONFIG,
|
|
29911
|
-
dataSource:
|
|
29912
|
-
type: "range",
|
|
29913
|
-
dataSets,
|
|
29914
|
-
dataSetsHaveTitle: false
|
|
29915
|
-
}
|
|
29974
|
+
dataSource: rangeSource(dataSets, false)
|
|
29916
29975
|
};
|
|
29917
29976
|
}
|
|
29918
29977
|
const nonEmptyColumns = columns.filter((col) => col.type !== "empty");
|
|
@@ -31600,7 +31659,7 @@ var SelectionInput = class extends Component {
|
|
|
31600
31659
|
dragAndDrop = useDragAndDropListItems();
|
|
31601
31660
|
focusedInputRef = signal.ref(HTMLInputElement);
|
|
31602
31661
|
unfocusedInputRef = signal.ref(HTMLInputElement);
|
|
31603
|
-
selectionRef = signal(
|
|
31662
|
+
selectionRef = signal.ref();
|
|
31604
31663
|
DOMFocusableElementStore;
|
|
31605
31664
|
store;
|
|
31606
31665
|
get ranges() {
|
|
@@ -32107,7 +32166,7 @@ var ColorPickerWidget = class extends Component {
|
|
|
32107
32166
|
dropdownMaxHeight: types$1.Pixel().optional(),
|
|
32108
32167
|
class: types$1.string().optional()
|
|
32109
32168
|
});
|
|
32110
|
-
colorPickerButtonRef = signal(
|
|
32169
|
+
colorPickerButtonRef = signal.ref();
|
|
32111
32170
|
get iconStyle() {
|
|
32112
32171
|
return this.props.currentColor ? `border-color: ${this.props.currentColor}` : "border-bottom-style: hidden";
|
|
32113
32172
|
}
|
|
@@ -32135,8 +32194,8 @@ var NumberEditor = class extends Component {
|
|
|
32135
32194
|
});
|
|
32136
32195
|
dropdown = proxy({ isOpen: false });
|
|
32137
32196
|
inputRef = signal.ref(HTMLInputElement);
|
|
32138
|
-
rootEditorRef = signal(
|
|
32139
|
-
valueListRef = signal(
|
|
32197
|
+
rootEditorRef = signal.ref();
|
|
32198
|
+
valueListRef = signal.ref();
|
|
32140
32199
|
DOMFocusableElementStore;
|
|
32141
32200
|
setup() {
|
|
32142
32201
|
this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
|
|
@@ -32442,7 +32501,7 @@ var ChartTypePickerPopover = class extends Component {
|
|
|
32442
32501
|
});
|
|
32443
32502
|
categories = chartCategories;
|
|
32444
32503
|
chartTypeByCategories = {};
|
|
32445
|
-
popoverRef = signal(
|
|
32504
|
+
popoverRef = signal.ref();
|
|
32446
32505
|
setup() {
|
|
32447
32506
|
useListener(window, "pointerdown", this.onExternalClick.bind(this), { capture: true });
|
|
32448
32507
|
for (const subtypeProperties of chartSubtypeRegistry.getAll()) {
|
|
@@ -32467,7 +32526,7 @@ var CogWheelMenu = class extends Component {
|
|
|
32467
32526
|
static template = "o-spreadsheet-CogWheelMenu";
|
|
32468
32527
|
static components = { MenuPopover };
|
|
32469
32528
|
props = useProps({ items: types$1.array(types$1.ActionSpec()) });
|
|
32470
|
-
buttonRef = signal(
|
|
32529
|
+
buttonRef = signal.ref();
|
|
32471
32530
|
menuState = proxy({
|
|
32472
32531
|
isOpen: false,
|
|
32473
32532
|
anchorRect: null,
|
|
@@ -32511,8 +32570,8 @@ var CarouselPanel = class extends Component {
|
|
|
32511
32570
|
});
|
|
32512
32571
|
DEFAULT_CAROUSEL_TITLE_STYLE = DEFAULT_CAROUSEL_TITLE_STYLE;
|
|
32513
32572
|
dragAndDrop = useDragAndDropListItems();
|
|
32514
|
-
previewListRef = signal(
|
|
32515
|
-
addChartButton = signal(
|
|
32573
|
+
previewListRef = signal.ref();
|
|
32574
|
+
addChartButton = signal.ref(HTMLButtonElement);
|
|
32516
32575
|
state = proxy({
|
|
32517
32576
|
popoverProps: void 0,
|
|
32518
32577
|
currentRange: void 0
|
|
@@ -32956,7 +33015,7 @@ var BarConfigPanel = class extends GenericChartConfigPanel {
|
|
|
32956
33015
|
var Collapse = class extends Component {
|
|
32957
33016
|
static template = "o-spreadsheet-Collapse";
|
|
32958
33017
|
props = useProps({ isCollapsed: types$1.boolean() });
|
|
32959
|
-
contentRef = signal(
|
|
33018
|
+
contentRef = signal.ref();
|
|
32960
33019
|
setup() {
|
|
32961
33020
|
onMounted(() => {
|
|
32962
33021
|
if (this.props.isCollapsed) this.contentRef()?.classList.add("d-none");
|
|
@@ -33009,7 +33068,7 @@ var ChartAnnotation = class extends Component {
|
|
|
33009
33068
|
TextInput
|
|
33010
33069
|
};
|
|
33011
33070
|
props = useProps(chartSidePanelPropsDefinition);
|
|
33012
|
-
editorRef = signal(
|
|
33071
|
+
editorRef = signal.ref();
|
|
33013
33072
|
state = proxy({
|
|
33014
33073
|
annotationTextInput: this.props.definition.annotationText || "",
|
|
33015
33074
|
isOverflowing: false
|
|
@@ -33331,7 +33390,7 @@ var RoundColorPicker = class extends Component {
|
|
|
33331
33390
|
onColorPicked: types$1.function(),
|
|
33332
33391
|
disableNoColor: types$1.boolean().optional()
|
|
33333
33392
|
});
|
|
33334
|
-
colorPickerButtonRef = signal(
|
|
33393
|
+
colorPickerButtonRef = signal.ref();
|
|
33335
33394
|
state;
|
|
33336
33395
|
setup() {
|
|
33337
33396
|
this.state = proxy({ pickerOpened: false });
|
|
@@ -36173,6 +36232,7 @@ var helpers_index_exports$1 = /* @__PURE__ */ __exportAll({
|
|
|
36173
36232
|
getDefinedAxis: () => getDefinedAxis,
|
|
36174
36233
|
getPieColors: () => getPieColors,
|
|
36175
36234
|
isTrendLineAxis: () => isTrendLineAxis,
|
|
36235
|
+
limitChartConfigDataPoints: () => limitChartConfigDataPoints,
|
|
36176
36236
|
shouldRemoveFirstLabel: () => shouldRemoveFirstLabel,
|
|
36177
36237
|
toExcelDataset: () => toExcelDataset,
|
|
36178
36238
|
toExcelLabelRange: () => toExcelLabelRange,
|
|
@@ -36943,7 +37003,7 @@ var ColorScalePicker = class extends Component {
|
|
|
36943
37003
|
popoverProps: void 0,
|
|
36944
37004
|
popoverStyle: ""
|
|
36945
37005
|
});
|
|
36946
|
-
popoverRef = signal(
|
|
37006
|
+
popoverRef = signal.ref(HTMLTableElement);
|
|
36947
37007
|
setup() {
|
|
36948
37008
|
useExternalListener(window, "click", this.closePopover);
|
|
36949
37009
|
}
|
|
@@ -37192,7 +37252,7 @@ var TextValueProvider = class extends Component {
|
|
|
37192
37252
|
onValueSelected: types$1.function(),
|
|
37193
37253
|
onValueHovered: types$1.function()
|
|
37194
37254
|
});
|
|
37195
|
-
autoCompleteListRef = signal(
|
|
37255
|
+
autoCompleteListRef = signal.ref();
|
|
37196
37256
|
setup() {
|
|
37197
37257
|
useEffect(() => {
|
|
37198
37258
|
const selectedIndex = this.props.selectedIndex;
|
|
@@ -37483,7 +37543,7 @@ var SpeechBubble = class extends Component {
|
|
|
37483
37543
|
anchorRect: types$1.Rect()
|
|
37484
37544
|
});
|
|
37485
37545
|
spreadsheetRect = useSpreadsheetRect();
|
|
37486
|
-
bubbleRef = signal(
|
|
37546
|
+
bubbleRef = signal.ref();
|
|
37487
37547
|
setup() {
|
|
37488
37548
|
useEffect(() => {
|
|
37489
37549
|
const el = this.bubbleRef();
|
|
@@ -37528,8 +37588,8 @@ var Composer = class extends Component {
|
|
|
37528
37588
|
showAssistant: types$1.boolean().optional(true)
|
|
37529
37589
|
});
|
|
37530
37590
|
DOMFocusableElementStore;
|
|
37531
|
-
composerRef = signal(
|
|
37532
|
-
containerRef = signal(
|
|
37591
|
+
composerRef = signal.ref();
|
|
37592
|
+
containerRef = signal.ref();
|
|
37533
37593
|
contentHelper = new ContentEditableHelper(this.composerRef());
|
|
37534
37594
|
composerState = proxy({
|
|
37535
37595
|
positionStart: 0,
|
|
@@ -39593,10 +39653,10 @@ var ScatterConfigPanel = class extends GenericChartConfigPanel {
|
|
|
39593
39653
|
var ScorecardChartConfigPanel = class extends Component {
|
|
39594
39654
|
static template = "o-spreadsheet-ScorecardChartConfigPanel";
|
|
39595
39655
|
static components = {
|
|
39596
|
-
SelectionInput,
|
|
39597
39656
|
ChartErrorSection,
|
|
39598
39657
|
Section,
|
|
39599
|
-
Select
|
|
39658
|
+
Select,
|
|
39659
|
+
StandaloneComposer
|
|
39600
39660
|
};
|
|
39601
39661
|
props = useProps(chartSidePanelPropsDefinition);
|
|
39602
39662
|
state = proxy({
|
|
@@ -39608,30 +39668,18 @@ var ScorecardChartConfigPanel = class extends Component {
|
|
|
39608
39668
|
get errorMessages() {
|
|
39609
39669
|
return [...this.state.keyValueDispatchResult?.reasons || [], ...this.state.baselineDispatchResult?.reasons || []].filter((reason) => reason !== "NoChanges").map((error) => ChartTerms.Errors[error] || ChartTerms.Errors.Unexpected);
|
|
39610
39670
|
}
|
|
39611
|
-
|
|
39612
|
-
|
|
39613
|
-
}
|
|
39614
|
-
get isBaselineInvalid() {
|
|
39615
|
-
return !!this.state.baselineDispatchResult?.isCancelledBecause("InvalidScorecardBaseline");
|
|
39616
|
-
}
|
|
39617
|
-
onKeyValueRangeChanged(ranges) {
|
|
39618
|
-
this.keyValue = ranges[0];
|
|
39619
|
-
this.state.keyValueDispatchResult = this.props.canUpdateChart(this.props.chartId, { keyValue: this.keyValue });
|
|
39620
|
-
}
|
|
39621
|
-
updateKeyValueRange() {
|
|
39671
|
+
onConfirmKeyValue(keyValue) {
|
|
39672
|
+
this.keyValue = keyValue;
|
|
39622
39673
|
this.state.keyValueDispatchResult = this.props.updateChart(this.props.chartId, { keyValue: this.keyValue });
|
|
39623
39674
|
}
|
|
39624
|
-
|
|
39675
|
+
getKeyValue() {
|
|
39625
39676
|
return this.keyValue || "";
|
|
39626
39677
|
}
|
|
39627
|
-
|
|
39628
|
-
this.baseline =
|
|
39629
|
-
this.state.baselineDispatchResult = this.props.canUpdateChart(this.props.chartId, { baseline: this.baseline });
|
|
39630
|
-
}
|
|
39631
|
-
updateBaselineRange() {
|
|
39678
|
+
onConfirmBaseline(baseline) {
|
|
39679
|
+
this.baseline = baseline;
|
|
39632
39680
|
this.state.baselineDispatchResult = this.props.updateChart(this.props.chartId, { baseline: this.baseline });
|
|
39633
39681
|
}
|
|
39634
|
-
|
|
39682
|
+
getBaseline() {
|
|
39635
39683
|
return this.baseline || "";
|
|
39636
39684
|
}
|
|
39637
39685
|
updateBaselineMode(baselineMode) {
|
|
@@ -40031,7 +40079,7 @@ var ChartTypePicker = class extends Component {
|
|
|
40031
40079
|
chartId: types$1.UID(),
|
|
40032
40080
|
chartPanelStore: types$1.Store()
|
|
40033
40081
|
});
|
|
40034
|
-
selectRef = signal(
|
|
40082
|
+
selectRef = signal.ref();
|
|
40035
40083
|
state = proxy({ popoverProps: void 0 });
|
|
40036
40084
|
getSupportedChartTypes() {
|
|
40037
40085
|
let supportedTypes;
|
|
@@ -40088,6 +40136,7 @@ var MainChartPanelStore = class extends SpreadsheetStore {
|
|
|
40088
40136
|
changeChartType(chartId, newDisplayType) {
|
|
40089
40137
|
const currentCreationContext = this.getters.getContextCreationChart(chartId);
|
|
40090
40138
|
const savedCreationContext = this.creationContexts[chartId] || {};
|
|
40139
|
+
const auxiliaryRange = currentCreationContext && "auxiliaryRange" in currentCreationContext ? currentCreationContext.auxiliaryRange : savedCreationContext.auxiliaryRange;
|
|
40091
40140
|
let dataSetStyles = savedCreationContext.dataSetStyles ?? currentCreationContext?.dataSetStyles;
|
|
40092
40141
|
let dataSource = {
|
|
40093
40142
|
...savedCreationContext.dataSource,
|
|
@@ -40109,7 +40158,8 @@ var MainChartPanelStore = class extends SpreadsheetStore {
|
|
|
40109
40158
|
dataSetsHaveTitle: false,
|
|
40110
40159
|
...savedCreationContext.dataSource,
|
|
40111
40160
|
...currentCreationContext?.dataSource,
|
|
40112
|
-
dataSets: newRanges ?? []
|
|
40161
|
+
dataSets: newRanges ?? [],
|
|
40162
|
+
labelRange: auxiliaryRange
|
|
40113
40163
|
};
|
|
40114
40164
|
}
|
|
40115
40165
|
this.creationContexts[chartId] = {
|
|
@@ -40138,7 +40188,7 @@ var MainChartPanelStore = class extends SpreadsheetStore {
|
|
|
40138
40188
|
dataSetsStartWithSameRanges(currentDataSets, savedDataSets, currentStyles, savedStyles) {
|
|
40139
40189
|
return currentDataSets.every((ds, i) => {
|
|
40140
40190
|
const savedDs = savedDataSets[i];
|
|
40141
|
-
return deepEquals(ds.dataRange, savedDs
|
|
40191
|
+
return deepEquals(ds.dataRange, savedDs?.dataRange) && deepEquals(currentStyles?.[ds.dataSetId], savedStyles?.[savedDs?.dataSetId]);
|
|
40142
40192
|
});
|
|
40143
40193
|
}
|
|
40144
40194
|
/**
|
|
@@ -40599,7 +40649,7 @@ var ColumnStatsPanel = class extends Component {
|
|
|
40599
40649
|
highlightPositions: []
|
|
40600
40650
|
});
|
|
40601
40651
|
store;
|
|
40602
|
-
chartCanvasRef = signal(
|
|
40652
|
+
chartCanvasRef = signal.ref(HTMLCanvasElement);
|
|
40603
40653
|
chart;
|
|
40604
40654
|
setup() {
|
|
40605
40655
|
this.store = useStore(ColumnStatisticsStore);
|
|
@@ -42027,7 +42077,7 @@ var ConditionalFormatPreview = class extends Component {
|
|
|
42027
42077
|
class: types$1.string()
|
|
42028
42078
|
});
|
|
42029
42079
|
icons = ICONS;
|
|
42030
|
-
cfPreviewRef = signal(
|
|
42080
|
+
cfPreviewRef = signal.ref();
|
|
42031
42081
|
setup() {
|
|
42032
42082
|
useHighlightsOnHover(this.cfPreviewRef, this);
|
|
42033
42083
|
}
|
|
@@ -42084,7 +42134,7 @@ var ConditionalFormatPreviewList = class extends Component {
|
|
|
42084
42134
|
static components = { ConditionalFormatPreview };
|
|
42085
42135
|
props = useProps({ onCloseSidePanel: types$1.function() });
|
|
42086
42136
|
dragAndDrop = useDragAndDropListItems();
|
|
42087
|
-
cfListRef = signal(
|
|
42137
|
+
cfListRef = signal.ref();
|
|
42088
42138
|
get conditionalFormats() {
|
|
42089
42139
|
return this.env.model.getters.getConditionalFormats(this.env.model.getters.getActiveSheetId());
|
|
42090
42140
|
}
|
|
@@ -42144,6 +42194,1477 @@ var ConditionalFormatPreviewList = class extends Component {
|
|
|
42144
42194
|
}
|
|
42145
42195
|
};
|
|
42146
42196
|
|
|
42197
|
+
//#endregion
|
|
42198
|
+
//#region src/stores/chart_drag_store.ts
|
|
42199
|
+
/**
|
|
42200
|
+
* Holds the id of the figure that a dragged chart suggestion is currently
|
|
42201
|
+
* hovering over, so it can be highlighted reactively instead of through direct DOM manipulation.
|
|
42202
|
+
*/
|
|
42203
|
+
var ChartDragStore = class {
|
|
42204
|
+
mutators = ["setHighlightedFigure"];
|
|
42205
|
+
highlightedFigureId = void 0;
|
|
42206
|
+
setHighlightedFigure(figureId) {
|
|
42207
|
+
this.highlightedFigureId = figureId;
|
|
42208
|
+
}
|
|
42209
|
+
};
|
|
42210
|
+
|
|
42211
|
+
//#endregion
|
|
42212
|
+
//#region src/components/helpers/chart_drag_and_drop.ts
|
|
42213
|
+
function getDefaultChartFigureSize(type) {
|
|
42214
|
+
if (type === "scorecard") return {
|
|
42215
|
+
width: 213,
|
|
42216
|
+
height: 101
|
|
42217
|
+
};
|
|
42218
|
+
return {
|
|
42219
|
+
width: 536,
|
|
42220
|
+
height: 335
|
|
42221
|
+
};
|
|
42222
|
+
}
|
|
42223
|
+
function getCarouselOverlappingChart(figureUI, otherFigures, matchTags) {
|
|
42224
|
+
if (figureUI.tag !== "chart") return;
|
|
42225
|
+
const figureCenterX = figureUI.x + figureUI.width / 2;
|
|
42226
|
+
const figureCenterY = figureUI.y + figureUI.height / 2;
|
|
42227
|
+
let bestMatch;
|
|
42228
|
+
let smallestDistance = Infinity;
|
|
42229
|
+
for (const figure of otherFigures) {
|
|
42230
|
+
if (!matchTags.includes(figure.tag)) continue;
|
|
42231
|
+
const targetCenterX = figure.x + figure.width / 2;
|
|
42232
|
+
const targetCenterY = figure.y + figure.height / 2;
|
|
42233
|
+
const distanceX = Math.abs(figureCenterX - targetCenterX);
|
|
42234
|
+
const distanceY = Math.abs(figureCenterY - targetCenterY);
|
|
42235
|
+
const squaredDistance = distanceX ** 2 + distanceY ** 2;
|
|
42236
|
+
if (distanceX <= figureUI.width / 2 && distanceY <= figureUI.height / 2 && squaredDistance < smallestDistance) {
|
|
42237
|
+
smallestDistance = squaredDistance;
|
|
42238
|
+
bestMatch = figure;
|
|
42239
|
+
}
|
|
42240
|
+
}
|
|
42241
|
+
return bestMatch;
|
|
42242
|
+
}
|
|
42243
|
+
/**
|
|
42244
|
+
* Start dragging a floating preview of the given chart definition, following the mouse.
|
|
42245
|
+
* On drop, creates the chart on the grid (or inside a carousel if dropped on one).
|
|
42246
|
+
*/
|
|
42247
|
+
function startChartDragAndDrop(env, definition, ev) {
|
|
42248
|
+
const zoom = env.getStore(ZoomStore).zoomLevel;
|
|
42249
|
+
const gridPosition = gridOverlayPosition(zoom);
|
|
42250
|
+
const spreadsheet = document.querySelector(".o-spreadsheet");
|
|
42251
|
+
if (!spreadsheet) return;
|
|
42252
|
+
const startX = ev.clientX / zoom;
|
|
42253
|
+
const startY = ev.clientY / zoom;
|
|
42254
|
+
const { width, height } = getDefaultChartFigureSize(definition.type);
|
|
42255
|
+
const figureWidth = width * zoom;
|
|
42256
|
+
const figureHeight = height * zoom;
|
|
42257
|
+
const getters = env.model.getters;
|
|
42258
|
+
const sheetId = getters.getActiveSheetId();
|
|
42259
|
+
let container = null;
|
|
42260
|
+
let destroyChart = void 0;
|
|
42261
|
+
const chartDragStore = env.getStore(ChartDragStore);
|
|
42262
|
+
const previousCursor = document.body.style.cursor;
|
|
42263
|
+
document.body.style.cursor = "grabbing";
|
|
42264
|
+
/** Grid coordinates (in sheet pixels) of a mouse position, or undefined if outside the grid. */
|
|
42265
|
+
const getGridPosition = (clientX, clientY) => {
|
|
42266
|
+
if (clientX > gridPosition.x + gridPosition.width || clientY > gridPosition.y + gridPosition.height) return;
|
|
42267
|
+
const { scrollX, scrollY } = env.getStore(ViewportsStore).activeSheetScrollInfo;
|
|
42268
|
+
return {
|
|
42269
|
+
x: Math.max(0, (clientX - gridPosition.x) / zoom + scrollX),
|
|
42270
|
+
y: Math.max(0, (clientY - gridPosition.y) / zoom + scrollY)
|
|
42271
|
+
};
|
|
42272
|
+
};
|
|
42273
|
+
/** Carousel or standalone chart the dragged chart is dropped onto, if any. */
|
|
42274
|
+
const getOverlappingFigure = (clientX, clientY) => {
|
|
42275
|
+
const position = getGridPosition(clientX, clientY);
|
|
42276
|
+
if (!position) return;
|
|
42277
|
+
const figureUI = {
|
|
42278
|
+
tag: "chart",
|
|
42279
|
+
...position,
|
|
42280
|
+
width,
|
|
42281
|
+
height
|
|
42282
|
+
};
|
|
42283
|
+
const otherFigures = env.getStore(ViewportsStore).visibleFigures;
|
|
42284
|
+
return getCarouselOverlappingChart(figureUI, otherFigures, ["carousel", "chart"]);
|
|
42285
|
+
};
|
|
42286
|
+
const halfWidth = figureWidth / 2;
|
|
42287
|
+
const halfHeight = figureHeight / 2;
|
|
42288
|
+
const onMouseMove = (e) => {
|
|
42289
|
+
if (Math.abs(e.clientX - startX) <= 5 && Math.abs(e.clientY - startY) <= 5) return;
|
|
42290
|
+
if (container === null) {
|
|
42291
|
+
container = document.createElement("div");
|
|
42292
|
+
container.className = "o-chart-drag-preview os-theme-dependant position-fixed border pe-none";
|
|
42293
|
+
container.style.width = `${width}px`;
|
|
42294
|
+
container.style.height = `${height}px`;
|
|
42295
|
+
container.style.zoom = `${zoom}`;
|
|
42296
|
+
const canvas = document.createElement("canvas");
|
|
42297
|
+
canvas.className = "w-100 h-100";
|
|
42298
|
+
container.appendChild(canvas);
|
|
42299
|
+
spreadsheet.appendChild(container);
|
|
42300
|
+
destroyChart = drawChartOnCanvas(canvas, SpreadsheetChart.fromStrDefinition(getters, sheetId, definition).getRuntime(getters, "newChart"), {
|
|
42301
|
+
width,
|
|
42302
|
+
height
|
|
42303
|
+
}, definition.type, zoom);
|
|
42304
|
+
}
|
|
42305
|
+
container.style.left = `${Math.max(gridPosition.x, (e.clientX - halfWidth) / zoom)}px`;
|
|
42306
|
+
container.style.top = `${Math.max(gridPosition.y, (e.clientY - halfHeight) / zoom)}px`;
|
|
42307
|
+
const overlappingFigure = getOverlappingFigure(e.clientX - halfWidth, e.clientY - halfHeight);
|
|
42308
|
+
container.style.opacity = overlappingFigure?.id ? "0.6" : "0.9";
|
|
42309
|
+
chartDragStore.setHighlightedFigure(overlappingFigure?.id);
|
|
42310
|
+
};
|
|
42311
|
+
const onMouseUp = (mouseEvent) => {
|
|
42312
|
+
chartDragStore.setHighlightedFigure(void 0);
|
|
42313
|
+
if (container !== null) {
|
|
42314
|
+
spreadsheet.removeChild(container);
|
|
42315
|
+
container = null;
|
|
42316
|
+
}
|
|
42317
|
+
destroyChart?.();
|
|
42318
|
+
document.body.style.cursor = previousCursor;
|
|
42319
|
+
let position = getGridPosition(mouseEvent.clientX - halfWidth, mouseEvent.clientY - halfHeight);
|
|
42320
|
+
if (Math.abs(mouseEvent.clientX / zoom - startX) <= 5 && Math.abs(mouseEvent.clientY / zoom - startY) <= 5) position = {
|
|
42321
|
+
x: 0,
|
|
42322
|
+
y: 0
|
|
42323
|
+
};
|
|
42324
|
+
else if (!position || position.x + halfWidth > gridPosition.width) return;
|
|
42325
|
+
const { col, row, offset } = env.getStore(ViewportsStore).viewports.getPositionAnchorOffset(sheetId, position);
|
|
42326
|
+
const payload = {
|
|
42327
|
+
chartId: UuidGenerator.smallUuid(),
|
|
42328
|
+
figureId: UuidGenerator.smallUuid(),
|
|
42329
|
+
sheetId,
|
|
42330
|
+
size: {
|
|
42331
|
+
width,
|
|
42332
|
+
height
|
|
42333
|
+
},
|
|
42334
|
+
definition,
|
|
42335
|
+
col,
|
|
42336
|
+
row,
|
|
42337
|
+
offset
|
|
42338
|
+
};
|
|
42339
|
+
const overlappingFigure = getOverlappingFigure(mouseEvent.clientX - halfWidth, mouseEvent.clientY - halfHeight);
|
|
42340
|
+
if (overlappingFigure?.tag === "carousel") env.model.dispatch("ADD_NEW_CHART_TO_CAROUSEL", {
|
|
42341
|
+
sheetId,
|
|
42342
|
+
figureId: overlappingFigure.id,
|
|
42343
|
+
newChartId: UuidGenerator.smallUuid(),
|
|
42344
|
+
chartDefinition: definition
|
|
42345
|
+
});
|
|
42346
|
+
else if (overlappingFigure?.tag === "chart") env.model.dispatch("CREATE_CHART_AND_MERGE_INTO_CAROUSEL", {
|
|
42347
|
+
chartId: payload.chartId,
|
|
42348
|
+
figureId: payload.figureId,
|
|
42349
|
+
sheetId: payload.sheetId,
|
|
42350
|
+
definition: payload.definition,
|
|
42351
|
+
baseFigureId: overlappingFigure.id
|
|
42352
|
+
});
|
|
42353
|
+
else env.model.dispatch("CREATE_CHART", payload);
|
|
42354
|
+
};
|
|
42355
|
+
startDnd(onMouseMove, onMouseUp);
|
|
42356
|
+
}
|
|
42357
|
+
|
|
42358
|
+
//#endregion
|
|
42359
|
+
//#region src/components/side_panel/data_analysis/chart_suggestion_preview.ts
|
|
42360
|
+
const PREVIEW_BUBBLE_RADIUS_RATIO = .3;
|
|
42361
|
+
const MIN_PREVIEW_BUBBLE_RADIUS = 1;
|
|
42362
|
+
var ChartSuggestionPreview = class extends Component {
|
|
42363
|
+
static template = "o-spreadsheet-ChartSuggestionPreview";
|
|
42364
|
+
props = useProps({
|
|
42365
|
+
definition: types$1.ChartDefinition(),
|
|
42366
|
+
description: types$1.string(),
|
|
42367
|
+
onPointerDown: types$1.function()
|
|
42368
|
+
});
|
|
42369
|
+
chartCanvasRef = signal.ref(HTMLCanvasElement);
|
|
42370
|
+
chart;
|
|
42371
|
+
setup() {
|
|
42372
|
+
onMounted(() => registerChartJSExtensions());
|
|
42373
|
+
onWillUnmount(() => this.destroyChart());
|
|
42374
|
+
useLayoutEffect(() => {
|
|
42375
|
+
this.updateChart();
|
|
42376
|
+
}, () => [this.props.definition]);
|
|
42377
|
+
}
|
|
42378
|
+
getChartConfiguration() {
|
|
42379
|
+
const getters = this.env.model.getters;
|
|
42380
|
+
const activeSheetId = getters.getActiveSheetId();
|
|
42381
|
+
const runtime = SpreadsheetChart.fromStrDefinition(getters, activeSheetId, this.props.definition).getRuntime(getters, "myChart");
|
|
42382
|
+
if (!("chartJsConfig" in runtime)) return null;
|
|
42383
|
+
let config = runtime.chartJsConfig;
|
|
42384
|
+
const existingScales = config.options?.scales ?? {};
|
|
42385
|
+
config = {
|
|
42386
|
+
...config,
|
|
42387
|
+
data: this.props.definition.type === "bubble" ? {
|
|
42388
|
+
...config.data,
|
|
42389
|
+
datasets: (config.data?.datasets || []).map((dataset) => ({
|
|
42390
|
+
...dataset,
|
|
42391
|
+
pointRadius: this.scaleBubblePointRadius(dataset.pointRadius),
|
|
42392
|
+
pointHoverRadius: this.scaleBubblePointRadius(dataset.pointHoverRadius)
|
|
42393
|
+
}))
|
|
42394
|
+
} : config.data,
|
|
42395
|
+
options: {
|
|
42396
|
+
...config.options,
|
|
42397
|
+
plugins: {
|
|
42398
|
+
...config.options?.plugins,
|
|
42399
|
+
legend: { display: false },
|
|
42400
|
+
title: { display: false },
|
|
42401
|
+
tooltip: { enabled: false }
|
|
42402
|
+
},
|
|
42403
|
+
events: [],
|
|
42404
|
+
animation: false,
|
|
42405
|
+
scales: this.getScalesCongif(existingScales)
|
|
42406
|
+
}
|
|
42407
|
+
};
|
|
42408
|
+
return limitChartConfigDataPoints(config);
|
|
42409
|
+
}
|
|
42410
|
+
getScalesCongif(scales) {
|
|
42411
|
+
if (scales.x) scales.x = {
|
|
42412
|
+
...scales.x,
|
|
42413
|
+
ticks: { display: false },
|
|
42414
|
+
border: { display: false }
|
|
42415
|
+
};
|
|
42416
|
+
if (scales.y) scales.y = {
|
|
42417
|
+
...scales.y,
|
|
42418
|
+
ticks: { display: false },
|
|
42419
|
+
border: { display: false }
|
|
42420
|
+
};
|
|
42421
|
+
if (scales.r) scales.r = {
|
|
42422
|
+
...scales.r,
|
|
42423
|
+
ticks: { display: false },
|
|
42424
|
+
pointLabels: { display: false }
|
|
42425
|
+
};
|
|
42426
|
+
return scales;
|
|
42427
|
+
}
|
|
42428
|
+
scaleBubblePointRadius(pointRadius) {
|
|
42429
|
+
if (Array.isArray(pointRadius)) return pointRadius.map((radius) => radius <= 0 ? radius : Math.max(MIN_PREVIEW_BUBBLE_RADIUS, radius * PREVIEW_BUBBLE_RADIUS_RATIO));
|
|
42430
|
+
if (typeof pointRadius === "number") return pointRadius <= 0 ? pointRadius : Math.max(MIN_PREVIEW_BUBBLE_RADIUS, pointRadius * PREVIEW_BUBBLE_RADIUS_RATIO);
|
|
42431
|
+
return pointRadius ?? MIN_PREVIEW_BUBBLE_RADIUS;
|
|
42432
|
+
}
|
|
42433
|
+
updateChart() {
|
|
42434
|
+
this.destroyChart();
|
|
42435
|
+
const config = this.getChartConfiguration();
|
|
42436
|
+
if (!config) {
|
|
42437
|
+
this.drawNativeChart();
|
|
42438
|
+
return;
|
|
42439
|
+
}
|
|
42440
|
+
const ctx = this.chartCanvasRef()?.getContext("2d");
|
|
42441
|
+
if (!ctx || !globalThis.Chart) return;
|
|
42442
|
+
this.chart = new globalThis.Chart(ctx, config);
|
|
42443
|
+
}
|
|
42444
|
+
drawNativeChart() {
|
|
42445
|
+
const canvas = this.chartCanvasRef();
|
|
42446
|
+
if (!canvas) return;
|
|
42447
|
+
const getters = this.env.model.getters;
|
|
42448
|
+
let runtime = SpreadsheetChart.fromStrDefinition(getters, getters.getActiveSheetId(), this.props.definition).getRuntime(getters, "myChart");
|
|
42449
|
+
const { type } = this.props.definition;
|
|
42450
|
+
if (type === "scorecard") {
|
|
42451
|
+
const rect = canvas.getBoundingClientRect();
|
|
42452
|
+
runtime = {
|
|
42453
|
+
...runtime,
|
|
42454
|
+
keyValueStyle: {
|
|
42455
|
+
...runtime.keyValueStyle,
|
|
42456
|
+
fontSize: 16
|
|
42457
|
+
}
|
|
42458
|
+
};
|
|
42459
|
+
drawScoreChart(getScorecardConfiguration({
|
|
42460
|
+
width: rect.width || 130,
|
|
42461
|
+
height: rect.height || 120
|
|
42462
|
+
}, runtime), canvas);
|
|
42463
|
+
} else if (type === "gauge") {
|
|
42464
|
+
let gaugeRuntime = runtime;
|
|
42465
|
+
if (gaugeRuntime.title) gaugeRuntime = {
|
|
42466
|
+
...gaugeRuntime,
|
|
42467
|
+
title: { text: "" }
|
|
42468
|
+
};
|
|
42469
|
+
drawGaugeChart(canvas, gaugeRuntime, 1, void 0, { labelFontSize: 8 });
|
|
42470
|
+
}
|
|
42471
|
+
}
|
|
42472
|
+
destroyChart() {
|
|
42473
|
+
this.chart?.destroy();
|
|
42474
|
+
this.chart = void 0;
|
|
42475
|
+
}
|
|
42476
|
+
};
|
|
42477
|
+
|
|
42478
|
+
//#endregion
|
|
42479
|
+
//#region src/helpers/data_analysis.ts
|
|
42480
|
+
function analyzeColumns(zones, getters) {
|
|
42481
|
+
return getZonesByColumns(zones).map((zone) => analyzeColumn(zone, getters));
|
|
42482
|
+
}
|
|
42483
|
+
function analyzeColumn(zone, getters) {
|
|
42484
|
+
const sheetId = getters.getActiveSheetId();
|
|
42485
|
+
const cells = getters.getEvaluatedCellsInZone(sheetId, zone).filter((c) => c.type !== "empty");
|
|
42486
|
+
if (!cells.length) return {
|
|
42487
|
+
zone,
|
|
42488
|
+
type: "empty",
|
|
42489
|
+
hasHeader: false,
|
|
42490
|
+
rowCount: 0,
|
|
42491
|
+
uniqueCount: 0,
|
|
42492
|
+
uniqueRatio: 0,
|
|
42493
|
+
nonEmpty: []
|
|
42494
|
+
};
|
|
42495
|
+
const firstCell = cells[0];
|
|
42496
|
+
const rest = cells.slice(1);
|
|
42497
|
+
const hasHeader = firstCell.type === "text" && rest.some((c) => c.type !== "text");
|
|
42498
|
+
const dataCells = hasHeader ? rest : cells;
|
|
42499
|
+
const numericValues = dataCells.filter((c) => c.type === "number").map((c) => c.value);
|
|
42500
|
+
const allVals = dataCells.map((c) => String(c.value ?? ""));
|
|
42501
|
+
const uniqueCount = new Set(allVals).size;
|
|
42502
|
+
return {
|
|
42503
|
+
zone,
|
|
42504
|
+
type: computeColumnType(dataCells),
|
|
42505
|
+
header: hasHeader ? firstCell.value : void 0,
|
|
42506
|
+
hasHeader,
|
|
42507
|
+
rowCount: dataCells.length,
|
|
42508
|
+
uniqueCount,
|
|
42509
|
+
uniqueRatio: allVals.length > 0 ? uniqueCount / allVals.length : 0,
|
|
42510
|
+
maxValue: numericValues.length ? numericValues.reduce((max, v) => v > max ? v : max, numericValues[0]) : void 0,
|
|
42511
|
+
nonEmpty: dataCells
|
|
42512
|
+
};
|
|
42513
|
+
}
|
|
42514
|
+
function computeColumnType(cells) {
|
|
42515
|
+
if (cells.length === 0) return "empty";
|
|
42516
|
+
if (cells.every((c) => c.type === "error")) return "error";
|
|
42517
|
+
cells = cells.filter((c) => c.type !== "error");
|
|
42518
|
+
if (cells.every((c) => c.type === "boolean")) return "boolean";
|
|
42519
|
+
if (cells.every((c) => c.type === "number" && !!c.format && isDateTimeFormat(c.format))) return "date";
|
|
42520
|
+
else if (cells.every((c) => c.type === "number")) {
|
|
42521
|
+
if (cells.every((c) => c.format?.includes("%"))) return "percentage";
|
|
42522
|
+
return "number";
|
|
42523
|
+
} else {
|
|
42524
|
+
const textVals = cells.filter((c) => c.type === "text").map((c) => c.value);
|
|
42525
|
+
if (textVals.length > 0) {
|
|
42526
|
+
const unique = new Set(textVals).size;
|
|
42527
|
+
return unique / textVals.length < .75 && unique <= 20 ? "categorical" : "label";
|
|
42528
|
+
}
|
|
42529
|
+
}
|
|
42530
|
+
return "empty";
|
|
42531
|
+
}
|
|
42532
|
+
|
|
42533
|
+
//#endregion
|
|
42534
|
+
//#region src/helpers/figures/charts/charts_suggestions_constants.ts
|
|
42535
|
+
/** Above this many rows, shape-based charts (radar, scatter, pyramid) become unreadable. */
|
|
42536
|
+
const MAX_ROWS_FOR_SHAPE_CHART = 10;
|
|
42537
|
+
function barChart(titleText, source, opts = {}) {
|
|
42538
|
+
return {
|
|
42539
|
+
...opts,
|
|
42540
|
+
type: "bar",
|
|
42541
|
+
title: { text: titleText },
|
|
42542
|
+
dataSource: source,
|
|
42543
|
+
dataSetStyles: {},
|
|
42544
|
+
legendPosition: opts.legendPosition ?? "none",
|
|
42545
|
+
stacked: opts.stacked ?? false,
|
|
42546
|
+
humanize: true,
|
|
42547
|
+
aggregated: opts.aggregated ?? false
|
|
42548
|
+
};
|
|
42549
|
+
}
|
|
42550
|
+
function lineChart(titleText, source, opts = {}) {
|
|
42551
|
+
return {
|
|
42552
|
+
...opts,
|
|
42553
|
+
type: "line",
|
|
42554
|
+
title: { text: titleText },
|
|
42555
|
+
dataSource: source,
|
|
42556
|
+
dataSetStyles: {},
|
|
42557
|
+
legendPosition: opts.legendPosition ?? "none",
|
|
42558
|
+
stacked: opts.stacked ?? false,
|
|
42559
|
+
cumulative: opts.cumulative ?? false,
|
|
42560
|
+
labelsAsText: false,
|
|
42561
|
+
humanize: true
|
|
42562
|
+
};
|
|
42563
|
+
}
|
|
42564
|
+
function pieChart(titleText, source, opts = {}) {
|
|
42565
|
+
return {
|
|
42566
|
+
...opts,
|
|
42567
|
+
type: "pie",
|
|
42568
|
+
title: { text: titleText },
|
|
42569
|
+
dataSource: source,
|
|
42570
|
+
dataSetStyles: {},
|
|
42571
|
+
legendPosition: opts.legendPosition ?? "top",
|
|
42572
|
+
humanize: true
|
|
42573
|
+
};
|
|
42574
|
+
}
|
|
42575
|
+
function radarChart(titleText, source, opts = {}) {
|
|
42576
|
+
return {
|
|
42577
|
+
...opts,
|
|
42578
|
+
type: "radar",
|
|
42579
|
+
title: { text: titleText },
|
|
42580
|
+
dataSource: source,
|
|
42581
|
+
dataSetStyles: {},
|
|
42582
|
+
legendPosition: opts.legendPosition ?? "none",
|
|
42583
|
+
stacked: false,
|
|
42584
|
+
humanize: true
|
|
42585
|
+
};
|
|
42586
|
+
}
|
|
42587
|
+
function sunburstChart(titleText, source) {
|
|
42588
|
+
return {
|
|
42589
|
+
type: "sunburst",
|
|
42590
|
+
title: { text: titleText },
|
|
42591
|
+
dataSource: source,
|
|
42592
|
+
dataSetStyles: {},
|
|
42593
|
+
legendPosition: "none"
|
|
42594
|
+
};
|
|
42595
|
+
}
|
|
42596
|
+
function treemapChart(titleText, source) {
|
|
42597
|
+
return {
|
|
42598
|
+
type: "treemap",
|
|
42599
|
+
title: { text: titleText },
|
|
42600
|
+
dataSource: source,
|
|
42601
|
+
dataSetStyles: {},
|
|
42602
|
+
legendPosition: "none"
|
|
42603
|
+
};
|
|
42604
|
+
}
|
|
42605
|
+
function calendarChart(titleText, source) {
|
|
42606
|
+
return {
|
|
42607
|
+
type: "calendar",
|
|
42608
|
+
title: { text: titleText },
|
|
42609
|
+
dataSource: source,
|
|
42610
|
+
dataSetStyles: {},
|
|
42611
|
+
legendPosition: "none",
|
|
42612
|
+
horizontalGroupBy: "month_number",
|
|
42613
|
+
verticalGroupBy: "day_of_week",
|
|
42614
|
+
humanize: true
|
|
42615
|
+
};
|
|
42616
|
+
}
|
|
42617
|
+
function scatterChart(titleText, source) {
|
|
42618
|
+
return {
|
|
42619
|
+
type: "scatter",
|
|
42620
|
+
title: { text: titleText },
|
|
42621
|
+
dataSource: source,
|
|
42622
|
+
dataSetStyles: {},
|
|
42623
|
+
legendPosition: "none",
|
|
42624
|
+
labelsAsText: false,
|
|
42625
|
+
humanize: true
|
|
42626
|
+
};
|
|
42627
|
+
}
|
|
42628
|
+
function pyramidChart(titleText, source, opts = {}) {
|
|
42629
|
+
return {
|
|
42630
|
+
type: "pyramid",
|
|
42631
|
+
title: { text: titleText },
|
|
42632
|
+
dataSource: source,
|
|
42633
|
+
dataSetStyles: {},
|
|
42634
|
+
legendPosition: opts.legendPosition ?? "top",
|
|
42635
|
+
stacked: false,
|
|
42636
|
+
horizontal: true,
|
|
42637
|
+
humanize: true
|
|
42638
|
+
};
|
|
42639
|
+
}
|
|
42640
|
+
function comboChart(titleText, source, opts = {}) {
|
|
42641
|
+
return {
|
|
42642
|
+
...opts,
|
|
42643
|
+
type: "combo",
|
|
42644
|
+
title: { text: titleText },
|
|
42645
|
+
dataSource: source,
|
|
42646
|
+
dataSetStyles: {},
|
|
42647
|
+
legendPosition: "top",
|
|
42648
|
+
humanize: true
|
|
42649
|
+
};
|
|
42650
|
+
}
|
|
42651
|
+
function bubbleChart(titleText, xRange, yRanges, sizeRange, labelRange, dataSetsHaveTitle) {
|
|
42652
|
+
return {
|
|
42653
|
+
type: "bubble",
|
|
42654
|
+
title: { text: titleText },
|
|
42655
|
+
humanize: true,
|
|
42656
|
+
dataSetsHaveTitle,
|
|
42657
|
+
yRanges,
|
|
42658
|
+
xRange,
|
|
42659
|
+
sizeRange,
|
|
42660
|
+
labelRange,
|
|
42661
|
+
labelsAsText: false,
|
|
42662
|
+
legendPosition: "none",
|
|
42663
|
+
bubbleColor: { color: "multiple" },
|
|
42664
|
+
verticalAxisPosition: "left"
|
|
42665
|
+
};
|
|
42666
|
+
}
|
|
42667
|
+
function scorecardChart(titleText, keyValue, opts = {}) {
|
|
42668
|
+
return {
|
|
42669
|
+
...opts,
|
|
42670
|
+
type: "scorecard",
|
|
42671
|
+
title: { text: titleText },
|
|
42672
|
+
keyValue,
|
|
42673
|
+
baselineMode: opts.baselineMode ?? "difference",
|
|
42674
|
+
baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP,
|
|
42675
|
+
baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
|
|
42676
|
+
humanize: opts.humanize ?? true
|
|
42677
|
+
};
|
|
42678
|
+
}
|
|
42679
|
+
function gaugeChart(titleText, dataRange, rangeMin, rangeMax) {
|
|
42680
|
+
return {
|
|
42681
|
+
type: "gauge",
|
|
42682
|
+
title: { text: titleText },
|
|
42683
|
+
dataRange,
|
|
42684
|
+
sectionRule: {
|
|
42685
|
+
colors: {
|
|
42686
|
+
lowerColor: DEFAULT_GAUGE_LOWER_COLOR,
|
|
42687
|
+
middleColor: DEFAULT_GAUGE_MIDDLE_COLOR,
|
|
42688
|
+
upperColor: DEFAULT_GAUGE_UPPER_COLOR
|
|
42689
|
+
},
|
|
42690
|
+
rangeMin,
|
|
42691
|
+
rangeMax,
|
|
42692
|
+
lowerInflectionPoint: {
|
|
42693
|
+
type: "percentage",
|
|
42694
|
+
value: "33",
|
|
42695
|
+
operator: "<="
|
|
42696
|
+
},
|
|
42697
|
+
upperInflectionPoint: {
|
|
42698
|
+
type: "percentage",
|
|
42699
|
+
value: "66",
|
|
42700
|
+
operator: "<="
|
|
42701
|
+
}
|
|
42702
|
+
},
|
|
42703
|
+
humanize: true
|
|
42704
|
+
};
|
|
42705
|
+
}
|
|
42706
|
+
/** Pattern A — Single numeric column */
|
|
42707
|
+
const SINGLE_NUMBER_COLUMN_SUGGESTIONS = [
|
|
42708
|
+
{
|
|
42709
|
+
description: _t("Highlights the most recent value compared to the previous one."),
|
|
42710
|
+
isApplicable: ({ rowCount }) => rowCount < 3,
|
|
42711
|
+
build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`, {
|
|
42712
|
+
baseline: ctx.prevCellXC ? `=${ctx.prevCellXC}` : void 0,
|
|
42713
|
+
baselineMode: "difference"
|
|
42714
|
+
})
|
|
42715
|
+
},
|
|
42716
|
+
{
|
|
42717
|
+
description: _t("Shows the position of the last value within the data's min-max range."),
|
|
42718
|
+
isApplicable: ({ rowCount }) => rowCount === 3,
|
|
42719
|
+
build: (ctx) => gaugeChart(ctx.title, ctx.lastCellXC, `=${ctx.firstCellXC}`, `=${ctx.prevCellXC}`)
|
|
42720
|
+
},
|
|
42721
|
+
{
|
|
42722
|
+
description: _t("Compares individual values side-by-side."),
|
|
42723
|
+
isApplicable: ({ rowCount }) => rowCount > 1,
|
|
42724
|
+
build: (ctx) => barChart(ctx.title, ctx.source)
|
|
42725
|
+
},
|
|
42726
|
+
{
|
|
42727
|
+
description: _t("Shows the evolution of all values over the range."),
|
|
42728
|
+
isApplicable: ({ rowCount }) => rowCount > 2,
|
|
42729
|
+
build: (ctx) => lineChart(ctx.title, ctx.source)
|
|
42730
|
+
},
|
|
42731
|
+
{
|
|
42732
|
+
description: _t("Emphasizes total accumulation over the range."),
|
|
42733
|
+
isApplicable: ({ rowCount }) => rowCount > 2,
|
|
42734
|
+
build: (ctx) => lineChart(ctx.title, ctx.source, {
|
|
42735
|
+
fillArea: true,
|
|
42736
|
+
cumulative: true
|
|
42737
|
+
})
|
|
42738
|
+
}
|
|
42739
|
+
];
|
|
42740
|
+
/** Pattern B — Single percentage column */
|
|
42741
|
+
const SINGLE_PERCENTAGE_COLUMN_SUGGESTIONS = [
|
|
42742
|
+
{
|
|
42743
|
+
description: _t("Shows the last percentage value with its baseline."),
|
|
42744
|
+
isApplicable: ({ rowCount }) => rowCount < 3,
|
|
42745
|
+
build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`, {
|
|
42746
|
+
baseline: ctx.prevCellXC ? `=${ctx.prevCellXC}` : void 0,
|
|
42747
|
+
baselineMode: "percentage"
|
|
42748
|
+
})
|
|
42749
|
+
},
|
|
42750
|
+
{
|
|
42751
|
+
description: _t("Natural fit for a 0–100% range."),
|
|
42752
|
+
isApplicable: ({ rowCount }) => rowCount === 1,
|
|
42753
|
+
build: (ctx) => gaugeChart(ctx.title, ctx.lastCellXC, "0", ctx.isAboveOne ? "100" : "1")
|
|
42754
|
+
},
|
|
42755
|
+
{
|
|
42756
|
+
description: _t("Natural fit for a 0–100% range."),
|
|
42757
|
+
isApplicable: ({ rowCount }) => rowCount === 3,
|
|
42758
|
+
build: (ctx) => gaugeChart(ctx.title, ctx.lastCellXC, `=${ctx.firstCellXC}`, `=${ctx.prevCellXC}`)
|
|
42759
|
+
},
|
|
42760
|
+
{
|
|
42761
|
+
description: _t("Shows completion against total."),
|
|
42762
|
+
isApplicable: ({ rowCount }) => rowCount > 1,
|
|
42763
|
+
build: (ctx) => pieChart(ctx.title, ctx.source, { isDoughnut: true })
|
|
42764
|
+
},
|
|
42765
|
+
{
|
|
42766
|
+
description: _t("Compares all percentage values side-by-side."),
|
|
42767
|
+
isApplicable: ({ rowCount }) => rowCount > 1,
|
|
42768
|
+
build: (ctx) => barChart(ctx.title, ctx.source)
|
|
42769
|
+
}
|
|
42770
|
+
];
|
|
42771
|
+
/** Pattern C — Single date column */
|
|
42772
|
+
const SINGLE_DATE_COLUMN_SUGGESTIONS = [{
|
|
42773
|
+
description: _t("Shows the last date value."),
|
|
42774
|
+
isApplicable: ({ rowCount }) => rowCount === 1,
|
|
42775
|
+
build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`)
|
|
42776
|
+
}];
|
|
42777
|
+
/** Pattern D — Single categorical column */
|
|
42778
|
+
const SINGLE_CATEGORICAL_COLUMN_SUGGESTIONS = [
|
|
42779
|
+
{
|
|
42780
|
+
description: _t("Shows the share of each category."),
|
|
42781
|
+
build: (ctx) => pieChart(ctx.title, ctx.source, {
|
|
42782
|
+
aggregated: true,
|
|
42783
|
+
legendPosition: "top"
|
|
42784
|
+
})
|
|
42785
|
+
},
|
|
42786
|
+
{
|
|
42787
|
+
description: _t("Same as pie, cleaner proportional look."),
|
|
42788
|
+
build: (ctx) => pieChart(ctx.title, ctx.source, {
|
|
42789
|
+
aggregated: true,
|
|
42790
|
+
isDoughnut: true,
|
|
42791
|
+
legendPosition: "top"
|
|
42792
|
+
})
|
|
42793
|
+
},
|
|
42794
|
+
{
|
|
42795
|
+
description: _t("Absolute count per category."),
|
|
42796
|
+
build: (ctx) => barChart(ctx.title, {
|
|
42797
|
+
...ctx.source,
|
|
42798
|
+
labelRange: ctx.range
|
|
42799
|
+
}, {
|
|
42800
|
+
legendPosition: "none",
|
|
42801
|
+
aggregated: true
|
|
42802
|
+
})
|
|
42803
|
+
}
|
|
42804
|
+
];
|
|
42805
|
+
/** Pattern E — Single label column */
|
|
42806
|
+
const SINGLE_LABEL_COLUMN_SUGGESTIONS = [{
|
|
42807
|
+
description: _t("Displays a key performance indicator."),
|
|
42808
|
+
isApplicable: ({ rowCount }) => rowCount === 1,
|
|
42809
|
+
build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`, {
|
|
42810
|
+
baselineMode: "text",
|
|
42811
|
+
humanize: false
|
|
42812
|
+
})
|
|
42813
|
+
}];
|
|
42814
|
+
/** Pattern F — Categorical + Number */
|
|
42815
|
+
const CATEGORICAL_VS_NUMBER_SUGGESTIONS = [
|
|
42816
|
+
{
|
|
42817
|
+
description: _t("Classic category-vs-value comparison."),
|
|
42818
|
+
build: (ctx) => barChart(ctx.title, ctx.source, { aggregated: true })
|
|
42819
|
+
},
|
|
42820
|
+
{
|
|
42821
|
+
description: _t("Better when category labels are long."),
|
|
42822
|
+
build: (ctx) => barChart(ctx.title, ctx.source, {
|
|
42823
|
+
horizontal: true,
|
|
42824
|
+
aggregated: true
|
|
42825
|
+
})
|
|
42826
|
+
},
|
|
42827
|
+
{
|
|
42828
|
+
description: _t("Share of total per category."),
|
|
42829
|
+
build: (ctx) => pieChart(ctx.title, ctx.source, {
|
|
42830
|
+
legendPosition: "top",
|
|
42831
|
+
aggregated: true
|
|
42832
|
+
})
|
|
42833
|
+
},
|
|
42834
|
+
{
|
|
42835
|
+
description: _t("Proportional area — good for many categories."),
|
|
42836
|
+
build: (ctx) => treemapChart(ctx.title, ctx.treemapSource)
|
|
42837
|
+
}
|
|
42838
|
+
];
|
|
42839
|
+
/** Patterns G & J — Date + Number or Date + Percentage */
|
|
42840
|
+
const DATE_VS_SERIES_SUGGESTIONS = [
|
|
42841
|
+
{
|
|
42842
|
+
description: _t("Best for visualizing time-series trends."),
|
|
42843
|
+
build: (ctx) => lineChart(ctx.title, ctx.source)
|
|
42844
|
+
},
|
|
42845
|
+
{
|
|
42846
|
+
description: _t("Emphasizes total volume over time."),
|
|
42847
|
+
build: (ctx) => lineChart(ctx.title, ctx.source, {
|
|
42848
|
+
fillArea: true,
|
|
42849
|
+
cumulative: true
|
|
42850
|
+
})
|
|
42851
|
+
},
|
|
42852
|
+
{
|
|
42853
|
+
description: _t("Period-by-period comparison."),
|
|
42854
|
+
build: (ctx) => barChart(ctx.title, ctx.source)
|
|
42855
|
+
},
|
|
42856
|
+
{
|
|
42857
|
+
description: _t("Shows intensity variation across days of the year."),
|
|
42858
|
+
isApplicable: ({ isPercentage }) => !isPercentage,
|
|
42859
|
+
build: (ctx) => calendarChart(ctx.title, ctx.source)
|
|
42860
|
+
}
|
|
42861
|
+
];
|
|
42862
|
+
/** Pattern H — Number + Number */
|
|
42863
|
+
const NUMBER_VS_NUMBER_SUGGESTIONS = [
|
|
42864
|
+
{
|
|
42865
|
+
description: _t("Highlights the second metric compared to the first one."),
|
|
42866
|
+
isApplicable: ({ rowCount1, rowCount2 }) => rowCount1 === 1 && rowCount2 === 1,
|
|
42867
|
+
build: (ctx) => scorecardChart(ctx.title, `=${ctx.lastCellXC}`, {
|
|
42868
|
+
baseline: ctx.prevCellXC ? `=${ctx.prevCellXC}` : void 0,
|
|
42869
|
+
baselineMode: "difference"
|
|
42870
|
+
})
|
|
42871
|
+
},
|
|
42872
|
+
{
|
|
42873
|
+
description: _t("Side-by-side comparison of two numeric series."),
|
|
42874
|
+
build: (ctx) => barChart(ctx.title, ctx.sourceBoth, { legendPosition: "top" })
|
|
42875
|
+
},
|
|
42876
|
+
{
|
|
42877
|
+
description: _t("Reveals correlation between two numeric variables."),
|
|
42878
|
+
isApplicable: ({ rowCount1 }) => rowCount1 > 2,
|
|
42879
|
+
build: (ctx) => scatterChart(ctx.title, ctx.source2)
|
|
42880
|
+
},
|
|
42881
|
+
{
|
|
42882
|
+
description: _t("Bar for the first series, line for the second — good for mixed scales."),
|
|
42883
|
+
isApplicable: ({ rowCount1 }) => rowCount1 > 2,
|
|
42884
|
+
build: (ctx) => comboChart(ctx.title, ctx.sourceBoth)
|
|
42885
|
+
},
|
|
42886
|
+
{
|
|
42887
|
+
description: _t("When both metrics contribute to a total."),
|
|
42888
|
+
isApplicable: ({ rowCount1 }) => rowCount1 > 2,
|
|
42889
|
+
build: (ctx) => lineChart(ctx.title, ctx.sourceBoth, {
|
|
42890
|
+
stacked: true,
|
|
42891
|
+
fillArea: true,
|
|
42892
|
+
legendPosition: "top"
|
|
42893
|
+
})
|
|
42894
|
+
},
|
|
42895
|
+
{
|
|
42896
|
+
description: _t("Shape-based comparison when rows represent named entities."),
|
|
42897
|
+
isApplicable: ({ rowCount1 }) => rowCount1 > 2 && rowCount1 <= MAX_ROWS_FOR_SHAPE_CHART,
|
|
42898
|
+
build: (ctx) => radarChart(ctx.title, ctx.sourceBoth, { legendPosition: "top" })
|
|
42899
|
+
}
|
|
42900
|
+
];
|
|
42901
|
+
/** Pattern I — Categorical + Percentage */
|
|
42902
|
+
const CATEGORICAL_VS_PERCENTAGE_SUGGESTIONS = [
|
|
42903
|
+
{
|
|
42904
|
+
description: _t("Vertical comparison of rates per category."),
|
|
42905
|
+
build: (ctx) => barChart(ctx.title, ctx.source)
|
|
42906
|
+
},
|
|
42907
|
+
{
|
|
42908
|
+
description: _t("Progress-bar style per category."),
|
|
42909
|
+
build: (ctx) => barChart(ctx.title, ctx.source, { horizontal: true })
|
|
42910
|
+
},
|
|
42911
|
+
{
|
|
42912
|
+
description: _t("Share of total percentage across categories."),
|
|
42913
|
+
build: (ctx) => pieChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
42914
|
+
},
|
|
42915
|
+
{
|
|
42916
|
+
description: _t("Comparison of completion rates across categories."),
|
|
42917
|
+
isApplicable: ({ rowCount }) => rowCount > 2 && rowCount <= MAX_ROWS_FOR_SHAPE_CHART,
|
|
42918
|
+
build: (ctx) => radarChart(ctx.title, ctx.source)
|
|
42919
|
+
}
|
|
42920
|
+
];
|
|
42921
|
+
/** Pattern K — Label + Number */
|
|
42922
|
+
const LABEL_VS_NUMBER_SUGGESTIONS = [
|
|
42923
|
+
{
|
|
42924
|
+
description: _t("Highlights the most recent value for the named entity."),
|
|
42925
|
+
isApplicable: ({ rowCount }) => rowCount === 1,
|
|
42926
|
+
build: (ctx) => scorecardChart("", `=${ctx.lastCellXC}`, {
|
|
42927
|
+
humanize: false,
|
|
42928
|
+
baselineMode: "text",
|
|
42929
|
+
baseline: ctx.prevCellXC ? `=${ctx.prevCellXC}` : void 0
|
|
42930
|
+
})
|
|
42931
|
+
},
|
|
42932
|
+
{
|
|
42933
|
+
description: _t("Vertical comparison across named items."),
|
|
42934
|
+
isApplicable: ({ rowCount }) => rowCount > 1,
|
|
42935
|
+
build: (ctx) => barChart(ctx.title, ctx.source)
|
|
42936
|
+
},
|
|
42937
|
+
{
|
|
42938
|
+
description: _t("Works well for named entities with long labels."),
|
|
42939
|
+
isApplicable: ({ rowCount }) => rowCount > 1,
|
|
42940
|
+
build: (ctx) => barChart(ctx.title, ctx.source, { horizontal: true })
|
|
42941
|
+
},
|
|
42942
|
+
{
|
|
42943
|
+
description: _t("Share of total per category."),
|
|
42944
|
+
isApplicable: ({ rowCount }) => rowCount > 1 && rowCount <= MAX_ROWS_FOR_SHAPE_CHART,
|
|
42945
|
+
build: (ctx) => pieChart(ctx.title, ctx.source, {
|
|
42946
|
+
legendPosition: "top",
|
|
42947
|
+
aggregated: true
|
|
42948
|
+
})
|
|
42949
|
+
},
|
|
42950
|
+
{
|
|
42951
|
+
description: _t("Shape-based comparison across labeled items."),
|
|
42952
|
+
isApplicable: ({ rowCount }) => rowCount > 2 && rowCount <= MAX_ROWS_FOR_SHAPE_CHART,
|
|
42953
|
+
build: (ctx) => radarChart(ctx.title, ctx.source)
|
|
42954
|
+
}
|
|
42955
|
+
];
|
|
42956
|
+
/** Pattern M — Categorical + Multiple Numbers */
|
|
42957
|
+
const CATEGORICAL_VS_MULTIPLE_NUMBERS_SUGGESTIONS = [
|
|
42958
|
+
{
|
|
42959
|
+
description: _t("Side-by-side comparison across categories for each series."),
|
|
42960
|
+
build: (ctx) => barChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
42961
|
+
},
|
|
42962
|
+
{
|
|
42963
|
+
description: _t("Shows composition and total per category."),
|
|
42964
|
+
build: (ctx) => barChart(ctx.title, ctx.source, {
|
|
42965
|
+
stacked: true,
|
|
42966
|
+
legendPosition: "top"
|
|
42967
|
+
})
|
|
42968
|
+
},
|
|
42969
|
+
{
|
|
42970
|
+
description: _t("Trend per series across categories."),
|
|
42971
|
+
build: (ctx) => lineChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
42972
|
+
},
|
|
42973
|
+
{
|
|
42974
|
+
description: _t("Volume and composition across categories."),
|
|
42975
|
+
build: (ctx) => lineChart(ctx.title, ctx.source, {
|
|
42976
|
+
stacked: true,
|
|
42977
|
+
fillArea: true,
|
|
42978
|
+
legendPosition: "top"
|
|
42979
|
+
})
|
|
42980
|
+
},
|
|
42981
|
+
{
|
|
42982
|
+
description: _t("Shape comparison across metrics (best for ≤ 10 rows)."),
|
|
42983
|
+
isApplicable: ({ rowCount }) => rowCount > 2 && rowCount <= MAX_ROWS_FOR_SHAPE_CHART,
|
|
42984
|
+
build: (ctx) => radarChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
42985
|
+
}
|
|
42986
|
+
];
|
|
42987
|
+
/** Pattern N — Date + Multiple Numbers */
|
|
42988
|
+
const DATE_VS_MULTIPLE_NUMBERS_SUGGESTIONS = [
|
|
42989
|
+
{
|
|
42990
|
+
description: _t("Trend comparison across multiple metrics over time."),
|
|
42991
|
+
build: (ctx) => lineChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
42992
|
+
},
|
|
42993
|
+
{
|
|
42994
|
+
description: _t("Volume composition over time."),
|
|
42995
|
+
build: (ctx) => lineChart(ctx.title, ctx.source, {
|
|
42996
|
+
stacked: true,
|
|
42997
|
+
fillArea: true,
|
|
42998
|
+
legendPosition: "top"
|
|
42999
|
+
})
|
|
43000
|
+
},
|
|
43001
|
+
{
|
|
43002
|
+
description: _t("Bar for the primary metric, line for the others."),
|
|
43003
|
+
build: (ctx) => comboChart(ctx.title, ctx.source)
|
|
43004
|
+
},
|
|
43005
|
+
{
|
|
43006
|
+
description: _t("Period-by-period grouped comparison."),
|
|
43007
|
+
build: (ctx) => barChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
43008
|
+
}
|
|
43009
|
+
];
|
|
43010
|
+
/** Pattern O — Categorical + Categorical + Number */
|
|
43011
|
+
const MULTIPLE_CATEGORICALS_VS_NUMBER_SUGGESTIONS = [
|
|
43012
|
+
{
|
|
43013
|
+
description: _t("Two-level hierarchy weighted by value."),
|
|
43014
|
+
build: (ctx) => sunburstChart(ctx.title, ctx.hierarchySource)
|
|
43015
|
+
},
|
|
43016
|
+
{
|
|
43017
|
+
description: _t("Proportional nested area weighted by value."),
|
|
43018
|
+
build: (ctx) => treemapChart(ctx.title, ctx.hierarchySource)
|
|
43019
|
+
},
|
|
43020
|
+
{
|
|
43021
|
+
description: _t("One series per inner category, grouped by outer category."),
|
|
43022
|
+
build: (ctx) => barChart(ctx.title, ctx.barSource, { legendPosition: "top" })
|
|
43023
|
+
},
|
|
43024
|
+
{
|
|
43025
|
+
description: _t("Contribution of inner categories per outer category."),
|
|
43026
|
+
build: (ctx) => barChart(ctx.title, ctx.barSource, {
|
|
43027
|
+
stacked: true,
|
|
43028
|
+
legendPosition: "top"
|
|
43029
|
+
})
|
|
43030
|
+
}
|
|
43031
|
+
];
|
|
43032
|
+
/** Pattern P — Categorical + Date + Number */
|
|
43033
|
+
const CATEGORICAL_DATE_NUMBER_SUGGESTIONS = [
|
|
43034
|
+
{
|
|
43035
|
+
description: _t("Trend of values over time."),
|
|
43036
|
+
build: (ctx) => lineChart(ctx.title, ctx.sourceByDate, { legendPosition: "top" })
|
|
43037
|
+
},
|
|
43038
|
+
{
|
|
43039
|
+
description: _t("Volume contribution over time."),
|
|
43040
|
+
build: (ctx) => lineChart(ctx.title, ctx.sourceByDate, {
|
|
43041
|
+
stacked: true,
|
|
43042
|
+
fillArea: true,
|
|
43043
|
+
legendPosition: "top"
|
|
43044
|
+
})
|
|
43045
|
+
},
|
|
43046
|
+
{
|
|
43047
|
+
description: _t("Period × category side-by-side comparison."),
|
|
43048
|
+
build: (ctx) => barChart(ctx.title, ctx.sourceByCat, { legendPosition: "top" })
|
|
43049
|
+
},
|
|
43050
|
+
{
|
|
43051
|
+
description: _t("Composition per period over time."),
|
|
43052
|
+
build: (ctx) => barChart(ctx.title, ctx.sourceByDate, {
|
|
43053
|
+
stacked: true,
|
|
43054
|
+
legendPosition: "top"
|
|
43055
|
+
})
|
|
43056
|
+
}
|
|
43057
|
+
];
|
|
43058
|
+
/** Pattern Q — Label + Multiple Numbers */
|
|
43059
|
+
const LABEL_VS_MULTIPLE_NUMBERS_SUGGESTIONS = [
|
|
43060
|
+
{
|
|
43061
|
+
description: _t("Side-by-side per entity — works well for long labels."),
|
|
43062
|
+
build: (ctx) => barChart(ctx.title, ctx.source, {
|
|
43063
|
+
horizontal: true,
|
|
43064
|
+
legendPosition: "top"
|
|
43065
|
+
})
|
|
43066
|
+
},
|
|
43067
|
+
{
|
|
43068
|
+
description: _t("Grouped bars per entity for direct metric comparison."),
|
|
43069
|
+
build: (ctx) => barChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
43070
|
+
},
|
|
43071
|
+
{
|
|
43072
|
+
description: _t("Shape/profile comparison across metrics for each entity."),
|
|
43073
|
+
isApplicable: ({ rowCount }) => rowCount > 2 && rowCount <= MAX_ROWS_FOR_SHAPE_CHART,
|
|
43074
|
+
build: (ctx) => radarChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
43075
|
+
},
|
|
43076
|
+
{
|
|
43077
|
+
description: _t("Correlation between the two numeric metrics across entities."),
|
|
43078
|
+
isApplicable: ({ rowCount, numColsCount }) => rowCount > 2 && numColsCount === 2,
|
|
43079
|
+
build: (ctx) => scatterChart(ctx.title, ctx.scatterSource)
|
|
43080
|
+
},
|
|
43081
|
+
{
|
|
43082
|
+
description: _t("Three metrics in one view: X position, Y position and bubble size."),
|
|
43083
|
+
isApplicable: ({ numColsCount }) => numColsCount === 3,
|
|
43084
|
+
build: (ctx) => {
|
|
43085
|
+
const bubble = ctx.bubble;
|
|
43086
|
+
return bubbleChart(ctx.title, bubble.xRange, bubble.yRanges, bubble.sizeRange, bubble.labelRange, bubble.hasTitle);
|
|
43087
|
+
}
|
|
43088
|
+
}
|
|
43089
|
+
];
|
|
43090
|
+
/** Pattern R — Categorical + Two Numbers (Population Pyramid or Grouped) */
|
|
43091
|
+
const CATEGORICAL_TWO_NUMBERS_SUGGESTIONS = [
|
|
43092
|
+
{
|
|
43093
|
+
description: _t("Natural fit for symmetric or opposing values per category."),
|
|
43094
|
+
isApplicable: ({ isPyramid }) => isPyramid,
|
|
43095
|
+
build: (ctx) => pyramidChart(ctx.title, ctx.sourceBoth)
|
|
43096
|
+
},
|
|
43097
|
+
{
|
|
43098
|
+
description: _t("Side-by-side comparison of both metrics per category."),
|
|
43099
|
+
build: (ctx) => barChart(ctx.title, ctx.sourceBoth, {
|
|
43100
|
+
legendPosition: "top",
|
|
43101
|
+
aggregated: true
|
|
43102
|
+
})
|
|
43103
|
+
},
|
|
43104
|
+
{
|
|
43105
|
+
description: _t("Bar for the first series, line for the second — good for mixed scales."),
|
|
43106
|
+
build: (ctx) => comboChart(ctx.title, ctx.sourceBoth, { aggregated: true })
|
|
43107
|
+
},
|
|
43108
|
+
{
|
|
43109
|
+
description: _t("Shows total and composition per category."),
|
|
43110
|
+
isApplicable: ({ isPyramid }) => !isPyramid,
|
|
43111
|
+
build: (ctx) => barChart(ctx.title, ctx.sourceBoth, {
|
|
43112
|
+
stacked: true,
|
|
43113
|
+
legendPosition: "top",
|
|
43114
|
+
aggregated: true
|
|
43115
|
+
})
|
|
43116
|
+
}
|
|
43117
|
+
];
|
|
43118
|
+
/** Pattern S — Many Numbers (3+ numeric columns, no categorical/date) */
|
|
43119
|
+
const MANY_NUMBERS_SUGGESTIONS = [
|
|
43120
|
+
{
|
|
43121
|
+
description: _t("Shows the position of the last value within the data's min-max range."),
|
|
43122
|
+
isApplicable: ({ colsLength, rowCount }) => colsLength === 3 && rowCount === 1,
|
|
43123
|
+
build: (ctx) => gaugeChart(ctx.title, ctx.lastCellXC, `=${ctx.firstCellXC}`, `=${ctx.secondCellXC}`)
|
|
43124
|
+
},
|
|
43125
|
+
{
|
|
43126
|
+
description: _t("Trend comparison across all metrics."),
|
|
43127
|
+
isApplicable: ({ rowCount }) => rowCount > 2,
|
|
43128
|
+
build: (ctx) => lineChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
43129
|
+
},
|
|
43130
|
+
{
|
|
43131
|
+
description: _t("Overall shape/profile across all metrics."),
|
|
43132
|
+
isApplicable: ({ rowCount }) => rowCount > 2 && rowCount <= MAX_ROWS_FOR_SHAPE_CHART,
|
|
43133
|
+
build: (ctx) => radarChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
43134
|
+
},
|
|
43135
|
+
{
|
|
43136
|
+
description: _t("Side-by-side comparison of all numeric metrics."),
|
|
43137
|
+
build: (ctx) => barChart(ctx.title, ctx.source, { legendPosition: "top" })
|
|
43138
|
+
},
|
|
43139
|
+
{
|
|
43140
|
+
description: _t("Side-by-side comparison of all numeric metrics."),
|
|
43141
|
+
build: (ctx) => barChart(ctx.title, ctx.source, {
|
|
43142
|
+
legendPosition: "top",
|
|
43143
|
+
stacked: true
|
|
43144
|
+
})
|
|
43145
|
+
}
|
|
43146
|
+
];
|
|
43147
|
+
|
|
43148
|
+
//#endregion
|
|
43149
|
+
//#region src/helpers/figures/charts/chart_suggestion_engine.ts
|
|
43150
|
+
const PYRAMID_HEADER_KEYWORD_PATTERNS = [
|
|
43151
|
+
"male",
|
|
43152
|
+
"female",
|
|
43153
|
+
"man",
|
|
43154
|
+
"woman",
|
|
43155
|
+
"boy",
|
|
43156
|
+
"girl",
|
|
43157
|
+
"before",
|
|
43158
|
+
"after",
|
|
43159
|
+
"previous",
|
|
43160
|
+
"current",
|
|
43161
|
+
"positive",
|
|
43162
|
+
"negative",
|
|
43163
|
+
"gain",
|
|
43164
|
+
"loss",
|
|
43165
|
+
"income",
|
|
43166
|
+
"expense",
|
|
43167
|
+
"revenue",
|
|
43168
|
+
"cost"
|
|
43169
|
+
].map((keyword) => new RegExp(`\\b${keyword}\\b`, "i"));
|
|
43170
|
+
function matchesColumnType(spec, type) {
|
|
43171
|
+
return Array.isArray(spec) ? spec.includes(type) : spec === type;
|
|
43172
|
+
}
|
|
43173
|
+
function matchesShape(pattern, shape) {
|
|
43174
|
+
const { leadingTypes, extension } = pattern;
|
|
43175
|
+
const expectedLength = leadingTypes.length + (extension?.minSize ?? 0);
|
|
43176
|
+
if (extension ? shape.length < expectedLength : shape.length !== expectedLength) return false;
|
|
43177
|
+
return shape.every((type, i) => {
|
|
43178
|
+
const spec = i < leadingTypes.length ? leadingTypes[i] : extension?.type;
|
|
43179
|
+
return spec !== void 0 && matchesColumnType(spec, type);
|
|
43180
|
+
});
|
|
43181
|
+
}
|
|
43182
|
+
function isPyramidLike(numCol1, numCol2) {
|
|
43183
|
+
const h1 = numCol1.header ?? "";
|
|
43184
|
+
const h2 = numCol2.header ?? "";
|
|
43185
|
+
return PYRAMID_HEADER_KEYWORD_PATTERNS.some((re) => re.test(h1) || re.test(h2));
|
|
43186
|
+
}
|
|
43187
|
+
/** Returns the first and last cell XC coordinates, and the second-to-last cell XC coordinate if it exists. */
|
|
43188
|
+
function interestingCellsXc(col) {
|
|
43189
|
+
if (!col) return {
|
|
43190
|
+
firstCellXC: "",
|
|
43191
|
+
lastCellXC: "",
|
|
43192
|
+
prevCellXC: void 0
|
|
43193
|
+
};
|
|
43194
|
+
const firstCellPosition = col.nonEmpty.at(0)?.position;
|
|
43195
|
+
const firstCellXC = firstCellPosition ? toXC(firstCellPosition?.col, firstCellPosition?.row) : "";
|
|
43196
|
+
const lastCellPosition = col.nonEmpty.at(-1)?.position;
|
|
43197
|
+
const lastCellXC = lastCellPosition ? toXC(lastCellPosition?.col, lastCellPosition?.row) : "";
|
|
43198
|
+
const prevCellPosition = col.nonEmpty.at(-2)?.position;
|
|
43199
|
+
return {
|
|
43200
|
+
firstCellXC,
|
|
43201
|
+
lastCellXC,
|
|
43202
|
+
prevCellXC: prevCellPosition ? toXC(prevCellPosition?.col, prevCellPosition?.row) : void 0
|
|
43203
|
+
};
|
|
43204
|
+
}
|
|
43205
|
+
/** Pattern A — Single numeric column */
|
|
43206
|
+
function buildSingleNumberContext([col], getters) {
|
|
43207
|
+
const title = col.header ?? _t("Value");
|
|
43208
|
+
const { firstCellXC, lastCellXC, prevCellXC } = interestingCellsXc(col);
|
|
43209
|
+
return {
|
|
43210
|
+
title,
|
|
43211
|
+
source: rangeSource([dataset(col.zone, getters)], col.hasHeader),
|
|
43212
|
+
rowCount: col.rowCount,
|
|
43213
|
+
firstCellXC,
|
|
43214
|
+
lastCellXC,
|
|
43215
|
+
prevCellXC
|
|
43216
|
+
};
|
|
43217
|
+
}
|
|
43218
|
+
/** Pattern B — Single percentage column */
|
|
43219
|
+
function buildSinglePercentageContext([col], getters) {
|
|
43220
|
+
const hasTitle = col.hasHeader;
|
|
43221
|
+
const title = col.header ?? _t("Rate");
|
|
43222
|
+
const { firstCellXC, lastCellXC, prevCellXC } = interestingCellsXc(col);
|
|
43223
|
+
const source = rangeSource([dataset(col.zone, getters)], hasTitle);
|
|
43224
|
+
const isAboveOne = (col.maxValue ?? 0) > 1;
|
|
43225
|
+
return {
|
|
43226
|
+
title,
|
|
43227
|
+
source,
|
|
43228
|
+
rowCount: col.rowCount,
|
|
43229
|
+
firstCellXC,
|
|
43230
|
+
lastCellXC,
|
|
43231
|
+
prevCellXC,
|
|
43232
|
+
isAboveOne
|
|
43233
|
+
};
|
|
43234
|
+
}
|
|
43235
|
+
/** Pattern C — Single date column */
|
|
43236
|
+
function buildSingleDateContext([col]) {
|
|
43237
|
+
const { lastCellXC } = interestingCellsXc(col);
|
|
43238
|
+
return {
|
|
43239
|
+
title: col.header ?? _t("Date"),
|
|
43240
|
+
lastCellXC,
|
|
43241
|
+
rowCount: col.rowCount
|
|
43242
|
+
};
|
|
43243
|
+
}
|
|
43244
|
+
/** Pattern D — Single categorical column */
|
|
43245
|
+
function buildSingleCategoricalContext([col], getters) {
|
|
43246
|
+
const hasTitle = col.hasHeader;
|
|
43247
|
+
const title = col.header ?? _t("Category");
|
|
43248
|
+
const range = getUnboundRange(getters, col.zone);
|
|
43249
|
+
return {
|
|
43250
|
+
title,
|
|
43251
|
+
source: rangeSource([dataset(col.zone, getters)], hasTitle, range),
|
|
43252
|
+
range
|
|
43253
|
+
};
|
|
43254
|
+
}
|
|
43255
|
+
/** Pattern E — Single label column */
|
|
43256
|
+
function buildSingleLabelContext([col]) {
|
|
43257
|
+
const title = col.header ?? _t("Label");
|
|
43258
|
+
const { lastCellXC } = interestingCellsXc(col);
|
|
43259
|
+
return {
|
|
43260
|
+
title,
|
|
43261
|
+
lastCellXC,
|
|
43262
|
+
rowCount: col.rowCount
|
|
43263
|
+
};
|
|
43264
|
+
}
|
|
43265
|
+
/** Pattern F — Categorical + Number */
|
|
43266
|
+
function buildCategoricalVsNumberContext([catCol, numCol], getters) {
|
|
43267
|
+
const labelRange = getUnboundRange(getters, catCol.zone);
|
|
43268
|
+
const hasTitle = numCol.hasHeader;
|
|
43269
|
+
return {
|
|
43270
|
+
title: numCol.header ? _t("%(numberHeader)s by %(categoryHeader)s", {
|
|
43271
|
+
numberHeader: numCol.header,
|
|
43272
|
+
categoryHeader: catCol.header ?? _t("Category")
|
|
43273
|
+
}) : _t("By Category"),
|
|
43274
|
+
source: rangeSource([dataset(numCol.zone, getters)], hasTitle, labelRange),
|
|
43275
|
+
treemapSource: rangeSource([dataset(catCol.zone, getters)], hasTitle, getUnboundRange(getters, numCol.zone))
|
|
43276
|
+
};
|
|
43277
|
+
}
|
|
43278
|
+
/** Patterns G & J — Date + Number or Date + Percentage */
|
|
43279
|
+
function buildDateVsSeriesContext([dateCol, seriesCol], getters) {
|
|
43280
|
+
const isPercentage = seriesCol.type === "percentage";
|
|
43281
|
+
const labelRange = getUnboundRange(getters, dateCol.zone);
|
|
43282
|
+
const hasTitle = isDatasetTitled(getters, dateCol.zone);
|
|
43283
|
+
return {
|
|
43284
|
+
title: seriesCol.header ? _t("%(seriesHeader)s over time", { seriesHeader: seriesCol.header }) : isPercentage ? _t("Rate over Time") : _t("Over Time"),
|
|
43285
|
+
source: rangeSource([dataset(seriesCol.zone, getters)], hasTitle, labelRange),
|
|
43286
|
+
isPercentage
|
|
43287
|
+
};
|
|
43288
|
+
}
|
|
43289
|
+
/** Pattern H — Number + Number */
|
|
43290
|
+
function buildNumberVsNumberContext([col1, col2], getters) {
|
|
43291
|
+
const title = col1.header && col2.header ? _t("%(col2Header)s vs %(col1Header)s", {
|
|
43292
|
+
col2Header: col2.header,
|
|
43293
|
+
col1Header: col1.header
|
|
43294
|
+
}) : _t("Correlation");
|
|
43295
|
+
const hasTitle = col1.hasHeader || col2.hasHeader;
|
|
43296
|
+
const source2 = rangeSource([dataset(col2.zone, getters)], col2.hasHeader, getUnboundRange(getters, col1.zone));
|
|
43297
|
+
const sourceBoth = rangeSource([dataset(col1.zone, getters, "0"), dataset(col2.zone, getters, "1")], hasTitle);
|
|
43298
|
+
const { lastCellXC } = interestingCellsXc(col2);
|
|
43299
|
+
const { lastCellXC: prevCellXC } = interestingCellsXc(col1);
|
|
43300
|
+
return {
|
|
43301
|
+
title,
|
|
43302
|
+
source2,
|
|
43303
|
+
sourceBoth,
|
|
43304
|
+
rowCount1: col1.rowCount,
|
|
43305
|
+
rowCount2: col2.rowCount,
|
|
43306
|
+
lastCellXC,
|
|
43307
|
+
prevCellXC
|
|
43308
|
+
};
|
|
43309
|
+
}
|
|
43310
|
+
/** Pattern I — Categorical + Percentage */
|
|
43311
|
+
function buildCategoricalVsPercentageContext([catCol, pctCol], getters) {
|
|
43312
|
+
const labelRange = getUnboundRange(getters, catCol.zone);
|
|
43313
|
+
const hasTitle = pctCol.hasHeader;
|
|
43314
|
+
return {
|
|
43315
|
+
title: pctCol.header ? _t("%(percentageHeader)s by %(categoryHeader)s", {
|
|
43316
|
+
percentageHeader: pctCol.header,
|
|
43317
|
+
categoryHeader: catCol.header ?? _t("Category")
|
|
43318
|
+
}) : _t("Rates by Category"),
|
|
43319
|
+
source: rangeSource([dataset(pctCol.zone, getters)], hasTitle, labelRange),
|
|
43320
|
+
rowCount: catCol.rowCount
|
|
43321
|
+
};
|
|
43322
|
+
}
|
|
43323
|
+
/** Pattern K — Label + Number */
|
|
43324
|
+
function buildLabelVsNumberContext([labelCol, numCol], getters) {
|
|
43325
|
+
const labelRange = getUnboundRange(getters, labelCol.zone);
|
|
43326
|
+
const hasTitle = numCol.hasHeader;
|
|
43327
|
+
const title = numCol.header ? _t("%(numberHeader)s by %(labelHeader)s", {
|
|
43328
|
+
numberHeader: numCol.header,
|
|
43329
|
+
labelHeader: labelCol.header ?? _t("Name")
|
|
43330
|
+
}) : _t("By Name");
|
|
43331
|
+
const source = rangeSource([dataset(numCol.zone, getters)], hasTitle, labelRange);
|
|
43332
|
+
const { lastCellXC } = interestingCellsXc(numCol);
|
|
43333
|
+
const { lastCellXC: prevCellXC } = interestingCellsXc(labelCol);
|
|
43334
|
+
return {
|
|
43335
|
+
title,
|
|
43336
|
+
source,
|
|
43337
|
+
rowCount: labelCol.rowCount,
|
|
43338
|
+
lastCellXC,
|
|
43339
|
+
prevCellXC
|
|
43340
|
+
};
|
|
43341
|
+
}
|
|
43342
|
+
/** Pattern M — Categorical + Multiple Numbers */
|
|
43343
|
+
function buildCategoricalVsMultipleNumbersContext([catCol, ...numCols], getters) {
|
|
43344
|
+
const labelRange = getUnboundRange(getters, catCol.zone);
|
|
43345
|
+
const hasTitle = numCols.some((c) => c.hasHeader);
|
|
43346
|
+
return {
|
|
43347
|
+
title: catCol.header ? _t("By %(header)s", { header: catCol.header }) : _t("Multi-series"),
|
|
43348
|
+
source: rangeSource(numCols.map((c, i) => dataset(c.zone, getters, String(i))), hasTitle, labelRange),
|
|
43349
|
+
rowCount: catCol.rowCount
|
|
43350
|
+
};
|
|
43351
|
+
}
|
|
43352
|
+
/** Pattern N — Date + Multiple Numbers */
|
|
43353
|
+
function buildDateVsMultipleNumbersContext([dateCol, ...numCols], getters) {
|
|
43354
|
+
const labelRange = getUnboundRange(getters, dateCol.zone);
|
|
43355
|
+
const hasTitle = numCols.some((c) => c.hasHeader) || isDatasetTitled(getters, dateCol.zone);
|
|
43356
|
+
return {
|
|
43357
|
+
title: numCols.length === 1 && numCols[0].header ? _t("%(header)s over time", { header: numCols[0].header }) : _t("Multi-series over Time"),
|
|
43358
|
+
source: rangeSource(numCols.map((c, i) => dataset(c.zone, getters, String(i))), hasTitle, labelRange)
|
|
43359
|
+
};
|
|
43360
|
+
}
|
|
43361
|
+
/** Pattern O — Categorical + Categorical + Number */
|
|
43362
|
+
function buildMultipleCategoricalsVsNumberContext([cat1, cat2, numCol], getters) {
|
|
43363
|
+
const title = numCol.header ? _t("%(numHeader)s by %(cat1Header)s and %(cat2Header)s", {
|
|
43364
|
+
numHeader: numCol.header,
|
|
43365
|
+
cat1Header: cat1.header ?? _t("Level 1"),
|
|
43366
|
+
cat2Header: cat2.header ?? _t("Level 2")
|
|
43367
|
+
}) : _t("Two-level hierarchy");
|
|
43368
|
+
const hasTitle = numCol.hasHeader;
|
|
43369
|
+
return {
|
|
43370
|
+
title,
|
|
43371
|
+
hierarchySource: rangeSource([dataset(cat1.zone, getters, "0"), dataset(cat2.zone, getters, "1")], hasTitle, getUnboundRange(getters, numCol.zone)),
|
|
43372
|
+
barSource: rangeSource([dataset(numCol.zone, getters)], hasTitle, getUnboundRange(getters, cat1.zone))
|
|
43373
|
+
};
|
|
43374
|
+
}
|
|
43375
|
+
/** Pattern P — Categorical + Date + Number */
|
|
43376
|
+
function buildCategoricalDateNumberContext([catCol, dateCol, numCol], getters) {
|
|
43377
|
+
const dateRange = getUnboundRange(getters, dateCol.zone);
|
|
43378
|
+
const catRange = getUnboundRange(getters, catCol.zone);
|
|
43379
|
+
const hasTitle = numCol.hasHeader;
|
|
43380
|
+
return {
|
|
43381
|
+
title: numCol.header ? _t("%(numHeader)s by %(catHeader)s over %(dateHeader)s", {
|
|
43382
|
+
numHeader: numCol.header,
|
|
43383
|
+
catHeader: catCol.header ?? _t("Category"),
|
|
43384
|
+
dateHeader: dateCol.header ?? _t("Time")
|
|
43385
|
+
}) : _t("Multi-series over Time"),
|
|
43386
|
+
sourceByDate: rangeSource([dataset(numCol.zone, getters)], hasTitle, dateRange),
|
|
43387
|
+
sourceByCat: rangeSource([dataset(numCol.zone, getters)], hasTitle, catRange)
|
|
43388
|
+
};
|
|
43389
|
+
}
|
|
43390
|
+
/** Pattern Q — Label + Multiple Numbers */
|
|
43391
|
+
function buildLabelVsMultipleNumbersContext([labelCol, ...numCols], getters) {
|
|
43392
|
+
const labelRange = getUnboundRange(getters, labelCol.zone);
|
|
43393
|
+
const hasTitle = numCols.some((c) => c.hasHeader);
|
|
43394
|
+
const title = labelCol.header ? _t("By %(header)s", { header: labelCol.header }) : _t("Profile Comparison");
|
|
43395
|
+
const source = rangeSource(numCols.map((c, i) => dataset(c.zone, getters, String(i))), hasTitle, labelRange);
|
|
43396
|
+
const scatterSource = rangeSource([dataset(numCols[1].zone, getters)], numCols[1].hasHeader, getUnboundRange(getters, numCols[0].zone));
|
|
43397
|
+
const bubble = numCols.length === 3 ? {
|
|
43398
|
+
xRange: getUnboundRange(getters, numCols[0].zone),
|
|
43399
|
+
yRanges: [getUnboundRange(getters, numCols[1].zone)],
|
|
43400
|
+
sizeRange: getUnboundRange(getters, numCols[2].zone),
|
|
43401
|
+
labelRange,
|
|
43402
|
+
hasTitle
|
|
43403
|
+
} : void 0;
|
|
43404
|
+
return {
|
|
43405
|
+
title,
|
|
43406
|
+
source,
|
|
43407
|
+
rowCount: labelCol.rowCount,
|
|
43408
|
+
numColsCount: numCols.length,
|
|
43409
|
+
scatterSource,
|
|
43410
|
+
bubble
|
|
43411
|
+
};
|
|
43412
|
+
}
|
|
43413
|
+
/** Pattern R — Categorical + Two Numbers (Population Pyramid or Grouped) */
|
|
43414
|
+
function buildCategoricalTwoNumbersContext([catCol, numCol1, numCol2], getters) {
|
|
43415
|
+
const catRange = getUnboundRange(getters, catCol.zone);
|
|
43416
|
+
const hasTitle = numCol1.hasHeader || numCol2.hasHeader;
|
|
43417
|
+
const title = numCol1.header && numCol2.header ? _t("%(num1Header)s vs %(num2Header)s by %(catHeader)s", {
|
|
43418
|
+
num1Header: numCol1.header,
|
|
43419
|
+
num2Header: numCol2.header,
|
|
43420
|
+
catHeader: catCol.header ?? _t("Category")
|
|
43421
|
+
}) : catCol.header ? _t("By %(header)s", { header: catCol.header }) : _t("Category Comparison");
|
|
43422
|
+
const isPyramid = isPyramidLike(numCol1, numCol2);
|
|
43423
|
+
return {
|
|
43424
|
+
title,
|
|
43425
|
+
sourceBoth: rangeSource([dataset(numCol1.zone, getters, "0"), dataset(numCol2.zone, getters, "1")], hasTitle, catRange),
|
|
43426
|
+
isPyramid
|
|
43427
|
+
};
|
|
43428
|
+
}
|
|
43429
|
+
/** Pattern S — Many Numbers (3+ numeric columns, no categorical/date) */
|
|
43430
|
+
function buildManyNumbersContext(cols, getters) {
|
|
43431
|
+
const title = cols.every((c) => c.hasHeader) ? cols.map((c) => c.header).join(" / ") : _t("KPI Overview");
|
|
43432
|
+
const hasTitle = cols.some((c) => c.hasHeader);
|
|
43433
|
+
const source = rangeSource(cols.map((c, i) => dataset(c.zone, getters, String(i))), hasTitle);
|
|
43434
|
+
const { lastCellXC } = interestingCellsXc(cols[2]);
|
|
43435
|
+
const { lastCellXC: firstCellXC } = interestingCellsXc(cols[0]);
|
|
43436
|
+
const { lastCellXC: secondCellXC } = interestingCellsXc(cols[1]);
|
|
43437
|
+
return {
|
|
43438
|
+
title,
|
|
43439
|
+
source,
|
|
43440
|
+
colsLength: cols.length,
|
|
43441
|
+
rowCount: cols[0].rowCount,
|
|
43442
|
+
lastCellXC,
|
|
43443
|
+
firstCellXC,
|
|
43444
|
+
secondCellXC
|
|
43445
|
+
};
|
|
43446
|
+
}
|
|
43447
|
+
const NUMBER_OR_PERCENTAGE = ["number", "percentage"];
|
|
43448
|
+
const EXACT_PATTERNS = [
|
|
43449
|
+
{
|
|
43450
|
+
pattern: { leadingTypes: ["number"] },
|
|
43451
|
+
buildContext: buildSingleNumberContext,
|
|
43452
|
+
suggestions: SINGLE_NUMBER_COLUMN_SUGGESTIONS
|
|
43453
|
+
},
|
|
43454
|
+
{
|
|
43455
|
+
pattern: { leadingTypes: ["percentage"] },
|
|
43456
|
+
buildContext: buildSinglePercentageContext,
|
|
43457
|
+
suggestions: SINGLE_PERCENTAGE_COLUMN_SUGGESTIONS
|
|
43458
|
+
},
|
|
43459
|
+
{
|
|
43460
|
+
pattern: { leadingTypes: ["date"] },
|
|
43461
|
+
buildContext: buildSingleDateContext,
|
|
43462
|
+
suggestions: SINGLE_DATE_COLUMN_SUGGESTIONS
|
|
43463
|
+
},
|
|
43464
|
+
{
|
|
43465
|
+
pattern: { leadingTypes: ["categorical"] },
|
|
43466
|
+
buildContext: buildSingleCategoricalContext,
|
|
43467
|
+
suggestions: SINGLE_CATEGORICAL_COLUMN_SUGGESTIONS
|
|
43468
|
+
},
|
|
43469
|
+
{
|
|
43470
|
+
pattern: { leadingTypes: ["label"] },
|
|
43471
|
+
buildContext: buildSingleLabelContext,
|
|
43472
|
+
suggestions: SINGLE_LABEL_COLUMN_SUGGESTIONS
|
|
43473
|
+
},
|
|
43474
|
+
{
|
|
43475
|
+
pattern: { leadingTypes: ["categorical", "number"] },
|
|
43476
|
+
buildContext: buildCategoricalVsNumberContext,
|
|
43477
|
+
suggestions: CATEGORICAL_VS_NUMBER_SUGGESTIONS
|
|
43478
|
+
},
|
|
43479
|
+
{
|
|
43480
|
+
pattern: { leadingTypes: ["date", NUMBER_OR_PERCENTAGE] },
|
|
43481
|
+
buildContext: buildDateVsSeriesContext,
|
|
43482
|
+
suggestions: DATE_VS_SERIES_SUGGESTIONS
|
|
43483
|
+
},
|
|
43484
|
+
{
|
|
43485
|
+
pattern: { leadingTypes: ["number", "number"] },
|
|
43486
|
+
buildContext: buildNumberVsNumberContext,
|
|
43487
|
+
suggestions: NUMBER_VS_NUMBER_SUGGESTIONS
|
|
43488
|
+
},
|
|
43489
|
+
{
|
|
43490
|
+
pattern: { leadingTypes: ["categorical", "percentage"] },
|
|
43491
|
+
buildContext: buildCategoricalVsPercentageContext,
|
|
43492
|
+
suggestions: CATEGORICAL_VS_PERCENTAGE_SUGGESTIONS
|
|
43493
|
+
},
|
|
43494
|
+
{
|
|
43495
|
+
pattern: { leadingTypes: ["label", "number"] },
|
|
43496
|
+
buildContext: buildLabelVsNumberContext,
|
|
43497
|
+
suggestions: LABEL_VS_NUMBER_SUGGESTIONS
|
|
43498
|
+
},
|
|
43499
|
+
{
|
|
43500
|
+
pattern: { leadingTypes: ["label", "percentage"] },
|
|
43501
|
+
buildContext: buildCategoricalVsPercentageContext,
|
|
43502
|
+
suggestions: CATEGORICAL_VS_PERCENTAGE_SUGGESTIONS
|
|
43503
|
+
},
|
|
43504
|
+
{
|
|
43505
|
+
pattern: { leadingTypes: [
|
|
43506
|
+
"categorical",
|
|
43507
|
+
"categorical",
|
|
43508
|
+
"number"
|
|
43509
|
+
] },
|
|
43510
|
+
buildContext: buildMultipleCategoricalsVsNumberContext,
|
|
43511
|
+
suggestions: MULTIPLE_CATEGORICALS_VS_NUMBER_SUGGESTIONS
|
|
43512
|
+
},
|
|
43513
|
+
{
|
|
43514
|
+
pattern: { leadingTypes: [
|
|
43515
|
+
"categorical",
|
|
43516
|
+
"label",
|
|
43517
|
+
"number"
|
|
43518
|
+
] },
|
|
43519
|
+
buildContext: buildMultipleCategoricalsVsNumberContext,
|
|
43520
|
+
suggestions: MULTIPLE_CATEGORICALS_VS_NUMBER_SUGGESTIONS
|
|
43521
|
+
},
|
|
43522
|
+
{
|
|
43523
|
+
pattern: { leadingTypes: [
|
|
43524
|
+
"categorical",
|
|
43525
|
+
"date",
|
|
43526
|
+
NUMBER_OR_PERCENTAGE
|
|
43527
|
+
] },
|
|
43528
|
+
buildContext: buildCategoricalDateNumberContext,
|
|
43529
|
+
suggestions: CATEGORICAL_DATE_NUMBER_SUGGESTIONS
|
|
43530
|
+
},
|
|
43531
|
+
{
|
|
43532
|
+
pattern: { leadingTypes: [
|
|
43533
|
+
"categorical",
|
|
43534
|
+
NUMBER_OR_PERCENTAGE,
|
|
43535
|
+
NUMBER_OR_PERCENTAGE
|
|
43536
|
+
] },
|
|
43537
|
+
buildContext: buildCategoricalTwoNumbersContext,
|
|
43538
|
+
suggestions: CATEGORICAL_TWO_NUMBERS_SUGGESTIONS
|
|
43539
|
+
}
|
|
43540
|
+
];
|
|
43541
|
+
const EXTENDABLE_PATTERNS = [
|
|
43542
|
+
{
|
|
43543
|
+
pattern: {
|
|
43544
|
+
leadingTypes: [],
|
|
43545
|
+
extension: {
|
|
43546
|
+
type: NUMBER_OR_PERCENTAGE,
|
|
43547
|
+
minSize: 3
|
|
43548
|
+
}
|
|
43549
|
+
},
|
|
43550
|
+
buildContext: buildManyNumbersContext,
|
|
43551
|
+
suggestions: MANY_NUMBERS_SUGGESTIONS
|
|
43552
|
+
},
|
|
43553
|
+
{
|
|
43554
|
+
pattern: {
|
|
43555
|
+
leadingTypes: ["label"],
|
|
43556
|
+
extension: {
|
|
43557
|
+
type: NUMBER_OR_PERCENTAGE,
|
|
43558
|
+
minSize: 2
|
|
43559
|
+
}
|
|
43560
|
+
},
|
|
43561
|
+
buildContext: buildLabelVsMultipleNumbersContext,
|
|
43562
|
+
suggestions: LABEL_VS_MULTIPLE_NUMBERS_SUGGESTIONS
|
|
43563
|
+
},
|
|
43564
|
+
{
|
|
43565
|
+
pattern: {
|
|
43566
|
+
leadingTypes: ["categorical"],
|
|
43567
|
+
extension: {
|
|
43568
|
+
type: NUMBER_OR_PERCENTAGE,
|
|
43569
|
+
minSize: 2
|
|
43570
|
+
}
|
|
43571
|
+
},
|
|
43572
|
+
buildContext: buildCategoricalVsMultipleNumbersContext,
|
|
43573
|
+
suggestions: CATEGORICAL_VS_MULTIPLE_NUMBERS_SUGGESTIONS
|
|
43574
|
+
},
|
|
43575
|
+
{
|
|
43576
|
+
pattern: {
|
|
43577
|
+
leadingTypes: ["date"],
|
|
43578
|
+
extension: {
|
|
43579
|
+
type: NUMBER_OR_PERCENTAGE,
|
|
43580
|
+
minSize: 2
|
|
43581
|
+
}
|
|
43582
|
+
},
|
|
43583
|
+
buildContext: buildDateVsMultipleNumbersContext,
|
|
43584
|
+
suggestions: DATE_VS_MULTIPLE_NUMBERS_SUGGESTIONS
|
|
43585
|
+
}
|
|
43586
|
+
];
|
|
43587
|
+
function getChartSuggestions(zones, getters) {
|
|
43588
|
+
const cols = analyzeColumns(zones, getters);
|
|
43589
|
+
if (cols.some((c) => c.type === "error")) return [];
|
|
43590
|
+
const nonEmpty = cols.filter((c) => c.type !== "empty");
|
|
43591
|
+
if (!nonEmpty.length) return [];
|
|
43592
|
+
const shape = nonEmpty.map((c) => c.type);
|
|
43593
|
+
const rule = EXACT_PATTERNS.find((rule) => matchesShape(rule.pattern, shape)) ?? EXTENDABLE_PATTERNS.find((rule) => matchesShape(rule.pattern, shape));
|
|
43594
|
+
if (!rule) return [];
|
|
43595
|
+
const ctx = rule.buildContext(nonEmpty, getters);
|
|
43596
|
+
return rule.suggestions.filter((suggestion) => suggestion.isApplicable?.(ctx) ?? true).map((suggestion) => ({
|
|
43597
|
+
description: suggestion.description.toString(),
|
|
43598
|
+
definition: suggestion.build(ctx)
|
|
43599
|
+
}));
|
|
43600
|
+
}
|
|
43601
|
+
|
|
43602
|
+
//#endregion
|
|
43603
|
+
//#region src/components/side_panel/data_analysis/data_analysis_store.ts
|
|
43604
|
+
var DataAnalysisStore = class extends SpreadsheetStore {
|
|
43605
|
+
mutators = [];
|
|
43606
|
+
hasData = false;
|
|
43607
|
+
chartSuggestions = [];
|
|
43608
|
+
isDirty = false;
|
|
43609
|
+
ranges;
|
|
43610
|
+
constructor(get) {
|
|
43611
|
+
super(get);
|
|
43612
|
+
this.model.selection.observe(this, { handleEvent: () => this.refresh() });
|
|
43613
|
+
this.onDispose(() => {
|
|
43614
|
+
this.model.selection.unobserve(this);
|
|
43615
|
+
});
|
|
43616
|
+
this.refresh();
|
|
43617
|
+
}
|
|
43618
|
+
handle(cmd) {
|
|
43619
|
+
if (invalidateEvaluationCommands.has(cmd.type) || cmd.type === "UPDATE_CELL" && ("content" in cmd || "format" in cmd)) this.isDirty = true;
|
|
43620
|
+
switch (cmd.type) {
|
|
43621
|
+
case "HIDE_COLUMNS_ROWS":
|
|
43622
|
+
case "UNHIDE_COLUMNS_ROWS":
|
|
43623
|
+
case "GROUP_HEADERS":
|
|
43624
|
+
case "UNGROUP_HEADERS":
|
|
43625
|
+
case "ACTIVATE_SHEET":
|
|
43626
|
+
case "ACTIVATE_NEXT_SHEET":
|
|
43627
|
+
case "ACTIVATE_PREVIOUS_SHEET":
|
|
43628
|
+
case "EVALUATE_CELLS":
|
|
43629
|
+
case "SET_FORMATTING":
|
|
43630
|
+
case "CLEAR_FORMATTING":
|
|
43631
|
+
this.isDirty = true;
|
|
43632
|
+
break;
|
|
43633
|
+
}
|
|
43634
|
+
}
|
|
43635
|
+
finalize() {
|
|
43636
|
+
if (this.isDirty) {
|
|
43637
|
+
this.refresh();
|
|
43638
|
+
this.isDirty = false;
|
|
43639
|
+
}
|
|
43640
|
+
}
|
|
43641
|
+
refresh() {
|
|
43642
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
43643
|
+
const zones = this.getters.getSelectedZones();
|
|
43644
|
+
this.ranges = zones.map(zoneToXc);
|
|
43645
|
+
this.hasData = zones.some((zone) => this.getters.getEvaluatedCellsInZone(sheetId, zone).some((cell) => cell.type !== "empty"));
|
|
43646
|
+
const suggestions = this.hasData ? getChartSuggestions(zones, this.getters) : [];
|
|
43647
|
+
this.chartSuggestions = suggestions;
|
|
43648
|
+
}
|
|
43649
|
+
};
|
|
43650
|
+
|
|
43651
|
+
//#endregion
|
|
43652
|
+
//#region src/components/side_panel/data_analysis/data_analysis_panel.ts
|
|
43653
|
+
var DataAnalysisPanel = class extends Component {
|
|
43654
|
+
static template = "o-spreadsheet-DataAnalysisPanel";
|
|
43655
|
+
static components = {
|
|
43656
|
+
Section,
|
|
43657
|
+
ChartSuggestionPreview
|
|
43658
|
+
};
|
|
43659
|
+
store;
|
|
43660
|
+
setup() {
|
|
43661
|
+
this.store = useLocalStore(DataAnalysisStore);
|
|
43662
|
+
}
|
|
43663
|
+
onStartChartSuggestionDrag(definition, ev) {
|
|
43664
|
+
startChartDragAndDrop(this.env, definition, ev);
|
|
43665
|
+
}
|
|
43666
|
+
};
|
|
43667
|
+
|
|
42147
43668
|
//#endregion
|
|
42148
43669
|
//#region src/registries/data_source_registry.ts
|
|
42149
43670
|
const unusedDataSourceRegistry = new Registry();
|
|
@@ -42220,7 +43741,7 @@ var DataSourcesCleanup = class extends Component {
|
|
|
42220
43741
|
var DataValidationPreview = class extends Component {
|
|
42221
43742
|
static template = "o-spreadsheet-DataValidationPreview";
|
|
42222
43743
|
props = useProps({ rule: types$1.DataValidationRule() });
|
|
42223
|
-
dvPreviewRef = signal(
|
|
43744
|
+
dvPreviewRef = signal.ref();
|
|
42224
43745
|
setup() {
|
|
42225
43746
|
useHighlightsOnHover(this.dvPreviewRef, this);
|
|
42226
43747
|
}
|
|
@@ -43719,7 +45240,7 @@ var NamedRangePreview = class extends Component {
|
|
|
43719
45240
|
};
|
|
43720
45241
|
props = useProps({ namedRange: types$1.NamedRange() });
|
|
43721
45242
|
state = proxy({});
|
|
43722
|
-
namedRangePreviewRef = signal(
|
|
45243
|
+
namedRangePreviewRef = signal.ref();
|
|
43723
45244
|
setup() {
|
|
43724
45245
|
useHighlightsOnHover(this.namedRangePreviewRef, this);
|
|
43725
45246
|
}
|
|
@@ -44210,7 +45731,7 @@ var FilterMenuValueItem = class extends Component {
|
|
|
44210
45731
|
onClick: types$1.function(),
|
|
44211
45732
|
scrolledTo: types$1.or([types$1.literal("top"), types$1.literal("bottom")]).optional()
|
|
44212
45733
|
});
|
|
44213
|
-
itemRef = signal(
|
|
45734
|
+
itemRef = signal.ref();
|
|
44214
45735
|
setup() {
|
|
44215
45736
|
onWillPatch(() => {
|
|
44216
45737
|
if (this.props.scrolledTo) this.scrollListToSelectedValue();
|
|
@@ -44450,7 +45971,7 @@ var PivotFilterEditor = class extends Component {
|
|
|
44450
45971
|
onFiltersUpdated: types$1.function()
|
|
44451
45972
|
});
|
|
44452
45973
|
state;
|
|
44453
|
-
buttonFilter = signal(
|
|
45974
|
+
buttonFilter = signal.ref();
|
|
44454
45975
|
popover;
|
|
44455
45976
|
setup() {
|
|
44456
45977
|
useExternalListener(window, "click", (ev) => {
|
|
@@ -44579,11 +46100,11 @@ var AddDimensionButton = class extends Component {
|
|
|
44579
46100
|
onFieldPicked: types$1.function(),
|
|
44580
46101
|
fields: types$1.array()
|
|
44581
46102
|
});
|
|
44582
|
-
buttonRef = signal(
|
|
46103
|
+
buttonRef = signal.ref(HTMLButtonElement);
|
|
44583
46104
|
popover = proxy({ isOpen: false });
|
|
44584
46105
|
search = proxy({ input: "" });
|
|
44585
46106
|
autoComplete;
|
|
44586
|
-
autofocusRef = signal(
|
|
46107
|
+
autofocusRef = signal.ref(HTMLInputElement);
|
|
44587
46108
|
setup() {
|
|
44588
46109
|
this.autoComplete = useLocalStore(AutoCompleteStore);
|
|
44589
46110
|
this.autoComplete.useProvider(this.getProvider());
|
|
@@ -44992,7 +46513,7 @@ var PivotLayoutConfigurator = class extends Component {
|
|
|
44992
46513
|
getScrollableContainerEl: types$1.function().optional(),
|
|
44993
46514
|
pivotId: types$1.UID()
|
|
44994
46515
|
});
|
|
44995
|
-
dimensionsRef = signal(
|
|
46516
|
+
dimensionsRef = signal.ref();
|
|
44996
46517
|
dragAndDrop = useDragAndDropListItems();
|
|
44997
46518
|
AGGREGATORS = AGGREGATORS;
|
|
44998
46519
|
isDateOrDatetimeField = isDateOrDatetimeField;
|
|
@@ -45063,16 +46584,7 @@ var PivotLayoutConfigurator = class extends Component {
|
|
|
45063
46584
|
});
|
|
45064
46585
|
}
|
|
45065
46586
|
getDimensionElementsRects() {
|
|
45066
|
-
return Array.from(this.dimensionsRef().children).map((el) =>
|
|
45067
|
-
const style = getComputedStyle(el);
|
|
45068
|
-
const rect = el.getBoundingClientRect();
|
|
45069
|
-
return {
|
|
45070
|
-
x: rect.x,
|
|
45071
|
-
y: rect.y,
|
|
45072
|
-
width: rect.width + parseInt(style.marginLeft || "0") + parseInt(style.marginRight || "0"),
|
|
45073
|
-
height: rect.height + parseInt(style.marginTop || "0") + parseInt(style.marginBottom || "0")
|
|
45074
|
-
};
|
|
45075
|
-
});
|
|
46587
|
+
return Array.from(this.dimensionsRef().children).map((el) => getBoundingRectWithMargins(el));
|
|
45076
46588
|
}
|
|
45077
46589
|
removeDimension(dimension) {
|
|
45078
46590
|
const { columns, rows } = this.props.definition;
|
|
@@ -45637,7 +47149,7 @@ var PivotSpreadsheetSidePanel = class extends Component {
|
|
|
45637
47149
|
});
|
|
45638
47150
|
store;
|
|
45639
47151
|
state;
|
|
45640
|
-
pivotSidePanelRef = signal(
|
|
47152
|
+
pivotSidePanelRef = signal.ref();
|
|
45641
47153
|
setup() {
|
|
45642
47154
|
this.store = useLocalStore(PivotSidePanelStore, this.props.pivotId);
|
|
45643
47155
|
this.state = proxy({
|
|
@@ -46877,7 +48389,7 @@ var TableStylePreview = class extends Component {
|
|
|
46877
48389
|
selected: types$1.boolean().optional(),
|
|
46878
48390
|
onClick: types$1.function().optional()
|
|
46879
48391
|
});
|
|
46880
|
-
canvasRef = signal(
|
|
48392
|
+
canvasRef = signal.ref(HTMLCanvasElement);
|
|
46881
48393
|
menu = proxy({
|
|
46882
48394
|
isOpen: false,
|
|
46883
48395
|
anchorRect: null,
|
|
@@ -46971,7 +48483,7 @@ var TableStylesPopover = class extends Component {
|
|
|
46971
48483
|
tableStyles: types$1.RecordOf(),
|
|
46972
48484
|
type: types$1.or([types$1.literal("table"), types$1.literal("pivot")])
|
|
46973
48485
|
});
|
|
46974
|
-
tableStyleListRef = signal(
|
|
48486
|
+
tableStyleListRef = signal.ref();
|
|
46975
48487
|
state = proxy({ selectedCategory: this.initialSelectedCategory });
|
|
46976
48488
|
setup() {
|
|
46977
48489
|
useExternalListener(window, "click", this.onExternalClick, { capture: true });
|
|
@@ -48342,6 +49854,10 @@ sidePanelRegistry.add("ColumnStats", {
|
|
|
48342
49854
|
title: _t("Column statistics"),
|
|
48343
49855
|
Body: ColumnStatsPanel
|
|
48344
49856
|
});
|
|
49857
|
+
sidePanelRegistry.add("DataAnalysisPanel", {
|
|
49858
|
+
title: _t("Data analysis"),
|
|
49859
|
+
Body: DataAnalysisPanel
|
|
49860
|
+
});
|
|
48345
49861
|
sidePanelRegistry.add("TableSidePanel", {
|
|
48346
49862
|
title: _t("Edit table"),
|
|
48347
49863
|
Body: TablePanel,
|
|
@@ -48449,6 +49965,7 @@ var SidePanelStore = class extends SpreadsheetStore {
|
|
|
48449
49965
|
"changePanelSize",
|
|
48450
49966
|
"resetPanelSize",
|
|
48451
49967
|
"togglePinPanel",
|
|
49968
|
+
"togglePinnedSidePanel",
|
|
48452
49969
|
"closeMainPanel",
|
|
48453
49970
|
"changeSpreadsheetWidth",
|
|
48454
49971
|
"toggleCollapsePanel"
|
|
@@ -48600,6 +50117,19 @@ var SidePanelStore = class extends SpreadsheetStore {
|
|
|
48600
50117
|
this.secondaryPanel = void 0;
|
|
48601
50118
|
}
|
|
48602
50119
|
}
|
|
50120
|
+
togglePinnedSidePanel(componentTag, panelProps = {}) {
|
|
50121
|
+
if (this.mainPanel?.componentTag === componentTag && this.isMainPanelOpen) {
|
|
50122
|
+
this.closeMainPanel();
|
|
50123
|
+
return;
|
|
50124
|
+
}
|
|
50125
|
+
if (this.secondaryPanel?.componentTag === componentTag && this.isSecondaryPanelOpen) {
|
|
50126
|
+
this.secondaryPanel?.currentPanelProps.onCloseSidePanel?.();
|
|
50127
|
+
this.secondaryPanel = void 0;
|
|
50128
|
+
return;
|
|
50129
|
+
}
|
|
50130
|
+
this.open(componentTag, panelProps);
|
|
50131
|
+
if (!this.mainPanel?.isPinned) this.togglePinPanel();
|
|
50132
|
+
}
|
|
48603
50133
|
toggleCollapsePanel(panel) {
|
|
48604
50134
|
const panelInfo = this[panel];
|
|
48605
50135
|
if (!panelInfo) return;
|
|
@@ -48978,6 +50508,219 @@ var MenuItemRegistry = class extends Registry {
|
|
|
48978
50508
|
}
|
|
48979
50509
|
};
|
|
48980
50510
|
|
|
50511
|
+
//#endregion
|
|
50512
|
+
//#region src/helpers/sort_interactive.ts
|
|
50513
|
+
function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
|
|
50514
|
+
let multiColumns = zone.right > zone.left;
|
|
50515
|
+
if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
|
|
50516
|
+
multiColumns = false;
|
|
50517
|
+
let table;
|
|
50518
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
50519
|
+
table = [];
|
|
50520
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
50521
|
+
const merge = env.model.getters.getMerge({
|
|
50522
|
+
sheetId,
|
|
50523
|
+
col,
|
|
50524
|
+
row
|
|
50525
|
+
});
|
|
50526
|
+
if (merge && !table.includes(merge.id.toString())) table.push(merge.id.toString());
|
|
50527
|
+
}
|
|
50528
|
+
if (table.length >= 2) {
|
|
50529
|
+
multiColumns = true;
|
|
50530
|
+
break;
|
|
50531
|
+
}
|
|
50532
|
+
}
|
|
50533
|
+
}
|
|
50534
|
+
if (multiColumns) {
|
|
50535
|
+
interactiveSort(env, sheetId, anchor, zone, sortDirection);
|
|
50536
|
+
return;
|
|
50537
|
+
}
|
|
50538
|
+
const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
|
|
50539
|
+
if (isEqual(contiguousZone, zone)) interactiveSort(env, sheetId, anchor, zone, sortDirection);
|
|
50540
|
+
else env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => interactiveSort(env, sheetId, anchor, contiguousZone, sortDirection), () => interactiveSort(env, sheetId, anchor, zone, sortDirection));
|
|
50541
|
+
}
|
|
50542
|
+
function interactiveSort(env, sheetId, anchor, zone, sortDirection, sortOptions) {
|
|
50543
|
+
const result = env.model.dispatch("SORT_CELLS", {
|
|
50544
|
+
sheetId,
|
|
50545
|
+
col: anchor.col,
|
|
50546
|
+
row: anchor.row,
|
|
50547
|
+
zone,
|
|
50548
|
+
sortDirection,
|
|
50549
|
+
sortOptions
|
|
50550
|
+
});
|
|
50551
|
+
if (result.isCancelledBecause("InvalidSortZone")) {
|
|
50552
|
+
const { col, row } = anchor;
|
|
50553
|
+
env.model.selection.selectZone({
|
|
50554
|
+
cell: {
|
|
50555
|
+
col,
|
|
50556
|
+
row
|
|
50557
|
+
},
|
|
50558
|
+
zone
|
|
50559
|
+
});
|
|
50560
|
+
env.raiseError(_t("Cannot sort. To sort, select only cells or only merges that have the same size."));
|
|
50561
|
+
}
|
|
50562
|
+
if (result.isCancelledBecause("SortZoneWithArrayFormulas")) {
|
|
50563
|
+
const { col, row } = anchor;
|
|
50564
|
+
env.model.selection.selectZone({
|
|
50565
|
+
cell: {
|
|
50566
|
+
col,
|
|
50567
|
+
row
|
|
50568
|
+
},
|
|
50569
|
+
zone
|
|
50570
|
+
});
|
|
50571
|
+
env.raiseError(_t("Cannot sort a zone with array formulas."));
|
|
50572
|
+
}
|
|
50573
|
+
}
|
|
50574
|
+
|
|
50575
|
+
//#endregion
|
|
50576
|
+
//#region src/stores/trim_whitespace_store.ts
|
|
50577
|
+
var TrimWhitespaceStore = class extends SpreadsheetStore {
|
|
50578
|
+
mutators = ["trimWhitespace"];
|
|
50579
|
+
notificationStore = this.get(NotificationStore);
|
|
50580
|
+
trimWhitespace() {
|
|
50581
|
+
this.model.dispatch("TRIM_WHITESPACE");
|
|
50582
|
+
}
|
|
50583
|
+
handle(cmd) {
|
|
50584
|
+
switch (cmd.type) {
|
|
50585
|
+
case "TRIM_WHITESPACE":
|
|
50586
|
+
this._trimWhitespace();
|
|
50587
|
+
break;
|
|
50588
|
+
}
|
|
50589
|
+
}
|
|
50590
|
+
_trimWhitespace() {
|
|
50591
|
+
const zones = recomputeZones(this.getters.getSelectedZones());
|
|
50592
|
+
const sheetId = this.getters.getActiveSheetId();
|
|
50593
|
+
let count = 0;
|
|
50594
|
+
for (const { col, row } of zones.map(positions).flat()) {
|
|
50595
|
+
const cell = this.getters.getCell({
|
|
50596
|
+
col,
|
|
50597
|
+
row,
|
|
50598
|
+
sheetId
|
|
50599
|
+
});
|
|
50600
|
+
if (!cell) continue;
|
|
50601
|
+
const currentContent = !cell.isFormula ? cell.content : cell.compiledFormula.toFormulaString(this.getters);
|
|
50602
|
+
const trimmedContent = trimContent(currentContent);
|
|
50603
|
+
if (trimmedContent !== currentContent) {
|
|
50604
|
+
count += 1;
|
|
50605
|
+
this.model.dispatch("UPDATE_CELL", {
|
|
50606
|
+
sheetId,
|
|
50607
|
+
col,
|
|
50608
|
+
row,
|
|
50609
|
+
content: trimmedContent
|
|
50610
|
+
});
|
|
50611
|
+
}
|
|
50612
|
+
}
|
|
50613
|
+
const text = count ? _t("Trimmed whitespace from %s cells.", count) : _t("No selected cells had whitespace trimmed.");
|
|
50614
|
+
this.notificationStore.notifyUser({
|
|
50615
|
+
type: "info",
|
|
50616
|
+
text,
|
|
50617
|
+
sticky: false
|
|
50618
|
+
});
|
|
50619
|
+
}
|
|
50620
|
+
};
|
|
50621
|
+
|
|
50622
|
+
//#endregion
|
|
50623
|
+
//#region src/actions/data_actions.ts
|
|
50624
|
+
const sortRange = {
|
|
50625
|
+
name: _t("Sort range"),
|
|
50626
|
+
isVisible: IS_ONLY_ONE_RANGE,
|
|
50627
|
+
icon: "o-spreadsheet-Icon.SORT_RANGE"
|
|
50628
|
+
};
|
|
50629
|
+
const sortAscending = {
|
|
50630
|
+
name: _t("Ascending (A ⟶ Z)"),
|
|
50631
|
+
execute: (env) => {
|
|
50632
|
+
const { anchor, zones } = env.model.getters.getSelection();
|
|
50633
|
+
interactiveSortSelection(env, env.model.getters.getActiveSheetId(), anchor.cell, zones[0], "asc");
|
|
50634
|
+
},
|
|
50635
|
+
icon: "o-spreadsheet-Icon.SORT_ASCENDING"
|
|
50636
|
+
};
|
|
50637
|
+
const dataCleanup = {
|
|
50638
|
+
name: _t("Data cleanup"),
|
|
50639
|
+
icon: "o-spreadsheet-Icon.DATA_CLEANUP"
|
|
50640
|
+
};
|
|
50641
|
+
const removeDuplicates = {
|
|
50642
|
+
name: _t("Remove duplicates"),
|
|
50643
|
+
execute: (env) => {
|
|
50644
|
+
if (getZoneArea(env.model.getters.getSelectedZone()) === 1) env.model.selection.selectTableAroundSelection();
|
|
50645
|
+
env.openSidePanel("RemoveDuplicates", {});
|
|
50646
|
+
},
|
|
50647
|
+
isEnabled: (env) => !env.isSmall
|
|
50648
|
+
};
|
|
50649
|
+
const trimWhitespace = {
|
|
50650
|
+
name: _t("Trim whitespace"),
|
|
50651
|
+
execute: (env) => {
|
|
50652
|
+
env.getStore(TrimWhitespaceStore).trimWhitespace();
|
|
50653
|
+
}
|
|
50654
|
+
};
|
|
50655
|
+
const cleanupDataSources = {
|
|
50656
|
+
name: _t("Remove unused data sources"),
|
|
50657
|
+
execute: (env) => env.openSidePanel("DataSourceCleanup", {}),
|
|
50658
|
+
isEnabled: (env) => !env.isSmall
|
|
50659
|
+
};
|
|
50660
|
+
const sortDescending = {
|
|
50661
|
+
name: _t("Descending (Z ⟶ A)"),
|
|
50662
|
+
execute: (env) => {
|
|
50663
|
+
const { anchor, zones } = env.model.getters.getSelection();
|
|
50664
|
+
interactiveSortSelection(env, env.model.getters.getActiveSheetId(), anchor.cell, zones[0], "desc");
|
|
50665
|
+
},
|
|
50666
|
+
icon: "o-spreadsheet-Icon.SORT_DESCENDING"
|
|
50667
|
+
};
|
|
50668
|
+
const createRemoveFilter = { ...CREATE_OR_REMOVE_FILTER_ACTION };
|
|
50669
|
+
const createRemoveFilterTool = {
|
|
50670
|
+
...CREATE_OR_REMOVE_FILTER_ACTION,
|
|
50671
|
+
isActive: (env) => SELECTED_TABLE_HAS_FILTERS(env)
|
|
50672
|
+
};
|
|
50673
|
+
const splitToColumns = {
|
|
50674
|
+
name: _t("Split text to columns"),
|
|
50675
|
+
sequence: 1,
|
|
50676
|
+
execute: (env) => env.openSidePanel("SplitToColumns", {}),
|
|
50677
|
+
isEnabled: (env) => !env.isSmall && env.model.getters.isSingleColSelected(),
|
|
50678
|
+
icon: "o-spreadsheet-Icon.SPLIT_TEXT"
|
|
50679
|
+
};
|
|
50680
|
+
const dataAnalysis = {
|
|
50681
|
+
name: _t("Data analysis"),
|
|
50682
|
+
execute: (env) => {
|
|
50683
|
+
env.getStore(SidePanelStore).togglePinnedSidePanel("DataAnalysisPanel");
|
|
50684
|
+
},
|
|
50685
|
+
icon: "o-spreadsheet-Icon.COLUMN_STATS",
|
|
50686
|
+
isEnabled: (env) => !env.isSmall
|
|
50687
|
+
};
|
|
50688
|
+
const columnStatistics = {
|
|
50689
|
+
name: _t("Column statistics"),
|
|
50690
|
+
execute: (env) => env.openSidePanel("ColumnStats", {}),
|
|
50691
|
+
icon: "o-spreadsheet-Icon.COLUMN_STATS"
|
|
50692
|
+
};
|
|
50693
|
+
const reinsertDynamicPivotMenu = {
|
|
50694
|
+
id: "reinsert_dynamic_pivot",
|
|
50695
|
+
name: _t("Re-insert dynamic pivot"),
|
|
50696
|
+
sequence: 60,
|
|
50697
|
+
icon: "o-spreadsheet-Icon.INSERT_PIVOT",
|
|
50698
|
+
children: [REINSERT_DYNAMIC_PIVOT_CHILDREN],
|
|
50699
|
+
isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid())
|
|
50700
|
+
};
|
|
50701
|
+
const reinsertStaticPivotMenu = {
|
|
50702
|
+
id: "reinsert_static_pivot",
|
|
50703
|
+
name: _t("Re-insert static pivot"),
|
|
50704
|
+
sequence: 70,
|
|
50705
|
+
icon: "o-spreadsheet-Icon.INSERT_PIVOT",
|
|
50706
|
+
children: [REINSERT_STATIC_PIVOT_CHILDREN],
|
|
50707
|
+
isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid())
|
|
50708
|
+
};
|
|
50709
|
+
const calculationMode = {
|
|
50710
|
+
name: _t("Calculation"),
|
|
50711
|
+
icon: "o-spreadsheet-Icon.CALCULATOR"
|
|
50712
|
+
};
|
|
50713
|
+
const automaticCalculation = {
|
|
50714
|
+
name: _t("Automatic"),
|
|
50715
|
+
execute: (env) => env.model.dispatch("SET_AUTOMATIC_EVALUATION", { enabled: true }),
|
|
50716
|
+
isActive: (env) => env.model.getters.isAutomaticEvaluationEnabled()
|
|
50717
|
+
};
|
|
50718
|
+
const manualCalculation = {
|
|
50719
|
+
name: _t("Manual"),
|
|
50720
|
+
execute: (env) => env.model.dispatch("SET_AUTOMATIC_EVALUATION", { enabled: false }),
|
|
50721
|
+
isActive: (env) => !env.model.getters.isAutomaticEvaluationEnabled()
|
|
50722
|
+
};
|
|
50723
|
+
|
|
48981
50724
|
//#endregion
|
|
48982
50725
|
//#region src/helpers/ui/merge_interactive.ts
|
|
48983
50726
|
const AddMergeInteractiveContent = {
|
|
@@ -49192,6 +50935,10 @@ cellMenuRegistry.add("cut", {
|
|
|
49192
50935
|
}).addChild("paste_format_only", ["paste_special"], {
|
|
49193
50936
|
...pasteSpecialFormat,
|
|
49194
50937
|
sequence: 20
|
|
50938
|
+
}).add("data_analysis", {
|
|
50939
|
+
...dataAnalysis,
|
|
50940
|
+
sequence: 50,
|
|
50941
|
+
separator: true
|
|
49195
50942
|
}).add("add_row_before", {
|
|
49196
50943
|
...cellInsertRowsBefore,
|
|
49197
50944
|
sequence: 70
|
|
@@ -49299,211 +51046,6 @@ cellMenuRegistry.add("cut", {
|
|
|
49299
51046
|
sequence: 30
|
|
49300
51047
|
});
|
|
49301
51048
|
|
|
49302
|
-
//#endregion
|
|
49303
|
-
//#region src/helpers/sort_interactive.ts
|
|
49304
|
-
function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
|
|
49305
|
-
let multiColumns = zone.right > zone.left;
|
|
49306
|
-
if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
|
|
49307
|
-
multiColumns = false;
|
|
49308
|
-
let table;
|
|
49309
|
-
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
49310
|
-
table = [];
|
|
49311
|
-
for (let col = zone.left; col <= zone.right; col++) {
|
|
49312
|
-
const merge = env.model.getters.getMerge({
|
|
49313
|
-
sheetId,
|
|
49314
|
-
col,
|
|
49315
|
-
row
|
|
49316
|
-
});
|
|
49317
|
-
if (merge && !table.includes(merge.id.toString())) table.push(merge.id.toString());
|
|
49318
|
-
}
|
|
49319
|
-
if (table.length >= 2) {
|
|
49320
|
-
multiColumns = true;
|
|
49321
|
-
break;
|
|
49322
|
-
}
|
|
49323
|
-
}
|
|
49324
|
-
}
|
|
49325
|
-
if (multiColumns) {
|
|
49326
|
-
interactiveSort(env, sheetId, anchor, zone, sortDirection);
|
|
49327
|
-
return;
|
|
49328
|
-
}
|
|
49329
|
-
const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
|
|
49330
|
-
if (isEqual(contiguousZone, zone)) interactiveSort(env, sheetId, anchor, zone, sortDirection);
|
|
49331
|
-
else env.askConfirmation(_t("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => interactiveSort(env, sheetId, anchor, contiguousZone, sortDirection), () => interactiveSort(env, sheetId, anchor, zone, sortDirection));
|
|
49332
|
-
}
|
|
49333
|
-
function interactiveSort(env, sheetId, anchor, zone, sortDirection, sortOptions) {
|
|
49334
|
-
const result = env.model.dispatch("SORT_CELLS", {
|
|
49335
|
-
sheetId,
|
|
49336
|
-
col: anchor.col,
|
|
49337
|
-
row: anchor.row,
|
|
49338
|
-
zone,
|
|
49339
|
-
sortDirection,
|
|
49340
|
-
sortOptions
|
|
49341
|
-
});
|
|
49342
|
-
if (result.isCancelledBecause("InvalidSortZone")) {
|
|
49343
|
-
const { col, row } = anchor;
|
|
49344
|
-
env.model.selection.selectZone({
|
|
49345
|
-
cell: {
|
|
49346
|
-
col,
|
|
49347
|
-
row
|
|
49348
|
-
},
|
|
49349
|
-
zone
|
|
49350
|
-
});
|
|
49351
|
-
env.raiseError(_t("Cannot sort. To sort, select only cells or only merges that have the same size."));
|
|
49352
|
-
}
|
|
49353
|
-
if (result.isCancelledBecause("SortZoneWithArrayFormulas")) {
|
|
49354
|
-
const { col, row } = anchor;
|
|
49355
|
-
env.model.selection.selectZone({
|
|
49356
|
-
cell: {
|
|
49357
|
-
col,
|
|
49358
|
-
row
|
|
49359
|
-
},
|
|
49360
|
-
zone
|
|
49361
|
-
});
|
|
49362
|
-
env.raiseError(_t("Cannot sort a zone with array formulas."));
|
|
49363
|
-
}
|
|
49364
|
-
}
|
|
49365
|
-
|
|
49366
|
-
//#endregion
|
|
49367
|
-
//#region src/stores/trim_whitespace_store.ts
|
|
49368
|
-
var TrimWhitespaceStore = class extends SpreadsheetStore {
|
|
49369
|
-
mutators = ["trimWhitespace"];
|
|
49370
|
-
notificationStore = this.get(NotificationStore);
|
|
49371
|
-
trimWhitespace() {
|
|
49372
|
-
this.model.dispatch("TRIM_WHITESPACE");
|
|
49373
|
-
}
|
|
49374
|
-
handle(cmd) {
|
|
49375
|
-
switch (cmd.type) {
|
|
49376
|
-
case "TRIM_WHITESPACE":
|
|
49377
|
-
this._trimWhitespace();
|
|
49378
|
-
break;
|
|
49379
|
-
}
|
|
49380
|
-
}
|
|
49381
|
-
_trimWhitespace() {
|
|
49382
|
-
const zones = recomputeZones(this.getters.getSelectedZones());
|
|
49383
|
-
const sheetId = this.getters.getActiveSheetId();
|
|
49384
|
-
let count = 0;
|
|
49385
|
-
for (const { col, row } of zones.map(positions).flat()) {
|
|
49386
|
-
const cell = this.getters.getCell({
|
|
49387
|
-
col,
|
|
49388
|
-
row,
|
|
49389
|
-
sheetId
|
|
49390
|
-
});
|
|
49391
|
-
if (!cell) continue;
|
|
49392
|
-
const currentContent = !cell.isFormula ? cell.content : cell.compiledFormula.toFormulaString(this.getters);
|
|
49393
|
-
const trimmedContent = trimContent(currentContent);
|
|
49394
|
-
if (trimmedContent !== currentContent) {
|
|
49395
|
-
count += 1;
|
|
49396
|
-
this.model.dispatch("UPDATE_CELL", {
|
|
49397
|
-
sheetId,
|
|
49398
|
-
col,
|
|
49399
|
-
row,
|
|
49400
|
-
content: trimmedContent
|
|
49401
|
-
});
|
|
49402
|
-
}
|
|
49403
|
-
}
|
|
49404
|
-
const text = count ? _t("Trimmed whitespace from %s cells.", count) : _t("No selected cells had whitespace trimmed.");
|
|
49405
|
-
this.notificationStore.notifyUser({
|
|
49406
|
-
type: "info",
|
|
49407
|
-
text,
|
|
49408
|
-
sticky: false
|
|
49409
|
-
});
|
|
49410
|
-
}
|
|
49411
|
-
};
|
|
49412
|
-
|
|
49413
|
-
//#endregion
|
|
49414
|
-
//#region src/actions/data_actions.ts
|
|
49415
|
-
const sortRange = {
|
|
49416
|
-
name: _t("Sort range"),
|
|
49417
|
-
isVisible: IS_ONLY_ONE_RANGE,
|
|
49418
|
-
icon: "o-spreadsheet-Icon.SORT_RANGE"
|
|
49419
|
-
};
|
|
49420
|
-
const sortAscending = {
|
|
49421
|
-
name: _t("Ascending (A ⟶ Z)"),
|
|
49422
|
-
execute: (env) => {
|
|
49423
|
-
const { anchor, zones } = env.model.getters.getSelection();
|
|
49424
|
-
interactiveSortSelection(env, env.model.getters.getActiveSheetId(), anchor.cell, zones[0], "asc");
|
|
49425
|
-
},
|
|
49426
|
-
icon: "o-spreadsheet-Icon.SORT_ASCENDING"
|
|
49427
|
-
};
|
|
49428
|
-
const dataCleanup = {
|
|
49429
|
-
name: _t("Data cleanup"),
|
|
49430
|
-
icon: "o-spreadsheet-Icon.DATA_CLEANUP"
|
|
49431
|
-
};
|
|
49432
|
-
const removeDuplicates = {
|
|
49433
|
-
name: _t("Remove duplicates"),
|
|
49434
|
-
execute: (env) => {
|
|
49435
|
-
if (getZoneArea(env.model.getters.getSelectedZone()) === 1) env.model.selection.selectTableAroundSelection();
|
|
49436
|
-
env.openSidePanel("RemoveDuplicates", {});
|
|
49437
|
-
},
|
|
49438
|
-
isEnabled: (env) => !env.isSmall
|
|
49439
|
-
};
|
|
49440
|
-
const trimWhitespace = {
|
|
49441
|
-
name: _t("Trim whitespace"),
|
|
49442
|
-
execute: (env) => {
|
|
49443
|
-
env.getStore(TrimWhitespaceStore).trimWhitespace();
|
|
49444
|
-
}
|
|
49445
|
-
};
|
|
49446
|
-
const cleanupDataSources = {
|
|
49447
|
-
name: _t("Remove unused data sources"),
|
|
49448
|
-
execute: (env) => env.openSidePanel("DataSourceCleanup", {}),
|
|
49449
|
-
isEnabled: (env) => !env.isSmall
|
|
49450
|
-
};
|
|
49451
|
-
const sortDescending = {
|
|
49452
|
-
name: _t("Descending (Z ⟶ A)"),
|
|
49453
|
-
execute: (env) => {
|
|
49454
|
-
const { anchor, zones } = env.model.getters.getSelection();
|
|
49455
|
-
interactiveSortSelection(env, env.model.getters.getActiveSheetId(), anchor.cell, zones[0], "desc");
|
|
49456
|
-
},
|
|
49457
|
-
icon: "o-spreadsheet-Icon.SORT_DESCENDING"
|
|
49458
|
-
};
|
|
49459
|
-
const createRemoveFilter = { ...CREATE_OR_REMOVE_FILTER_ACTION };
|
|
49460
|
-
const createRemoveFilterTool = {
|
|
49461
|
-
...CREATE_OR_REMOVE_FILTER_ACTION,
|
|
49462
|
-
isActive: (env) => SELECTED_TABLE_HAS_FILTERS(env)
|
|
49463
|
-
};
|
|
49464
|
-
const splitToColumns = {
|
|
49465
|
-
name: _t("Split text to columns"),
|
|
49466
|
-
sequence: 1,
|
|
49467
|
-
execute: (env) => env.openSidePanel("SplitToColumns", {}),
|
|
49468
|
-
isEnabled: (env) => !env.isSmall && env.model.getters.isSingleColSelected(),
|
|
49469
|
-
icon: "o-spreadsheet-Icon.SPLIT_TEXT"
|
|
49470
|
-
};
|
|
49471
|
-
const columnStatistics = {
|
|
49472
|
-
name: _t("Column statistics"),
|
|
49473
|
-
execute: (env) => env.openSidePanel("ColumnStats", {}),
|
|
49474
|
-
icon: "o-spreadsheet-Icon.COLUMN_STATS"
|
|
49475
|
-
};
|
|
49476
|
-
const reinsertDynamicPivotMenu = {
|
|
49477
|
-
id: "reinsert_dynamic_pivot",
|
|
49478
|
-
name: _t("Re-insert dynamic pivot"),
|
|
49479
|
-
sequence: 60,
|
|
49480
|
-
icon: "o-spreadsheet-Icon.INSERT_PIVOT",
|
|
49481
|
-
children: [REINSERT_DYNAMIC_PIVOT_CHILDREN],
|
|
49482
|
-
isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid())
|
|
49483
|
-
};
|
|
49484
|
-
const reinsertStaticPivotMenu = {
|
|
49485
|
-
id: "reinsert_static_pivot",
|
|
49486
|
-
name: _t("Re-insert static pivot"),
|
|
49487
|
-
sequence: 70,
|
|
49488
|
-
icon: "o-spreadsheet-Icon.INSERT_PIVOT",
|
|
49489
|
-
children: [REINSERT_STATIC_PIVOT_CHILDREN],
|
|
49490
|
-
isVisible: (env) => env.model.getters.getPivotIds().some((id) => env.model.getters.getPivot(id).isValid())
|
|
49491
|
-
};
|
|
49492
|
-
const calculationMode = {
|
|
49493
|
-
name: _t("Calculation"),
|
|
49494
|
-
icon: "o-spreadsheet-Icon.CALCULATOR"
|
|
49495
|
-
};
|
|
49496
|
-
const automaticCalculation = {
|
|
49497
|
-
name: _t("Automatic"),
|
|
49498
|
-
execute: (env) => env.model.dispatch("SET_AUTOMATIC_EVALUATION", { enabled: true }),
|
|
49499
|
-
isActive: (env) => env.model.getters.isAutomaticEvaluationEnabled()
|
|
49500
|
-
};
|
|
49501
|
-
const manualCalculation = {
|
|
49502
|
-
name: _t("Manual"),
|
|
49503
|
-
execute: (env) => env.model.dispatch("SET_AUTOMATIC_EVALUATION", { enabled: false }),
|
|
49504
|
-
isActive: (env) => !env.model.getters.isAutomaticEvaluationEnabled()
|
|
49505
|
-
};
|
|
49506
|
-
|
|
49507
51049
|
//#endregion
|
|
49508
51050
|
//#region src/registries/menus/col_menu_registry.ts
|
|
49509
51051
|
const colMenuRegistry = new MenuItemRegistry();
|
|
@@ -49537,6 +51079,9 @@ colMenuRegistry.add("cut", {
|
|
|
49537
51079
|
}).addChild("sort_descending", ["sort_columns"], {
|
|
49538
51080
|
...sortDescending,
|
|
49539
51081
|
sequence: 20
|
|
51082
|
+
}).add("data_analysis", {
|
|
51083
|
+
...dataAnalysis,
|
|
51084
|
+
sequence: 55
|
|
49540
51085
|
}).add("column_statistics", {
|
|
49541
51086
|
...columnStatistics,
|
|
49542
51087
|
sequence: 60,
|
|
@@ -52352,14 +53897,15 @@ var FiguresContainer = class extends Component {
|
|
|
52352
53897
|
selectedRect: void 0,
|
|
52353
53898
|
horizontalSnap: void 0,
|
|
52354
53899
|
verticalSnap: void 0,
|
|
52355
|
-
cancelDnd: void 0
|
|
52356
|
-
overlappingChartOrCarousel: void 0
|
|
53900
|
+
cancelDnd: void 0
|
|
52357
53901
|
});
|
|
52358
53902
|
viewStore;
|
|
52359
53903
|
zoomStore;
|
|
53904
|
+
chartDragStore;
|
|
52360
53905
|
setup() {
|
|
52361
53906
|
this.viewStore = useStore(ViewportsStore);
|
|
52362
53907
|
this.zoomStore = useStore(ZoomStore);
|
|
53908
|
+
this.chartDragStore = useStore(ChartDragStore);
|
|
52363
53909
|
onMounted(() => {
|
|
52364
53910
|
this.render();
|
|
52365
53911
|
});
|
|
@@ -52373,7 +53919,7 @@ var FiguresContainer = class extends Component {
|
|
|
52373
53919
|
this.dnd.selectedRect = void 0;
|
|
52374
53920
|
this.dnd.horizontalSnap = void 0;
|
|
52375
53921
|
this.dnd.verticalSnap = void 0;
|
|
52376
|
-
this.
|
|
53922
|
+
this.chartDragStore.setHighlightedFigure(void 0);
|
|
52377
53923
|
this.dnd.cancelDnd = void 0;
|
|
52378
53924
|
}
|
|
52379
53925
|
});
|
|
@@ -52499,6 +54045,7 @@ var FiguresContainer = class extends Component {
|
|
|
52499
54045
|
maxY: this.env.model.getters.getRowDimensions(sheetId, this.env.model.getters.getNumberRows(sheetId) - 1).end
|
|
52500
54046
|
};
|
|
52501
54047
|
let hasStartedDnd = false;
|
|
54048
|
+
let overlappingChartOrCarousel = void 0;
|
|
52502
54049
|
const onMouseMove = (ev) => {
|
|
52503
54050
|
const currentMousePosition = {
|
|
52504
54051
|
x: ev.clientX / zoom,
|
|
@@ -52510,10 +54057,10 @@ var FiguresContainer = class extends Component {
|
|
|
52510
54057
|
hasStartedDnd = true;
|
|
52511
54058
|
const selectedFigures = dragFigureForMove(currentMousePosition, initialMousePosition, initialFigures, maxDimensions, initialScrollPosition, this.viewStore.activeSheetScrollInfo);
|
|
52512
54059
|
const draggedFigure = selectedFigures.find((f) => f.id === draggedFigureId);
|
|
52513
|
-
|
|
54060
|
+
overlappingChartOrCarousel = void 0;
|
|
52514
54061
|
const otherFigures = this.getOtherFigures(selectedFigures.map((f) => f.id));
|
|
52515
|
-
if (draggedFigure && !selectedFigures.find((f) => f.tag !== "chart")) overlappingChartOrCarousel =
|
|
52516
|
-
this.
|
|
54062
|
+
if (draggedFigure && !selectedFigures.find((f) => f.tag !== "chart")) overlappingChartOrCarousel = getCarouselOverlappingChart(draggedFigure, otherFigures, ["carousel", "chart"]);
|
|
54063
|
+
this.chartDragStore.setHighlightedFigure(overlappingChartOrCarousel?.id);
|
|
52517
54064
|
if (!overlappingChartOrCarousel) {
|
|
52518
54065
|
const snapReturn = snapForMove(this.env, selectedFigures, otherFigures);
|
|
52519
54066
|
this.dnd.selectedFigures = snapReturn.snappedFigures;
|
|
@@ -52535,7 +54082,7 @@ var FiguresContainer = class extends Component {
|
|
|
52535
54082
|
else this.env.model.dispatch("SELECT_FIGURE", { figureId: figureUI.id });
|
|
52536
54083
|
return;
|
|
52537
54084
|
}
|
|
52538
|
-
if (!
|
|
54085
|
+
if (!overlappingChartOrCarousel) {
|
|
52539
54086
|
const payloads = this.dnd.selectedFigures?.map((f) => {
|
|
52540
54087
|
return {
|
|
52541
54088
|
sheetId,
|
|
@@ -52545,14 +54092,14 @@ var FiguresContainer = class extends Component {
|
|
|
52545
54092
|
}) || [];
|
|
52546
54093
|
this.env.model.dispatch("MOVE_FIGURES", { figures: payloads });
|
|
52547
54094
|
} else {
|
|
52548
|
-
const overlappingFigureId =
|
|
54095
|
+
const overlappingFigureId = overlappingChartOrCarousel.id;
|
|
52549
54096
|
const chartFigureIds = this.dnd.selectedFigures?.map((f) => f.id) || [];
|
|
52550
|
-
if (
|
|
54097
|
+
if (overlappingChartOrCarousel.tag === "carousel") this.env.model.dispatch("ADD_FIGURES_CHART_TO_CAROUSEL", {
|
|
52551
54098
|
sheetId,
|
|
52552
54099
|
carouselFigureId: overlappingFigureId,
|
|
52553
54100
|
chartFigureIds
|
|
52554
54101
|
});
|
|
52555
|
-
else if (
|
|
54102
|
+
else if (overlappingChartOrCarousel.tag === "chart") this.env.model.dispatch("MERGE_CHART_FIGURES_INTO_CAROUSEL", {
|
|
52556
54103
|
sheetId,
|
|
52557
54104
|
baseFigureId: overlappingFigureId,
|
|
52558
54105
|
chartFigureIds: [overlappingFigureId, ...chartFigureIds]
|
|
@@ -52563,7 +54110,7 @@ var FiguresContainer = class extends Component {
|
|
|
52563
54110
|
this.dnd.selectedRect = void 0;
|
|
52564
54111
|
this.dnd.horizontalSnap = void 0;
|
|
52565
54112
|
this.dnd.verticalSnap = void 0;
|
|
52566
|
-
this.
|
|
54113
|
+
this.chartDragStore.setHighlightedFigure(void 0);
|
|
52567
54114
|
};
|
|
52568
54115
|
this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
|
|
52569
54116
|
}
|
|
@@ -52619,7 +54166,6 @@ var FiguresContainer = class extends Component {
|
|
|
52619
54166
|
this.dnd.selectedRect = void 0;
|
|
52620
54167
|
this.dnd.horizontalSnap = void 0;
|
|
52621
54168
|
this.dnd.verticalSnap = void 0;
|
|
52622
|
-
this.dnd.overlappingChartOrCarousel = void 0;
|
|
52623
54169
|
};
|
|
52624
54170
|
this.dnd.cancelDnd = startDnd(onMouseMove, onMouseUp);
|
|
52625
54171
|
}
|
|
@@ -52629,12 +54175,12 @@ var FiguresContainer = class extends Component {
|
|
|
52629
54175
|
getFigureStyle(figureUI) {
|
|
52630
54176
|
if (figureUI.id !== this.dnd.draggedFigure?.id) return "";
|
|
52631
54177
|
return cssPropertiesToCss({
|
|
52632
|
-
opacity: this.
|
|
54178
|
+
opacity: this.chartDragStore.highlightedFigureId ? "0.6" : "0.9",
|
|
52633
54179
|
cursor: "grabbing"
|
|
52634
54180
|
});
|
|
52635
54181
|
}
|
|
52636
54182
|
getFigureClass(figureUI) {
|
|
52637
|
-
if (figureUI.id !== this.
|
|
54183
|
+
if (figureUI.id !== this.chartDragStore.highlightedFigureId) return "";
|
|
52638
54184
|
return "o-add-to-carousel";
|
|
52639
54185
|
}
|
|
52640
54186
|
getSnap(snapLine) {
|
|
@@ -52677,26 +54223,6 @@ var FiguresContainer = class extends Component {
|
|
|
52677
54223
|
height: `100%`
|
|
52678
54224
|
});
|
|
52679
54225
|
}
|
|
52680
|
-
getOverlappingFigure(figureUI, otherFigures) {
|
|
52681
|
-
if (figureUI.tag !== "chart") return;
|
|
52682
|
-
const figureCenterX = figureUI.x + figureUI.width / 2;
|
|
52683
|
-
const figureCenterY = figureUI.y + figureUI.height / 2;
|
|
52684
|
-
let bestMatch;
|
|
52685
|
-
let smallestDistance = Infinity;
|
|
52686
|
-
for (const figure of otherFigures) {
|
|
52687
|
-
if (figure.tag !== "chart" && figure.tag !== "carousel") continue;
|
|
52688
|
-
const targetCenterX = figure.x + figure.width / 2;
|
|
52689
|
-
const targetCenterY = figure.y + figure.height / 2;
|
|
52690
|
-
const distanceX = Math.abs(figureCenterX - targetCenterX);
|
|
52691
|
-
const distanceY = Math.abs(figureCenterY - targetCenterY);
|
|
52692
|
-
const squaredDistance = distanceX ** 2 + distanceY ** 2;
|
|
52693
|
-
if (distanceX <= figureUI.width / 2 && distanceY <= figureUI.height / 2 && squaredDistance < smallestDistance) {
|
|
52694
|
-
smallestDistance = squaredDistance;
|
|
52695
|
-
bestMatch = figure;
|
|
52696
|
-
}
|
|
52697
|
-
}
|
|
52698
|
-
return bestMatch;
|
|
52699
|
-
}
|
|
52700
54226
|
};
|
|
52701
54227
|
|
|
52702
54228
|
//#endregion
|
|
@@ -52996,7 +54522,7 @@ var AbstractResizer = class extends Component {
|
|
|
52996
54522
|
var ColResizer = class extends AbstractResizer {
|
|
52997
54523
|
static template = "o-spreadsheet-ColResizer";
|
|
52998
54524
|
static components = { UnhideColumnHeaders };
|
|
52999
|
-
colResizerRef = signal(
|
|
54525
|
+
colResizerRef = signal.ref();
|
|
53000
54526
|
setup() {
|
|
53001
54527
|
super.setup();
|
|
53002
54528
|
this.PADDING = 15;
|
|
@@ -53130,7 +54656,7 @@ var ColResizer = class extends AbstractResizer {
|
|
|
53130
54656
|
var RowResizer = class extends AbstractResizer {
|
|
53131
54657
|
static template = "o-spreadsheet-RowResizer";
|
|
53132
54658
|
static components = { UnhideRowHeaders };
|
|
53133
|
-
rowResizerRef = signal(
|
|
54659
|
+
rowResizerRef = signal.ref();
|
|
53134
54660
|
setup() {
|
|
53135
54661
|
super.setup();
|
|
53136
54662
|
this.PADDING = 5;
|
|
@@ -53739,9 +55265,8 @@ var TableResizer = class extends Component {
|
|
|
53739
55265
|
useStore(TableResizeStore);
|
|
53740
55266
|
}
|
|
53741
55267
|
get containerStyle() {
|
|
53742
|
-
const tableZone = this.props.table.range.zone;
|
|
53743
55268
|
const sheetId = this.props.table.range.sheetId;
|
|
53744
|
-
|
|
55269
|
+
const tableZone = this.props.table.range.zone;
|
|
53745
55270
|
const bottomRight = {
|
|
53746
55271
|
...tableZone,
|
|
53747
55272
|
left: tableZone.right,
|
|
@@ -53828,8 +55353,8 @@ var Grid = class extends Component {
|
|
|
53828
55353
|
HEADER_HEIGHT = 26;
|
|
53829
55354
|
HEADER_WIDTH = 48;
|
|
53830
55355
|
menuState;
|
|
53831
|
-
gridRef = signal(
|
|
53832
|
-
canvasRef = signal(
|
|
55356
|
+
gridRef = signal.ref();
|
|
55357
|
+
canvasRef = signal.ref(HTMLCanvasElement);
|
|
53833
55358
|
highlightStore;
|
|
53834
55359
|
viewStore;
|
|
53835
55360
|
zoomStore;
|
|
@@ -54460,6 +55985,9 @@ var Grid = class extends Component {
|
|
|
54460
55985
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
54461
55986
|
return this.env.model.getters.getClientsToDisplay(sheetId);
|
|
54462
55987
|
}
|
|
55988
|
+
get displayTableResizer() {
|
|
55989
|
+
return this.env.model.getters.isGridSelectionActive() && !this.env.model.getters.isReadonly() && !this.env.model.getters.isSheetLocked(this.env.model.getters.getActiveSheetId());
|
|
55990
|
+
}
|
|
54463
55991
|
};
|
|
54464
55992
|
|
|
54465
55993
|
//#endregion
|
|
@@ -54478,7 +56006,7 @@ var FullScreenFigure = class extends Component {
|
|
|
54478
56006
|
static template = "o-spreadsheet-FullScreenFigure";
|
|
54479
56007
|
static components = { ChartFigure };
|
|
54480
56008
|
fullScreenFigureStore;
|
|
54481
|
-
fullScreenFigureRef = signal(
|
|
56009
|
+
fullScreenFigureRef = signal.ref();
|
|
54482
56010
|
spreadsheetRect = useSpreadsheetRect();
|
|
54483
56011
|
figureRegistry = figureRegistry;
|
|
54484
56012
|
setup() {
|
|
@@ -54726,6 +56254,41 @@ var PivotHTMLRenderer = class extends Component {
|
|
|
54726
56254
|
}
|
|
54727
56255
|
};
|
|
54728
56256
|
|
|
56257
|
+
//#endregion
|
|
56258
|
+
//#region src/components/tables/hovered_table_store.ts
|
|
56259
|
+
var HoveredTableStore = class extends SpreadsheetStore {
|
|
56260
|
+
mutators = ["hover"];
|
|
56261
|
+
storeGetters = ["getCellHoverOverlayColor"];
|
|
56262
|
+
position;
|
|
56263
|
+
constructor(get) {
|
|
56264
|
+
super(get);
|
|
56265
|
+
const cellHoverOverlayStore = this.get(CellHoverOverlayStore);
|
|
56266
|
+
cellHoverOverlayStore.register(this);
|
|
56267
|
+
this.onDispose(() => cellHoverOverlayStore.unRegister(this));
|
|
56268
|
+
}
|
|
56269
|
+
getHighlightedPositions(hoveredPosition) {
|
|
56270
|
+
const highlightedPositions = [];
|
|
56271
|
+
const { sheetId, row } = hoveredPosition;
|
|
56272
|
+
const table = this.getters.getTable(hoveredPosition);
|
|
56273
|
+
if (!table) return highlightedPositions;
|
|
56274
|
+
const { left, right, top } = table.range.zone;
|
|
56275
|
+
const isTableHeader = row < top + table.config.numberOfHeaders;
|
|
56276
|
+
const doesTableRowHaveContent = range(left, right + 1).some((col) => {
|
|
56277
|
+
return !this.getters.isColHidden(sheetId, col) && this.getters.getEvaluatedCell({
|
|
56278
|
+
sheetId,
|
|
56279
|
+
col,
|
|
56280
|
+
row
|
|
56281
|
+
}).formattedValue;
|
|
56282
|
+
});
|
|
56283
|
+
if (!isTableHeader && doesTableRowHaveContent) for (let col = left; col <= right; col++) highlightedPositions.push({
|
|
56284
|
+
sheetId,
|
|
56285
|
+
col,
|
|
56286
|
+
row
|
|
56287
|
+
});
|
|
56288
|
+
return highlightedPositions;
|
|
56289
|
+
}
|
|
56290
|
+
};
|
|
56291
|
+
|
|
54729
56292
|
//#endregion
|
|
54730
56293
|
//#region src/helpers/figures/charts/runtime/helpers_index.ts
|
|
54731
56294
|
var helpers_index_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -67828,6 +69391,12 @@ var FigureUIPlugin = class extends UIPlugin {
|
|
|
67828
69391
|
const figures = cmd.chartFigureIds.map((id) => this.getters.getFigure(cmd.sheetId, id));
|
|
67829
69392
|
const baseFigureId = this.getters.getFigure(cmd.sheetId, cmd.baseFigureId);
|
|
67830
69393
|
if (figures.some((f) => f === void 0 || f.tag !== "chart") || !baseFigureId || baseFigureId.tag !== "chart") return "FigureDoesNotExist";
|
|
69394
|
+
break;
|
|
69395
|
+
case "CREATE_CHART_AND_MERGE_INTO_CAROUSEL":
|
|
69396
|
+
const baseFigure = this.getters.getFigure(cmd.sheetId, cmd.baseFigureId);
|
|
69397
|
+
if (this.getters.getFigure(cmd.sheetId, cmd.figureId) || !baseFigure) return "InvalidFigureId";
|
|
69398
|
+
if (baseFigure.tag !== "chart") return "FigureDoesNotExist";
|
|
69399
|
+
break;
|
|
67831
69400
|
}
|
|
67832
69401
|
return "Success";
|
|
67833
69402
|
}
|
|
@@ -67863,6 +69432,29 @@ var FigureUIPlugin = class extends UIPlugin {
|
|
|
67863
69432
|
carouselFigureId,
|
|
67864
69433
|
chartFigureIds: cmd.chartFigureIds
|
|
67865
69434
|
});
|
|
69435
|
+
break;
|
|
69436
|
+
case "CREATE_CHART_AND_MERGE_INTO_CAROUSEL":
|
|
69437
|
+
const baseFigureToMerge = this.getters.getFigure(cmd.sheetId, cmd.baseFigureId);
|
|
69438
|
+
if (!baseFigureToMerge) throw new Error(`Figure ${cmd.baseFigureId} does not exists.`);
|
|
69439
|
+
this.dispatch("CREATE_CHART", {
|
|
69440
|
+
chartId: cmd.chartId,
|
|
69441
|
+
figureId: cmd.figureId,
|
|
69442
|
+
sheetId: cmd.sheetId,
|
|
69443
|
+
definition: cmd.definition,
|
|
69444
|
+
col: baseFigureToMerge.col,
|
|
69445
|
+
row: baseFigureToMerge.row,
|
|
69446
|
+
offset: baseFigureToMerge.offset,
|
|
69447
|
+
size: {
|
|
69448
|
+
width: baseFigureToMerge.width,
|
|
69449
|
+
height: baseFigureToMerge.height
|
|
69450
|
+
}
|
|
69451
|
+
});
|
|
69452
|
+
this.dispatch("MERGE_CHART_FIGURES_INTO_CAROUSEL", {
|
|
69453
|
+
sheetId: cmd.sheetId,
|
|
69454
|
+
baseFigureId: cmd.baseFigureId,
|
|
69455
|
+
chartFigureIds: [cmd.baseFigureId, cmd.figureId]
|
|
69456
|
+
});
|
|
69457
|
+
break;
|
|
67866
69458
|
}
|
|
67867
69459
|
}
|
|
67868
69460
|
getFigureUI(sheetId, figure) {
|
|
@@ -69552,6 +71144,9 @@ topbarMenuRegistry.add("file", {
|
|
|
69552
71144
|
}).addChild("split_to_columns", ["data"], {
|
|
69553
71145
|
...splitToColumns,
|
|
69554
71146
|
sequence: 20
|
|
71147
|
+
}).addChild("data_analysis", ["data"], {
|
|
71148
|
+
...dataAnalysis,
|
|
71149
|
+
sequence: 24
|
|
69555
71150
|
}).addChild("column_statistics", ["data"], {
|
|
69556
71151
|
...columnStatistics,
|
|
69557
71152
|
sequence: 25
|
|
@@ -69896,6 +71491,31 @@ var ImageProvider = class {
|
|
|
69896
71491
|
var MainViewportStore = class extends SpreadsheetStore {
|
|
69897
71492
|
viewStore = this.get(ViewportsStore);
|
|
69898
71493
|
sheetIdAtFinalize = void 0;
|
|
71494
|
+
constructor(get) {
|
|
71495
|
+
super(get);
|
|
71496
|
+
this.model.selection.observe(this, { handleEvent: this.handleEvent.bind(this) });
|
|
71497
|
+
this.onDispose(() => {
|
|
71498
|
+
this.model.selection.unobserve(this);
|
|
71499
|
+
});
|
|
71500
|
+
}
|
|
71501
|
+
handleEvent(event) {
|
|
71502
|
+
const eventSheetId = this.getters.getActiveSheetId();
|
|
71503
|
+
if (event.options.scrollIntoView) {
|
|
71504
|
+
const oldZone = event.previousAnchor.zone;
|
|
71505
|
+
const newZone = event.anchor.zone;
|
|
71506
|
+
const isUpdateAnchorEvent = event.mode === "updateAnchor";
|
|
71507
|
+
const sameZone = isEqual(oldZone, newZone);
|
|
71508
|
+
let { col, row } = isUpdateAnchorEvent && sameZone ? event.anchor.cell : findCellInNewZone(oldZone, newZone);
|
|
71509
|
+
if (isUpdateAnchorEvent && !sameZone) {
|
|
71510
|
+
const { top, bottom, left, right } = this.viewStore.viewports.getMainInternalViewport(eventSheetId);
|
|
71511
|
+
if (oldZone.left === newZone.left && oldZone.right === newZone.right) col = left > col || col > right ? left : col;
|
|
71512
|
+
if (oldZone.top === newZone.top && oldZone.bottom === newZone.bottom) row = top > row || row > bottom ? top : row;
|
|
71513
|
+
}
|
|
71514
|
+
col = Math.min(col, this.getters.getNumberCols(eventSheetId) - 1);
|
|
71515
|
+
row = Math.min(row, this.getters.getNumberRows(eventSheetId) - 1);
|
|
71516
|
+
this.viewStore.scrollToCell(eventSheetId, col, row);
|
|
71517
|
+
}
|
|
71518
|
+
}
|
|
69899
71519
|
handle(cmd) {
|
|
69900
71520
|
switch (cmd.type) {
|
|
69901
71521
|
case "UNDO":
|
|
@@ -69953,7 +71573,7 @@ var RippleEffect = class extends Component {
|
|
|
69953
71573
|
onAnimationEnd: types$1.function(),
|
|
69954
71574
|
style: types$1.string()
|
|
69955
71575
|
});
|
|
69956
|
-
rippleRef = signal(
|
|
71576
|
+
rippleRef = signal.ref();
|
|
69957
71577
|
setup() {
|
|
69958
71578
|
let animation = void 0;
|
|
69959
71579
|
onMounted(() => {
|
|
@@ -70007,7 +71627,7 @@ var Ripple = class extends Component {
|
|
|
70007
71627
|
onAnimationEnd: types$1.function().optional(() => () => {}),
|
|
70008
71628
|
class: types$1.string().optional("")
|
|
70009
71629
|
});
|
|
70010
|
-
childContainerRef = signal(
|
|
71630
|
+
childContainerRef = signal.ref();
|
|
70011
71631
|
state = proxy({ ripples: [] });
|
|
70012
71632
|
currentId = 1;
|
|
70013
71633
|
onClick(ev) {
|
|
@@ -70278,9 +71898,9 @@ var BottomBarSheet = class extends Component {
|
|
|
70278
71898
|
isEditing: false,
|
|
70279
71899
|
openedPicker: void 0
|
|
70280
71900
|
});
|
|
70281
|
-
sheetDivRef = signal(
|
|
70282
|
-
iconRef = signal(
|
|
70283
|
-
sheetNameRef = signal(
|
|
71901
|
+
sheetDivRef = signal.ref();
|
|
71902
|
+
iconRef = signal.ref();
|
|
71903
|
+
sheetNameRef = signal.ref();
|
|
70284
71904
|
editionState = "initializing";
|
|
70285
71905
|
DOMFocusableElementStore;
|
|
70286
71906
|
setup() {
|
|
@@ -70564,8 +72184,10 @@ var BottomBarStatistic = class extends Component {
|
|
|
70564
72184
|
});
|
|
70565
72185
|
state = proxy({ selectedStatisticFn: "" });
|
|
70566
72186
|
store;
|
|
72187
|
+
sidePanelStore;
|
|
70567
72188
|
setup() {
|
|
70568
72189
|
this.store = useStore(AggregateStatisticsStore);
|
|
72190
|
+
this.sidePanelStore = useStore(SidePanelStore);
|
|
70569
72191
|
onWillUpdateProps(() => {
|
|
70570
72192
|
if (Object.values(this.store.statisticFnResults).every((result) => result?.value === void 0)) this.props.closeContextMenu();
|
|
70571
72193
|
});
|
|
@@ -70605,6 +72227,12 @@ var BottomBarStatistic = class extends Component {
|
|
|
70605
72227
|
format: fnValue.format()
|
|
70606
72228
|
});
|
|
70607
72229
|
}
|
|
72230
|
+
get isDataAnalysisOpen() {
|
|
72231
|
+
return this.sidePanelStore.mainPanel?.componentTag === "DataAnalysisPanel" && this.sidePanelStore.isMainPanelOpen || this.sidePanelStore.secondaryPanel?.componentTag === "DataAnalysisPanel" && this.sidePanelStore.isSecondaryPanelOpen;
|
|
72232
|
+
}
|
|
72233
|
+
toggleDataAnalysisPanel() {
|
|
72234
|
+
this.sidePanelStore.togglePinnedSidePanel("DataAnalysisPanel");
|
|
72235
|
+
}
|
|
70608
72236
|
};
|
|
70609
72237
|
|
|
70610
72238
|
//#endregion
|
|
@@ -70619,8 +72247,8 @@ var BottomBar = class extends Component {
|
|
|
70619
72247
|
BottomBarSheet,
|
|
70620
72248
|
BottomBarStatistic
|
|
70621
72249
|
};
|
|
70622
|
-
bottomBarRef = signal(
|
|
70623
|
-
sheetListRef = signal(
|
|
72250
|
+
bottomBarRef = signal.ref();
|
|
72251
|
+
sheetListRef = signal.ref();
|
|
70624
72252
|
dragAndDrop = useDragAndDropListItems();
|
|
70625
72253
|
targetScroll = void 0;
|
|
70626
72254
|
state = proxy({
|
|
@@ -70833,12 +72461,13 @@ var SpreadsheetDashboard = class extends Component {
|
|
|
70833
72461
|
hoveredCell;
|
|
70834
72462
|
viewStore;
|
|
70835
72463
|
zoomStore;
|
|
70836
|
-
gridRef = signal(
|
|
70837
|
-
canvasRef = signal(
|
|
72464
|
+
gridRef = signal.ref();
|
|
72465
|
+
canvasRef = signal.ref(HTMLCanvasElement);
|
|
70838
72466
|
setup() {
|
|
70839
72467
|
this.hoveredCell = useStore(DelayedHoveredCellStore);
|
|
70840
72468
|
this.viewStore = useStore(ViewportsStore);
|
|
70841
72469
|
this.zoomStore = useStore(ZoomStore);
|
|
72470
|
+
useLocalStore(HoveredTableStore);
|
|
70842
72471
|
const rendererStore = useLocalStore(RendererStore, OrderedLayers().filter((layer) => layer !== "Headers"));
|
|
70843
72472
|
useChildSubEnv({ getPopoverContainerRect: () => this.zoomStore.getZoomedRect(this.getGridRect()) });
|
|
70844
72473
|
useGridDrawing({
|
|
@@ -71256,8 +72885,8 @@ var RibbonMenu = class extends Component {
|
|
|
71256
72885
|
static components = { Menu };
|
|
71257
72886
|
props = useProps({ onClose: types$1.function() });
|
|
71258
72887
|
rootItems = topbarMenuRegistry.getMenuItems();
|
|
71259
|
-
menuRef = signal(
|
|
71260
|
-
containerRef = signal(
|
|
72888
|
+
menuRef = signal.ref();
|
|
72889
|
+
containerRef = signal.ref();
|
|
71261
72890
|
state = proxy({
|
|
71262
72891
|
menuItems: this.rootItems,
|
|
71263
72892
|
title: _t("Menu Bar"),
|
|
@@ -71330,7 +72959,7 @@ var SmallBottomBar = class extends Component {
|
|
|
71330
72959
|
composerStore;
|
|
71331
72960
|
viewStore;
|
|
71332
72961
|
composerInterface;
|
|
71333
|
-
composerRef = signal(
|
|
72962
|
+
composerRef = signal.ref();
|
|
71334
72963
|
menuState = proxy({ isOpen: false });
|
|
71335
72964
|
setup() {
|
|
71336
72965
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
@@ -71409,6 +73038,49 @@ var SmallBottomBar = class extends Component {
|
|
|
71409
73038
|
}
|
|
71410
73039
|
};
|
|
71411
73040
|
|
|
73041
|
+
//#endregion
|
|
73042
|
+
//#region src/components/standalone_grid_canvas/carousel_data_view_print.ts
|
|
73043
|
+
var CarouselDataViewPrint = class extends Component {
|
|
73044
|
+
static template = xml``;
|
|
73045
|
+
props = useProps({
|
|
73046
|
+
range: types$1.Range(),
|
|
73047
|
+
columnWeights: types$1.array().optional(),
|
|
73048
|
+
rect: types$1.object(),
|
|
73049
|
+
rendererStore: types$1.object()
|
|
73050
|
+
});
|
|
73051
|
+
viewportsStore;
|
|
73052
|
+
gridRender;
|
|
73053
|
+
setup() {
|
|
73054
|
+
useChildStoreProvider([ViewportsStore]);
|
|
73055
|
+
const store = useLocalStore(StandaloneViewportStore, this.props.range, this.props.columnWeights);
|
|
73056
|
+
this.gridRender = useLocalStore(GridRenderer, null);
|
|
73057
|
+
this.viewportsStore = useStore(ViewportsStore);
|
|
73058
|
+
useEffect(() => {
|
|
73059
|
+
store.setContainerSize(this.props.rect.width, this.props.rect.height);
|
|
73060
|
+
});
|
|
73061
|
+
onMounted(() => this.props.rendererStore.register(this));
|
|
73062
|
+
onWillUnmount(() => this.props.rendererStore.unRegister(this));
|
|
73063
|
+
}
|
|
73064
|
+
get renderingLayers() {
|
|
73065
|
+
return ["Chart"];
|
|
73066
|
+
}
|
|
73067
|
+
drawLayer(renderingContext) {
|
|
73068
|
+
const { ctx } = renderingContext;
|
|
73069
|
+
ctx.save();
|
|
73070
|
+
ctx.translate(this.props.rect.x, this.props.rect.y);
|
|
73071
|
+
ctx.beginPath();
|
|
73072
|
+
ctx.rect(0, 0, this.props.rect.width, this.props.rect.height);
|
|
73073
|
+
ctx.clip();
|
|
73074
|
+
const subRenderingCtx = {
|
|
73075
|
+
...renderingContext,
|
|
73076
|
+
sheetId: this.viewportsStore.displayedSheetId,
|
|
73077
|
+
viewports: this.viewportsStore.viewports
|
|
73078
|
+
};
|
|
73079
|
+
this.gridRender.drawLayer(subRenderingCtx, "Background", void 0);
|
|
73080
|
+
ctx.restore();
|
|
73081
|
+
}
|
|
73082
|
+
};
|
|
73083
|
+
|
|
71412
73084
|
//#endregion
|
|
71413
73085
|
//#region src/components/standalone_grid_canvas/figure_renderer_store.ts
|
|
71414
73086
|
/** Store that draws the figures directly onto the canvas */
|
|
@@ -71434,29 +73106,25 @@ var FigureRendererStore = class extends DisposableStore {
|
|
|
71434
73106
|
drawLayer(renderingCtx) {
|
|
71435
73107
|
const { viewports, ctx } = renderingCtx;
|
|
71436
73108
|
const visibleFigures = viewports.getVisibleFigures(renderingCtx.sheetId);
|
|
71437
|
-
const
|
|
73109
|
+
const scrollOffset = viewports.getViewportOffset(renderingCtx.sheetId);
|
|
71438
73110
|
for (const figure of visibleFigures) {
|
|
71439
|
-
const
|
|
71440
|
-
|
|
73111
|
+
const figureRect = {
|
|
73112
|
+
...figure,
|
|
73113
|
+
x: figure.x - scrollOffset.x,
|
|
73114
|
+
y: figure.y - scrollOffset.y
|
|
73115
|
+
};
|
|
73116
|
+
const { x, y, width, height } = figureRect;
|
|
71441
73117
|
if (figure.tag === "chart") {
|
|
71442
73118
|
const chartId = this.getters.getChartIdFromFigureId(figure.id);
|
|
71443
|
-
if (chartId)
|
|
71444
|
-
const chartRect = {
|
|
71445
|
-
x,
|
|
71446
|
-
y,
|
|
71447
|
-
width: figure.width,
|
|
71448
|
-
height: figure.height
|
|
71449
|
-
};
|
|
71450
|
-
this.drawChart(renderingCtx, chartId, chartRect);
|
|
71451
|
-
}
|
|
73119
|
+
if (chartId) this.drawChart(renderingCtx, chartId, figureRect);
|
|
71452
73120
|
} else if (figure.tag === "image") {
|
|
71453
73121
|
const loadedImage = this.loadedImages[this.getters.getImagePath(figure.id)];
|
|
71454
|
-
if (loadedImage) ctx.drawImage(loadedImage, x, y,
|
|
71455
|
-
} else if (figure.tag === "carousel") this.drawCarousel(renderingCtx, figure);
|
|
73122
|
+
if (loadedImage) ctx.drawImage(loadedImage, x, y, width, height);
|
|
73123
|
+
} else if (figure.tag === "carousel") this.drawCarousel(renderingCtx, figure, figureRect);
|
|
71456
73124
|
if (!this.getters.isDashboard()) {
|
|
71457
73125
|
ctx.strokeStyle = GRAY_400;
|
|
71458
73126
|
ctx.lineWidth = 1;
|
|
71459
|
-
ctx.strokeRect(x, y,
|
|
73127
|
+
ctx.strokeRect(x, y, width, height);
|
|
71460
73128
|
}
|
|
71461
73129
|
}
|
|
71462
73130
|
}
|
|
@@ -71474,53 +73142,37 @@ var FigureRendererStore = class extends DisposableStore {
|
|
|
71474
73142
|
renderingCtx.ctx.drawImage(chartCanvas, x, y, width, height);
|
|
71475
73143
|
cleanUp();
|
|
71476
73144
|
}
|
|
71477
|
-
drawCarousel(renderingCtx, figure) {
|
|
71478
|
-
const {
|
|
71479
|
-
const { x: offsetX, y: offsetY } = sheetView.getViewportOffset(renderingCtx.sheetId);
|
|
71480
|
-
const x = figure.x - offsetX;
|
|
71481
|
-
const y = figure.y - offsetY;
|
|
73145
|
+
drawCarousel(renderingCtx, figure, figureRect) {
|
|
73146
|
+
const { ctx } = renderingCtx;
|
|
71482
73147
|
const carousel = this.getters.getCarousel(figure.id);
|
|
71483
73148
|
const chartId = this.getters.getChartIdFromFigureId(figure.id);
|
|
71484
73149
|
if (!carousel) return;
|
|
73150
|
+
const layout = getCarouselLayout(figureRect, carousel, this.getters.getSelectedCarouselItem(figure.id));
|
|
71485
73151
|
const chartDefinition = chartId ? this.getters.getChartDefinition(chartId) : void 0;
|
|
73152
|
+
ctx.save();
|
|
73153
|
+
ctx.beginPath();
|
|
73154
|
+
ctx.rect(figureRect.x, figureRect.y, figureRect.width, figureRect.height);
|
|
73155
|
+
ctx.clip();
|
|
73156
|
+
ctx.fillStyle = chartDefinition?.background || this.getters.getSpreadsheetTheme().backgroundColor;
|
|
73157
|
+
ctx.fillRect(figureRect.x, figureRect.y, figureRect.width, figureRect.height);
|
|
71486
73158
|
const title = {
|
|
71487
73159
|
...DEFAULT_CAROUSEL_TITLE_STYLE,
|
|
71488
73160
|
...carousel.title
|
|
71489
73161
|
};
|
|
71490
|
-
|
|
71491
|
-
|
|
71492
|
-
if (!title.text && chartId) {
|
|
71493
|
-
const chartRect = {
|
|
71494
|
-
x,
|
|
71495
|
-
y,
|
|
71496
|
-
width: figure.width,
|
|
71497
|
-
height: figure.height
|
|
71498
|
-
};
|
|
71499
|
-
this.drawChart(renderingCtx, chartId, chartRect);
|
|
71500
|
-
} else if (title.text) {
|
|
71501
|
-
ctx.save();
|
|
71502
|
-
ctx.fillStyle = chartDefinition?.background || this.getters.getSpreadsheetTheme().backgroundColor;
|
|
71503
|
-
ctx.fillRect(x, y, figure.width, headerSize);
|
|
71504
|
-
ctx.font = computeTextFont(chartStyleToCellStyle(title));
|
|
73162
|
+
if (title.text) {
|
|
73163
|
+
ctx.font = computeTextFont(chartStyleToCellStyle(title), "px", 500);
|
|
71505
73164
|
ctx.fillStyle = title.color;
|
|
71506
|
-
ctx.
|
|
71507
|
-
|
|
71508
|
-
|
|
71509
|
-
x,
|
|
71510
|
-
y: y + headerSize,
|
|
71511
|
-
width: figure.width,
|
|
71512
|
-
height: figure.height - headerSize
|
|
71513
|
-
};
|
|
71514
|
-
if (chartId) this.drawChart(renderingCtx, chartId, chartRect);
|
|
71515
|
-
else if (!this.getters.isDashboard()) {
|
|
71516
|
-
ctx.strokeStyle = GRAY_400;
|
|
71517
|
-
ctx.lineWidth = 1;
|
|
71518
|
-
ctx.beginPath();
|
|
71519
|
-
ctx.moveTo(x, y + headerSize);
|
|
71520
|
-
ctx.lineTo(x + figure.width, y + headerSize);
|
|
71521
|
-
ctx.stroke();
|
|
71522
|
-
}
|
|
73165
|
+
ctx.textBaseline = "middle";
|
|
73166
|
+
const textY = Math.ceil(layout.headerRect.y + layout.headerRect.height / 2);
|
|
73167
|
+
ctx.fillText(title.text, layout.headerRect.x, textY);
|
|
71523
73168
|
}
|
|
73169
|
+
const separator = layout.separatorRect;
|
|
73170
|
+
if (separator) {
|
|
73171
|
+
ctx.fillStyle = GRAY_400;
|
|
73172
|
+
ctx.fillRect(separator.x, separator.y, separator.width, separator.height);
|
|
73173
|
+
}
|
|
73174
|
+
ctx.restore();
|
|
73175
|
+
if (chartId) this.drawChart(renderingCtx, chartId, layout.contentRect);
|
|
71524
73176
|
}
|
|
71525
73177
|
};
|
|
71526
73178
|
|
|
@@ -71528,12 +73180,13 @@ var FigureRendererStore = class extends DisposableStore {
|
|
|
71528
73180
|
//#region src/components/standalone_grid_canvas/standalone_grid_canvas.ts
|
|
71529
73181
|
var StandaloneGridCanvas = class extends Component {
|
|
71530
73182
|
static template = "o-spreadsheet-StandaloneGridCanvas";
|
|
73183
|
+
static components = { CarouselDataViewPrint };
|
|
71531
73184
|
props = useProps({
|
|
71532
73185
|
sheetId: types$1.UID(),
|
|
71533
73186
|
zone: types$1.Zone(),
|
|
71534
73187
|
renderingCtx: types$1.object()
|
|
71535
73188
|
});
|
|
71536
|
-
canvasRef = signal(
|
|
73189
|
+
canvasRef = signal.ref(HTMLCanvasElement);
|
|
71537
73190
|
rendererStore;
|
|
71538
73191
|
figureRendererStore;
|
|
71539
73192
|
setup() {
|
|
@@ -71555,6 +73208,28 @@ var StandaloneGridCanvas = class extends Component {
|
|
|
71555
73208
|
this.figureRendererStore.addLoadedImage(path, await createImageBitmap(blob));
|
|
71556
73209
|
}));
|
|
71557
73210
|
}
|
|
73211
|
+
get carouselDataViews() {
|
|
73212
|
+
const { sheetId, viewports } = this.props.renderingCtx;
|
|
73213
|
+
return viewports.getVisibleFigures(sheetId).filter((figure) => figure.tag === "carousel").map((figure) => {
|
|
73214
|
+
const carouselItem = this.env.model.getters.getSelectedCarouselItem(figure.id);
|
|
73215
|
+
if (carouselItem?.type !== "carouselDataView" || !carouselItem.range) return;
|
|
73216
|
+
const carousel = this.env.model.getters.getCarousel(figure.id);
|
|
73217
|
+
const scroll = viewports.getViewportOffset(sheetId);
|
|
73218
|
+
const { x, y, width, height } = figure;
|
|
73219
|
+
const layout = getCarouselLayout({
|
|
73220
|
+
x: x - scroll.x,
|
|
73221
|
+
y: y - scroll.y,
|
|
73222
|
+
width,
|
|
73223
|
+
height
|
|
73224
|
+
}, carousel, carouselItem);
|
|
73225
|
+
return {
|
|
73226
|
+
range: carouselItem.range,
|
|
73227
|
+
columnWeights: carouselItem.columnWeights,
|
|
73228
|
+
rect: layout.contentRect,
|
|
73229
|
+
rendererStore: this.rendererStore
|
|
73230
|
+
};
|
|
73231
|
+
}).filter(isDefined);
|
|
73232
|
+
}
|
|
71558
73233
|
};
|
|
71559
73234
|
|
|
71560
73235
|
//#endregion
|
|
@@ -72016,7 +73691,7 @@ var NamedRangeSelector = class extends Component {
|
|
|
72016
73691
|
anchorRect: null,
|
|
72017
73692
|
menuItems: []
|
|
72018
73693
|
});
|
|
72019
|
-
namedRangeSelectorRef = signal(
|
|
73694
|
+
namedRangeSelectorRef = signal.ref();
|
|
72020
73695
|
setup() {
|
|
72021
73696
|
this.topBarToolStore = useToolBarDropdownStore();
|
|
72022
73697
|
this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
|
|
@@ -72214,7 +73889,7 @@ var BorderEditor = class extends Component {
|
|
|
72214
73889
|
anchorRect: types$1.Rect()
|
|
72215
73890
|
});
|
|
72216
73891
|
BORDER_POSITIONS = BORDER_POSITIONS;
|
|
72217
|
-
lineStyleButtonRef = signal(
|
|
73892
|
+
lineStyleButtonRef = signal.ref();
|
|
72218
73893
|
borderStyles = borderStyles;
|
|
72219
73894
|
state = proxy({ activeTool: void 0 });
|
|
72220
73895
|
toggleDropdownTool(tool) {
|
|
@@ -72281,7 +73956,7 @@ var BorderEditorWidget = class extends Component {
|
|
|
72281
73956
|
});
|
|
72282
73957
|
topBarToolStore;
|
|
72283
73958
|
viewStore;
|
|
72284
|
-
borderEditorButtonRef = signal(
|
|
73959
|
+
borderEditorButtonRef = signal.ref();
|
|
72285
73960
|
state = proxy({
|
|
72286
73961
|
currentColor: DEFAULT_BORDER_DESC.color,
|
|
72287
73962
|
currentStyle: DEFAULT_BORDER_DESC.style,
|
|
@@ -72511,7 +74186,7 @@ var DropdownAction = class extends Component {
|
|
|
72511
74186
|
childClass: types$1.string()
|
|
72512
74187
|
});
|
|
72513
74188
|
topBarToolStore;
|
|
72514
|
-
actionRef = signal(
|
|
74189
|
+
actionRef = signal.ref();
|
|
72515
74190
|
setup() {
|
|
72516
74191
|
this.topBarToolStore = useToolBarDropdownStore();
|
|
72517
74192
|
}
|
|
@@ -72574,7 +74249,7 @@ var NumberFormatsTool = class extends Component {
|
|
|
72574
74249
|
props = useProps({ class: types$1.string() });
|
|
72575
74250
|
formatNumberMenuItemSpec = formatNumberMenuItemSpec;
|
|
72576
74251
|
topBarToolStore;
|
|
72577
|
-
buttonRef = signal(
|
|
74252
|
+
buttonRef = signal.ref();
|
|
72578
74253
|
state = proxy({
|
|
72579
74254
|
anchorRect: {
|
|
72580
74255
|
x: 0,
|
|
@@ -72863,12 +74538,12 @@ var TopBar = class extends Component {
|
|
|
72863
74538
|
composerFocusStore;
|
|
72864
74539
|
fingerprints;
|
|
72865
74540
|
topBarToolStore;
|
|
72866
|
-
toolBarContainerRef = signal(
|
|
72867
|
-
toolbarRef = signal(
|
|
72868
|
-
namedRangesRef = signal(
|
|
72869
|
-
topBarTopRef = signal(
|
|
72870
|
-
moreToolsContainerRef = signal(
|
|
72871
|
-
moreToolsButtonRef = signal(
|
|
74541
|
+
toolBarContainerRef = signal.ref();
|
|
74542
|
+
toolbarRef = signal.ref();
|
|
74543
|
+
namedRangesRef = signal.ref();
|
|
74544
|
+
topBarTopRef = signal.ref();
|
|
74545
|
+
moreToolsContainerRef = signal.ref();
|
|
74546
|
+
moreToolsButtonRef = signal.ref();
|
|
72872
74547
|
spreadsheetRect = useSpreadsheetRect();
|
|
72873
74548
|
setup() {
|
|
72874
74549
|
this.composerFocusStore = useStore(ComposerFocusStore);
|
|
@@ -73130,7 +74805,7 @@ var Spreadsheet = class extends Component {
|
|
|
73130
74805
|
SpreadsheetPrint
|
|
73131
74806
|
};
|
|
73132
74807
|
sidePanel;
|
|
73133
|
-
spreadsheetRef = signal(
|
|
74808
|
+
spreadsheetRef = signal.ref();
|
|
73134
74809
|
spreadsheetRect = useSpreadsheetRect();
|
|
73135
74810
|
state = proxy({ printModeEnabled: false });
|
|
73136
74811
|
_focusGrid;
|
|
@@ -79138,12 +80813,13 @@ var FilterMenu = class extends Component {
|
|
|
79138
80813
|
}
|
|
79139
80814
|
sortFilterZone(sortDirection) {
|
|
79140
80815
|
const filterPosition = this.props.filterPosition;
|
|
79141
|
-
const
|
|
80816
|
+
const table = this.table;
|
|
80817
|
+
const tableZone = table?.range.zone;
|
|
79142
80818
|
if (!filterPosition || !tableZone || tableZone.top === tableZone.bottom) return;
|
|
79143
80819
|
const sheetId = this.viewStore.displayedSheetId;
|
|
79144
80820
|
const contentZone = {
|
|
79145
80821
|
...tableZone,
|
|
79146
|
-
top: tableZone.top +
|
|
80822
|
+
top: tableZone.top + table.config.numberOfHeaders
|
|
79147
80823
|
};
|
|
79148
80824
|
const sortAnchor = {
|
|
79149
80825
|
col: filterPosition.col,
|
|
@@ -79301,9 +80977,9 @@ var LinkEditor = class extends Component {
|
|
|
79301
80977
|
onClosed: types$1.function().optional()
|
|
79302
80978
|
});
|
|
79303
80979
|
static size = { maxHeight: 500 };
|
|
79304
|
-
urlInput = signal(
|
|
79305
|
-
suggestionListRef = signal(
|
|
79306
|
-
urlInputContainer = signal(
|
|
80980
|
+
urlInput = signal.ref(HTMLInputElement);
|
|
80981
|
+
suggestionListRef = signal.ref();
|
|
80982
|
+
urlInputContainer = signal.ref();
|
|
79307
80983
|
state;
|
|
79308
80984
|
viewStore;
|
|
79309
80985
|
setup() {
|
|
@@ -81421,7 +83097,7 @@ const ARRAYTOTEXT = {
|
|
|
81421
83097
|
else if (_format === 0) {
|
|
81422
83098
|
const rowSeparator = this.locale.decimalSeparator === "," ? "/" : ",";
|
|
81423
83099
|
return { value: transposeMatrix(_array).flatMap((row) => row.map((value) => {
|
|
81424
|
-
return isEvaluationError(value.value) ? value.value :
|
|
83100
|
+
return isEvaluationError(value.value) ? value.value : toLocaleString(value, this.locale);
|
|
81425
83101
|
})).join(rowSeparator) };
|
|
81426
83102
|
} else return new EvaluationError(_t("Format must be 0 or 1"));
|
|
81427
83103
|
},
|
|
@@ -87469,7 +89145,7 @@ const CONCAT = {
|
|
|
87469
89145
|
description: _t("Concatenation of two values."),
|
|
87470
89146
|
args: [arg("value1 (string)", _t("The value to which value2 will be appended.")), arg("value2 (string)", _t("The value to append to value1."))],
|
|
87471
89147
|
compute: function(value1, value2) {
|
|
87472
|
-
return { value:
|
|
89148
|
+
return { value: toLocaleString(value1, this.locale) + toLocaleString(value2, this.locale) };
|
|
87473
89149
|
},
|
|
87474
89150
|
isExported: true
|
|
87475
89151
|
};
|
|
@@ -88352,7 +90028,7 @@ const CLEAN = {
|
|
|
88352
90028
|
description: _t("Remove non-printable characters from a piece of text."),
|
|
88353
90029
|
args: [arg("text (string)", _t("The text whose non-printable characters are to be removed."))],
|
|
88354
90030
|
compute: function(text) {
|
|
88355
|
-
const _text =
|
|
90031
|
+
const _text = toLocaleString(text, this.locale);
|
|
88356
90032
|
let cleanedStr = "";
|
|
88357
90033
|
for (const char of _text) if (char && char.charCodeAt(0) > 31) cleanedStr += char;
|
|
88358
90034
|
return { value: cleanedStr };
|
|
@@ -88363,7 +90039,7 @@ const CONCATENATE = {
|
|
|
88363
90039
|
description: _t("Appends strings to one another."),
|
|
88364
90040
|
args: [arg("string (string, range<string>, repeating)", _t("String to append in sequence."))],
|
|
88365
90041
|
compute: function(...datas) {
|
|
88366
|
-
return { value: reduceAny(datas, (acc, a) => acc +
|
|
90042
|
+
return { value: reduceAny(datas, (acc, a) => acc + toLocaleString(a, this.locale), "") };
|
|
88367
90043
|
},
|
|
88368
90044
|
isExported: true
|
|
88369
90045
|
};
|
|
@@ -88371,7 +90047,7 @@ const EXACT = {
|
|
|
88371
90047
|
description: _t("Tests whether two strings are identical."),
|
|
88372
90048
|
args: [arg("string1 (string)", _t("The first string to compare.")), arg("string2 (string)", _t("The second string to compare."))],
|
|
88373
90049
|
compute: function(string1, string2) {
|
|
88374
|
-
return { value:
|
|
90050
|
+
return { value: toLocaleString(string1, this.locale) === toLocaleString(string2, this.locale) };
|
|
88375
90051
|
},
|
|
88376
90052
|
isExported: true
|
|
88377
90053
|
};
|
|
@@ -88383,8 +90059,8 @@ const FIND = {
|
|
|
88383
90059
|
arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
|
|
88384
90060
|
],
|
|
88385
90061
|
compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
|
|
88386
|
-
const _searchFor =
|
|
88387
|
-
const _textToSearch =
|
|
90062
|
+
const _searchFor = toLocaleString(searchFor, this.locale);
|
|
90063
|
+
const _textToSearch = toLocaleString(textToSearch, this.locale);
|
|
88388
90064
|
const _startingAt = toNumber(startingAt, this.locale);
|
|
88389
90065
|
if (_textToSearch === "") return new EvaluationError(_t("The text_to_search must be non-empty."));
|
|
88390
90066
|
if (_startingAt < 1) return new EvaluationError(_t("The starting_at (%s) must be greater than or equal to 1.", _startingAt));
|
|
@@ -88421,8 +90097,8 @@ const JOIN = {
|
|
|
88421
90097
|
description: _t("Concatenates elements of arrays with delimiter."),
|
|
88422
90098
|
args: [arg("delimiter (string)", _t("The character or string to place between each concatenated value.")), arg("value_or_array (string, range<string>, repeating)", _t("Value to be appended using delimiter."))],
|
|
88423
90099
|
compute: function(delimiter, ...valuesOrArrays) {
|
|
88424
|
-
const _delimiter =
|
|
88425
|
-
return { value: reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") +
|
|
90100
|
+
const _delimiter = toLocaleString(delimiter, this.locale);
|
|
90101
|
+
return { value: reduceAny(valuesOrArrays, (acc, a) => (acc ? acc + _delimiter : "") + toLocaleString(a, this.locale), "") };
|
|
88426
90102
|
}
|
|
88427
90103
|
};
|
|
88428
90104
|
const LEFT = {
|
|
@@ -88431,7 +90107,7 @@ const LEFT = {
|
|
|
88431
90107
|
compute: function(text, ...args) {
|
|
88432
90108
|
const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
|
|
88433
90109
|
if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or null.", _numberOfCharacters));
|
|
88434
|
-
return { value:
|
|
90110
|
+
return { value: toLocaleString(text, this.locale).substring(0, _numberOfCharacters) };
|
|
88435
90111
|
},
|
|
88436
90112
|
isExported: true
|
|
88437
90113
|
};
|
|
@@ -88439,7 +90115,7 @@ const LEN = {
|
|
|
88439
90115
|
description: _t("Length of a string."),
|
|
88440
90116
|
args: [arg("text (string)", _t("The string whose length will be returned."))],
|
|
88441
90117
|
compute: function(text) {
|
|
88442
|
-
return { value:
|
|
90118
|
+
return { value: toLocaleString(text, this.locale).length };
|
|
88443
90119
|
},
|
|
88444
90120
|
isExported: true
|
|
88445
90121
|
};
|
|
@@ -88447,7 +90123,7 @@ const LOWER = {
|
|
|
88447
90123
|
description: _t("Converts a specified string to lowercase."),
|
|
88448
90124
|
args: [arg("text (string)", _t("The string to convert to lowercase."))],
|
|
88449
90125
|
compute: function(text) {
|
|
88450
|
-
return { value:
|
|
90126
|
+
return { value: toLocaleString(text, this.locale).toLowerCase() };
|
|
88451
90127
|
},
|
|
88452
90128
|
isExported: true
|
|
88453
90129
|
};
|
|
@@ -88459,7 +90135,7 @@ const MID = {
|
|
|
88459
90135
|
arg("extract_length (number)", _t("The length of the segment to extract."))
|
|
88460
90136
|
],
|
|
88461
90137
|
compute: function(text, starting_at, extract_length) {
|
|
88462
|
-
const _text =
|
|
90138
|
+
const _text = toLocaleString(text, this.locale);
|
|
88463
90139
|
const _starting_at = toNumber(starting_at, this.locale);
|
|
88464
90140
|
const _extract_length = toNumber(extract_length, this.locale);
|
|
88465
90141
|
if (_starting_at < 1) return new EvaluationError(_t("The starting_at argument (%s) must be positive greater than one.", _starting_at.toString()));
|
|
@@ -88472,7 +90148,7 @@ const PROPER = {
|
|
|
88472
90148
|
description: _t("Capitalizes each word in a specified string."),
|
|
88473
90149
|
args: [arg("text_to_capitalize (string)", _t("The text which will be returned with the first letter of each word in uppercase and all other letters in lowercase."))],
|
|
88474
90150
|
compute: function(text) {
|
|
88475
|
-
return { value:
|
|
90151
|
+
return { value: toLocaleString(text, this.locale).replace(wordRegex, (word) => {
|
|
88476
90152
|
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
88477
90153
|
}) };
|
|
88478
90154
|
},
|
|
@@ -88622,8 +90298,8 @@ const REPLACE = {
|
|
|
88622
90298
|
if (_position < 1) return new EvaluationError(_t("The position (%s) must be greater than or equal to 1.", _position));
|
|
88623
90299
|
const _length = toNumber(length, this.locale);
|
|
88624
90300
|
if (_length < 0) return new EvaluationError(_t("The length (%s) must be positive or zero.", _length));
|
|
88625
|
-
const _text =
|
|
88626
|
-
const _newText =
|
|
90301
|
+
const _text = toLocaleString(text, this.locale);
|
|
90302
|
+
const _newText = toLocaleString(newText, this.locale);
|
|
88627
90303
|
return { value: _text.substring(0, _position - 1) + _newText + _text.substring(_position - 1 + _length) };
|
|
88628
90304
|
},
|
|
88629
90305
|
isExported: true
|
|
@@ -88634,7 +90310,7 @@ const RIGHT = {
|
|
|
88634
90310
|
compute: function(text, ...args) {
|
|
88635
90311
|
const _numberOfCharacters = args.length ? toNumber(args[0], this.locale) : 1;
|
|
88636
90312
|
if (_numberOfCharacters < 0) return new EvaluationError(_t("The number_of_characters (%s) must be positive or zero.", _numberOfCharacters));
|
|
88637
|
-
const _text =
|
|
90313
|
+
const _text = toLocaleString(text, this.locale);
|
|
88638
90314
|
const stringLength = _text.length;
|
|
88639
90315
|
return { value: _text.substring(stringLength - _numberOfCharacters, stringLength) };
|
|
88640
90316
|
},
|
|
@@ -88648,8 +90324,8 @@ const SEARCH = {
|
|
|
88648
90324
|
arg(`starting_at (number, default=${DEFAULT_STARTING_AT})`, _t("The character within text_to_search at which to start the search."))
|
|
88649
90325
|
],
|
|
88650
90326
|
compute: function(searchFor, textToSearch, startingAt = { value: DEFAULT_STARTING_AT }) {
|
|
88651
|
-
const _searchFor =
|
|
88652
|
-
const _textToSearch =
|
|
90327
|
+
const _searchFor = toLocaleString(searchFor, this.locale).toLowerCase();
|
|
90328
|
+
const _textToSearch = toLocaleString(textToSearch, this.locale).toLowerCase();
|
|
88653
90329
|
const _startingAt = toNumber(startingAt, this.locale);
|
|
88654
90330
|
if (_textToSearch === "") return {
|
|
88655
90331
|
value: CellErrorType.GenericError,
|
|
@@ -88679,8 +90355,8 @@ const SPLIT = {
|
|
|
88679
90355
|
arg(`remove_empty_text (boolean, default=${SPLIT_DEFAULT_REMOVE_EMPTY_TEXT})`, _t("Whether or not to remove empty text messages from the split results. The default behavior is to treat consecutive delimiters as one (if TRUE). If FALSE, empty cells values are added between consecutive delimiters."))
|
|
88680
90356
|
],
|
|
88681
90357
|
computeArray: function(text, delimiter, splitByEach = { value: SPLIT_DEFAULT_SPLIT_BY_EACH }, removeEmptyText = { value: SPLIT_DEFAULT_REMOVE_EMPTY_TEXT }) {
|
|
88682
|
-
const _text =
|
|
88683
|
-
const _delimiter = escapeRegExp(
|
|
90358
|
+
const _text = toLocaleString(text, this.locale);
|
|
90359
|
+
const _delimiter = escapeRegExp(toLocaleString(delimiter, this.locale));
|
|
88684
90360
|
const _splitByEach = toBoolean(splitByEach);
|
|
88685
90361
|
const _removeEmptyText = toBoolean(removeEmptyText);
|
|
88686
90362
|
if (_delimiter.length <= 0) return new EvaluationError(_t("The delimiter (%s) must be not be empty.", _delimiter));
|
|
@@ -88702,10 +90378,10 @@ const SUBSTITUTE = {
|
|
|
88702
90378
|
compute: function(textToSearch, searchFor, replaceWith, occurrenceNumber) {
|
|
88703
90379
|
const _occurrenceNumber = toNumber(occurrenceNumber, this.locale);
|
|
88704
90380
|
if (_occurrenceNumber < 0) return new EvaluationError(_t("The occurrenceNumber (%s) must be positive or null.", _occurrenceNumber));
|
|
88705
|
-
const _textToSearch =
|
|
88706
|
-
const _searchFor =
|
|
90381
|
+
const _textToSearch = toLocaleString(textToSearch, this.locale);
|
|
90382
|
+
const _searchFor = toLocaleString(searchFor, this.locale);
|
|
88707
90383
|
if (_searchFor === "") return { value: _textToSearch };
|
|
88708
|
-
const _replaceWith =
|
|
90384
|
+
const _replaceWith = toLocaleString(replaceWith, this.locale);
|
|
88709
90385
|
const reg = new RegExp(escapeRegExp(_searchFor), "g");
|
|
88710
90386
|
if (_occurrenceNumber === 0) return { value: _textToSearch.replace(reg, _replaceWith) };
|
|
88711
90387
|
let n = 0;
|
|
@@ -88728,10 +90404,10 @@ const TEXTJOIN = {
|
|
|
88728
90404
|
arg("texts (string, range<string>, repeating)", _t("Text item to join."))
|
|
88729
90405
|
],
|
|
88730
90406
|
compute: function(delimiter, ignoreEmpty = { value: TEXTJOIN_DEFAULT_IGNORE_EMPTY }, ...textsOrArrays) {
|
|
88731
|
-
const _delimiter =
|
|
90407
|
+
const _delimiter = toLocaleString(delimiter, this.locale);
|
|
88732
90408
|
const _ignoreEmpty = toBoolean(ignoreEmpty);
|
|
88733
90409
|
let n = 0;
|
|
88734
|
-
return { value: reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty &&
|
|
90410
|
+
return { value: reduceAny(textsOrArrays, (acc, a) => !(_ignoreEmpty && toLocaleString(a, this.locale) === "") ? (n++ ? acc + _delimiter : "") + toLocaleString(a, this.locale) : acc, "") };
|
|
88735
90411
|
},
|
|
88736
90412
|
isExported: true
|
|
88737
90413
|
};
|
|
@@ -88784,7 +90460,7 @@ const TRIM = {
|
|
|
88784
90460
|
description: _t("Removes space characters."),
|
|
88785
90461
|
args: [arg("text (string)", _t("The text or reference to a cell containing text to be trimmed."))],
|
|
88786
90462
|
compute: function(text) {
|
|
88787
|
-
return { value: trimContent(
|
|
90463
|
+
return { value: trimContent(toLocaleString(text, this.locale)) };
|
|
88788
90464
|
},
|
|
88789
90465
|
isExported: true
|
|
88790
90466
|
};
|
|
@@ -88792,7 +90468,7 @@ const UPPER = {
|
|
|
88792
90468
|
description: _t("Converts a specified string to uppercase."),
|
|
88793
90469
|
args: [arg("text (string)", _t("The string to convert to uppercase."))],
|
|
88794
90470
|
compute: function(text) {
|
|
88795
|
-
return { value:
|
|
90471
|
+
return { value: toLocaleString(text, this.locale).toUpperCase() };
|
|
88796
90472
|
},
|
|
88797
90473
|
isExported: true
|
|
88798
90474
|
};
|
|
@@ -88889,7 +90565,7 @@ const HYPERLINK = {
|
|
|
88889
90565
|
args: [arg("url (string)", _t("The full URL of the link enclosed in quotation marks.")), arg("link_label (string, optional)", _t("The text to display in the cell, enclosed in quotation marks."))],
|
|
88890
90566
|
compute: function(url, linkLabel) {
|
|
88891
90567
|
const processedUrl = toString(url).trim();
|
|
88892
|
-
const processedLabel =
|
|
90568
|
+
const processedLabel = toLocaleString(linkLabel, this.locale) || processedUrl;
|
|
88893
90569
|
if (processedUrl === "") return { value: processedLabel };
|
|
88894
90570
|
return { value: markdownLink(processedLabel, processedUrl) };
|
|
88895
90571
|
},
|
|
@@ -89119,7 +90795,9 @@ const helpers = {
|
|
|
89119
90795
|
computeCachedTextDimension,
|
|
89120
90796
|
createComputeFunction,
|
|
89121
90797
|
isMobileOS,
|
|
89122
|
-
drawHighlight
|
|
90798
|
+
drawHighlight,
|
|
90799
|
+
getBoundingRectWithMargins,
|
|
90800
|
+
hasInteractiveElementInEventTree
|
|
89123
90801
|
};
|
|
89124
90802
|
const links = {
|
|
89125
90803
|
isMarkdownLink,
|
|
@@ -89257,6 +90935,6 @@ const chartHelpers = {
|
|
|
89257
90935
|
//#endregion
|
|
89258
90936
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, BadExpressionError, CHART_TYPES, CellErrorType, CellValueType, CircularDependencyError, ClientDisconnectedError, ClipboardMIMEType, CommandResult, CompiledFormula, CorePlugin, CoreViewPlugin, DEFAULT_LOCALE, DEFAULT_LOCALES, DEFAULT_LOCALE_DIGIT_GROUPING, DIRECTION, DispatchResult, DivisionByZeroError, EvaluationError, InvalidReferenceError, LocalTransportService, Model, NEXT_VALUE, NotAvailableError, NumberTooLargeError, OrderedLayers, PREVIOUS_VALUE, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, SplillBlockedError, Spreadsheet, SpreadsheetPivotTable, UIPlugin, UnknownFunctionError, __info__, addFunction, addRenderingLayer, astToFormula, availableConditionalFormatOperators, availableDataValidationOperators, availableFiltersOperators, borderPositions, borderStyles, canExecuteInReadonly, categories, chartHelpers, compatibility, components, composerFocusTypes, constants, convertAstNodes, coreTypes, createAutocompleteArgumentsProvider, errorTypes, filterDateCriterionOperators, filterNumberCriterionOperators, filterTextCriterionOperators, findCellInNewZone, functionCache, getCaretDownSvg, getCaretUpSvg, helpers, hooks, invalidSubtotalFormulasCommands, invalidateBordersCommands, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, isCoreCommand, isHeadersDependant, isMatrix, isPositionDependent, isRangeDependant, isSheetDependent, isTargetDependent, isZoneDependent, iterateAstNodes, links, load, lockedSheetAllowedCommands, parse, parseTokens, readonlyAllowedCommands, registries, schemeToColorScale, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
89259
90937
|
|
|
89260
|
-
__info__.version = "19.5.0-alpha.
|
|
89261
|
-
__info__.date = "2026-08-
|
|
89262
|
-
__info__.hash = "
|
|
90938
|
+
__info__.version = "19.5.0-alpha.13";
|
|
90939
|
+
__info__.date = "2026-08-21T15:33:44.046Z";
|
|
90940
|
+
__info__.hash = "a73559e";
|