@odoo/o-spreadsheet 18.4.0-alpha.8 → 18.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/o-spreadsheet.cjs.js +2207 -1134
- package/dist/o-spreadsheet.d.ts +177 -54
- package/dist/o-spreadsheet.esm.js +2207 -1134
- package/dist/o-spreadsheet.iife.js +1996 -923
- package/dist/o-spreadsheet.iife.min.js +582 -570
- package/dist/o_spreadsheet.xml +98 -25
- package/package.json +2 -2
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 18.4.0
|
|
6
|
-
* @date 2025-06-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.4.0
|
|
6
|
+
* @date 2025-06-24T11:19:24.606Z
|
|
7
|
+
* @hash a5b7cad
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, App, blockDom, useState, onPatched, useExternalListener, onWillUpdateProps, onWillStart, onWillPatch, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
|
|
@@ -20,13 +20,21 @@ function createAction(item) {
|
|
|
20
20
|
const icon = item.icon;
|
|
21
21
|
const secondaryIcon = item.secondaryIcon;
|
|
22
22
|
const itemId = item.id || nextItemId++;
|
|
23
|
+
const isEnabled = item.isEnabled ? item.isEnabled : () => true;
|
|
23
24
|
return {
|
|
24
25
|
id: itemId.toString(),
|
|
25
26
|
name: typeof name === "function" ? name : () => name,
|
|
26
27
|
isVisible: item.isVisible ? item.isVisible : () => true,
|
|
27
|
-
isEnabled:
|
|
28
|
+
isEnabled: isEnabled,
|
|
28
29
|
isActive: item.isActive,
|
|
29
|
-
execute: item.execute
|
|
30
|
+
execute: item.execute
|
|
31
|
+
? (env, isMiddleClick) => {
|
|
32
|
+
if (isEnabled(env)) {
|
|
33
|
+
return item.execute(env, isMiddleClick);
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
: undefined,
|
|
30
38
|
children: children
|
|
31
39
|
? (env) => {
|
|
32
40
|
return children
|
|
@@ -154,6 +162,7 @@ const FROZEN_PANE_HEADER_BORDER_COLOR = "#BCBCBC";
|
|
|
154
162
|
const FROZEN_PANE_BORDER_COLOR = "#DADFE8";
|
|
155
163
|
const COMPOSER_ASSISTANT_COLOR = "#9B359B";
|
|
156
164
|
const COLOR_TRANSPARENT = "#00000000";
|
|
165
|
+
const TABLE_HOVER_BACKGROUND_COLOR = "#017E8414";
|
|
157
166
|
const CHART_WATERFALL_POSITIVE_COLOR = "#4EA7F2";
|
|
158
167
|
const CHART_WATERFALL_NEGATIVE_COLOR = "#EA6175";
|
|
159
168
|
const CHART_WATERFALL_SUBTOTAL_COLOR = "#AAAAAA";
|
|
@@ -297,6 +306,7 @@ const GROUP_LAYER_WIDTH = 21;
|
|
|
297
306
|
const GRID_ICON_MARGIN = 2;
|
|
298
307
|
const GRID_ICON_EDGE_LENGTH = 17;
|
|
299
308
|
const FOOTER_HEIGHT = 2 * DEFAULT_CELL_HEIGHT;
|
|
309
|
+
const DATA_VALIDATION_CHIP_MARGIN = 5;
|
|
300
310
|
// 768px is a common breakpoint for small screens
|
|
301
311
|
// Typically inside Odoo, it is the threshold for switching to mobile view
|
|
302
312
|
const MOBILE_WIDTH_BREAKPOINT = 768;
|
|
@@ -642,9 +652,6 @@ function parseSheetUrl(sheetLink) {
|
|
|
642
652
|
function isDefined(argument) {
|
|
643
653
|
return argument !== undefined;
|
|
644
654
|
}
|
|
645
|
-
function isNotNull(argument) {
|
|
646
|
-
return argument !== null;
|
|
647
|
-
}
|
|
648
655
|
/**
|
|
649
656
|
* Check if all the values of an object, and all the values of the objects inside of it, are undefined.
|
|
650
657
|
*/
|
|
@@ -1355,9 +1362,16 @@ function darkenColor(color, percentage) {
|
|
|
1355
1362
|
if (percentage === 1) {
|
|
1356
1363
|
return "#000";
|
|
1357
1364
|
}
|
|
1365
|
+
// increase saturation to compensate and make it more vivid
|
|
1366
|
+
hsla.s = Math.min(100, percentage * hsla.s + hsla.s);
|
|
1358
1367
|
hsla.l = hsla.l - percentage * hsla.l;
|
|
1359
1368
|
return hslaToHex(hsla);
|
|
1360
1369
|
}
|
|
1370
|
+
function chipTextColor(chipBackgroundColor) {
|
|
1371
|
+
return relativeLuminance(chipBackgroundColor) < 0.6
|
|
1372
|
+
? lightenColor(chipBackgroundColor, 0.9)
|
|
1373
|
+
: darkenColor(chipBackgroundColor, 0.75);
|
|
1374
|
+
}
|
|
1361
1375
|
const COLORS_SM = [
|
|
1362
1376
|
"#4EA7F2", // Blue
|
|
1363
1377
|
"#EA6175", // Red
|
|
@@ -6834,19 +6848,17 @@ function getDefaultContextFont(fontSize, bold = false, italic = false) {
|
|
|
6834
6848
|
const textWidthCache = {};
|
|
6835
6849
|
function computeTextWidth(context, text, style, fontUnit = "pt") {
|
|
6836
6850
|
const font = computeTextFont(style, fontUnit);
|
|
6837
|
-
context
|
|
6838
|
-
context.font = font;
|
|
6839
|
-
const width = computeCachedTextWidth(context, text);
|
|
6840
|
-
context.restore();
|
|
6841
|
-
return width;
|
|
6851
|
+
return computeCachedTextWidth(context, text, font);
|
|
6842
6852
|
}
|
|
6843
|
-
function computeCachedTextWidth(context, text) {
|
|
6844
|
-
const font = context.font;
|
|
6853
|
+
function computeCachedTextWidth(context, text, font) {
|
|
6845
6854
|
if (!textWidthCache[font]) {
|
|
6846
6855
|
textWidthCache[font] = {};
|
|
6847
6856
|
}
|
|
6848
6857
|
if (textWidthCache[font][text] === undefined) {
|
|
6858
|
+
const oldFont = context.font;
|
|
6859
|
+
context.font = font;
|
|
6849
6860
|
textWidthCache[font][text] = context.measureText(text).width;
|
|
6861
|
+
context.font = oldFont;
|
|
6850
6862
|
}
|
|
6851
6863
|
return textWidthCache[font][text];
|
|
6852
6864
|
}
|
|
@@ -7011,19 +7023,19 @@ function getContextFontSize(font) {
|
|
|
7011
7023
|
}
|
|
7012
7024
|
// Inspired from https://stackoverflow.com/a/10511598
|
|
7013
7025
|
function clipTextWithEllipsis(ctx, text, maxWidth) {
|
|
7014
|
-
let width = computeCachedTextWidth(ctx, text);
|
|
7026
|
+
let width = computeCachedTextWidth(ctx, text, ctx.font);
|
|
7015
7027
|
if (width <= maxWidth) {
|
|
7016
7028
|
return text;
|
|
7017
7029
|
}
|
|
7018
7030
|
const ellipsis = "…";
|
|
7019
|
-
const ellipsisWidth = computeCachedTextWidth(ctx, ellipsis);
|
|
7031
|
+
const ellipsisWidth = computeCachedTextWidth(ctx, ellipsis, ctx.font);
|
|
7020
7032
|
if (width <= ellipsisWidth) {
|
|
7021
7033
|
return text;
|
|
7022
7034
|
}
|
|
7023
7035
|
let len = text.length;
|
|
7024
7036
|
while (width >= maxWidth - ellipsisWidth && len-- > 0) {
|
|
7025
7037
|
text = text.substring(0, len);
|
|
7026
|
-
width = computeCachedTextWidth(ctx, text);
|
|
7038
|
+
width = computeCachedTextWidth(ctx, text, ctx.font);
|
|
7027
7039
|
}
|
|
7028
7040
|
return text + ellipsis;
|
|
7029
7041
|
}
|
|
@@ -7237,6 +7249,63 @@ function parseOSClipboardContent(content, clipboardId) {
|
|
|
7237
7249
|
};
|
|
7238
7250
|
return osClipboardContent;
|
|
7239
7251
|
}
|
|
7252
|
+
/**
|
|
7253
|
+
* Applies each clipboard handler to paste its corresponding data into the target.
|
|
7254
|
+
*/
|
|
7255
|
+
const applyClipboardHandlersPaste = (handlers, copiedData, target, options) => {
|
|
7256
|
+
handlers.forEach(({ handlerName, handler }) => {
|
|
7257
|
+
const data = copiedData[handlerName];
|
|
7258
|
+
if (data) {
|
|
7259
|
+
handler.paste(target, data, options);
|
|
7260
|
+
}
|
|
7261
|
+
});
|
|
7262
|
+
};
|
|
7263
|
+
/**
|
|
7264
|
+
* Returns the paste target based on clipboard handlers.
|
|
7265
|
+
* Also includes the full affected zone and the list of pasted zones for selection.
|
|
7266
|
+
*/
|
|
7267
|
+
function getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options) {
|
|
7268
|
+
let zone = undefined;
|
|
7269
|
+
const selectedZones = [];
|
|
7270
|
+
const target = {
|
|
7271
|
+
sheetId,
|
|
7272
|
+
zones,
|
|
7273
|
+
};
|
|
7274
|
+
for (const { handlerName, handler } of handlers) {
|
|
7275
|
+
const handlerData = copiedData[handlerName];
|
|
7276
|
+
if (!handlerData) {
|
|
7277
|
+
continue;
|
|
7278
|
+
}
|
|
7279
|
+
const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
|
|
7280
|
+
if (currentTarget.figureId) {
|
|
7281
|
+
target.figureId = currentTarget.figureId;
|
|
7282
|
+
}
|
|
7283
|
+
for (const targetZone of currentTarget.zones) {
|
|
7284
|
+
selectedZones.push(targetZone);
|
|
7285
|
+
if (zone === undefined) {
|
|
7286
|
+
zone = targetZone;
|
|
7287
|
+
continue;
|
|
7288
|
+
}
|
|
7289
|
+
zone = union(zone, targetZone);
|
|
7290
|
+
}
|
|
7291
|
+
}
|
|
7292
|
+
return {
|
|
7293
|
+
target,
|
|
7294
|
+
zone,
|
|
7295
|
+
selectedZones,
|
|
7296
|
+
};
|
|
7297
|
+
}
|
|
7298
|
+
/**
|
|
7299
|
+
* Updates the selection after a paste operation.
|
|
7300
|
+
*/
|
|
7301
|
+
const selectPastedZone = (selection, sourceZones, pastedZones) => {
|
|
7302
|
+
const anchorCell = {
|
|
7303
|
+
col: sourceZones[0].left,
|
|
7304
|
+
row: sourceZones[0].top,
|
|
7305
|
+
};
|
|
7306
|
+
selection.getBackToDefault();
|
|
7307
|
+
selection.selectZone({ cell: anchorCell, zone: union(...pastedZones) }, { scrollIntoView: false });
|
|
7308
|
+
};
|
|
7240
7309
|
|
|
7241
7310
|
class ClipboardHandler {
|
|
7242
7311
|
getters;
|
|
@@ -9941,8 +10010,15 @@ function getDependencyContainer(env) {
|
|
|
9941
10010
|
const ModelStore = createAbstractStore("Model");
|
|
9942
10011
|
|
|
9943
10012
|
class RendererStore {
|
|
9944
|
-
mutators = ["register", "unRegister", "
|
|
10013
|
+
mutators = ["register", "unRegister", "draw", "startAnimation", "stopAnimation"];
|
|
9945
10014
|
renderers = {};
|
|
10015
|
+
model;
|
|
10016
|
+
context = undefined;
|
|
10017
|
+
animationFrameId = null;
|
|
10018
|
+
registeredAnimations = new Set();
|
|
10019
|
+
constructor(get) {
|
|
10020
|
+
this.model = get(ModelStore);
|
|
10021
|
+
}
|
|
9946
10022
|
register(renderer) {
|
|
9947
10023
|
if (!renderer.renderingLayers.length) {
|
|
9948
10024
|
return;
|
|
@@ -9959,17 +10035,54 @@ class RendererStore {
|
|
|
9959
10035
|
this.renderers[layer] = this.renderers[layer].filter((r) => r !== renderer);
|
|
9960
10036
|
}
|
|
9961
10037
|
}
|
|
9962
|
-
drawLayer(context, layer) {
|
|
10038
|
+
drawLayer(context, layer, timeStamp) {
|
|
9963
10039
|
const renderers = this.renderers[layer];
|
|
9964
10040
|
if (renderers) {
|
|
9965
10041
|
for (const renderer of renderers) {
|
|
9966
10042
|
context.ctx.save();
|
|
9967
|
-
renderer.drawLayer(context, layer);
|
|
10043
|
+
renderer.drawLayer(context, layer, timeStamp);
|
|
9968
10044
|
context.ctx.restore();
|
|
9969
10045
|
}
|
|
9970
10046
|
}
|
|
9971
10047
|
return "noStateChange";
|
|
9972
10048
|
}
|
|
10049
|
+
draw(context, timestamp) {
|
|
10050
|
+
context = context || this.context;
|
|
10051
|
+
if (!context) {
|
|
10052
|
+
throw new Error("Rendering context is not defined");
|
|
10053
|
+
}
|
|
10054
|
+
this.context = context;
|
|
10055
|
+
for (const layer of OrderedLayers()) {
|
|
10056
|
+
this.model.drawLayer(context, layer);
|
|
10057
|
+
this.drawLayer(context, layer, timestamp);
|
|
10058
|
+
}
|
|
10059
|
+
return "noStateChange";
|
|
10060
|
+
}
|
|
10061
|
+
startAnimation(animationId) {
|
|
10062
|
+
this.registeredAnimations.add(animationId);
|
|
10063
|
+
if (!this.animationFrameId) {
|
|
10064
|
+
const animationCallback = (timestamp) => {
|
|
10065
|
+
this.animationFrameId = requestAnimationFrame(animationCallback);
|
|
10066
|
+
this.draw(undefined, timestamp);
|
|
10067
|
+
};
|
|
10068
|
+
this.animationFrameId = requestAnimationFrame(animationCallback);
|
|
10069
|
+
}
|
|
10070
|
+
return "noStateChange";
|
|
10071
|
+
}
|
|
10072
|
+
stopAnimation(animationId) {
|
|
10073
|
+
this.registeredAnimations.delete(animationId);
|
|
10074
|
+
if (this.registeredAnimations.size === 0 && this.animationFrameId !== null) {
|
|
10075
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
10076
|
+
this.animationFrameId = null;
|
|
10077
|
+
}
|
|
10078
|
+
return "noStateChange";
|
|
10079
|
+
}
|
|
10080
|
+
dispose() {
|
|
10081
|
+
if (this.animationFrameId) {
|
|
10082
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
10083
|
+
this.animationFrameId = null;
|
|
10084
|
+
}
|
|
10085
|
+
}
|
|
9973
10086
|
}
|
|
9974
10087
|
|
|
9975
10088
|
class SpreadsheetStore extends DisposableStore {
|
|
@@ -9993,7 +10106,7 @@ class SpreadsheetStore extends DisposableStore {
|
|
|
9993
10106
|
}
|
|
9994
10107
|
handle(cmd) { }
|
|
9995
10108
|
finalize() { }
|
|
9996
|
-
drawLayer(ctx, layer) { }
|
|
10109
|
+
drawLayer(ctx, layer, timestamp) { }
|
|
9997
10110
|
}
|
|
9998
10111
|
|
|
9999
10112
|
const VOID_COMPOSER = {
|
|
@@ -21686,6 +21799,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
|
|
|
21686
21799
|
if (isTrendLineAxis(dataset.xAxisID) || dataset.hidden) {
|
|
21687
21800
|
continue;
|
|
21688
21801
|
}
|
|
21802
|
+
const yAxisScale = chart.scales[dataset.yAxisID];
|
|
21689
21803
|
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
21690
21804
|
const parsedValue = dataset._parsed[i];
|
|
21691
21805
|
const value = Number(chart.config.type === "radar" ? parsedValue.r : parsedValue.y);
|
|
@@ -21696,10 +21810,18 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
|
|
|
21696
21810
|
const xPosition = point.x;
|
|
21697
21811
|
let yPosition = 0;
|
|
21698
21812
|
if (chart.config.type === "line" || chart.config.type === "radar") {
|
|
21699
|
-
yPosition = point.y - 10;
|
|
21813
|
+
yPosition = value < 0 ? point.y + 10 : point.y - 10;
|
|
21700
21814
|
}
|
|
21701
21815
|
else {
|
|
21702
|
-
|
|
21816
|
+
const yZeroLine = yAxisScale.getPixelForValue(0);
|
|
21817
|
+
const distanceFromAxisOrigin = Math.abs(yZeroLine - point.y);
|
|
21818
|
+
const textHeight = 12; // ChartJS default text height
|
|
21819
|
+
if (distanceFromAxisOrigin < textHeight) {
|
|
21820
|
+
yPosition = value < 0 ? yZeroLine + textHeight / 2 : yZeroLine - textHeight / 2;
|
|
21821
|
+
}
|
|
21822
|
+
else {
|
|
21823
|
+
yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
|
|
21824
|
+
}
|
|
21703
21825
|
}
|
|
21704
21826
|
yPosition = Math.min(yPosition, yMax);
|
|
21705
21827
|
yPosition = Math.max(yPosition, yMin);
|
|
@@ -21709,7 +21831,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
|
|
|
21709
21831
|
}
|
|
21710
21832
|
for (const otherPosition of textsPositions[xPosition] || []) {
|
|
21711
21833
|
if (Math.abs(otherPosition - yPosition) < 13) {
|
|
21712
|
-
yPosition = otherPosition - 13;
|
|
21834
|
+
yPosition = value < 0 ? otherPosition + 13 : otherPosition - 13;
|
|
21713
21835
|
}
|
|
21714
21836
|
}
|
|
21715
21837
|
textsPositions[xPosition].push(yPosition);
|
|
@@ -21728,6 +21850,8 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
|
|
|
21728
21850
|
if (isTrendLineAxis(dataset.xAxisID)) {
|
|
21729
21851
|
return; // ignore trend lines
|
|
21730
21852
|
}
|
|
21853
|
+
const xAxisScale = chart.scales[dataset.xAxisID];
|
|
21854
|
+
const xZeroLine = xAxisScale.getPixelForValue(0);
|
|
21731
21855
|
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
21732
21856
|
const value = Number(dataset._parsed[i].x);
|
|
21733
21857
|
if (isNaN(value)) {
|
|
@@ -21736,17 +21860,27 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
|
|
|
21736
21860
|
const displayValue = options.callback(value, dataset, i);
|
|
21737
21861
|
const point = dataset.data[i];
|
|
21738
21862
|
const yPosition = point.y;
|
|
21739
|
-
|
|
21740
|
-
|
|
21741
|
-
|
|
21863
|
+
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
|
|
21864
|
+
const distanceFromAxisOrigin = Math.abs(point.x - xZeroLine);
|
|
21865
|
+
const PADDING = 3;
|
|
21866
|
+
let xPosition;
|
|
21867
|
+
if (distanceFromAxisOrigin < textWidth) {
|
|
21868
|
+
xPosition =
|
|
21869
|
+
value < 0 ? xZeroLine - textWidth / 2 - PADDING : xZeroLine + textWidth / 2 + PADDING;
|
|
21870
|
+
}
|
|
21871
|
+
else {
|
|
21872
|
+
xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
|
|
21873
|
+
xPosition = Math.min(xPosition, xMax);
|
|
21874
|
+
xPosition = Math.max(xPosition, xMin);
|
|
21875
|
+
}
|
|
21742
21876
|
// Avoid overlapping texts with same Y
|
|
21743
21877
|
if (!textsPositions[yPosition]) {
|
|
21744
21878
|
textsPositions[yPosition] = [];
|
|
21745
21879
|
}
|
|
21746
|
-
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
|
|
21747
21880
|
for (const otherPosition of textsPositions[yPosition]) {
|
|
21748
21881
|
if (Math.abs(otherPosition - xPosition) < textWidth) {
|
|
21749
|
-
xPosition =
|
|
21882
|
+
xPosition =
|
|
21883
|
+
value < 0 ? otherPosition - textWidth - PADDING : otherPosition + textWidth + PADDING;
|
|
21750
21884
|
}
|
|
21751
21885
|
}
|
|
21752
21886
|
textsPositions[yPosition].push(xPosition);
|
|
@@ -23694,11 +23828,11 @@ function drawTitle(ctx, config) {
|
|
|
23694
23828
|
function getGaugeRenderingConfig(boundingRect, runtime, ctx) {
|
|
23695
23829
|
const maxValue = runtime.maxValue;
|
|
23696
23830
|
const minValue = runtime.minValue;
|
|
23697
|
-
const gaugeValue = runtime
|
|
23831
|
+
const gaugeValue = getGaugeValue(runtime, "animated");
|
|
23698
23832
|
const gaugeRect = getGaugeRect(boundingRect, runtime.title.text);
|
|
23699
23833
|
const gaugeArcWidth = gaugeRect.width / 6;
|
|
23700
23834
|
const gaugePercentage = gaugeValue
|
|
23701
|
-
? (gaugeValue
|
|
23835
|
+
? (gaugeValue - minValue.value) / (maxValue.value - minValue.value)
|
|
23702
23836
|
: 0;
|
|
23703
23837
|
const gaugeValuePosition = {
|
|
23704
23838
|
x: boundingRect.width / 2,
|
|
@@ -23711,7 +23845,7 @@ function getGaugeRenderingConfig(boundingRect, runtime, ctx) {
|
|
|
23711
23845
|
}
|
|
23712
23846
|
// Scale down the font size if the text is too long
|
|
23713
23847
|
const maxTextWidth = gaugeRect.width / 2;
|
|
23714
|
-
const gaugeLabel = gaugeValue?.label || "-";
|
|
23848
|
+
const gaugeLabel = runtime.gaugeValue?.label || "-";
|
|
23715
23849
|
if (computeTextWidth(ctx, gaugeLabel, { fontSize: gaugeValueFontSize }, "px") > maxTextWidth) {
|
|
23716
23850
|
gaugeValueFontSize = getFontSizeMatchingWidth(maxTextWidth, gaugeValueFontSize, (fontSize) => computeTextWidth(ctx, gaugeLabel, { fontSize }, "px"));
|
|
23717
23851
|
}
|
|
@@ -23851,7 +23985,7 @@ function getInflectionValues(runtime, gaugeRect, textColor, ctx) {
|
|
|
23851
23985
|
return inflectionValues;
|
|
23852
23986
|
}
|
|
23853
23987
|
function getGaugeColor(runtime) {
|
|
23854
|
-
const gaugeValue = runtime
|
|
23988
|
+
const gaugeValue = getGaugeValue(runtime, "final");
|
|
23855
23989
|
if (gaugeValue === undefined) {
|
|
23856
23990
|
return GAUGE_BACKGROUND_COLOR;
|
|
23857
23991
|
}
|
|
@@ -23939,6 +24073,11 @@ function getRectangleTangentToCircle(angle, radius, circleCenterX, circleCenterY
|
|
|
23939
24073
|
};
|
|
23940
24074
|
return { bottomLeft, bottomRight, topRight, topLeft };
|
|
23941
24075
|
}
|
|
24076
|
+
function getGaugeValue(runtime, mode) {
|
|
24077
|
+
return mode === "animated" && runtime.animationValue !== undefined
|
|
24078
|
+
? runtime.animationValue
|
|
24079
|
+
: runtime.gaugeValue?.value;
|
|
24080
|
+
}
|
|
23942
24081
|
|
|
23943
24082
|
const CHART_COMMON_OPTIONS = {
|
|
23944
24083
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
@@ -25680,7 +25819,9 @@ function getPyramidChartShowValues(definition, args) {
|
|
|
25680
25819
|
background: definition.background,
|
|
25681
25820
|
callback: (value, dataset) => {
|
|
25682
25821
|
value = Math.abs(Number(value));
|
|
25683
|
-
return
|
|
25822
|
+
return value === 0
|
|
25823
|
+
? ""
|
|
25824
|
+
: formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
|
|
25684
25825
|
},
|
|
25685
25826
|
};
|
|
25686
25827
|
}
|
|
@@ -26291,6 +26432,320 @@ function createBarChartRuntime(chart, getters) {
|
|
|
26291
26432
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
26292
26433
|
}
|
|
26293
26434
|
|
|
26435
|
+
const cellAnimationRegistry = new Registry();
|
|
26436
|
+
cellAnimationRegistry.add("animatedBackgroundColorChange", {
|
|
26437
|
+
id: "animatedBackgroundColorChange",
|
|
26438
|
+
easingFn: "easeOutCubic",
|
|
26439
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26440
|
+
return oldBox?.style?.fillColor !== newBox?.style?.fillColor;
|
|
26441
|
+
},
|
|
26442
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26443
|
+
const colorScale = getColorScale([
|
|
26444
|
+
{ value: 0, color: oldBox.style.fillColor || "#ffffff" },
|
|
26445
|
+
{ value: 1, color: newBox.style.fillColor || "#ffffff" },
|
|
26446
|
+
]);
|
|
26447
|
+
animatedBox.style.fillColor = colorScale(EASING_FN[this.easingFn](progress));
|
|
26448
|
+
},
|
|
26449
|
+
});
|
|
26450
|
+
cellAnimationRegistry.add("animatedTextColorChange", {
|
|
26451
|
+
id: "animatedTextColorChange",
|
|
26452
|
+
easingFn: "easeOutCubic",
|
|
26453
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26454
|
+
return oldBox?.style?.textColor !== newBox?.style?.textColor;
|
|
26455
|
+
},
|
|
26456
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26457
|
+
const colorScale = getColorScale([
|
|
26458
|
+
{ value: 0, color: oldBox.style.textColor || "#000000" },
|
|
26459
|
+
{ value: 1, color: newBox.style.textColor || "#000000" },
|
|
26460
|
+
]);
|
|
26461
|
+
animatedBox.style.textColor = colorScale(EASING_FN[this.easingFn](progress));
|
|
26462
|
+
},
|
|
26463
|
+
});
|
|
26464
|
+
cellAnimationRegistry.add("animatedDataBar", {
|
|
26465
|
+
id: "animatedDataBar",
|
|
26466
|
+
easingFn: "easeOutCubic",
|
|
26467
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26468
|
+
return oldBox?.dataBarFill?.percentage !== newBox?.dataBarFill?.percentage;
|
|
26469
|
+
},
|
|
26470
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26471
|
+
const startingPercentage = oldBox?.dataBarFill?.percentage || 0;
|
|
26472
|
+
const endingPercentage = newBox?.dataBarFill?.percentage || 0;
|
|
26473
|
+
const value = EASING_FN[this.easingFn](progress);
|
|
26474
|
+
const percentage = startingPercentage + (endingPercentage - startingPercentage) * value;
|
|
26475
|
+
animatedBox.dataBarFill = {
|
|
26476
|
+
color: newBox.dataBarFill?.color || oldBox.dataBarFill?.color || "#ffffff",
|
|
26477
|
+
percentage: percentage,
|
|
26478
|
+
};
|
|
26479
|
+
},
|
|
26480
|
+
});
|
|
26481
|
+
cellAnimationRegistry.add("textFadeIn", {
|
|
26482
|
+
id: "textFadeIn",
|
|
26483
|
+
easingFn: "easeInCubic",
|
|
26484
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26485
|
+
const oldText = oldBox?.content?.textLines?.join("\n");
|
|
26486
|
+
const newText = newBox?.content?.textLines?.join("\n");
|
|
26487
|
+
return Boolean(!oldText && newText);
|
|
26488
|
+
},
|
|
26489
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26490
|
+
animatedBox.textOpacity = EASING_FN[this.easingFn](progress);
|
|
26491
|
+
},
|
|
26492
|
+
});
|
|
26493
|
+
cellAnimationRegistry.add("textFadeOut", {
|
|
26494
|
+
id: "textFadeOut",
|
|
26495
|
+
easingFn: "easeOutCubic",
|
|
26496
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26497
|
+
const oldText = oldBox?.content?.textLines?.join("\n");
|
|
26498
|
+
const newText = newBox?.content?.textLines?.join("\n");
|
|
26499
|
+
return Boolean(oldText && !newText);
|
|
26500
|
+
},
|
|
26501
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26502
|
+
const textOpacity = 1 - EASING_FN[this.easingFn](progress);
|
|
26503
|
+
const style = { ...oldBox.style };
|
|
26504
|
+
delete style.fillColor;
|
|
26505
|
+
animatedBox.textOpacity = textOpacity;
|
|
26506
|
+
animatedBox.content = oldBox.content;
|
|
26507
|
+
animatedBox.clipRect = oldBox.clipRect;
|
|
26508
|
+
Object.assign(animatedBox.style, style);
|
|
26509
|
+
},
|
|
26510
|
+
});
|
|
26511
|
+
cellAnimationRegistry.add("textChange", {
|
|
26512
|
+
id: "textChange",
|
|
26513
|
+
easingFn: "easeOutCubic",
|
|
26514
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26515
|
+
const oldText = oldBox?.content?.textLines?.join("\n");
|
|
26516
|
+
const newText = newBox?.content?.textLines?.join("\n");
|
|
26517
|
+
// Note: here, we also animate changes to icons layout (margins/size change, or icon appearing/disappearing)
|
|
26518
|
+
// because a change to the icon layout will impact where the text is positioned.
|
|
26519
|
+
return (Boolean(oldText && newText && oldText !== newText) || hasIconLayoutChange(newBox, oldBox));
|
|
26520
|
+
},
|
|
26521
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26522
|
+
const value = EASING_FN[this.easingFn](progress);
|
|
26523
|
+
const slideInY = newBox.y + (value - 1) * newBox.height;
|
|
26524
|
+
const slideOutY = newBox.y + value * newBox.height;
|
|
26525
|
+
const iconLayoutChange = hasIconLayoutChange(newBox, oldBox);
|
|
26526
|
+
const slideInBox = {
|
|
26527
|
+
id: newBox.id + "-text-slide-in",
|
|
26528
|
+
x: newBox.x,
|
|
26529
|
+
y: slideInY,
|
|
26530
|
+
width: newBox.width,
|
|
26531
|
+
height: newBox.height,
|
|
26532
|
+
style: { ...newBox.style },
|
|
26533
|
+
skipCellGridLines: true,
|
|
26534
|
+
content: newBox.content,
|
|
26535
|
+
clipRect: newBox.clipRect || {
|
|
26536
|
+
...newBox,
|
|
26537
|
+
// large width to avoid clipping the text it it didn't have a clipRect before,
|
|
26538
|
+
// we mainly want to clip the Y for the animation
|
|
26539
|
+
x: Math.max(0, newBox.x - (newBox.content?.width || 0)),
|
|
26540
|
+
width: newBox.width + (newBox.content?.width || 0) * 2,
|
|
26541
|
+
},
|
|
26542
|
+
icons: iconLayoutChange
|
|
26543
|
+
? addClipRectToIcons(newBox.icons, newBox)
|
|
26544
|
+
: makeIconsEmpty(newBox.icons),
|
|
26545
|
+
};
|
|
26546
|
+
const slideOutBox = {
|
|
26547
|
+
id: oldBox.id + "-text-slide-out",
|
|
26548
|
+
x: newBox.x,
|
|
26549
|
+
y: slideOutY,
|
|
26550
|
+
width: newBox.width,
|
|
26551
|
+
height: newBox.height,
|
|
26552
|
+
style: { ...oldBox.style },
|
|
26553
|
+
skipCellGridLines: true,
|
|
26554
|
+
content: oldBox.content,
|
|
26555
|
+
clipRect: oldBox.clipRect || {
|
|
26556
|
+
...newBox,
|
|
26557
|
+
x: Math.max(0, newBox.x - (oldBox.content?.width || 0)),
|
|
26558
|
+
width: newBox.width + (oldBox.content?.width || 0) * 2,
|
|
26559
|
+
},
|
|
26560
|
+
icons: iconLayoutChange
|
|
26561
|
+
? addClipRectToIcons(oldBox.icons, newBox)
|
|
26562
|
+
: makeIconsEmpty(oldBox.icons),
|
|
26563
|
+
};
|
|
26564
|
+
if (newBox.content && oldBox.content && slideInBox.content && slideOutBox.content) {
|
|
26565
|
+
const slideInContentY = newBox.content.y + (value - 1) * newBox.height;
|
|
26566
|
+
const slideOutContentY = newBox.content.y + value * newBox.height;
|
|
26567
|
+
slideInBox.content.y = slideInContentY;
|
|
26568
|
+
slideOutBox.content.y = slideOutContentY;
|
|
26569
|
+
}
|
|
26570
|
+
slideOutBox.style.fillColor = slideInBox.style.fillColor = undefined;
|
|
26571
|
+
animatedBox.content = undefined;
|
|
26572
|
+
animatedBox.icons = iconLayoutChange ? {} : animatedBox.icons;
|
|
26573
|
+
return { newBoxes: [slideInBox, slideOutBox] };
|
|
26574
|
+
},
|
|
26575
|
+
});
|
|
26576
|
+
cellAnimationRegistry.add("borderFadeIn", {
|
|
26577
|
+
id: "borderFadeIn",
|
|
26578
|
+
easingFn: "easeInCubic",
|
|
26579
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26580
|
+
return Boolean((!oldBox?.border?.bottom && newBox?.border?.bottom) ||
|
|
26581
|
+
(!oldBox?.border?.top && newBox?.border?.top) ||
|
|
26582
|
+
(!oldBox?.border?.left && newBox?.border?.left) ||
|
|
26583
|
+
(!oldBox?.border?.right && newBox?.border?.right));
|
|
26584
|
+
},
|
|
26585
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26586
|
+
const borderOpacity = EASING_FN[this.easingFn](progress);
|
|
26587
|
+
if (animatedBox.border?.top && newBox.border?.top && !oldBox.border?.top) {
|
|
26588
|
+
animatedBox.border.top.opacity = borderOpacity;
|
|
26589
|
+
}
|
|
26590
|
+
if (animatedBox.border?.bottom && newBox.border?.bottom && !oldBox.border?.bottom) {
|
|
26591
|
+
animatedBox.border.bottom.opacity = borderOpacity;
|
|
26592
|
+
}
|
|
26593
|
+
if (animatedBox.border?.left && newBox.border?.left && !oldBox.border?.left) {
|
|
26594
|
+
animatedBox.border.left.opacity = borderOpacity;
|
|
26595
|
+
}
|
|
26596
|
+
if (animatedBox.border?.right && newBox.border?.right && !oldBox.border?.right) {
|
|
26597
|
+
animatedBox.border.right.opacity = borderOpacity;
|
|
26598
|
+
}
|
|
26599
|
+
},
|
|
26600
|
+
});
|
|
26601
|
+
cellAnimationRegistry.add("borderFadeOut", {
|
|
26602
|
+
id: "borderFadeOut",
|
|
26603
|
+
easingFn: "easeOutCubic",
|
|
26604
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26605
|
+
return Boolean((oldBox?.border?.bottom && !newBox?.border?.bottom) ||
|
|
26606
|
+
(oldBox?.border?.top && !newBox?.border?.top) ||
|
|
26607
|
+
(oldBox?.border?.left && !newBox?.border?.left) ||
|
|
26608
|
+
(oldBox?.border?.right && !newBox?.border?.right));
|
|
26609
|
+
},
|
|
26610
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26611
|
+
const borderOpacity = 1 - EASING_FN[this.easingFn](progress);
|
|
26612
|
+
if (!animatedBox.border) {
|
|
26613
|
+
animatedBox.border = {};
|
|
26614
|
+
}
|
|
26615
|
+
if (oldBox.border?.top && !newBox.border?.top) {
|
|
26616
|
+
animatedBox.border.top = { ...oldBox.border.top, opacity: borderOpacity };
|
|
26617
|
+
}
|
|
26618
|
+
if (oldBox.border?.bottom && !newBox.border?.bottom) {
|
|
26619
|
+
animatedBox.border.bottom = { ...oldBox.border.bottom, opacity: borderOpacity };
|
|
26620
|
+
}
|
|
26621
|
+
if (oldBox.border?.left && !newBox.border?.left) {
|
|
26622
|
+
animatedBox.border.left = { ...oldBox.border.left, opacity: borderOpacity };
|
|
26623
|
+
}
|
|
26624
|
+
if (oldBox.border?.right && !newBox.border?.right) {
|
|
26625
|
+
animatedBox.border.right = { ...oldBox.border.right, opacity: borderOpacity };
|
|
26626
|
+
}
|
|
26627
|
+
},
|
|
26628
|
+
});
|
|
26629
|
+
cellAnimationRegistry.add("borderColorChange", {
|
|
26630
|
+
id: "borderColorChange",
|
|
26631
|
+
easingFn: "easeOutCubic",
|
|
26632
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26633
|
+
const oldBorder = oldBox?.border;
|
|
26634
|
+
const newBorder = newBox?.border;
|
|
26635
|
+
if (!oldBorder || !newBorder) {
|
|
26636
|
+
return false;
|
|
26637
|
+
}
|
|
26638
|
+
return Boolean(oldBorder.bottom?.color !== newBorder.bottom?.color ||
|
|
26639
|
+
oldBorder.top?.color !== newBorder.top?.color ||
|
|
26640
|
+
oldBorder.left?.color !== newBorder.left?.color ||
|
|
26641
|
+
oldBorder.right?.color !== newBorder.right?.color);
|
|
26642
|
+
},
|
|
26643
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26644
|
+
const animateBorderColor = (side) => {
|
|
26645
|
+
const oldBorder = oldBox?.border?.[side];
|
|
26646
|
+
const newBorder = newBox?.border?.[side];
|
|
26647
|
+
const animatedBorder = animatedBox.border?.[side];
|
|
26648
|
+
if (oldBorder && newBorder && animatedBorder) {
|
|
26649
|
+
const colorScale = getColorScale([
|
|
26650
|
+
{ value: 0, color: oldBorder.color || "#000000" },
|
|
26651
|
+
{ value: 1, color: newBorder.color || "#000000" },
|
|
26652
|
+
]);
|
|
26653
|
+
animatedBorder.color = colorScale(EASING_FN[this.easingFn](progress));
|
|
26654
|
+
}
|
|
26655
|
+
};
|
|
26656
|
+
animateBorderColor("top");
|
|
26657
|
+
animateBorderColor("bottom");
|
|
26658
|
+
animateBorderColor("left");
|
|
26659
|
+
animateBorderColor("right");
|
|
26660
|
+
},
|
|
26661
|
+
});
|
|
26662
|
+
cellAnimationRegistry.add("iconChange", {
|
|
26663
|
+
id: "iconChange",
|
|
26664
|
+
easingFn: "easeOutCubic",
|
|
26665
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26666
|
+
return (!hasIconLayoutChange(newBox, oldBox) &&
|
|
26667
|
+
Boolean(oldBox?.icons?.center?.svg?.name !== newBox?.icons?.center?.svg?.name ||
|
|
26668
|
+
oldBox?.icons?.left?.svg?.name !== newBox?.icons?.left?.svg?.name ||
|
|
26669
|
+
oldBox?.icons?.right?.svg?.name !== newBox?.icons?.right?.svg?.name));
|
|
26670
|
+
},
|
|
26671
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26672
|
+
const value = EASING_FN[this.easingFn](progress);
|
|
26673
|
+
const slideInY = newBox.y + (value - 1) * newBox.height;
|
|
26674
|
+
const slideOutY = newBox.y + value * newBox.height;
|
|
26675
|
+
const newBoxes = [];
|
|
26676
|
+
const animateIconChange = (side) => {
|
|
26677
|
+
const oldIcon = oldBox.icons?.[side];
|
|
26678
|
+
const newIcon = newBox.icons?.[side];
|
|
26679
|
+
const slideInBox = {
|
|
26680
|
+
id: `${newBox.id}-icon-${side}-slide-in`,
|
|
26681
|
+
style: { verticalAlign: newBox.style.verticalAlign },
|
|
26682
|
+
x: newBox.x,
|
|
26683
|
+
y: slideInY,
|
|
26684
|
+
width: newBox.width,
|
|
26685
|
+
height: newBox.height,
|
|
26686
|
+
skipCellGridLines: true,
|
|
26687
|
+
icons: { [side]: { ...newIcon, clipRect: newBox } },
|
|
26688
|
+
};
|
|
26689
|
+
const slideOutBox = {
|
|
26690
|
+
id: `${newBox.id}-icon-${side}-slide-out`,
|
|
26691
|
+
style: { verticalAlign: oldBox.style.verticalAlign },
|
|
26692
|
+
x: newBox.x,
|
|
26693
|
+
y: slideOutY,
|
|
26694
|
+
width: newBox.width,
|
|
26695
|
+
height: newBox.height,
|
|
26696
|
+
skipCellGridLines: true,
|
|
26697
|
+
icons: { [side]: { ...oldIcon, clipRect: newBox } },
|
|
26698
|
+
};
|
|
26699
|
+
animatedBox.icons[side] = makeIconsEmpty(newBox.icons)[side];
|
|
26700
|
+
newBoxes.push(slideInBox, slideOutBox);
|
|
26701
|
+
};
|
|
26702
|
+
animateIconChange("left");
|
|
26703
|
+
animateIconChange("right");
|
|
26704
|
+
animateIconChange("center");
|
|
26705
|
+
return { newBoxes };
|
|
26706
|
+
},
|
|
26707
|
+
});
|
|
26708
|
+
const EASING_FN = {
|
|
26709
|
+
linear: (t) => t,
|
|
26710
|
+
easeInCubic: (t) => t * t * t,
|
|
26711
|
+
easeOutCubic: (t) => (t -= 1) * t * t + 1,
|
|
26712
|
+
easeInOutCubic: (t) => ((t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2)),
|
|
26713
|
+
easeOutQuart: (t) => -((t -= 1) * t * t * t - 1),
|
|
26714
|
+
};
|
|
26715
|
+
function makeIconsEmpty(icons) {
|
|
26716
|
+
return {
|
|
26717
|
+
left: icons.left ? { ...icons.left, svg: undefined } : undefined,
|
|
26718
|
+
right: icons.right ? { ...icons.right, svg: undefined } : undefined,
|
|
26719
|
+
center: icons.center ? { ...icons.center, svg: undefined } : undefined,
|
|
26720
|
+
};
|
|
26721
|
+
}
|
|
26722
|
+
function addClipRectToIcons(icons, clipRect) {
|
|
26723
|
+
return {
|
|
26724
|
+
left: icons.left ? { ...icons.left, clipRect } : undefined,
|
|
26725
|
+
right: icons.right ? { ...icons.right, clipRect } : undefined,
|
|
26726
|
+
center: icons.center ? { ...icons.center, clipRect } : undefined,
|
|
26727
|
+
};
|
|
26728
|
+
}
|
|
26729
|
+
/**
|
|
26730
|
+
* Check if the icons have appeared, disappeared or changed margin/size/align. Those changes affect where the text is positioned.
|
|
26731
|
+
*/
|
|
26732
|
+
function hasIconLayoutChange(newBox, oldBox) {
|
|
26733
|
+
const hasLayoutChange = (newIcon, oldIcon) => {
|
|
26734
|
+
if (oldIcon && newIcon) {
|
|
26735
|
+
return !!(newIcon.horizontalAlign !== oldIcon.horizontalAlign ||
|
|
26736
|
+
newIcon.size !== oldIcon.size ||
|
|
26737
|
+
newIcon.margin !== oldIcon.margin ||
|
|
26738
|
+
(newIcon.svg && !oldIcon.svg) ||
|
|
26739
|
+
(!newIcon.svg && oldIcon.svg));
|
|
26740
|
+
}
|
|
26741
|
+
return !!((newIcon && !oldIcon) || (!newIcon && oldIcon));
|
|
26742
|
+
};
|
|
26743
|
+
return (hasLayoutChange(newBox?.icons.left, oldBox?.icons.left) ||
|
|
26744
|
+
hasLayoutChange(newBox?.icons.right, oldBox?.icons.right) ||
|
|
26745
|
+
hasLayoutChange(newBox?.icons.center, oldBox?.icons.center));
|
|
26746
|
+
}
|
|
26747
|
+
|
|
26748
|
+
const ANIMATION_DURATION = 1000;
|
|
26294
26749
|
class GaugeChartComponent extends Component {
|
|
26295
26750
|
static template = "o-spreadsheet-GaugeChartComponent";
|
|
26296
26751
|
static props = {
|
|
@@ -26298,16 +26753,101 @@ class GaugeChartComponent extends Component {
|
|
|
26298
26753
|
isFullScreen: { type: Boolean, optional: true },
|
|
26299
26754
|
};
|
|
26300
26755
|
canvas = useRef("chartContainer");
|
|
26756
|
+
animationStore;
|
|
26301
26757
|
get runtime() {
|
|
26302
26758
|
return this.env.model.getters.getChartRuntime(this.props.figureUI.id);
|
|
26303
26759
|
}
|
|
26304
26760
|
setup() {
|
|
26305
|
-
|
|
26306
|
-
|
|
26307
|
-
|
|
26761
|
+
if (this.env.model.getters.isDashboard()) {
|
|
26762
|
+
this.animationStore = useStore(ChartAnimationStore);
|
|
26763
|
+
}
|
|
26764
|
+
let animation = null;
|
|
26765
|
+
let lastRuntime = undefined;
|
|
26766
|
+
useEffect(() => {
|
|
26767
|
+
if (this.env.isDashboard() &&
|
|
26768
|
+
lastRuntime === undefined && // first render
|
|
26769
|
+
this.animationStore?.animationPlayed[this.animationFigureId] !== "gauge") {
|
|
26770
|
+
animation = this.drawGaugeWithAnimation();
|
|
26771
|
+
this.animationStore?.disableAnimationForChart(this.animationFigureId, "gauge");
|
|
26772
|
+
}
|
|
26773
|
+
else if (this.env.isDashboard() &&
|
|
26774
|
+
lastRuntime !== undefined && // not first render
|
|
26775
|
+
!deepEquals(this.runtime, lastRuntime)) {
|
|
26776
|
+
animation = this.drawGaugeWithAnimation();
|
|
26777
|
+
this.animationStore?.disableAnimationForChart(this.animationFigureId, "gauge");
|
|
26778
|
+
}
|
|
26779
|
+
else {
|
|
26780
|
+
drawGaugeChart(this.canvasEl, this.runtime);
|
|
26781
|
+
}
|
|
26782
|
+
lastRuntime = this.runtime;
|
|
26783
|
+
return () => animation?.stop();
|
|
26784
|
+
}, () => {
|
|
26785
|
+
const rect = this.canvasEl.getBoundingClientRect();
|
|
26308
26786
|
return [rect.width, rect.height, this.runtime, this.canvas.el, window.devicePixelRatio];
|
|
26309
26787
|
});
|
|
26310
26788
|
}
|
|
26789
|
+
drawGaugeWithAnimation() {
|
|
26790
|
+
drawGaugeChart(this.canvasEl, { ...this.runtime, animationValue: 0 });
|
|
26791
|
+
const gaugeValue = this.runtime.gaugeValue?.value || 0;
|
|
26792
|
+
const upperBound = this.runtime.maxValue.value;
|
|
26793
|
+
const finalValue = Math.sign(gaugeValue) * Math.min(Math.abs(gaugeValue), Math.abs(upperBound));
|
|
26794
|
+
if (finalValue === 0) {
|
|
26795
|
+
return null;
|
|
26796
|
+
}
|
|
26797
|
+
const lowerBound = this.runtime.minValue.value;
|
|
26798
|
+
const animation = new Animation(lowerBound, finalValue, ANIMATION_DURATION, (animationValue) => drawGaugeChart(this.canvasEl, { ...this.runtime, animationValue }));
|
|
26799
|
+
animation.start();
|
|
26800
|
+
return animation;
|
|
26801
|
+
}
|
|
26802
|
+
get canvasEl() {
|
|
26803
|
+
return this.canvas.el;
|
|
26804
|
+
}
|
|
26805
|
+
get animationFigureId() {
|
|
26806
|
+
return this.props.isFullScreen
|
|
26807
|
+
? this.props.figureUI.id + "-fullscreen"
|
|
26808
|
+
: this.props.figureUI.id;
|
|
26809
|
+
}
|
|
26810
|
+
}
|
|
26811
|
+
/**
|
|
26812
|
+
* Animation interpolating values using the ease-out quartic curve function (chartJS default easing)
|
|
26813
|
+
*/
|
|
26814
|
+
class Animation {
|
|
26815
|
+
startValue;
|
|
26816
|
+
endValue;
|
|
26817
|
+
duration;
|
|
26818
|
+
callback;
|
|
26819
|
+
startTime = undefined;
|
|
26820
|
+
animationFrameId = null;
|
|
26821
|
+
constructor(startValue, endValue, duration, callback) {
|
|
26822
|
+
this.startValue = startValue;
|
|
26823
|
+
this.endValue = endValue;
|
|
26824
|
+
this.duration = duration;
|
|
26825
|
+
this.callback = callback;
|
|
26826
|
+
}
|
|
26827
|
+
start() {
|
|
26828
|
+
this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
|
|
26829
|
+
}
|
|
26830
|
+
stop() {
|
|
26831
|
+
if (this.animationFrameId) {
|
|
26832
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
26833
|
+
this.animationFrameId = null;
|
|
26834
|
+
}
|
|
26835
|
+
}
|
|
26836
|
+
animate(timestamp) {
|
|
26837
|
+
if (!this.startTime) {
|
|
26838
|
+
this.startTime = timestamp;
|
|
26839
|
+
}
|
|
26840
|
+
const elapsed = timestamp - this.startTime;
|
|
26841
|
+
const progress = Math.min(elapsed / this.duration, 1);
|
|
26842
|
+
const currentValue = this.startValue + (this.endValue - this.startValue) * EASING_FN.easeOutQuart(progress);
|
|
26843
|
+
this.callback(currentValue);
|
|
26844
|
+
if (progress < 1) {
|
|
26845
|
+
this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
|
|
26846
|
+
}
|
|
26847
|
+
else {
|
|
26848
|
+
this.stop();
|
|
26849
|
+
}
|
|
26850
|
+
}
|
|
26311
26851
|
}
|
|
26312
26852
|
|
|
26313
26853
|
class ComboChart extends AbstractChart {
|
|
@@ -29341,6 +29881,12 @@ class Menu extends Component {
|
|
|
29341
29881
|
menu.onStopHover?.(this.env);
|
|
29342
29882
|
this.props.onMouseLeave?.(menu, ev);
|
|
29343
29883
|
}
|
|
29884
|
+
onClickMenu(menu, ev) {
|
|
29885
|
+
if (!this.isEnabled(menu)) {
|
|
29886
|
+
return;
|
|
29887
|
+
}
|
|
29888
|
+
this.props.onClickMenu?.(menu, ev);
|
|
29889
|
+
}
|
|
29344
29890
|
}
|
|
29345
29891
|
|
|
29346
29892
|
/**
|
|
@@ -29839,9 +30385,6 @@ class MenuPopover extends Component {
|
|
|
29839
30385
|
this.subMenu.parentMenu = undefined;
|
|
29840
30386
|
}
|
|
29841
30387
|
onClickMenu(menu, ev) {
|
|
29842
|
-
if (!this.isEnabled(menu)) {
|
|
29843
|
-
return;
|
|
29844
|
-
}
|
|
29845
30388
|
if (this.isRoot(menu)) {
|
|
29846
30389
|
this.openSubMenu(menu, ev.currentTarget);
|
|
29847
30390
|
}
|
|
@@ -31092,11 +31635,9 @@ criterionEvaluatorRegistry.add("isValueInRange", {
|
|
|
31092
31635
|
if (!value) {
|
|
31093
31636
|
return false;
|
|
31094
31637
|
}
|
|
31095
|
-
const
|
|
31096
|
-
const criterionValues = getters.getRangeValues(range);
|
|
31638
|
+
const criterionValues = getters.getDataValidationRangeValues(sheetId, criterion);
|
|
31097
31639
|
return criterionValues
|
|
31098
|
-
.
|
|
31099
|
-
.map((value) => value.toString().toLowerCase())
|
|
31640
|
+
.map((value) => value.toLowerCase())
|
|
31100
31641
|
.includes(value.toString().toLowerCase());
|
|
31101
31642
|
},
|
|
31102
31643
|
getErrorString: (criterion) => _t("The value must be a value in the range %s", String(criterion.values[0])),
|
|
@@ -31268,6 +31809,12 @@ class TextValueProvider extends Component {
|
|
|
31268
31809
|
selectedElement?.scrollIntoView?.({ block: "nearest" });
|
|
31269
31810
|
}, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
|
|
31270
31811
|
}
|
|
31812
|
+
getCss(html) {
|
|
31813
|
+
return cssPropertiesToCss({
|
|
31814
|
+
color: html.color || "#000000",
|
|
31815
|
+
background: html.backgroundColor,
|
|
31816
|
+
});
|
|
31817
|
+
}
|
|
31271
31818
|
}
|
|
31272
31819
|
|
|
31273
31820
|
class ContentEditableHelper {
|
|
@@ -31406,7 +31953,6 @@ class ContentEditableHelper {
|
|
|
31406
31953
|
// We can only modify a node in place if it has the same type as the content
|
|
31407
31954
|
// that we would insert, which are spans.
|
|
31408
31955
|
// Otherwise, it means that the node has been input by the user, through the keyboard or a copy/paste
|
|
31409
|
-
// @ts-ignore (somehow required because jest does not like child.tagName despite the prior check)
|
|
31410
31956
|
const childIsSpan = child && "tagName" in child && child.tagName === "SPAN";
|
|
31411
31957
|
if (childIsSpan && compareContentToSpanElement(content, child)) {
|
|
31412
31958
|
continue;
|
|
@@ -31441,9 +31987,7 @@ class ContentEditableHelper {
|
|
|
31441
31987
|
}
|
|
31442
31988
|
// Empty line
|
|
31443
31989
|
if (!p.hasChildNodes()) {
|
|
31444
|
-
|
|
31445
|
-
span.appendChild(document.createElement("br"));
|
|
31446
|
-
p.appendChild(span);
|
|
31990
|
+
p.appendChild(document.createElement("span"));
|
|
31447
31991
|
}
|
|
31448
31992
|
// replace p if necessary
|
|
31449
31993
|
if (newChild) {
|
|
@@ -31490,13 +32034,10 @@ class ContentEditableHelper {
|
|
|
31490
32034
|
}
|
|
31491
32035
|
getText() {
|
|
31492
32036
|
let text = "";
|
|
31493
|
-
const it = iterateChildren(this.el);
|
|
31494
|
-
let current = it.next();
|
|
31495
32037
|
let isFirstParagraph = true;
|
|
31496
|
-
|
|
31497
|
-
|
|
31498
|
-
|
|
31499
|
-
}
|
|
32038
|
+
let emptyParagraph = false;
|
|
32039
|
+
const it = iterateChildren(this.el);
|
|
32040
|
+
for (let current = it.next(); !current.done; current = it.next()) {
|
|
31500
32041
|
if (current.value.nodeName === "P" ||
|
|
31501
32042
|
(current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
|
|
31502
32043
|
) {
|
|
@@ -31506,8 +32047,15 @@ class ContentEditableHelper {
|
|
|
31506
32047
|
else {
|
|
31507
32048
|
text += NEWLINE;
|
|
31508
32049
|
}
|
|
32050
|
+
emptyParagraph = ["<br>", "<span><br></span>"].includes(current.value.innerHTML);
|
|
32051
|
+
continue;
|
|
32052
|
+
}
|
|
32053
|
+
if (!current.value.hasChildNodes()) {
|
|
32054
|
+
if (current.value.nodeName === "BR" && !emptyParagraph) {
|
|
32055
|
+
text += NEWLINE;
|
|
32056
|
+
}
|
|
32057
|
+
text += current.value.textContent;
|
|
31509
32058
|
}
|
|
31510
|
-
current = it.next();
|
|
31511
32059
|
}
|
|
31512
32060
|
return text;
|
|
31513
32061
|
}
|
|
@@ -32857,6 +33405,12 @@ class Composer extends Component {
|
|
|
32857
33405
|
useEffect(() => {
|
|
32858
33406
|
this.processTokenAtCursor();
|
|
32859
33407
|
}, () => [this.props.composerStore.editionMode !== "inactive"]);
|
|
33408
|
+
useEffect(() => {
|
|
33409
|
+
this.contentHelper.scrollSelectionIntoView();
|
|
33410
|
+
}, () => [
|
|
33411
|
+
this.props.composerStore.composerSelection.start,
|
|
33412
|
+
this.props.composerStore.composerSelection.end,
|
|
33413
|
+
]);
|
|
32860
33414
|
}
|
|
32861
33415
|
// ---------------------------------------------------------------------------
|
|
32862
33416
|
// Handlers
|
|
@@ -33067,6 +33621,7 @@ class Composer extends Component {
|
|
|
33067
33621
|
if (this.env.isMobile() && !isIOS()) {
|
|
33068
33622
|
return;
|
|
33069
33623
|
}
|
|
33624
|
+
this.debouncedHover.stopDebounce();
|
|
33070
33625
|
this.contentHelper.removeSelection();
|
|
33071
33626
|
}
|
|
33072
33627
|
onMouseup() {
|
|
@@ -33145,7 +33700,6 @@ class Composer extends Component {
|
|
|
33145
33700
|
const { start, end } = this.props.composerStore.composerSelection;
|
|
33146
33701
|
this.contentHelper.selectRange(start, end);
|
|
33147
33702
|
}
|
|
33148
|
-
this.contentHelper.scrollSelectionIntoView();
|
|
33149
33703
|
}
|
|
33150
33704
|
this.shouldProcessInputEvents = true;
|
|
33151
33705
|
}
|
|
@@ -33621,68 +34175,6 @@ class SingleInputCriterionForm extends CriterionForm {
|
|
|
33621
34175
|
}
|
|
33622
34176
|
}
|
|
33623
34177
|
|
|
33624
|
-
css /* scss */ `
|
|
33625
|
-
.o-dv-list-item-delete {
|
|
33626
|
-
color: #666666;
|
|
33627
|
-
cursor: pointer;
|
|
33628
|
-
}
|
|
33629
|
-
`;
|
|
33630
|
-
class ListCriterionForm extends CriterionForm {
|
|
33631
|
-
static template = "o-spreadsheet-ListCriterionForm";
|
|
33632
|
-
static components = { CriterionInput };
|
|
33633
|
-
state = useState({
|
|
33634
|
-
numberOfValues: Math.max(this.props.criterion.values.length, 2),
|
|
33635
|
-
});
|
|
33636
|
-
setup() {
|
|
33637
|
-
super.setup();
|
|
33638
|
-
const setupDefault = (props) => {
|
|
33639
|
-
if (props.criterion.displayStyle === undefined) {
|
|
33640
|
-
this.updateCriterion({ displayStyle: "arrow" });
|
|
33641
|
-
}
|
|
33642
|
-
};
|
|
33643
|
-
onWillUpdateProps(setupDefault);
|
|
33644
|
-
onWillStart(() => setupDefault(this.props));
|
|
33645
|
-
}
|
|
33646
|
-
onValueChanged(value, index) {
|
|
33647
|
-
const values = [...this.displayedValues];
|
|
33648
|
-
values[index] = value;
|
|
33649
|
-
this.updateCriterion({ values });
|
|
33650
|
-
}
|
|
33651
|
-
onAddAnotherValue() {
|
|
33652
|
-
this.state.numberOfValues++;
|
|
33653
|
-
}
|
|
33654
|
-
removeItem(index) {
|
|
33655
|
-
const values = [...this.displayedValues];
|
|
33656
|
-
values.splice(index, 1);
|
|
33657
|
-
this.state.numberOfValues--;
|
|
33658
|
-
this.updateCriterion({ values });
|
|
33659
|
-
}
|
|
33660
|
-
onChangedDisplayStyle(ev) {
|
|
33661
|
-
const displayStyle = ev.target.value;
|
|
33662
|
-
this.updateCriterion({ displayStyle });
|
|
33663
|
-
}
|
|
33664
|
-
onKeyDown(ev, index) {
|
|
33665
|
-
if ((ev.key === "Enter" || ev.key === "Tab") && index === this.state.numberOfValues - 1) {
|
|
33666
|
-
this.onAddAnotherValue();
|
|
33667
|
-
this.state.focusedValueIndex = index + 1;
|
|
33668
|
-
ev.preventDefault();
|
|
33669
|
-
}
|
|
33670
|
-
else if (ev.key === "Enter") {
|
|
33671
|
-
this.state.focusedValueIndex = index + 1;
|
|
33672
|
-
}
|
|
33673
|
-
}
|
|
33674
|
-
onBlurInput() {
|
|
33675
|
-
this.state.focusedValueIndex = undefined;
|
|
33676
|
-
}
|
|
33677
|
-
get displayedValues() {
|
|
33678
|
-
const values = [];
|
|
33679
|
-
for (let i = 0; i < this.state.numberOfValues; i++) {
|
|
33680
|
-
values.push(this.props.criterion.values[i] || "");
|
|
33681
|
-
}
|
|
33682
|
-
return values;
|
|
33683
|
-
}
|
|
33684
|
-
}
|
|
33685
|
-
|
|
33686
34178
|
/**
|
|
33687
34179
|
* Start listening to pointer events and apply the given callbacks.
|
|
33688
34180
|
*
|
|
@@ -33712,207 +34204,653 @@ function startDnd(onPointerMove, onPointerUp) {
|
|
|
33712
34204
|
return removeListeners;
|
|
33713
34205
|
}
|
|
33714
34206
|
|
|
33715
|
-
|
|
33716
|
-
|
|
33717
|
-
|
|
33718
|
-
|
|
33719
|
-
|
|
33720
|
-
|
|
33721
|
-
|
|
33722
|
-
|
|
33723
|
-
|
|
33724
|
-
|
|
33725
|
-
|
|
33726
|
-
|
|
33727
|
-
|
|
33728
|
-
|
|
33729
|
-
|
|
33730
|
-
|
|
33731
|
-
|
|
33732
|
-
|
|
33733
|
-
|
|
33734
|
-
|
|
33735
|
-
|
|
33736
|
-
|
|
33737
|
-
|
|
33738
|
-
|
|
33739
|
-
|
|
33740
|
-
const onDragEnd = (itemId, indexAtEnd) => {
|
|
33741
|
-
state.draggedItemId = undefined;
|
|
33742
|
-
state.itemsStyle = {};
|
|
33743
|
-
document.body.style.cursor = previousCursor;
|
|
33744
|
-
args.onDragEnd?.(itemId, indexAtEnd);
|
|
33745
|
-
cleanUp();
|
|
33746
|
-
};
|
|
33747
|
-
document.body.style.cursor = "move";
|
|
33748
|
-
state.draggedItemId = args.draggedItemId;
|
|
33749
|
-
const container = direction === "horizontal"
|
|
33750
|
-
? new HorizontalContainer(args.scrollableContainerEl)
|
|
33751
|
-
: new VerticalContainer(args.scrollableContainerEl);
|
|
33752
|
-
dndHelper = new DOMDndHelper({
|
|
33753
|
-
...args,
|
|
33754
|
-
container,
|
|
33755
|
-
onChange,
|
|
33756
|
-
onDragEnd,
|
|
33757
|
-
onCancel: state.cancel,
|
|
33758
|
-
});
|
|
33759
|
-
const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
|
|
33760
|
-
cleanupFns.push(stopListening);
|
|
33761
|
-
const onScroll = dndHelper.onScroll.bind(dndHelper);
|
|
33762
|
-
args.scrollableContainerEl.addEventListener("scroll", onScroll);
|
|
33763
|
-
cleanupFns.push(() => args.scrollableContainerEl.removeEventListener("scroll", onScroll));
|
|
33764
|
-
cleanupFns.push(dndHelper.destroy.bind(dndHelper));
|
|
33765
|
-
};
|
|
33766
|
-
onWillUnmount(() => {
|
|
33767
|
-
cleanUp();
|
|
33768
|
-
});
|
|
33769
|
-
const state = useState({
|
|
33770
|
-
itemsStyle: {},
|
|
33771
|
-
draggedItemId: undefined,
|
|
33772
|
-
start,
|
|
33773
|
-
cancel: () => { },
|
|
33774
|
-
});
|
|
33775
|
-
return state;
|
|
33776
|
-
}
|
|
33777
|
-
class DOMDndHelper {
|
|
33778
|
-
draggedItemId;
|
|
33779
|
-
items;
|
|
33780
|
-
container;
|
|
33781
|
-
initialMousePosition;
|
|
33782
|
-
currentMousePosition;
|
|
33783
|
-
initialScroll;
|
|
33784
|
-
minPosition;
|
|
33785
|
-
maxPosition;
|
|
33786
|
-
edgeScrollIntervalId;
|
|
33787
|
-
onChange;
|
|
33788
|
-
onCancel;
|
|
33789
|
-
onDragEnd;
|
|
33790
|
-
/**
|
|
33791
|
-
* The dead zone is an area in which the pointermove events are ignored.
|
|
33792
|
-
*
|
|
33793
|
-
* This is useful when swapping the dragged item with a larger item. After the swap,
|
|
33794
|
-
* the mouse is still hovering on the item we just swapped with. In this case, we don't want
|
|
33795
|
-
* a mouse move to trigger another swap the other way around, so we create a dead zone. We will clear
|
|
33796
|
-
* the dead zone when the mouse leaves the swapped item.
|
|
33797
|
-
*/
|
|
33798
|
-
deadZone;
|
|
33799
|
-
constructor(args) {
|
|
33800
|
-
this.items = args.items.map((item) => ({ ...item, positionAtStart: item.position }));
|
|
33801
|
-
this.draggedItemId = args.draggedItemId;
|
|
33802
|
-
this.container = args.container;
|
|
33803
|
-
this.onChange = args.onChange;
|
|
33804
|
-
this.onCancel = args.onCancel;
|
|
33805
|
-
this.onDragEnd = args.onDragEnd;
|
|
33806
|
-
this.initialMousePosition = args.initialMousePosition;
|
|
33807
|
-
this.currentMousePosition = args.initialMousePosition;
|
|
33808
|
-
this.initialScroll = this.container.scroll;
|
|
33809
|
-
this.minPosition = this.items[0].position;
|
|
33810
|
-
this.maxPosition =
|
|
33811
|
-
this.items[this.items.length - 1].position + this.items[this.items.length - 1].size;
|
|
33812
|
-
}
|
|
33813
|
-
getItemStyles() {
|
|
33814
|
-
const styles = {};
|
|
33815
|
-
for (const item of this.items) {
|
|
33816
|
-
styles[item.id] = this.getItemStyle(item.id);
|
|
33817
|
-
}
|
|
33818
|
-
return styles;
|
|
33819
|
-
}
|
|
33820
|
-
getItemStyle(itemId) {
|
|
33821
|
-
const position = this.container.cssPositionProperty;
|
|
33822
|
-
const style = {};
|
|
33823
|
-
style.position = "relative";
|
|
33824
|
-
style[position] = (this.getItemsPositions()[itemId] || 0) + "px";
|
|
33825
|
-
style.transition = `${position} 0.5s`;
|
|
33826
|
-
style["pointer-events"] = "none";
|
|
33827
|
-
if (this.draggedItemId === itemId) {
|
|
33828
|
-
style.transition = `${position} 0s`;
|
|
33829
|
-
style["z-index"] = "1000";
|
|
33830
|
-
}
|
|
33831
|
-
return cssPropertiesToCss(style);
|
|
34207
|
+
const LINE_VERTICAL_PADDING = 1;
|
|
34208
|
+
const PICKER_PADDING = 8;
|
|
34209
|
+
const ITEM_BORDER_WIDTH = 1;
|
|
34210
|
+
const ITEM_EDGE_LENGTH = 18;
|
|
34211
|
+
const ITEMS_PER_LINE = 10;
|
|
34212
|
+
const MAGNIFIER_EDGE = 16;
|
|
34213
|
+
const ITEM_GAP = 2;
|
|
34214
|
+
const CONTENT_WIDTH = ITEMS_PER_LINE * (ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH) + (ITEMS_PER_LINE - 1) * ITEM_GAP;
|
|
34215
|
+
const INNER_GRADIENT_WIDTH = CONTENT_WIDTH - 2 * ITEM_BORDER_WIDTH;
|
|
34216
|
+
const INNER_GRADIENT_HEIGHT = CONTENT_WIDTH - 30 - 2 * ITEM_BORDER_WIDTH;
|
|
34217
|
+
const CONTAINER_WIDTH = CONTENT_WIDTH + 2 * PICKER_PADDING;
|
|
34218
|
+
css /* scss */ `
|
|
34219
|
+
.o-color-picker {
|
|
34220
|
+
padding: ${PICKER_PADDING}px 0;
|
|
34221
|
+
/* FIXME: this is useless, overiden by the popover container */
|
|
34222
|
+
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
|
|
34223
|
+
background-color: white;
|
|
34224
|
+
line-height: 1.2;
|
|
34225
|
+
overflow-y: auto;
|
|
34226
|
+
overflow-x: hidden;
|
|
34227
|
+
width: ${CONTAINER_WIDTH}px;
|
|
34228
|
+
|
|
34229
|
+
.o-color-picker-section-name {
|
|
34230
|
+
margin: 0px ${ITEM_BORDER_WIDTH}px;
|
|
34231
|
+
padding: 4px ${PICKER_PADDING}px;
|
|
33832
34232
|
}
|
|
33833
|
-
|
|
33834
|
-
|
|
34233
|
+
.colors-grid {
|
|
34234
|
+
display: grid;
|
|
34235
|
+
padding: ${LINE_VERTICAL_PADDING}px ${PICKER_PADDING}px;
|
|
34236
|
+
grid-template-columns: repeat(${ITEMS_PER_LINE}, 1fr);
|
|
34237
|
+
grid-gap: ${ITEM_GAP}px;
|
|
33835
34238
|
}
|
|
33836
|
-
|
|
33837
|
-
|
|
33838
|
-
|
|
33839
|
-
|
|
33840
|
-
|
|
33841
|
-
|
|
33842
|
-
|
|
33843
|
-
|
|
33844
|
-
|
|
33845
|
-
|
|
33846
|
-
}
|
|
33847
|
-
else {
|
|
33848
|
-
this.stopEdgeScroll();
|
|
34239
|
+
.o-color-picker-toggler-button {
|
|
34240
|
+
display: flex;
|
|
34241
|
+
.o-color-picker-toggler-sign {
|
|
34242
|
+
display: flex;
|
|
34243
|
+
margin: auto auto;
|
|
34244
|
+
width: 55%;
|
|
34245
|
+
height: 55%;
|
|
34246
|
+
.o-icon {
|
|
34247
|
+
width: 100%;
|
|
34248
|
+
height: 100%;
|
|
33849
34249
|
}
|
|
33850
|
-
|
|
34250
|
+
}
|
|
33851
34251
|
}
|
|
33852
|
-
|
|
33853
|
-
|
|
33854
|
-
|
|
33855
|
-
|
|
33856
|
-
|
|
33857
|
-
|
|
33858
|
-
|
|
33859
|
-
|
|
33860
|
-
|
|
33861
|
-
|
|
33862
|
-
|
|
33863
|
-
|
|
33864
|
-
|
|
33865
|
-
|
|
33866
|
-
if (draggedItemIndex === hoveredItemIndex) {
|
|
33867
|
-
this.onChange(this.getItemsPositions());
|
|
33868
|
-
return;
|
|
33869
|
-
}
|
|
33870
|
-
const startIndex = Math.min(draggedItemIndex, hoveredItemIndex);
|
|
33871
|
-
const endIndex = Math.max(draggedItemIndex, hoveredItemIndex);
|
|
33872
|
-
const direction = Math.sign(hoveredItemIndex - draggedItemIndex);
|
|
33873
|
-
let draggedItemMoveSize = 0;
|
|
33874
|
-
for (let i = startIndex; i <= endIndex; i++) {
|
|
33875
|
-
if (i === draggedItemIndex) {
|
|
33876
|
-
continue;
|
|
33877
|
-
}
|
|
33878
|
-
this.items[i].position -= direction * draggedItem.size;
|
|
33879
|
-
draggedItemMoveSize += this.items[i].size;
|
|
33880
|
-
}
|
|
33881
|
-
draggedItem.position += direction * draggedItemMoveSize;
|
|
33882
|
-
this.items.sort((item1, item2) => item1.position - item2.position);
|
|
33883
|
-
this.deadZone =
|
|
33884
|
-
direction > 0
|
|
33885
|
-
? { start: position, end: draggedItem.position }
|
|
33886
|
-
: { start: draggedItem.position + draggedItem.size, end: position };
|
|
33887
|
-
this.onChange(this.getItemsPositions());
|
|
34252
|
+
.o-color-picker-line-item {
|
|
34253
|
+
width: ${ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
34254
|
+
height: ${ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
34255
|
+
margin: 0px;
|
|
34256
|
+
border-radius: 50px;
|
|
34257
|
+
border: ${ITEM_BORDER_WIDTH}px solid #666666;
|
|
34258
|
+
padding: 0px;
|
|
34259
|
+
font-size: 16px;
|
|
34260
|
+
background: white;
|
|
34261
|
+
&:hover {
|
|
34262
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
34263
|
+
outline: 1px solid gray;
|
|
34264
|
+
cursor: pointer;
|
|
34265
|
+
}
|
|
33888
34266
|
}
|
|
33889
|
-
|
|
33890
|
-
|
|
33891
|
-
|
|
34267
|
+
.o-buttons {
|
|
34268
|
+
padding: ${PICKER_PADDING}px;
|
|
34269
|
+
display: flex;
|
|
34270
|
+
.o-cancel {
|
|
34271
|
+
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
34272
|
+
width: 100%;
|
|
34273
|
+
padding: 5px;
|
|
34274
|
+
font-size: 14px;
|
|
34275
|
+
background: white;
|
|
34276
|
+
border-radius: 4px;
|
|
34277
|
+
&:hover:enabled {
|
|
34278
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
33892
34279
|
}
|
|
33893
|
-
|
|
33894
|
-
ev.preventDefault();
|
|
33895
|
-
const targetItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
|
|
33896
|
-
this.onDragEnd(this.draggedItemId, targetItemIndex);
|
|
33897
|
-
this.stopEdgeScroll();
|
|
33898
|
-
return false;
|
|
34280
|
+
}
|
|
33899
34281
|
}
|
|
33900
|
-
|
|
33901
|
-
|
|
33902
|
-
|
|
33903
|
-
|
|
33904
|
-
|
|
33905
|
-
|
|
33906
|
-
|
|
34282
|
+
.o-add-button {
|
|
34283
|
+
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
34284
|
+
padding: 4px;
|
|
34285
|
+
background: white;
|
|
34286
|
+
border-radius: 4px;
|
|
34287
|
+
&:hover:enabled {
|
|
34288
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
34289
|
+
}
|
|
33907
34290
|
}
|
|
33908
|
-
|
|
33909
|
-
|
|
33910
|
-
|
|
34291
|
+
.o-separator {
|
|
34292
|
+
border-bottom: ${MENU_SEPARATOR_BORDER_WIDTH}px solid ${SEPARATOR_COLOR};
|
|
34293
|
+
margin-top: ${MENU_SEPARATOR_PADDING}px;
|
|
34294
|
+
margin-bottom: ${MENU_SEPARATOR_PADDING}px;
|
|
33911
34295
|
}
|
|
33912
|
-
|
|
33913
|
-
|
|
33914
|
-
|
|
33915
|
-
|
|
34296
|
+
|
|
34297
|
+
.o-custom-selector {
|
|
34298
|
+
padding: ${PICKER_PADDING + 2}px ${PICKER_PADDING}px;
|
|
34299
|
+
position: relative;
|
|
34300
|
+
.o-gradient {
|
|
34301
|
+
margin-bottom: ${MAGNIFIER_EDGE / 2}px;
|
|
34302
|
+
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
34303
|
+
width: ${INNER_GRADIENT_WIDTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
34304
|
+
height: ${INNER_GRADIENT_HEIGHT + 2 * ITEM_BORDER_WIDTH}px;
|
|
34305
|
+
position: relative;
|
|
34306
|
+
}
|
|
34307
|
+
|
|
34308
|
+
.magnifier {
|
|
34309
|
+
height: ${MAGNIFIER_EDGE}px;
|
|
34310
|
+
width: ${MAGNIFIER_EDGE}px;
|
|
34311
|
+
border-radius: 50%;
|
|
34312
|
+
border: 2px solid #fff;
|
|
34313
|
+
box-shadow: 0px 0px 3px #c0c0c0;
|
|
34314
|
+
position: absolute;
|
|
34315
|
+
z-index: 2;
|
|
34316
|
+
}
|
|
34317
|
+
.saturation {
|
|
34318
|
+
background: linear-gradient(to right, #fff 0%, transparent 100%);
|
|
34319
|
+
}
|
|
34320
|
+
.lightness {
|
|
34321
|
+
background: linear-gradient(to top, #000 0%, transparent 100%);
|
|
34322
|
+
}
|
|
34323
|
+
.o-hue-picker {
|
|
34324
|
+
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
34325
|
+
width: 100%;
|
|
34326
|
+
height: 12px;
|
|
34327
|
+
border-radius: 4px;
|
|
34328
|
+
background: linear-gradient(
|
|
34329
|
+
to right,
|
|
34330
|
+
hsl(0 100% 50%) 0%,
|
|
34331
|
+
hsl(0.2turn 100% 50%) 20%,
|
|
34332
|
+
hsl(0.3turn 100% 50%) 30%,
|
|
34333
|
+
hsl(0.4turn 100% 50%) 40%,
|
|
34334
|
+
hsl(0.5turn 100% 50%) 50%,
|
|
34335
|
+
hsl(0.6turn 100% 50%) 60%,
|
|
34336
|
+
hsl(0.7turn 100% 50%) 70%,
|
|
34337
|
+
hsl(0.8turn 100% 50%) 80%,
|
|
34338
|
+
hsl(0.9turn 100% 50%) 90%,
|
|
34339
|
+
hsl(1turn 100% 50%) 100%
|
|
34340
|
+
);
|
|
34341
|
+
position: relative;
|
|
34342
|
+
cursor: crosshair;
|
|
34343
|
+
}
|
|
34344
|
+
.o-hue-slider {
|
|
34345
|
+
margin-top: -3px;
|
|
34346
|
+
}
|
|
34347
|
+
.o-custom-input-preview {
|
|
34348
|
+
padding: 2px 0px;
|
|
34349
|
+
display: flex;
|
|
34350
|
+
input {
|
|
34351
|
+
width: 50%;
|
|
34352
|
+
border-radius: 4px;
|
|
34353
|
+
padding: 4px 23px 4px 10px;
|
|
34354
|
+
height: 24px;
|
|
34355
|
+
border: 1px solid #c0c0c0;
|
|
34356
|
+
margin-right: 2px;
|
|
34357
|
+
}
|
|
34358
|
+
.o-wrong-color {
|
|
34359
|
+
/* FIXME bootstrap class instead? */
|
|
34360
|
+
outline-color: red;
|
|
34361
|
+
border-color: red;
|
|
34362
|
+
&:focus {
|
|
34363
|
+
outline-style: solid;
|
|
34364
|
+
outline-width: 1px;
|
|
34365
|
+
}
|
|
34366
|
+
}
|
|
34367
|
+
}
|
|
34368
|
+
.o-custom-input-buttons {
|
|
34369
|
+
padding: 2px 0px;
|
|
34370
|
+
display: flex;
|
|
34371
|
+
justify-content: end;
|
|
34372
|
+
}
|
|
34373
|
+
.o-color-preview {
|
|
34374
|
+
border: 1px solid #c0c0c0;
|
|
34375
|
+
border-radius: 4px;
|
|
34376
|
+
width: 50%;
|
|
34377
|
+
}
|
|
34378
|
+
}
|
|
34379
|
+
}
|
|
34380
|
+
`;
|
|
34381
|
+
class ColorPicker extends Component {
|
|
34382
|
+
static template = "o-spreadsheet-ColorPicker";
|
|
34383
|
+
static props = {
|
|
34384
|
+
onColorPicked: Function,
|
|
34385
|
+
currentColor: { type: String, optional: true },
|
|
34386
|
+
maxHeight: { type: Number, optional: true },
|
|
34387
|
+
anchorRect: Object,
|
|
34388
|
+
disableNoColor: { type: Boolean, optional: true },
|
|
34389
|
+
};
|
|
34390
|
+
static defaultProps = { currentColor: "" };
|
|
34391
|
+
static components = { Popover };
|
|
34392
|
+
COLORS = COLOR_PICKER_DEFAULTS;
|
|
34393
|
+
state = useState({
|
|
34394
|
+
showGradient: false,
|
|
34395
|
+
currentHslaColor: isColorValid(this.props.currentColor)
|
|
34396
|
+
? { ...hexToHSLA(this.props.currentColor), a: 1 }
|
|
34397
|
+
: { h: 0, s: 100, l: 100, a: 1 },
|
|
34398
|
+
customHexColor: isColorValid(this.props.currentColor) ? toHex(this.props.currentColor) : "",
|
|
34399
|
+
});
|
|
34400
|
+
get colorPickerStyle() {
|
|
34401
|
+
if (this.props.maxHeight !== undefined && this.props.maxHeight <= 0) {
|
|
34402
|
+
return cssPropertiesToCss({ display: "none" });
|
|
34403
|
+
}
|
|
34404
|
+
return "";
|
|
34405
|
+
}
|
|
34406
|
+
get popoverProps() {
|
|
34407
|
+
return {
|
|
34408
|
+
anchorRect: this.props.anchorRect,
|
|
34409
|
+
maxHeight: this.props.maxHeight,
|
|
34410
|
+
positioning: "bottom-left",
|
|
34411
|
+
verticalOffset: 0,
|
|
34412
|
+
};
|
|
34413
|
+
}
|
|
34414
|
+
get gradientHueStyle() {
|
|
34415
|
+
const hue = this.state.currentHslaColor?.h || 0;
|
|
34416
|
+
return cssPropertiesToCss({
|
|
34417
|
+
background: `hsl(${hue} 100% 50%)`,
|
|
34418
|
+
});
|
|
34419
|
+
}
|
|
34420
|
+
get sliderStyle() {
|
|
34421
|
+
const hue = this.state.currentHslaColor?.h || 0;
|
|
34422
|
+
const delta = Math.round((hue / 360) * INNER_GRADIENT_WIDTH);
|
|
34423
|
+
const left = clip(delta, 1, INNER_GRADIENT_WIDTH) - ICON_EDGE_LENGTH / 2;
|
|
34424
|
+
return cssPropertiesToCss({
|
|
34425
|
+
"margin-left": `${left}px`,
|
|
34426
|
+
});
|
|
34427
|
+
}
|
|
34428
|
+
get pointerStyle() {
|
|
34429
|
+
const { s, l } = this.state.currentHslaColor || { s: 0, l: 0 };
|
|
34430
|
+
const left = Math.round(INNER_GRADIENT_WIDTH * clip(s / 100, 0, 1));
|
|
34431
|
+
const top = Math.round(INNER_GRADIENT_HEIGHT * clip(1 - (2 * l) / (200 - s), 0, 1));
|
|
34432
|
+
return cssPropertiesToCss({
|
|
34433
|
+
left: `${-MAGNIFIER_EDGE / 2 + left}px`,
|
|
34434
|
+
top: `${-MAGNIFIER_EDGE / 2 + top}px`,
|
|
34435
|
+
background: hslaToHex(this.state.currentHslaColor),
|
|
34436
|
+
});
|
|
34437
|
+
}
|
|
34438
|
+
get colorPreviewStyle() {
|
|
34439
|
+
return cssPropertiesToCss({
|
|
34440
|
+
"background-color": hslaToHex(this.state.currentHslaColor),
|
|
34441
|
+
});
|
|
34442
|
+
}
|
|
34443
|
+
get checkmarkColor() {
|
|
34444
|
+
return chartFontColor(this.props.currentColor);
|
|
34445
|
+
}
|
|
34446
|
+
get isHexColorInputValid() {
|
|
34447
|
+
return !this.state.customHexColor || isColorValid(this.state.customHexColor);
|
|
34448
|
+
}
|
|
34449
|
+
setCustomGradient({ x, y }) {
|
|
34450
|
+
const offsetX = clip(x, 0, INNER_GRADIENT_WIDTH);
|
|
34451
|
+
const offsetY = clip(y, 0, INNER_GRADIENT_HEIGHT);
|
|
34452
|
+
const deltaX = offsetX / INNER_GRADIENT_WIDTH;
|
|
34453
|
+
const deltaY = offsetY / INNER_GRADIENT_HEIGHT;
|
|
34454
|
+
const s = 100 * deltaX;
|
|
34455
|
+
const l = 100 * (1 - deltaY) * (1 - 0.5 * deltaX);
|
|
34456
|
+
this.updateColor({ s, l });
|
|
34457
|
+
}
|
|
34458
|
+
setCustomHue(x) {
|
|
34459
|
+
// needs to be capped such that h is in [0°, 359°]
|
|
34460
|
+
const h = Math.round(clip((360 * x) / INNER_GRADIENT_WIDTH, 0, 359));
|
|
34461
|
+
this.updateColor({ h });
|
|
34462
|
+
}
|
|
34463
|
+
updateColor(newHsl) {
|
|
34464
|
+
this.state.currentHslaColor = { ...this.state.currentHslaColor, ...newHsl };
|
|
34465
|
+
this.state.customHexColor = hslaToHex(this.state.currentHslaColor);
|
|
34466
|
+
}
|
|
34467
|
+
onColorClick(color) {
|
|
34468
|
+
if (color) {
|
|
34469
|
+
this.props.onColorPicked(toHex(color));
|
|
34470
|
+
}
|
|
34471
|
+
}
|
|
34472
|
+
resetColor() {
|
|
34473
|
+
this.props.onColorPicked("");
|
|
34474
|
+
}
|
|
34475
|
+
toggleColorPicker() {
|
|
34476
|
+
this.state.showGradient = !this.state.showGradient;
|
|
34477
|
+
}
|
|
34478
|
+
dragGradientPointer(ev) {
|
|
34479
|
+
const initialGradientCoordinates = { x: ev.offsetX, y: ev.offsetY };
|
|
34480
|
+
this.setCustomGradient(initialGradientCoordinates);
|
|
34481
|
+
const initialMousePosition = { x: ev.clientX, y: ev.clientY };
|
|
34482
|
+
const onMouseMove = (ev) => {
|
|
34483
|
+
const currentMousePosition = { x: ev.clientX, y: ev.clientY };
|
|
34484
|
+
const deltaX = currentMousePosition.x - initialMousePosition.x;
|
|
34485
|
+
const deltaY = currentMousePosition.y - initialMousePosition.y;
|
|
34486
|
+
const currentGradientCoordinates = {
|
|
34487
|
+
x: initialGradientCoordinates.x + deltaX,
|
|
34488
|
+
y: initialGradientCoordinates.y + deltaY,
|
|
34489
|
+
};
|
|
34490
|
+
this.setCustomGradient(currentGradientCoordinates);
|
|
34491
|
+
};
|
|
34492
|
+
startDnd(onMouseMove, () => { });
|
|
34493
|
+
}
|
|
34494
|
+
dragHuePointer(ev) {
|
|
34495
|
+
const initialX = ev.offsetX;
|
|
34496
|
+
const initialMouseX = ev.clientX;
|
|
34497
|
+
this.setCustomHue(initialX);
|
|
34498
|
+
const onMouseMove = (ev) => {
|
|
34499
|
+
const currentMouseX = ev.clientX;
|
|
34500
|
+
const deltaX = currentMouseX - initialMouseX;
|
|
34501
|
+
const x = initialX + deltaX;
|
|
34502
|
+
this.setCustomHue(x);
|
|
34503
|
+
};
|
|
34504
|
+
startDnd(onMouseMove, () => { });
|
|
34505
|
+
}
|
|
34506
|
+
setHexColor(ev) {
|
|
34507
|
+
// only support HEX code input
|
|
34508
|
+
const val = ev.target.value.replace("##", "#").slice(0, 7);
|
|
34509
|
+
this.state.customHexColor = val;
|
|
34510
|
+
if (!isColorValid(val)) ;
|
|
34511
|
+
else {
|
|
34512
|
+
this.state.currentHslaColor = { ...hexToHSLA(val), a: 1 };
|
|
34513
|
+
}
|
|
34514
|
+
}
|
|
34515
|
+
addCustomColor(ev) {
|
|
34516
|
+
if (!isHSLAValid(this.state.currentHslaColor) || !isColorValid(this.state.customHexColor)) {
|
|
34517
|
+
return;
|
|
34518
|
+
}
|
|
34519
|
+
this.props.onColorPicked(toHex(this.state.customHexColor));
|
|
34520
|
+
}
|
|
34521
|
+
isSameColor(color1, color2) {
|
|
34522
|
+
return isSameColor(color1, color2);
|
|
34523
|
+
}
|
|
34524
|
+
}
|
|
34525
|
+
|
|
34526
|
+
class Section extends Component {
|
|
34527
|
+
static template = "o_spreadsheet.Section";
|
|
34528
|
+
static props = {
|
|
34529
|
+
class: { type: String, optional: true },
|
|
34530
|
+
title: { type: String, optional: true },
|
|
34531
|
+
slots: Object,
|
|
34532
|
+
};
|
|
34533
|
+
}
|
|
34534
|
+
|
|
34535
|
+
const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
|
|
34536
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
34537
|
+
<path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
|
|
34538
|
+
</svg>
|
|
34539
|
+
`;
|
|
34540
|
+
css /* scss */ `
|
|
34541
|
+
.o-round-color-picker-button {
|
|
34542
|
+
width: 20px;
|
|
34543
|
+
height: 20px;
|
|
34544
|
+
cursor: pointer;
|
|
34545
|
+
border: 1px solid ${GRAY_300};
|
|
34546
|
+
background-position: 1px 1px;
|
|
34547
|
+
background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
|
|
34548
|
+
}
|
|
34549
|
+
`;
|
|
34550
|
+
class RoundColorPicker extends Component {
|
|
34551
|
+
static template = "o-spreadsheet.RoundColorPicker";
|
|
34552
|
+
static components = { Section, ColorPicker };
|
|
34553
|
+
static props = {
|
|
34554
|
+
currentColor: { type: String, optional: true },
|
|
34555
|
+
title: { type: String, optional: true },
|
|
34556
|
+
onColorPicked: Function,
|
|
34557
|
+
disableNoColor: { type: Boolean, optional: true },
|
|
34558
|
+
};
|
|
34559
|
+
colorPickerButtonRef = useRef("colorPickerButton");
|
|
34560
|
+
state;
|
|
34561
|
+
setup() {
|
|
34562
|
+
this.state = useState({ pickerOpened: false });
|
|
34563
|
+
useExternalListener(window, "click", this.closePicker);
|
|
34564
|
+
}
|
|
34565
|
+
closePicker() {
|
|
34566
|
+
this.state.pickerOpened = false;
|
|
34567
|
+
}
|
|
34568
|
+
togglePicker() {
|
|
34569
|
+
this.state.pickerOpened = !this.state.pickerOpened;
|
|
34570
|
+
}
|
|
34571
|
+
onColorPicked(color) {
|
|
34572
|
+
this.props.onColorPicked(color);
|
|
34573
|
+
this.state.pickerOpened = false;
|
|
34574
|
+
}
|
|
34575
|
+
get colorPickerAnchorRect() {
|
|
34576
|
+
const button = this.colorPickerButtonRef.el;
|
|
34577
|
+
return getBoundingRectAsPOJO(button);
|
|
34578
|
+
}
|
|
34579
|
+
get buttonStyle() {
|
|
34580
|
+
return cssPropertiesToCss({
|
|
34581
|
+
background: this.props.currentColor,
|
|
34582
|
+
});
|
|
34583
|
+
}
|
|
34584
|
+
}
|
|
34585
|
+
|
|
34586
|
+
css /* scss */ `
|
|
34587
|
+
.o-dv-list-item-delete {
|
|
34588
|
+
color: #666666;
|
|
34589
|
+
cursor: pointer;
|
|
34590
|
+
}
|
|
34591
|
+
`;
|
|
34592
|
+
class ListCriterionForm extends CriterionForm {
|
|
34593
|
+
static template = "o-spreadsheet-ListCriterionForm";
|
|
34594
|
+
static components = { CriterionInput, RoundColorPicker };
|
|
34595
|
+
state = useState({
|
|
34596
|
+
numberOfValues: Math.max(this.props.criterion.values.length, 2),
|
|
34597
|
+
});
|
|
34598
|
+
setup() {
|
|
34599
|
+
super.setup();
|
|
34600
|
+
const setupDefault = (props) => {
|
|
34601
|
+
if (props.criterion.displayStyle === undefined) {
|
|
34602
|
+
this.updateCriterion({ displayStyle: "chip" });
|
|
34603
|
+
}
|
|
34604
|
+
};
|
|
34605
|
+
onWillUpdateProps(setupDefault);
|
|
34606
|
+
onWillStart(() => setupDefault(this.props));
|
|
34607
|
+
}
|
|
34608
|
+
onValueChanged(value, index) {
|
|
34609
|
+
const values = [...this.displayedValues];
|
|
34610
|
+
values[index] = value;
|
|
34611
|
+
this.updateCriterion({ values });
|
|
34612
|
+
}
|
|
34613
|
+
onColorChanged(color, value) {
|
|
34614
|
+
const colors = { ...this.props.criterion.colors };
|
|
34615
|
+
colors[value] = color || undefined;
|
|
34616
|
+
this.updateCriterion({ colors });
|
|
34617
|
+
}
|
|
34618
|
+
onAddAnotherValue() {
|
|
34619
|
+
this.state.numberOfValues++;
|
|
34620
|
+
}
|
|
34621
|
+
removeItem(index) {
|
|
34622
|
+
const values = [...this.displayedValues];
|
|
34623
|
+
values.splice(index, 1);
|
|
34624
|
+
this.state.numberOfValues--;
|
|
34625
|
+
this.updateCriterion({ values });
|
|
34626
|
+
}
|
|
34627
|
+
onChangedDisplayStyle(ev) {
|
|
34628
|
+
const displayStyle = ev.target.value;
|
|
34629
|
+
this.updateCriterion({ displayStyle });
|
|
34630
|
+
}
|
|
34631
|
+
onKeyDown(ev, index) {
|
|
34632
|
+
if ((ev.key === "Enter" || ev.key === "Tab") && index === this.state.numberOfValues - 1) {
|
|
34633
|
+
this.onAddAnotherValue();
|
|
34634
|
+
this.state.focusedValueIndex = index + 1;
|
|
34635
|
+
ev.preventDefault();
|
|
34636
|
+
}
|
|
34637
|
+
else if (ev.key === "Enter") {
|
|
34638
|
+
this.state.focusedValueIndex = index + 1;
|
|
34639
|
+
}
|
|
34640
|
+
}
|
|
34641
|
+
onBlurInput() {
|
|
34642
|
+
this.state.focusedValueIndex = undefined;
|
|
34643
|
+
}
|
|
34644
|
+
get displayedValues() {
|
|
34645
|
+
const values = [];
|
|
34646
|
+
for (let i = 0; i < this.state.numberOfValues; i++) {
|
|
34647
|
+
values.push(this.props.criterion.values[i] || "");
|
|
34648
|
+
}
|
|
34649
|
+
return values;
|
|
34650
|
+
}
|
|
34651
|
+
}
|
|
34652
|
+
|
|
34653
|
+
function useDragAndDropListItems() {
|
|
34654
|
+
let dndHelper;
|
|
34655
|
+
const previousCursor = document.body.style.cursor;
|
|
34656
|
+
let cleanupFns = [];
|
|
34657
|
+
const cleanUp = () => {
|
|
34658
|
+
dndHelper = undefined;
|
|
34659
|
+
document.body.style.cursor = previousCursor;
|
|
34660
|
+
cleanupFns.forEach((fn) => fn());
|
|
34661
|
+
cleanupFns = [];
|
|
34662
|
+
};
|
|
34663
|
+
const start = (direction, args) => {
|
|
34664
|
+
const onChange = () => {
|
|
34665
|
+
document.body.style.cursor = "move";
|
|
34666
|
+
if (!dndHelper)
|
|
34667
|
+
return;
|
|
34668
|
+
Object.assign(state.itemsStyle, dndHelper.getItemStyles());
|
|
34669
|
+
args.onChange?.();
|
|
34670
|
+
};
|
|
34671
|
+
state.cancel = () => {
|
|
34672
|
+
state.draggedItemId = undefined;
|
|
34673
|
+
state.itemsStyle = {};
|
|
34674
|
+
document.body.style.cursor = previousCursor;
|
|
34675
|
+
args.onCancel?.();
|
|
34676
|
+
cleanUp();
|
|
34677
|
+
};
|
|
34678
|
+
const onDragEnd = (itemId, indexAtEnd) => {
|
|
34679
|
+
state.draggedItemId = undefined;
|
|
34680
|
+
state.itemsStyle = {};
|
|
34681
|
+
document.body.style.cursor = previousCursor;
|
|
34682
|
+
args.onDragEnd?.(itemId, indexAtEnd);
|
|
34683
|
+
cleanUp();
|
|
34684
|
+
};
|
|
34685
|
+
document.body.style.cursor = "move";
|
|
34686
|
+
state.draggedItemId = args.draggedItemId;
|
|
34687
|
+
const container = direction === "horizontal"
|
|
34688
|
+
? new HorizontalContainer(args.scrollableContainerEl)
|
|
34689
|
+
: new VerticalContainer(args.scrollableContainerEl);
|
|
34690
|
+
dndHelper = new DOMDndHelper({
|
|
34691
|
+
...args,
|
|
34692
|
+
container,
|
|
34693
|
+
onChange,
|
|
34694
|
+
onDragEnd,
|
|
34695
|
+
onCancel: state.cancel,
|
|
34696
|
+
});
|
|
34697
|
+
const stopListening = startDnd(dndHelper.onMouseMove.bind(dndHelper), dndHelper.onMouseUp.bind(dndHelper));
|
|
34698
|
+
cleanupFns.push(stopListening);
|
|
34699
|
+
const onScroll = dndHelper.onScroll.bind(dndHelper);
|
|
34700
|
+
args.scrollableContainerEl.addEventListener("scroll", onScroll);
|
|
34701
|
+
cleanupFns.push(() => args.scrollableContainerEl.removeEventListener("scroll", onScroll));
|
|
34702
|
+
cleanupFns.push(dndHelper.destroy.bind(dndHelper));
|
|
34703
|
+
};
|
|
34704
|
+
onWillUnmount(() => {
|
|
34705
|
+
cleanUp();
|
|
34706
|
+
});
|
|
34707
|
+
const state = useState({
|
|
34708
|
+
itemsStyle: {},
|
|
34709
|
+
draggedItemId: undefined,
|
|
34710
|
+
start,
|
|
34711
|
+
cancel: () => { },
|
|
34712
|
+
});
|
|
34713
|
+
return state;
|
|
34714
|
+
}
|
|
34715
|
+
class DOMDndHelper {
|
|
34716
|
+
draggedItemId;
|
|
34717
|
+
items;
|
|
34718
|
+
container;
|
|
34719
|
+
initialMousePosition;
|
|
34720
|
+
currentMousePosition;
|
|
34721
|
+
initialScroll;
|
|
34722
|
+
minPosition;
|
|
34723
|
+
maxPosition;
|
|
34724
|
+
edgeScrollIntervalId;
|
|
34725
|
+
onChange;
|
|
34726
|
+
onCancel;
|
|
34727
|
+
onDragEnd;
|
|
34728
|
+
/**
|
|
34729
|
+
* The dead zone is an area in which the pointermove events are ignored.
|
|
34730
|
+
*
|
|
34731
|
+
* This is useful when swapping the dragged item with a larger item. After the swap,
|
|
34732
|
+
* the mouse is still hovering on the item we just swapped with. In this case, we don't want
|
|
34733
|
+
* a mouse move to trigger another swap the other way around, so we create a dead zone. We will clear
|
|
34734
|
+
* the dead zone when the mouse leaves the swapped item.
|
|
34735
|
+
*/
|
|
34736
|
+
deadZone;
|
|
34737
|
+
constructor(args) {
|
|
34738
|
+
this.items = args.items.map((item) => ({ ...item, positionAtStart: item.position }));
|
|
34739
|
+
this.draggedItemId = args.draggedItemId;
|
|
34740
|
+
this.container = args.container;
|
|
34741
|
+
this.onChange = args.onChange;
|
|
34742
|
+
this.onCancel = args.onCancel;
|
|
34743
|
+
this.onDragEnd = args.onDragEnd;
|
|
34744
|
+
this.initialMousePosition = args.initialMousePosition;
|
|
34745
|
+
this.currentMousePosition = args.initialMousePosition;
|
|
34746
|
+
this.initialScroll = this.container.scroll;
|
|
34747
|
+
this.minPosition = this.items[0].position;
|
|
34748
|
+
this.maxPosition =
|
|
34749
|
+
this.items[this.items.length - 1].position + this.items[this.items.length - 1].size;
|
|
34750
|
+
}
|
|
34751
|
+
getItemStyles() {
|
|
34752
|
+
const styles = {};
|
|
34753
|
+
for (const item of this.items) {
|
|
34754
|
+
styles[item.id] = this.getItemStyle(item.id);
|
|
34755
|
+
}
|
|
34756
|
+
return styles;
|
|
34757
|
+
}
|
|
34758
|
+
getItemStyle(itemId) {
|
|
34759
|
+
const position = this.container.cssPositionProperty;
|
|
34760
|
+
const style = {};
|
|
34761
|
+
style.position = "relative";
|
|
34762
|
+
style[position] = (this.getItemsPositions()[itemId] || 0) + "px";
|
|
34763
|
+
style.transition = `${position} 0.5s`;
|
|
34764
|
+
style["pointer-events"] = "none";
|
|
34765
|
+
if (this.draggedItemId === itemId) {
|
|
34766
|
+
style.transition = `${position} 0s`;
|
|
34767
|
+
style["z-index"] = "1000";
|
|
34768
|
+
}
|
|
34769
|
+
return cssPropertiesToCss(style);
|
|
34770
|
+
}
|
|
34771
|
+
onScroll() {
|
|
34772
|
+
this.moveDraggedItemToPosition(this.currentMousePosition + this.scrollOffset);
|
|
34773
|
+
}
|
|
34774
|
+
onMouseMove(ev) {
|
|
34775
|
+
if (ev.button > 1) {
|
|
34776
|
+
this.onCancel();
|
|
34777
|
+
return;
|
|
34778
|
+
}
|
|
34779
|
+
const mousePosition = this.container.getMousePosition(ev);
|
|
34780
|
+
this.currentMousePosition = mousePosition;
|
|
34781
|
+
if (mousePosition < this.container.start || mousePosition > this.container.end) {
|
|
34782
|
+
this.startEdgeScroll(mousePosition < this.container.start ? -1 : 1);
|
|
34783
|
+
return;
|
|
34784
|
+
}
|
|
34785
|
+
else {
|
|
34786
|
+
this.stopEdgeScroll();
|
|
34787
|
+
}
|
|
34788
|
+
this.moveDraggedItemToPosition(mousePosition + this.scrollOffset);
|
|
34789
|
+
}
|
|
34790
|
+
moveDraggedItemToPosition(position) {
|
|
34791
|
+
const hoveredItemIndex = this.getHoveredItemIndex(position, this.items);
|
|
34792
|
+
const draggedItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
|
|
34793
|
+
const draggedItem = this.items[draggedItemIndex];
|
|
34794
|
+
if (this.deadZone && this.isInZone(position, this.deadZone)) {
|
|
34795
|
+
this.onChange(this.getItemsPositions());
|
|
34796
|
+
return;
|
|
34797
|
+
}
|
|
34798
|
+
else if (this.isInZone(position, {
|
|
34799
|
+
start: draggedItem.position,
|
|
34800
|
+
end: draggedItem.position + draggedItem.size,
|
|
34801
|
+
})) {
|
|
34802
|
+
this.deadZone = undefined;
|
|
34803
|
+
}
|
|
34804
|
+
if (draggedItemIndex === hoveredItemIndex) {
|
|
34805
|
+
this.onChange(this.getItemsPositions());
|
|
34806
|
+
return;
|
|
34807
|
+
}
|
|
34808
|
+
const startIndex = Math.min(draggedItemIndex, hoveredItemIndex);
|
|
34809
|
+
const endIndex = Math.max(draggedItemIndex, hoveredItemIndex);
|
|
34810
|
+
const direction = Math.sign(hoveredItemIndex - draggedItemIndex);
|
|
34811
|
+
let draggedItemMoveSize = 0;
|
|
34812
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
34813
|
+
if (i === draggedItemIndex) {
|
|
34814
|
+
continue;
|
|
34815
|
+
}
|
|
34816
|
+
this.items[i].position -= direction * draggedItem.size;
|
|
34817
|
+
draggedItemMoveSize += this.items[i].size;
|
|
34818
|
+
}
|
|
34819
|
+
draggedItem.position += direction * draggedItemMoveSize;
|
|
34820
|
+
this.items.sort((item1, item2) => item1.position - item2.position);
|
|
34821
|
+
this.deadZone =
|
|
34822
|
+
direction > 0
|
|
34823
|
+
? { start: position, end: draggedItem.position }
|
|
34824
|
+
: { start: draggedItem.position + draggedItem.size, end: position };
|
|
34825
|
+
this.onChange(this.getItemsPositions());
|
|
34826
|
+
}
|
|
34827
|
+
onMouseUp(ev) {
|
|
34828
|
+
if (ev.button !== 0) {
|
|
34829
|
+
this.onCancel();
|
|
34830
|
+
}
|
|
34831
|
+
ev.stopPropagation();
|
|
34832
|
+
ev.preventDefault();
|
|
34833
|
+
const targetItemIndex = this.items.findIndex((item) => item.id === this.draggedItemId);
|
|
34834
|
+
this.onDragEnd(this.draggedItemId, targetItemIndex);
|
|
34835
|
+
this.stopEdgeScroll();
|
|
34836
|
+
return false;
|
|
34837
|
+
}
|
|
34838
|
+
startEdgeScroll(direction) {
|
|
34839
|
+
if (this.edgeScrollIntervalId)
|
|
34840
|
+
return;
|
|
34841
|
+
this.edgeScrollIntervalId = window.setInterval(() => {
|
|
34842
|
+
const offset = direction * 3;
|
|
34843
|
+
this.container.scroll += offset;
|
|
34844
|
+
}, 5);
|
|
34845
|
+
}
|
|
34846
|
+
stopEdgeScroll() {
|
|
34847
|
+
window.clearInterval(this.edgeScrollIntervalId);
|
|
34848
|
+
this.edgeScrollIntervalId = undefined;
|
|
34849
|
+
}
|
|
34850
|
+
/**
|
|
34851
|
+
* Get the index of the item the given mouse position is inside.
|
|
34852
|
+
* If the mouse is outside the container, return the first or last item index.
|
|
34853
|
+
*/
|
|
33916
34854
|
getHoveredItemIndex(mousePosition, items) {
|
|
33917
34855
|
if (mousePosition <= this.minPosition)
|
|
33918
34856
|
return 0;
|
|
@@ -34571,12 +35509,12 @@ class SelectionInput extends Component {
|
|
|
34571
35509
|
|
|
34572
35510
|
class ValueInRangeCriterionForm extends CriterionForm {
|
|
34573
35511
|
static template = "o-spreadsheet-ValueInRangeCriterionForm";
|
|
34574
|
-
static components = { SelectionInput };
|
|
35512
|
+
static components = { RoundColorPicker, SelectionInput };
|
|
34575
35513
|
setup() {
|
|
34576
35514
|
super.setup();
|
|
34577
35515
|
const setupDefault = (props) => {
|
|
34578
35516
|
if (props.criterion.displayStyle === undefined) {
|
|
34579
|
-
this.updateCriterion({ displayStyle: "
|
|
35517
|
+
this.updateCriterion({ displayStyle: "chip" });
|
|
34580
35518
|
}
|
|
34581
35519
|
};
|
|
34582
35520
|
onWillUpdateProps(setupDefault);
|
|
@@ -34589,6 +35527,16 @@ class ValueInRangeCriterionForm extends CriterionForm {
|
|
|
34589
35527
|
const displayStyle = ev.target.value;
|
|
34590
35528
|
this.updateCriterion({ displayStyle });
|
|
34591
35529
|
}
|
|
35530
|
+
onColorChanged(color, value) {
|
|
35531
|
+
const colors = { ...this.props.criterion.colors };
|
|
35532
|
+
colors[value] = color || undefined;
|
|
35533
|
+
this.updateCriterion({ colors });
|
|
35534
|
+
}
|
|
35535
|
+
get values() {
|
|
35536
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
35537
|
+
const values = this.env.model.getters.getDataValidationRangeValues(sheetId, this.props.criterion);
|
|
35538
|
+
return new Set(values);
|
|
35539
|
+
}
|
|
34592
35540
|
}
|
|
34593
35541
|
|
|
34594
35542
|
const criterionCategoriesSequences = {
|
|
@@ -36092,6 +37040,7 @@ css /* scss */ `
|
|
|
36092
37040
|
// We need here the svg of the icons that we need to convert to images for the renderer
|
|
36093
37041
|
// -----------------------------------------------------------------------------
|
|
36094
37042
|
const ARROW_DOWN = {
|
|
37043
|
+
name: "ARROW_DOWN",
|
|
36095
37044
|
width: 448,
|
|
36096
37045
|
height: 512,
|
|
36097
37046
|
paths: [
|
|
@@ -36102,6 +37051,7 @@ const ARROW_DOWN = {
|
|
|
36102
37051
|
],
|
|
36103
37052
|
};
|
|
36104
37053
|
const ARROW_UP = {
|
|
37054
|
+
name: "ARROW_UP",
|
|
36105
37055
|
width: 448,
|
|
36106
37056
|
height: 512,
|
|
36107
37057
|
paths: [
|
|
@@ -36112,6 +37062,7 @@ const ARROW_UP = {
|
|
|
36112
37062
|
],
|
|
36113
37063
|
};
|
|
36114
37064
|
const ARROW_RIGHT = {
|
|
37065
|
+
name: "ARROW_RIGHT",
|
|
36115
37066
|
width: 448,
|
|
36116
37067
|
height: 512,
|
|
36117
37068
|
paths: [
|
|
@@ -36122,6 +37073,7 @@ const ARROW_RIGHT = {
|
|
|
36122
37073
|
],
|
|
36123
37074
|
};
|
|
36124
37075
|
const SMILE = {
|
|
37076
|
+
name: "SMILE",
|
|
36125
37077
|
width: 496,
|
|
36126
37078
|
height: 512,
|
|
36127
37079
|
paths: [
|
|
@@ -36132,6 +37084,7 @@ const SMILE = {
|
|
|
36132
37084
|
],
|
|
36133
37085
|
};
|
|
36134
37086
|
const MEH = {
|
|
37087
|
+
name: "MEH",
|
|
36135
37088
|
width: 496,
|
|
36136
37089
|
height: 512,
|
|
36137
37090
|
paths: [
|
|
@@ -36142,6 +37095,7 @@ const MEH = {
|
|
|
36142
37095
|
],
|
|
36143
37096
|
};
|
|
36144
37097
|
const FROWN = {
|
|
37098
|
+
name: "FROWN",
|
|
36145
37099
|
width: 496,
|
|
36146
37100
|
height: 512,
|
|
36147
37101
|
paths: [
|
|
@@ -36153,44 +37107,79 @@ const FROWN = {
|
|
|
36153
37107
|
};
|
|
36154
37108
|
const DOT_PATH = "M256 9 a247 247 0 1 0.1 0 0";
|
|
36155
37109
|
const GREEN_DOT = {
|
|
37110
|
+
name: "GREEN_DOT",
|
|
36156
37111
|
width: 512,
|
|
36157
37112
|
height: 512,
|
|
36158
37113
|
paths: [{ fillColor: "#6AA84F", path: DOT_PATH }],
|
|
36159
37114
|
};
|
|
36160
37115
|
const YELLOW_DOT = {
|
|
37116
|
+
name: "YELLOW_DOT",
|
|
36161
37117
|
width: 512,
|
|
36162
37118
|
height: 512,
|
|
36163
37119
|
paths: [{ fillColor: "#F0AD4E", path: DOT_PATH }],
|
|
36164
37120
|
};
|
|
36165
37121
|
const RED_DOT = {
|
|
37122
|
+
name: "RED_DOT",
|
|
36166
37123
|
width: 512,
|
|
36167
37124
|
height: 512,
|
|
36168
37125
|
paths: [{ fillColor: "#E06666", path: DOT_PATH }],
|
|
36169
37126
|
};
|
|
36170
|
-
|
|
36171
|
-
|
|
36172
|
-
|
|
36173
|
-
|
|
36174
|
-
|
|
36175
|
-
|
|
36176
|
-
|
|
36177
|
-
|
|
36178
|
-
|
|
36179
|
-
|
|
36180
|
-
|
|
36181
|
-
|
|
36182
|
-
|
|
37127
|
+
function getCaretDownSvg(color) {
|
|
37128
|
+
return {
|
|
37129
|
+
name: "CARET_DOWN",
|
|
37130
|
+
width: 512,
|
|
37131
|
+
height: 512,
|
|
37132
|
+
paths: [{ fillColor: color.textColor || TEXT_BODY_MUTED, path: "M120 195 h270 l-135 130" }],
|
|
37133
|
+
};
|
|
37134
|
+
}
|
|
37135
|
+
function getHoveredCaretDownSvg(color) {
|
|
37136
|
+
return {
|
|
37137
|
+
name: "CARET_DOWN",
|
|
37138
|
+
width: 512,
|
|
37139
|
+
height: 512,
|
|
37140
|
+
paths: [
|
|
37141
|
+
{ fillColor: color.textColor || TEXT_BODY_MUTED, path: "M15 15 h482 v482 h-482" },
|
|
37142
|
+
{ fillColor: color.fillColor || "#fff", path: "M120 195 h270 l-135 130" },
|
|
37143
|
+
],
|
|
37144
|
+
};
|
|
37145
|
+
}
|
|
37146
|
+
const CHIP_CARET_DOWN_PATH = "M40 185 h270 l-135 128";
|
|
37147
|
+
function getChipSvg(chipStyle) {
|
|
37148
|
+
return {
|
|
37149
|
+
name: "CHIP",
|
|
37150
|
+
width: 512,
|
|
37151
|
+
height: 512,
|
|
37152
|
+
paths: [{ fillColor: chipStyle.textColor || TEXT_BODY_MUTED, path: CHIP_CARET_DOWN_PATH }],
|
|
37153
|
+
};
|
|
37154
|
+
}
|
|
37155
|
+
function getHoveredChipSvg(chipStyle) {
|
|
37156
|
+
return {
|
|
37157
|
+
name: "CHIP",
|
|
37158
|
+
width: 512,
|
|
37159
|
+
height: 512,
|
|
37160
|
+
paths: [
|
|
37161
|
+
{
|
|
37162
|
+
fillColor: chipStyle.textColor || TEXT_BODY_MUTED,
|
|
37163
|
+
path: "M0,225 A175,175 0 1,0 350,225 A175,175 0 1,0 0,225",
|
|
37164
|
+
},
|
|
37165
|
+
{ fillColor: chipStyle.fillColor || TEXT_BODY_MUTED, path: CHIP_CARET_DOWN_PATH },
|
|
37166
|
+
],
|
|
37167
|
+
};
|
|
37168
|
+
}
|
|
36183
37169
|
const CHECKBOX_UNCHECKED = {
|
|
37170
|
+
name: "CHECKBOX_UNCHECKED",
|
|
36184
37171
|
width: 512,
|
|
36185
37172
|
height: 512,
|
|
36186
37173
|
paths: [{ fillColor: GRAY_300, path: "M45,45 h422 v422 h-422 v-422 m30,30 v362 h362 v-362" }],
|
|
36187
37174
|
};
|
|
36188
37175
|
const CHECKBOX_UNCHECKED_HOVERED = {
|
|
37176
|
+
name: "CHECKBOX_UNCHECKED",
|
|
36189
37177
|
width: 512,
|
|
36190
37178
|
height: 512,
|
|
36191
37179
|
paths: [{ fillColor: ACTION_COLOR, path: "M45,45 h422 v422 h-422 v-422 m30,30 v362 h362 v-362" }],
|
|
36192
37180
|
};
|
|
36193
37181
|
const CHECKBOX_CHECKED = {
|
|
37182
|
+
name: "CHECKBOX_CHECKED",
|
|
36194
37183
|
width: 512,
|
|
36195
37184
|
height: 512,
|
|
36196
37185
|
paths: [
|
|
@@ -36203,6 +37192,7 @@ function getPivotIconSvg(isCollapsed, isHovered) {
|
|
|
36203
37192
|
? "M149,235 h213 v43 h-213 M235,149 h43 v213 h-43" // +
|
|
36204
37193
|
: "M149,235 h213 v43 h-213"; // -
|
|
36205
37194
|
return {
|
|
37195
|
+
name: "PIVOT_ICON",
|
|
36206
37196
|
width: 512,
|
|
36207
37197
|
height: 512,
|
|
36208
37198
|
paths: [
|
|
@@ -36229,6 +37219,7 @@ function getDataFilterIcon(isActive, isHighContrast, isHovered) {
|
|
|
36229
37219
|
colors.hoverBackgroundColor = "#fff";
|
|
36230
37220
|
}
|
|
36231
37221
|
return {
|
|
37222
|
+
name: "DATA_FILTER_ICON",
|
|
36232
37223
|
width: isActive ? 24 : 850,
|
|
36233
37224
|
height: isActive ? 24 : 850,
|
|
36234
37225
|
paths: [
|
|
@@ -40424,6 +41415,23 @@ migrationStepRegistry
|
|
|
40424
41415
|
}
|
|
40425
41416
|
return data;
|
|
40426
41417
|
},
|
|
41418
|
+
})
|
|
41419
|
+
.add("18.4.3", {
|
|
41420
|
+
migrate(data) {
|
|
41421
|
+
if (!data.pivots) {
|
|
41422
|
+
return data;
|
|
41423
|
+
}
|
|
41424
|
+
for (const pivotId in data.pivots) {
|
|
41425
|
+
const pivot = data.pivots[pivotId];
|
|
41426
|
+
if (pivot.sortedColumn) {
|
|
41427
|
+
const measure = pivot.measures.find((measure) => measure.fieldName === pivot.sortedColumn?.measure);
|
|
41428
|
+
if (measure) {
|
|
41429
|
+
pivot.sortedColumn.measure = measure.id;
|
|
41430
|
+
}
|
|
41431
|
+
}
|
|
41432
|
+
}
|
|
41433
|
+
return data;
|
|
41434
|
+
},
|
|
40427
41435
|
});
|
|
40428
41436
|
function fixOverlappingFilters(data) {
|
|
40429
41437
|
for (const sheet of data.sheets || []) {
|
|
@@ -41087,6 +42095,10 @@ const REMOVE_ROWS_ACTION = (env) => {
|
|
|
41087
42095
|
});
|
|
41088
42096
|
};
|
|
41089
42097
|
const CAN_REMOVE_COLUMNS_ROWS = (dimension, env) => {
|
|
42098
|
+
if ((dimension === "COL" && env.model.getters.getActiveRows().size > 0) ||
|
|
42099
|
+
(dimension === "ROW" && env.model.getters.getActiveCols().size > 0)) {
|
|
42100
|
+
return false;
|
|
42101
|
+
}
|
|
41090
42102
|
const sheetId = env.model.getters.getActiveSheetId();
|
|
41091
42103
|
const selectedElements = env.model.getters.getElementsFromSelection(dimension);
|
|
41092
42104
|
const includesAllVisibleHeaders = env.model.getters.checkElementsIncludeAllVisibleHeaders(sheetId, dimension, selectedElements);
|
|
@@ -42641,7 +43653,7 @@ const insertDropdown = {
|
|
|
42641
43653
|
criterion: {
|
|
42642
43654
|
type: "isValueInList",
|
|
42643
43655
|
values: [],
|
|
42644
|
-
displayStyle: "
|
|
43656
|
+
displayStyle: "chip",
|
|
42645
43657
|
},
|
|
42646
43658
|
},
|
|
42647
43659
|
});
|
|
@@ -44556,6 +45568,11 @@ class GridComposer extends Component {
|
|
|
44556
45568
|
rect = this.defaultRect;
|
|
44557
45569
|
isEditing = false;
|
|
44558
45570
|
isCellReferenceVisible = false;
|
|
45571
|
+
currentEditedCell = {
|
|
45572
|
+
col: 0,
|
|
45573
|
+
row: 0,
|
|
45574
|
+
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
45575
|
+
};
|
|
44559
45576
|
composerStore;
|
|
44560
45577
|
composerFocusStore;
|
|
44561
45578
|
composerInterface;
|
|
@@ -44680,12 +45697,17 @@ class GridComposer extends Component {
|
|
|
44680
45697
|
if (!isEditing && this.composerFocusStore.activeComposer !== this.composerInterface) {
|
|
44681
45698
|
this.composerFocusStore.focusComposer(this.composerInterface, { focusMode: "inactive" });
|
|
44682
45699
|
}
|
|
45700
|
+
let shouldRecomputeRect = !deepEquals(this.currentEditedCell, this.composerStore.currentEditedCell);
|
|
44683
45701
|
if (this.isEditing !== isEditing) {
|
|
44684
45702
|
this.isEditing = isEditing;
|
|
44685
45703
|
if (!isEditing) {
|
|
44686
45704
|
this.rect = this.defaultRect;
|
|
44687
45705
|
return;
|
|
44688
45706
|
}
|
|
45707
|
+
this.currentEditedCell = this.composerStore.currentEditedCell;
|
|
45708
|
+
shouldRecomputeRect = true;
|
|
45709
|
+
}
|
|
45710
|
+
if (shouldRecomputeRect) {
|
|
44689
45711
|
const position = this.env.model.getters.getActivePosition();
|
|
44690
45712
|
const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
|
|
44691
45713
|
this.rect = this.env.model.getters.getVisibleRect(zone);
|
|
@@ -45476,15 +46498,16 @@ function useHoveredElement(ref) {
|
|
|
45476
46498
|
return state;
|
|
45477
46499
|
}
|
|
45478
46500
|
|
|
46501
|
+
const PAINT_FORMAT_HANDLER_KEYS = [
|
|
46502
|
+
"cell",
|
|
46503
|
+
"border",
|
|
46504
|
+
"table",
|
|
46505
|
+
"conditionalFormat",
|
|
46506
|
+
"merge",
|
|
46507
|
+
];
|
|
45479
46508
|
class PaintFormatStore extends SpreadsheetStore {
|
|
45480
46509
|
mutators = ["activate", "cancel", "pasteFormat"];
|
|
45481
46510
|
highlightStore = this.get(HighlightStore);
|
|
45482
|
-
clipboardHandlers = [
|
|
45483
|
-
new CellClipboardHandler(this.getters, this.model.dispatch),
|
|
45484
|
-
new BorderClipboardHandler(this.getters, this.model.dispatch),
|
|
45485
|
-
new TableClipboardHandler(this.getters, this.model.dispatch),
|
|
45486
|
-
new ConditionalFormatClipboardHandler(this.getters, this.model.dispatch),
|
|
45487
|
-
];
|
|
45488
46511
|
status = "inactive";
|
|
45489
46512
|
copiedData;
|
|
45490
46513
|
constructor(get) {
|
|
@@ -45515,24 +46538,38 @@ class PaintFormatStore extends SpreadsheetStore {
|
|
|
45515
46538
|
get isActive() {
|
|
45516
46539
|
return this.status !== "inactive";
|
|
45517
46540
|
}
|
|
46541
|
+
get clipboardHandlers() {
|
|
46542
|
+
return PAINT_FORMAT_HANDLER_KEYS.map((handlerName) => {
|
|
46543
|
+
const HandlerClass = clipboardHandlersRegistries.cellHandlers.get(handlerName);
|
|
46544
|
+
return {
|
|
46545
|
+
handlerName,
|
|
46546
|
+
handler: new HandlerClass(this.getters, this.model.dispatch),
|
|
46547
|
+
};
|
|
46548
|
+
});
|
|
46549
|
+
}
|
|
45518
46550
|
copyFormats() {
|
|
45519
46551
|
const sheetId = this.getters.getActiveSheetId();
|
|
45520
46552
|
const zones = this.getters.getSelectedZones();
|
|
45521
|
-
const copiedData = {};
|
|
45522
|
-
for (const handler of this.clipboardHandlers) {
|
|
45523
|
-
|
|
46553
|
+
const copiedData = { zones, sheetId };
|
|
46554
|
+
for (const { handlerName, handler } of this.clipboardHandlers) {
|
|
46555
|
+
const handlerResult = handler.copy(getClipboardDataPositions(sheetId, zones), false);
|
|
46556
|
+
if (handlerResult !== undefined) {
|
|
46557
|
+
copiedData[handlerName] = handlerResult;
|
|
46558
|
+
}
|
|
45524
46559
|
}
|
|
45525
46560
|
return copiedData;
|
|
45526
46561
|
}
|
|
45527
46562
|
paintFormat(sheetId, target) {
|
|
45528
|
-
if (this.copiedData) {
|
|
45529
|
-
|
|
45530
|
-
handler.paste({ zones: target, sheetId }, this.copiedData, {
|
|
45531
|
-
isCutOperation: false,
|
|
45532
|
-
pasteOption: "onlyFormat",
|
|
45533
|
-
});
|
|
45534
|
-
}
|
|
46563
|
+
if (!this.copiedData) {
|
|
46564
|
+
return;
|
|
45535
46565
|
}
|
|
46566
|
+
const options = {
|
|
46567
|
+
isCutOperation: false,
|
|
46568
|
+
pasteOption: "onlyFormat",
|
|
46569
|
+
};
|
|
46570
|
+
const { target: pasteTarget, selectedZones } = getPasteTargetFromHandlers(sheetId, target, this.copiedData, this.clipboardHandlers, options);
|
|
46571
|
+
applyClipboardHandlersPaste(this.clipboardHandlers, this.copiedData, pasteTarget, options);
|
|
46572
|
+
selectPastedZone(this.model.selection, target, selectedZones);
|
|
45536
46573
|
if (this.status === "oneOff") {
|
|
45537
46574
|
this.cancel();
|
|
45538
46575
|
}
|
|
@@ -45579,12 +46616,8 @@ class HoveredTableStore extends SpreadsheetStore {
|
|
|
45579
46616
|
this.row = undefined;
|
|
45580
46617
|
}
|
|
45581
46618
|
computeOverlay() {
|
|
45582
|
-
if (!this.getters.isDashboard()) {
|
|
45583
|
-
return;
|
|
45584
|
-
}
|
|
45585
46619
|
this.overlayColors = new PositionMap();
|
|
45586
|
-
const col = this
|
|
45587
|
-
const row = this.row;
|
|
46620
|
+
const { col, row } = this;
|
|
45588
46621
|
if (col === undefined || row === undefined) {
|
|
45589
46622
|
return;
|
|
45590
46623
|
}
|
|
@@ -45593,9 +46626,16 @@ class HoveredTableStore extends SpreadsheetStore {
|
|
|
45593
46626
|
if (!table) {
|
|
45594
46627
|
return;
|
|
45595
46628
|
}
|
|
45596
|
-
const { left, right } = table.range.zone;
|
|
45597
|
-
|
|
45598
|
-
|
|
46629
|
+
const { left, right, top } = table.range.zone;
|
|
46630
|
+
const isTableHeader = row < top + table.config.numberOfHeaders;
|
|
46631
|
+
const doesTableRowHaveContent = range(left, right + 1).some((col) => {
|
|
46632
|
+
return (!this.getters.isColHidden(sheetId, col) &&
|
|
46633
|
+
this.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
|
|
46634
|
+
});
|
|
46635
|
+
if (!isTableHeader && doesTableRowHaveContent) {
|
|
46636
|
+
for (let col = left; col <= right; col++) {
|
|
46637
|
+
this.overlayColors.set({ sheetId, col, row }, TABLE_HOVER_BACKGROUND_COLOR);
|
|
46638
|
+
}
|
|
45599
46639
|
}
|
|
45600
46640
|
}
|
|
45601
46641
|
}
|
|
@@ -45791,11 +46831,14 @@ class GridOverlay extends Component {
|
|
|
45791
46831
|
onCellClicked(ev) {
|
|
45792
46832
|
const openedPopover = this.cellPopovers.persistentCellPopover;
|
|
45793
46833
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
46834
|
+
const clickedIcon = this.getInteractiveIconAtEvent(ev);
|
|
46835
|
+
if (clickedIcon) {
|
|
46836
|
+
this.env.model.selection.getBackToDefault();
|
|
46837
|
+
}
|
|
45794
46838
|
this.props.onCellClicked(col, row, {
|
|
45795
46839
|
expandZone: ev.shiftKey,
|
|
45796
46840
|
addZone: isCtrlKey(ev),
|
|
45797
46841
|
}, ev);
|
|
45798
|
-
const clickedIcon = this.getInteractiveIconAtEvent(ev);
|
|
45799
46842
|
if (clickedIcon?.onClick) {
|
|
45800
46843
|
clickedIcon.onClick(clickedIcon.position, this.env);
|
|
45801
46844
|
}
|
|
@@ -45840,7 +46883,10 @@ class GridOverlay extends Component {
|
|
|
45840
46883
|
}
|
|
45841
46884
|
const icons = this.env.model.getters.getCellIcons(position);
|
|
45842
46885
|
const icon = icons.find((icon) => {
|
|
45843
|
-
|
|
46886
|
+
const merge = this.env.model.getters.getMerge(position);
|
|
46887
|
+
const zone = merge || positionToZone(position);
|
|
46888
|
+
const cellRect = this.env.model.getters.getRect(zone);
|
|
46889
|
+
return isPointInsideRect(x, y, this.env.model.getters.getCellIconRect(icon, cellRect));
|
|
45844
46890
|
});
|
|
45845
46891
|
return icon?.onClick ? icon : undefined;
|
|
45846
46892
|
}
|
|
@@ -46577,19 +47623,56 @@ class HeadersOverlay extends Component {
|
|
|
46577
47623
|
}
|
|
46578
47624
|
}
|
|
46579
47625
|
|
|
46580
|
-
|
|
46581
|
-
|
|
46582
|
-
renderer;
|
|
47626
|
+
const CELL_ANIMATION_DURATION = 200;
|
|
47627
|
+
class GridRenderer extends SpreadsheetStore {
|
|
46583
47628
|
fingerprints;
|
|
46584
47629
|
hoveredTables;
|
|
46585
47630
|
hoveredIcon;
|
|
47631
|
+
lastRenderBoxes = new Map();
|
|
47632
|
+
preventNewAnimationsInNextFrame = false;
|
|
47633
|
+
zonesWithPreventedAnimationsInNextFrame = [];
|
|
47634
|
+
animations = new Map();
|
|
46586
47635
|
constructor(get) {
|
|
47636
|
+
super(get);
|
|
46587
47637
|
this.getters = get(ModelStore).getters;
|
|
46588
|
-
this.renderer = get(RendererStore);
|
|
46589
47638
|
this.fingerprints = get(FormulaFingerprintStore);
|
|
46590
47639
|
this.hoveredTables = get(HoveredTableStore);
|
|
46591
47640
|
this.hoveredIcon = get(HoveredIconStore);
|
|
46592
|
-
|
|
47641
|
+
}
|
|
47642
|
+
handle(cmd) {
|
|
47643
|
+
switch (cmd.type) {
|
|
47644
|
+
case "START":
|
|
47645
|
+
case "ACTIVATE_SHEET":
|
|
47646
|
+
case "ADD_COLUMNS_ROWS":
|
|
47647
|
+
case "REMOVE_COLUMNS_ROWS":
|
|
47648
|
+
this.animations.clear();
|
|
47649
|
+
this.preventNewAnimationsInNextFrame = true;
|
|
47650
|
+
break;
|
|
47651
|
+
case "RESIZE_COLUMNS_ROWS":
|
|
47652
|
+
this.preventNewAnimationsInNextFrame = true;
|
|
47653
|
+
break;
|
|
47654
|
+
case "REDO":
|
|
47655
|
+
this.zonesWithPreventedAnimationsInNextFrame = [];
|
|
47656
|
+
break;
|
|
47657
|
+
case "UNDO":
|
|
47658
|
+
for (const command of cmd.commands) {
|
|
47659
|
+
if (command.type === "ADD_COLUMNS_ROWS" ||
|
|
47660
|
+
command.type === "REMOVE_COLUMNS_ROWS" ||
|
|
47661
|
+
command.type === "RESIZE_COLUMNS_ROWS") {
|
|
47662
|
+
this.animations.clear();
|
|
47663
|
+
this.preventNewAnimationsInNextFrame = true;
|
|
47664
|
+
break;
|
|
47665
|
+
}
|
|
47666
|
+
}
|
|
47667
|
+
break;
|
|
47668
|
+
case "PASTE":
|
|
47669
|
+
this.zonesWithPreventedAnimationsInNextFrame.push(...this.getters.getSelectedZones());
|
|
47670
|
+
break;
|
|
47671
|
+
case "UPDATE_CELL":
|
|
47672
|
+
const zones = this.getters.getCommandZones(cmd);
|
|
47673
|
+
this.zonesWithPreventedAnimationsInNextFrame.push(...zones);
|
|
47674
|
+
break;
|
|
47675
|
+
}
|
|
46593
47676
|
}
|
|
46594
47677
|
get renderingLayers() {
|
|
46595
47678
|
return ["Background", "Headers"];
|
|
@@ -46597,17 +47680,20 @@ class GridRenderer {
|
|
|
46597
47680
|
// ---------------------------------------------------------------------------
|
|
46598
47681
|
// Grid rendering
|
|
46599
47682
|
// ---------------------------------------------------------------------------
|
|
46600
|
-
drawLayer(renderingContext, layer) {
|
|
47683
|
+
drawLayer(renderingContext, layer, timeStamp) {
|
|
46601
47684
|
switch (layer) {
|
|
46602
47685
|
case "Background":
|
|
46603
47686
|
this.drawGlobalBackground(renderingContext);
|
|
47687
|
+
const oldBoxes = this.lastRenderBoxes;
|
|
47688
|
+
this.lastRenderBoxes = new Map();
|
|
46604
47689
|
for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
|
|
46605
47690
|
const { ctx } = renderingContext;
|
|
46606
47691
|
ctx.save();
|
|
46607
47692
|
ctx.beginPath();
|
|
46608
47693
|
ctx.rect(rect.x, rect.y, rect.width, rect.height);
|
|
46609
47694
|
ctx.clip();
|
|
46610
|
-
const
|
|
47695
|
+
const boxesWithoutAnimations = this.getGridBoxes(zone);
|
|
47696
|
+
const boxes = this.getBoxesWithAnimations(boxesWithoutAnimations, oldBoxes, timeStamp);
|
|
46611
47697
|
this.drawBackground(renderingContext, boxes);
|
|
46612
47698
|
this.drawOverflowingCellBackground(renderingContext, boxes);
|
|
46613
47699
|
this.drawCellBackground(renderingContext, boxes);
|
|
@@ -46617,6 +47703,8 @@ class GridRenderer {
|
|
|
46617
47703
|
ctx.restore();
|
|
46618
47704
|
}
|
|
46619
47705
|
this.drawFrozenPanes(renderingContext);
|
|
47706
|
+
this.preventNewAnimationsInNextFrame = false;
|
|
47707
|
+
this.zonesWithPreventedAnimationsInNextFrame = [];
|
|
46620
47708
|
break;
|
|
46621
47709
|
case "Headers":
|
|
46622
47710
|
if (!this.getters.isDashboard()) {
|
|
@@ -46640,6 +47728,8 @@ class GridRenderer {
|
|
|
46640
47728
|
const inset = areGridLinesVisible ? 0.1 * thinLineWidth : 0;
|
|
46641
47729
|
if (areGridLinesVisible) {
|
|
46642
47730
|
for (const box of boxes) {
|
|
47731
|
+
if (box.skipCellGridLines)
|
|
47732
|
+
continue;
|
|
46643
47733
|
ctx.strokeStyle = CELL_BORDER_COLOR;
|
|
46644
47734
|
ctx.lineWidth = thinLineWidth;
|
|
46645
47735
|
ctx.strokeRect(box.x + inset, box.y + inset, box.width - 2 * inset, box.height - 2 * inset);
|
|
@@ -46660,6 +47750,19 @@ class GridRenderer {
|
|
|
46660
47750
|
const width = box.width * (percentage / 100);
|
|
46661
47751
|
ctx.fillRect(box.x, box.y, width, box.height);
|
|
46662
47752
|
}
|
|
47753
|
+
if (box?.chip) {
|
|
47754
|
+
ctx.save();
|
|
47755
|
+
ctx.beginPath();
|
|
47756
|
+
ctx.rect(box.x, box.y, box.width, box.height);
|
|
47757
|
+
ctx.clip();
|
|
47758
|
+
const chip = box.chip;
|
|
47759
|
+
ctx.fillStyle = chip.color;
|
|
47760
|
+
const radius = 10;
|
|
47761
|
+
ctx.beginPath();
|
|
47762
|
+
ctx.roundRect(chip.x, chip.y, chip.width, chip.height, radius);
|
|
47763
|
+
ctx.fill();
|
|
47764
|
+
ctx.restore();
|
|
47765
|
+
}
|
|
46663
47766
|
if (box.overlayColor) {
|
|
46664
47767
|
ctx.fillStyle = box.overlayColor;
|
|
46665
47768
|
ctx.fillRect(box.x, box.y, box.width, box.height);
|
|
@@ -46734,7 +47837,8 @@ class GridRenderer {
|
|
|
46734
47837
|
* each line and adding 1 pixel to the end of each line (depending on the direction of the
|
|
46735
47838
|
* line).
|
|
46736
47839
|
*/
|
|
46737
|
-
function drawBorder({ style,
|
|
47840
|
+
function drawBorder({ color, style, opacity }, x1, y1, x2, y2) {
|
|
47841
|
+
ctx.globalAlpha = opacity ?? 1;
|
|
46738
47842
|
ctx.strokeStyle = color;
|
|
46739
47843
|
switch (style) {
|
|
46740
47844
|
case "medium":
|
|
@@ -46782,6 +47886,7 @@ class GridRenderer {
|
|
|
46782
47886
|
ctx.stroke();
|
|
46783
47887
|
ctx.lineWidth = 1;
|
|
46784
47888
|
ctx.setLineDash([]);
|
|
47889
|
+
ctx.globalAlpha = 1;
|
|
46785
47890
|
}
|
|
46786
47891
|
}
|
|
46787
47892
|
drawTexts(renderingContext, boxes) {
|
|
@@ -46790,6 +47895,7 @@ class GridRenderer {
|
|
|
46790
47895
|
let currentFont;
|
|
46791
47896
|
for (const box of boxes) {
|
|
46792
47897
|
if (box.content) {
|
|
47898
|
+
ctx.globalAlpha = box.textOpacity ?? 1;
|
|
46793
47899
|
const style = box.style || {};
|
|
46794
47900
|
const align = box.content.align || "left";
|
|
46795
47901
|
// compute font and textColor
|
|
@@ -46799,19 +47905,6 @@ class GridRenderer {
|
|
|
46799
47905
|
ctx.font = font;
|
|
46800
47906
|
}
|
|
46801
47907
|
ctx.fillStyle = style.textColor || "#000";
|
|
46802
|
-
// compute horizontal align start point parameter
|
|
46803
|
-
let x = box.x;
|
|
46804
|
-
if (align === "left") {
|
|
46805
|
-
const leftIconSize = box.icons.left ? box.icons.left.size + box.icons.left.margin : 0;
|
|
46806
|
-
x += MIN_CELL_TEXT_MARGIN + leftIconSize;
|
|
46807
|
-
}
|
|
46808
|
-
else if (align === "right") {
|
|
46809
|
-
const rightIconSize = box.icons.right ? box.icons.right.size + box.icons.right.margin : 0;
|
|
46810
|
-
x += box.width - MIN_CELL_TEXT_MARGIN - rightIconSize;
|
|
46811
|
-
}
|
|
46812
|
-
else {
|
|
46813
|
-
x += box.width / 2;
|
|
46814
|
-
}
|
|
46815
47908
|
// horizontal align text direction
|
|
46816
47909
|
ctx.textAlign = align;
|
|
46817
47910
|
// clip rect if needed
|
|
@@ -46822,19 +47915,18 @@ class GridRenderer {
|
|
|
46822
47915
|
ctx.rect(x, y, width, height);
|
|
46823
47916
|
ctx.clip();
|
|
46824
47917
|
}
|
|
46825
|
-
|
|
46826
|
-
|
|
46827
|
-
const numberOfLines = box.content.textLines.length;
|
|
46828
|
-
let y = this.getters.computeTextYCoordinate(box, textLineHeight, style.verticalAlign, numberOfLines);
|
|
47918
|
+
const x = box.content.x;
|
|
47919
|
+
let y = box.content.y;
|
|
46829
47920
|
// use the horizontal and the vertical start points to:
|
|
46830
47921
|
// fill text / fill strikethrough / fill underline
|
|
46831
47922
|
for (const brokenLine of box.content.textLines) {
|
|
46832
|
-
drawDecoratedText(ctx, brokenLine, { x
|
|
46833
|
-
y += MIN_CELL_TEXT_MARGIN +
|
|
47923
|
+
drawDecoratedText(ctx, brokenLine, { x, y }, style.underline, style.strikethrough);
|
|
47924
|
+
y += MIN_CELL_TEXT_MARGIN + box.content.fontSizePx;
|
|
46834
47925
|
}
|
|
46835
47926
|
if (box.clipRect) {
|
|
46836
47927
|
ctx.restore();
|
|
46837
47928
|
}
|
|
47929
|
+
ctx.globalAlpha = 1;
|
|
46838
47930
|
}
|
|
46839
47931
|
}
|
|
46840
47932
|
}
|
|
@@ -46852,10 +47944,11 @@ class GridRenderer {
|
|
|
46852
47944
|
}
|
|
46853
47945
|
ctx.save();
|
|
46854
47946
|
ctx.beginPath();
|
|
46855
|
-
|
|
47947
|
+
const clipRect = icon.clipRect || box;
|
|
47948
|
+
ctx.rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
|
|
46856
47949
|
ctx.clip();
|
|
46857
47950
|
const iconSize = icon.size;
|
|
46858
|
-
const { x, y } = this.getters.getCellIconRect(icon);
|
|
47951
|
+
const { x, y } = this.getters.getCellIconRect(icon, box);
|
|
46859
47952
|
ctx.translate(x, y);
|
|
46860
47953
|
ctx.scale(iconSize / svg.width, iconSize / svg.height);
|
|
46861
47954
|
for (const path of svg.paths) {
|
|
@@ -47064,12 +48157,15 @@ class GridRenderer {
|
|
|
47064
48157
|
const cell = this.getters.getEvaluatedCell(position);
|
|
47065
48158
|
const showFormula = this.getters.shouldShowFormulas();
|
|
47066
48159
|
const { x, y, width, height } = this.getters.getRect(zone);
|
|
47067
|
-
const
|
|
48160
|
+
const chipStyle = this.getters.getDataValidationChipStyle(position);
|
|
47068
48161
|
let style = this.getters.getCellComputedStyle(position);
|
|
47069
48162
|
if (this.fingerprints.isEnabled) {
|
|
47070
48163
|
const fingerprintColor = this.fingerprints.colors.get(position);
|
|
47071
48164
|
style = { ...style, fillColor: fingerprintColor };
|
|
47072
48165
|
}
|
|
48166
|
+
if (chipStyle?.textColor) {
|
|
48167
|
+
style = { ...style, textColor: chipStyle.textColor };
|
|
48168
|
+
}
|
|
47073
48169
|
const dataBarFill = this.fingerprints.isEnabled
|
|
47074
48170
|
? undefined
|
|
47075
48171
|
: this.getters.getConditionalDataBar(position);
|
|
@@ -47080,6 +48176,7 @@ class GridRenderer {
|
|
|
47080
48176
|
center: iconsList.find((icon) => icon?.horizontalAlign === "center"),
|
|
47081
48177
|
};
|
|
47082
48178
|
const box = {
|
|
48179
|
+
id: zoneToXc(zone),
|
|
47083
48180
|
x,
|
|
47084
48181
|
y,
|
|
47085
48182
|
width,
|
|
@@ -47087,11 +48184,11 @@ class GridRenderer {
|
|
|
47087
48184
|
border: this.getters.getCellComputedBorder(position) || undefined,
|
|
47088
48185
|
style,
|
|
47089
48186
|
dataBarFill,
|
|
47090
|
-
verticalAlign,
|
|
47091
48187
|
overlayColor: this.hoveredTables.overlayColors.get(position),
|
|
47092
48188
|
isError: (cell.type === CellValueType.error && !!cell.message) ||
|
|
47093
48189
|
this.getters.isDataValidationInvalid(position),
|
|
47094
48190
|
icons: cellIcons,
|
|
48191
|
+
disabledAnimation: this.zonesWithPreventedAnimationsInNextFrame.some((z) => isZoneInside(zone, z) || overlap(zone, z)),
|
|
47095
48192
|
};
|
|
47096
48193
|
const fontSizePX = computeTextFontSizeInPixels(box.style);
|
|
47097
48194
|
if (cell.type === CellValueType.empty || box.icons.center) {
|
|
@@ -47103,22 +48200,55 @@ class GridRenderer {
|
|
|
47103
48200
|
const maxWidth = width - 2 * MIN_CELL_TEXT_MARGIN;
|
|
47104
48201
|
const multiLineText = this.getters.getCellMultiLineText(position, { maxWidth, wrapText });
|
|
47105
48202
|
const textWidth = Math.max(...multiLineText.map((line) => this.getters.getTextWidth(line, style) + MIN_CELL_TEXT_MARGIN));
|
|
48203
|
+
const chipMargin = chipStyle ? DATA_VALIDATION_CHIP_MARGIN : 0;
|
|
47106
48204
|
const leftIconWidth = box.icons.left ? box.icons.left.size + box.icons.left.margin : 0;
|
|
48205
|
+
const leftMargin = leftIconWidth + chipMargin;
|
|
47107
48206
|
const rightIconWidth = box.icons.right ? box.icons.right.size + box.icons.right.margin : 0;
|
|
47108
|
-
const
|
|
48207
|
+
const rightMargin = rightIconWidth + chipMargin;
|
|
48208
|
+
const contentWidth = leftMargin + textWidth + rightMargin;
|
|
47109
48209
|
const align = this.computeCellAlignment(position, contentWidth > width);
|
|
48210
|
+
// compute vertical align start point parameter:
|
|
48211
|
+
const numberOfLines = multiLineText.length;
|
|
48212
|
+
const contentY = Math.round(this.getters.computeTextYCoordinate(box, fontSizePX, style.verticalAlign, numberOfLines));
|
|
48213
|
+
// compute horizontal align start point parameter
|
|
48214
|
+
let contentX = box.x;
|
|
48215
|
+
if (align === "left") {
|
|
48216
|
+
contentX += MIN_CELL_TEXT_MARGIN + leftMargin;
|
|
48217
|
+
}
|
|
48218
|
+
else if (align === "right") {
|
|
48219
|
+
contentX += box.width - MIN_CELL_TEXT_MARGIN - rightMargin;
|
|
48220
|
+
}
|
|
48221
|
+
else {
|
|
48222
|
+
contentX += box.width / 2;
|
|
48223
|
+
}
|
|
48224
|
+
contentX = Math.round(contentX);
|
|
48225
|
+
const textHeight = computeTextLinesHeight(fontSizePX, numberOfLines);
|
|
47110
48226
|
box.content = {
|
|
47111
48227
|
textLines: multiLineText,
|
|
47112
48228
|
width: wrapping === "overflow" ? textWidth : width,
|
|
47113
48229
|
align,
|
|
48230
|
+
x: contentX,
|
|
48231
|
+
y: contentY,
|
|
48232
|
+
fontSizePx: fontSizePX,
|
|
47114
48233
|
};
|
|
48234
|
+
if (chipStyle?.fillColor) {
|
|
48235
|
+
const chipMarginLeft = leftMargin;
|
|
48236
|
+
const chipMarginRight = DATA_VALIDATION_CHIP_MARGIN;
|
|
48237
|
+
box.chip = {
|
|
48238
|
+
color: chipStyle.fillColor,
|
|
48239
|
+
width: box.width - chipMarginLeft - chipMarginRight,
|
|
48240
|
+
height: textHeight + 2,
|
|
48241
|
+
x: box.x + chipMarginLeft,
|
|
48242
|
+
y: contentY - 2,
|
|
48243
|
+
};
|
|
48244
|
+
}
|
|
47115
48245
|
/** ClipRect */
|
|
47116
48246
|
const isOverflowing = contentWidth > width || fontSizePX > height;
|
|
47117
|
-
if (box.icons.left || box.icons.right) {
|
|
48247
|
+
if (box.icons.left || box.icons.right || box.chip) {
|
|
47118
48248
|
box.clipRect = {
|
|
47119
|
-
x: box.x +
|
|
48249
|
+
x: box.x + leftMargin,
|
|
47120
48250
|
y: box.y,
|
|
47121
|
-
width: Math.max(0, width -
|
|
48251
|
+
width: Math.max(0, width - leftMargin - rightMargin),
|
|
47122
48252
|
height,
|
|
47123
48253
|
};
|
|
47124
48254
|
}
|
|
@@ -47228,6 +48358,77 @@ class GridRenderer {
|
|
|
47228
48358
|
}
|
|
47229
48359
|
return boxes;
|
|
47230
48360
|
}
|
|
48361
|
+
getBoxesWithAnimations(boxes, oldBoxes, timeStamp) {
|
|
48362
|
+
this.updateAnimationsProgress(timeStamp);
|
|
48363
|
+
this.addNewAnimations(boxes, oldBoxes, timeStamp);
|
|
48364
|
+
if (this.animations.size > 0) {
|
|
48365
|
+
this.renderer.startAnimation("grid_renderer_animation");
|
|
48366
|
+
return this.updateBoxesWithAnimations(boxes);
|
|
48367
|
+
}
|
|
48368
|
+
else {
|
|
48369
|
+
this.renderer.stopAnimation("grid_renderer_animation");
|
|
48370
|
+
return boxes;
|
|
48371
|
+
}
|
|
48372
|
+
}
|
|
48373
|
+
updateBoxesWithAnimations(boxes) {
|
|
48374
|
+
const boxesWithAnimations = [];
|
|
48375
|
+
for (const box of boxes) {
|
|
48376
|
+
const animation = this.animations.get(box.id);
|
|
48377
|
+
if (!animation) {
|
|
48378
|
+
boxesWithAnimations.push(box);
|
|
48379
|
+
continue;
|
|
48380
|
+
}
|
|
48381
|
+
const animatedBox = deepCopy(box);
|
|
48382
|
+
boxesWithAnimations.push(animatedBox);
|
|
48383
|
+
for (const animationId of animation.animationTypes) {
|
|
48384
|
+
const animationItem = cellAnimationRegistry.get(animationId);
|
|
48385
|
+
const newBoxes = animationItem.updateAnimation(animation.progress, animatedBox, animation.oldBox, box);
|
|
48386
|
+
if (newBoxes) {
|
|
48387
|
+
boxesWithAnimations.push(...newBoxes.newBoxes);
|
|
48388
|
+
}
|
|
48389
|
+
}
|
|
48390
|
+
}
|
|
48391
|
+
return boxesWithAnimations;
|
|
48392
|
+
}
|
|
48393
|
+
updateAnimationsProgress(timeStamp) {
|
|
48394
|
+
if (timeStamp === undefined) {
|
|
48395
|
+
return;
|
|
48396
|
+
}
|
|
48397
|
+
for (const boxId of this.animations.keys()) {
|
|
48398
|
+
const animation = this.animations.get(boxId);
|
|
48399
|
+
if (animation.startTime === undefined) {
|
|
48400
|
+
animation.startTime = timeStamp;
|
|
48401
|
+
continue;
|
|
48402
|
+
}
|
|
48403
|
+
const elapsedTime = timeStamp - animation.startTime;
|
|
48404
|
+
const progress = Math.min(1, elapsedTime / CELL_ANIMATION_DURATION);
|
|
48405
|
+
if (progress >= 1) {
|
|
48406
|
+
this.animations.delete(boxId);
|
|
48407
|
+
}
|
|
48408
|
+
animation.progress = progress;
|
|
48409
|
+
}
|
|
48410
|
+
}
|
|
48411
|
+
addNewAnimations(boxes, oldBoxes, timeStamp) {
|
|
48412
|
+
for (const box of boxes) {
|
|
48413
|
+
this.lastRenderBoxes.set(box.id, box);
|
|
48414
|
+
const oldBox = oldBoxes.get(box.id);
|
|
48415
|
+
if (this.preventNewAnimationsInNextFrame || !oldBox || box.disabledAnimation) {
|
|
48416
|
+
continue;
|
|
48417
|
+
}
|
|
48418
|
+
const animationTypes = [];
|
|
48419
|
+
for (const animationItem of cellAnimationRegistry.getAll()) {
|
|
48420
|
+
if (animationItem.hasAnimation(oldBox, box)) {
|
|
48421
|
+
animationTypes.push(animationItem.id);
|
|
48422
|
+
}
|
|
48423
|
+
}
|
|
48424
|
+
const animation = animationTypes.length > 0
|
|
48425
|
+
? { animationTypes, oldBox, progress: 0, startTime: timeStamp }
|
|
48426
|
+
: undefined;
|
|
48427
|
+
if (animation) {
|
|
48428
|
+
this.animations.set(box.id, animation);
|
|
48429
|
+
}
|
|
48430
|
+
}
|
|
48431
|
+
}
|
|
47231
48432
|
}
|
|
47232
48433
|
|
|
47233
48434
|
function useGridDrawing(refName, model, canvasSize) {
|
|
@@ -47262,10 +48463,7 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
47262
48463
|
// http://diveintohtml5.info/canvas.html#pixel-madness
|
|
47263
48464
|
ctx.translate(-CANVAS_SHIFT, -CANVAS_SHIFT);
|
|
47264
48465
|
ctx.scale(dpr, dpr);
|
|
47265
|
-
|
|
47266
|
-
model.drawLayer(renderingContext, layer);
|
|
47267
|
-
rendererStore.drawLayer(renderingContext, layer);
|
|
47268
|
-
}
|
|
48466
|
+
rendererStore.draw(renderingContext);
|
|
47269
48467
|
}
|
|
47270
48468
|
}
|
|
47271
48469
|
|
|
@@ -47818,15 +49016,6 @@ class Selection extends Component {
|
|
|
47818
49016
|
}
|
|
47819
49017
|
}
|
|
47820
49018
|
|
|
47821
|
-
class Section extends Component {
|
|
47822
|
-
static template = "o_spreadsheet.Section";
|
|
47823
|
-
static props = {
|
|
47824
|
-
class: { type: String, optional: true },
|
|
47825
|
-
title: { type: String, optional: true },
|
|
47826
|
-
slots: Object,
|
|
47827
|
-
};
|
|
47828
|
-
}
|
|
47829
|
-
|
|
47830
49019
|
class ChartDataSeries extends Component {
|
|
47831
49020
|
static template = "o-spreadsheet.ChartDataSeries";
|
|
47832
49021
|
static components = { SelectionInput, Section };
|
|
@@ -48165,532 +49354,213 @@ class GenericChartConfigPanel extends Component {
|
|
|
48165
49354
|
* Change the local labelRange. The model should be updated when the
|
|
48166
49355
|
* button "confirm" is clicked
|
|
48167
49356
|
*/
|
|
48168
|
-
onLabelRangeChanged(ranges) {
|
|
48169
|
-
this.labelRange = ranges[0];
|
|
48170
|
-
this.state.labelsDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
48171
|
-
labelRange: this.labelRange,
|
|
48172
|
-
});
|
|
48173
|
-
}
|
|
48174
|
-
onLabelRangeConfirmed() {
|
|
48175
|
-
this.state.labelsDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
48176
|
-
labelRange: this.labelRange,
|
|
48177
|
-
});
|
|
48178
|
-
}
|
|
48179
|
-
getLabelRange() {
|
|
48180
|
-
return this.labelRange || "";
|
|
48181
|
-
}
|
|
48182
|
-
onUpdateAggregated(aggregated) {
|
|
48183
|
-
this.props.updateChart(this.props.figureId, {
|
|
48184
|
-
aggregated,
|
|
48185
|
-
});
|
|
48186
|
-
}
|
|
48187
|
-
calculateHeaderPosition() {
|
|
48188
|
-
if (this.isDatasetInvalid || this.isLabelInvalid) {
|
|
48189
|
-
return undefined;
|
|
48190
|
-
}
|
|
48191
|
-
const getters = this.env.model.getters;
|
|
48192
|
-
const sheetId = getters.getActiveSheetId();
|
|
48193
|
-
const labelRange = createValidRange(getters, sheetId, this.labelRange);
|
|
48194
|
-
const dataSets = createDataSets(getters, this.dataSets, sheetId, this.props.definition.dataSetsHaveTitle);
|
|
48195
|
-
if (dataSets.length) {
|
|
48196
|
-
return this.datasetOrientation === "rows"
|
|
48197
|
-
? dataSets[0].dataRange.zone.left
|
|
48198
|
-
: dataSets[0].dataRange.zone.top + 1;
|
|
48199
|
-
}
|
|
48200
|
-
else if (labelRange) {
|
|
48201
|
-
return labelRange.zone.top + 1;
|
|
48202
|
-
}
|
|
48203
|
-
return undefined;
|
|
48204
|
-
}
|
|
48205
|
-
get maxNumberOfUsedRanges() {
|
|
48206
|
-
return chartRegistry.get(this.props.definition.type).dataSeriesLimit;
|
|
48207
|
-
}
|
|
48208
|
-
transposeDataSet(dataRanges, datasetOrientation) {
|
|
48209
|
-
const getters = this.env.model.getters;
|
|
48210
|
-
if (datasetOrientation === undefined) {
|
|
48211
|
-
return dataRanges.filter(isDefined).map((dataRange) => ({ dataRange }));
|
|
48212
|
-
}
|
|
48213
|
-
const zonesBySheetName = {};
|
|
48214
|
-
const transposedDatasets = [];
|
|
48215
|
-
const figureSheetId = getters.getFigureSheetId(this.props.figureId);
|
|
48216
|
-
let name = getters.getActiveSheet().name;
|
|
48217
|
-
if (figureSheetId) {
|
|
48218
|
-
name = getters.getSheet(figureSheetId).name;
|
|
48219
|
-
}
|
|
48220
|
-
for (const dataRange of dataRanges) {
|
|
48221
|
-
if (!dataRange) {
|
|
48222
|
-
continue;
|
|
48223
|
-
}
|
|
48224
|
-
if (!isXcRepresentation(dataRange)) {
|
|
48225
|
-
return dataRanges.filter(isDefined).map((dataRange) => ({ dataRange }));
|
|
48226
|
-
}
|
|
48227
|
-
let { sheetName, xc } = splitReference(dataRange);
|
|
48228
|
-
sheetName = sheetName ?? name;
|
|
48229
|
-
if (!zonesBySheetName[sheetName]) {
|
|
48230
|
-
zonesBySheetName[sheetName] = [];
|
|
48231
|
-
}
|
|
48232
|
-
zonesBySheetName[sheetName].push(toZone(xc));
|
|
48233
|
-
}
|
|
48234
|
-
for (const sheetName in zonesBySheetName) {
|
|
48235
|
-
const zones = zonesBySheetName[sheetName];
|
|
48236
|
-
const contiguousZones = mergeContiguousZones(zones);
|
|
48237
|
-
if (datasetOrientation === "columns") {
|
|
48238
|
-
for (const zone of contiguousZones) {
|
|
48239
|
-
for (let col = zone.left; col <= zone.right; col++) {
|
|
48240
|
-
const dataRange = `${sheetName === name ? "" : sheetName + "!"}${zoneToXc({
|
|
48241
|
-
...zone,
|
|
48242
|
-
left: col,
|
|
48243
|
-
right: col,
|
|
48244
|
-
})}`;
|
|
48245
|
-
transposedDatasets.push({ dataRange });
|
|
48246
|
-
}
|
|
48247
|
-
}
|
|
48248
|
-
}
|
|
48249
|
-
else {
|
|
48250
|
-
for (const zone of contiguousZones) {
|
|
48251
|
-
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
48252
|
-
const dataRange = `${sheetName === name ? "" : sheetName + "!"}${zoneToXc({
|
|
48253
|
-
...zone,
|
|
48254
|
-
top: row,
|
|
48255
|
-
bottom: row,
|
|
48256
|
-
})}`;
|
|
48257
|
-
transposedDatasets.push({ dataRange });
|
|
48258
|
-
}
|
|
48259
|
-
}
|
|
48260
|
-
}
|
|
48261
|
-
}
|
|
48262
|
-
return transposedDatasets;
|
|
48263
|
-
}
|
|
48264
|
-
}
|
|
48265
|
-
|
|
48266
|
-
class BarConfigPanel extends GenericChartConfigPanel {
|
|
48267
|
-
static template = "o-spreadsheet-BarConfigPanel";
|
|
48268
|
-
get stackedLabel() {
|
|
48269
|
-
const definition = this.props.definition;
|
|
48270
|
-
return definition.horizontal
|
|
48271
|
-
? this.chartTerms.StackedBarChart
|
|
48272
|
-
: this.chartTerms.StackedColumnChart;
|
|
48273
|
-
}
|
|
48274
|
-
onUpdateStacked(stacked) {
|
|
48275
|
-
this.props.updateChart(this.props.figureId, {
|
|
48276
|
-
stacked,
|
|
48277
|
-
});
|
|
48278
|
-
}
|
|
48279
|
-
}
|
|
48280
|
-
|
|
48281
|
-
css /* scss */ `
|
|
48282
|
-
.o-badge-selection {
|
|
48283
|
-
gap: 1px;
|
|
48284
|
-
button.o-button {
|
|
48285
|
-
border-radius: 0;
|
|
48286
|
-
&.selected {
|
|
48287
|
-
color: ${GRAY_900};
|
|
48288
|
-
border-color: ${ACTION_COLOR};
|
|
48289
|
-
background: ${BADGE_SELECTED_COLOR};
|
|
48290
|
-
font-weight: 600;
|
|
48291
|
-
}
|
|
48292
|
-
|
|
48293
|
-
&:first-child {
|
|
48294
|
-
border-radius: 4px 0 0 4px;
|
|
48295
|
-
}
|
|
48296
|
-
&:last-child {
|
|
48297
|
-
border-radius: 0 4px 4px 0;
|
|
48298
|
-
}
|
|
48299
|
-
}
|
|
48300
|
-
}
|
|
48301
|
-
`;
|
|
48302
|
-
class BadgeSelection extends Component {
|
|
48303
|
-
static template = "o-spreadsheet.BadgeSelection";
|
|
48304
|
-
static props = {
|
|
48305
|
-
choices: Array,
|
|
48306
|
-
onChange: Function,
|
|
48307
|
-
selectedValue: String,
|
|
48308
|
-
};
|
|
48309
|
-
}
|
|
48310
|
-
|
|
48311
|
-
css /* scss */ `
|
|
48312
|
-
.o-menu-item-button {
|
|
48313
|
-
display: flex;
|
|
48314
|
-
justify-content: center;
|
|
48315
|
-
align-items: center;
|
|
48316
|
-
margin: 2px 1px;
|
|
48317
|
-
padding: 0px 1px;
|
|
48318
|
-
border-radius: 2px;
|
|
48319
|
-
min-width: 22px;
|
|
48320
|
-
}
|
|
48321
|
-
.o-disabled {
|
|
48322
|
-
opacity: 0.6;
|
|
48323
|
-
cursor: default;
|
|
48324
|
-
}
|
|
48325
|
-
`;
|
|
48326
|
-
class ActionButton extends Component {
|
|
48327
|
-
static template = "o-spreadsheet-ActionButton";
|
|
48328
|
-
static props = {
|
|
48329
|
-
action: Object,
|
|
48330
|
-
hasTriangleDownIcon: { type: Boolean, optional: true },
|
|
48331
|
-
selectedColor: { type: String, optional: true },
|
|
48332
|
-
class: { type: String, optional: true },
|
|
48333
|
-
onClick: { type: Function, optional: true },
|
|
48334
|
-
};
|
|
48335
|
-
actionButton = createAction(this.props.action);
|
|
48336
|
-
setup() {
|
|
48337
|
-
onWillUpdateProps((nextProps) => {
|
|
48338
|
-
if (nextProps.action !== this.props.action) {
|
|
48339
|
-
this.actionButton = createAction(nextProps.action);
|
|
48340
|
-
}
|
|
48341
|
-
});
|
|
48342
|
-
}
|
|
48343
|
-
get isVisible() {
|
|
48344
|
-
return this.actionButton.isVisible(this.env);
|
|
48345
|
-
}
|
|
48346
|
-
get isEnabled() {
|
|
48347
|
-
return this.actionButton.isEnabled(this.env);
|
|
48348
|
-
}
|
|
48349
|
-
get isActive() {
|
|
48350
|
-
return this.actionButton.isActive?.(this.env);
|
|
48351
|
-
}
|
|
48352
|
-
get title() {
|
|
48353
|
-
const name = this.actionButton.name(this.env);
|
|
48354
|
-
const description = this.actionButton.description(this.env);
|
|
48355
|
-
return name + (description ? ` (${description})` : "");
|
|
48356
|
-
}
|
|
48357
|
-
get iconTitle() {
|
|
48358
|
-
return this.actionButton.icon(this.env);
|
|
48359
|
-
}
|
|
48360
|
-
onClick(ev) {
|
|
48361
|
-
if (this.isEnabled) {
|
|
48362
|
-
this.props.onClick?.(ev);
|
|
48363
|
-
this.actionButton.execute?.(this.env);
|
|
48364
|
-
}
|
|
48365
|
-
}
|
|
48366
|
-
get buttonStyle() {
|
|
48367
|
-
if (this.props.selectedColor) {
|
|
48368
|
-
return cssPropertiesToCss({
|
|
48369
|
-
"border-bottom": `4px solid ${this.props.selectedColor}`,
|
|
48370
|
-
height: "16px",
|
|
48371
|
-
"margin-top": "2px",
|
|
48372
|
-
});
|
|
48373
|
-
}
|
|
48374
|
-
return "";
|
|
48375
|
-
}
|
|
48376
|
-
}
|
|
48377
|
-
|
|
48378
|
-
const LINE_VERTICAL_PADDING = 1;
|
|
48379
|
-
const PICKER_PADDING = 8;
|
|
48380
|
-
const ITEM_BORDER_WIDTH = 1;
|
|
48381
|
-
const ITEM_EDGE_LENGTH = 18;
|
|
48382
|
-
const ITEMS_PER_LINE = 10;
|
|
48383
|
-
const MAGNIFIER_EDGE = 16;
|
|
48384
|
-
const ITEM_GAP = 2;
|
|
48385
|
-
const CONTENT_WIDTH = ITEMS_PER_LINE * (ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH) + (ITEMS_PER_LINE - 1) * ITEM_GAP;
|
|
48386
|
-
const INNER_GRADIENT_WIDTH = CONTENT_WIDTH - 2 * ITEM_BORDER_WIDTH;
|
|
48387
|
-
const INNER_GRADIENT_HEIGHT = CONTENT_WIDTH - 30 - 2 * ITEM_BORDER_WIDTH;
|
|
48388
|
-
const CONTAINER_WIDTH = CONTENT_WIDTH + 2 * PICKER_PADDING;
|
|
48389
|
-
css /* scss */ `
|
|
48390
|
-
.o-color-picker {
|
|
48391
|
-
padding: ${PICKER_PADDING}px 0;
|
|
48392
|
-
/* FIXME: this is useless, overiden by the popover container */
|
|
48393
|
-
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
|
|
48394
|
-
background-color: white;
|
|
48395
|
-
line-height: 1.2;
|
|
48396
|
-
overflow-y: auto;
|
|
48397
|
-
overflow-x: hidden;
|
|
48398
|
-
width: ${CONTAINER_WIDTH}px;
|
|
48399
|
-
|
|
48400
|
-
.o-color-picker-section-name {
|
|
48401
|
-
margin: 0px ${ITEM_BORDER_WIDTH}px;
|
|
48402
|
-
padding: 4px ${PICKER_PADDING}px;
|
|
48403
|
-
}
|
|
48404
|
-
.colors-grid {
|
|
48405
|
-
display: grid;
|
|
48406
|
-
padding: ${LINE_VERTICAL_PADDING}px ${PICKER_PADDING}px;
|
|
48407
|
-
grid-template-columns: repeat(${ITEMS_PER_LINE}, 1fr);
|
|
48408
|
-
grid-gap: ${ITEM_GAP}px;
|
|
48409
|
-
}
|
|
48410
|
-
.o-color-picker-toggler-button {
|
|
48411
|
-
display: flex;
|
|
48412
|
-
.o-color-picker-toggler-sign {
|
|
48413
|
-
display: flex;
|
|
48414
|
-
margin: auto auto;
|
|
48415
|
-
width: 55%;
|
|
48416
|
-
height: 55%;
|
|
48417
|
-
.o-icon {
|
|
48418
|
-
width: 100%;
|
|
48419
|
-
height: 100%;
|
|
48420
|
-
}
|
|
48421
|
-
}
|
|
48422
|
-
}
|
|
48423
|
-
.o-color-picker-line-item {
|
|
48424
|
-
width: ${ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
48425
|
-
height: ${ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
48426
|
-
margin: 0px;
|
|
48427
|
-
border-radius: 50px;
|
|
48428
|
-
border: ${ITEM_BORDER_WIDTH}px solid #666666;
|
|
48429
|
-
padding: 0px;
|
|
48430
|
-
font-size: 16px;
|
|
48431
|
-
background: white;
|
|
48432
|
-
&:hover {
|
|
48433
|
-
background-color: rgba(0, 0, 0, 0.08);
|
|
48434
|
-
outline: 1px solid gray;
|
|
48435
|
-
cursor: pointer;
|
|
48436
|
-
}
|
|
48437
|
-
}
|
|
48438
|
-
.o-buttons {
|
|
48439
|
-
padding: ${PICKER_PADDING}px;
|
|
48440
|
-
display: flex;
|
|
48441
|
-
.o-cancel {
|
|
48442
|
-
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
48443
|
-
width: 100%;
|
|
48444
|
-
padding: 5px;
|
|
48445
|
-
font-size: 14px;
|
|
48446
|
-
background: white;
|
|
48447
|
-
border-radius: 4px;
|
|
48448
|
-
&:hover:enabled {
|
|
48449
|
-
background-color: rgba(0, 0, 0, 0.08);
|
|
48450
|
-
}
|
|
48451
|
-
}
|
|
48452
|
-
}
|
|
48453
|
-
.o-add-button {
|
|
48454
|
-
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
48455
|
-
padding: 4px;
|
|
48456
|
-
background: white;
|
|
48457
|
-
border-radius: 4px;
|
|
48458
|
-
&:hover:enabled {
|
|
48459
|
-
background-color: rgba(0, 0, 0, 0.08);
|
|
48460
|
-
}
|
|
48461
|
-
}
|
|
48462
|
-
.o-separator {
|
|
48463
|
-
border-bottom: ${MENU_SEPARATOR_BORDER_WIDTH}px solid ${SEPARATOR_COLOR};
|
|
48464
|
-
margin-top: ${MENU_SEPARATOR_PADDING}px;
|
|
48465
|
-
margin-bottom: ${MENU_SEPARATOR_PADDING}px;
|
|
48466
|
-
}
|
|
48467
|
-
|
|
48468
|
-
.o-custom-selector {
|
|
48469
|
-
padding: ${PICKER_PADDING + 2}px ${PICKER_PADDING}px;
|
|
48470
|
-
position: relative;
|
|
48471
|
-
.o-gradient {
|
|
48472
|
-
margin-bottom: ${MAGNIFIER_EDGE / 2}px;
|
|
48473
|
-
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
48474
|
-
width: ${INNER_GRADIENT_WIDTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
48475
|
-
height: ${INNER_GRADIENT_HEIGHT + 2 * ITEM_BORDER_WIDTH}px;
|
|
48476
|
-
position: relative;
|
|
48477
|
-
}
|
|
48478
|
-
|
|
48479
|
-
.magnifier {
|
|
48480
|
-
height: ${MAGNIFIER_EDGE}px;
|
|
48481
|
-
width: ${MAGNIFIER_EDGE}px;
|
|
48482
|
-
border-radius: 50%;
|
|
48483
|
-
border: 2px solid #fff;
|
|
48484
|
-
box-shadow: 0px 0px 3px #c0c0c0;
|
|
48485
|
-
position: absolute;
|
|
48486
|
-
z-index: 2;
|
|
48487
|
-
}
|
|
48488
|
-
.saturation {
|
|
48489
|
-
background: linear-gradient(to right, #fff 0%, transparent 100%);
|
|
48490
|
-
}
|
|
48491
|
-
.lightness {
|
|
48492
|
-
background: linear-gradient(to top, #000 0%, transparent 100%);
|
|
48493
|
-
}
|
|
48494
|
-
.o-hue-picker {
|
|
48495
|
-
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
48496
|
-
width: 100%;
|
|
48497
|
-
height: 12px;
|
|
48498
|
-
border-radius: 4px;
|
|
48499
|
-
background: linear-gradient(
|
|
48500
|
-
to right,
|
|
48501
|
-
hsl(0 100% 50%) 0%,
|
|
48502
|
-
hsl(0.2turn 100% 50%) 20%,
|
|
48503
|
-
hsl(0.3turn 100% 50%) 30%,
|
|
48504
|
-
hsl(0.4turn 100% 50%) 40%,
|
|
48505
|
-
hsl(0.5turn 100% 50%) 50%,
|
|
48506
|
-
hsl(0.6turn 100% 50%) 60%,
|
|
48507
|
-
hsl(0.7turn 100% 50%) 70%,
|
|
48508
|
-
hsl(0.8turn 100% 50%) 80%,
|
|
48509
|
-
hsl(0.9turn 100% 50%) 90%,
|
|
48510
|
-
hsl(1turn 100% 50%) 100%
|
|
48511
|
-
);
|
|
48512
|
-
position: relative;
|
|
48513
|
-
cursor: crosshair;
|
|
48514
|
-
}
|
|
48515
|
-
.o-hue-slider {
|
|
48516
|
-
margin-top: -3px;
|
|
48517
|
-
}
|
|
48518
|
-
.o-custom-input-preview {
|
|
48519
|
-
padding: 2px 0px;
|
|
48520
|
-
display: flex;
|
|
48521
|
-
input {
|
|
48522
|
-
width: 50%;
|
|
48523
|
-
border-radius: 4px;
|
|
48524
|
-
padding: 4px 23px 4px 10px;
|
|
48525
|
-
height: 24px;
|
|
48526
|
-
border: 1px solid #c0c0c0;
|
|
48527
|
-
margin-right: 2px;
|
|
48528
|
-
}
|
|
48529
|
-
.o-wrong-color {
|
|
48530
|
-
/* FIXME bootstrap class instead? */
|
|
48531
|
-
outline-color: red;
|
|
48532
|
-
border-color: red;
|
|
48533
|
-
&:focus {
|
|
48534
|
-
outline-style: solid;
|
|
48535
|
-
outline-width: 1px;
|
|
48536
|
-
}
|
|
48537
|
-
}
|
|
48538
|
-
}
|
|
48539
|
-
.o-custom-input-buttons {
|
|
48540
|
-
padding: 2px 0px;
|
|
48541
|
-
display: flex;
|
|
48542
|
-
justify-content: end;
|
|
48543
|
-
}
|
|
48544
|
-
.o-color-preview {
|
|
48545
|
-
border: 1px solid #c0c0c0;
|
|
48546
|
-
border-radius: 4px;
|
|
48547
|
-
width: 50%;
|
|
48548
|
-
}
|
|
48549
|
-
}
|
|
48550
|
-
}
|
|
48551
|
-
`;
|
|
48552
|
-
class ColorPicker extends Component {
|
|
48553
|
-
static template = "o-spreadsheet-ColorPicker";
|
|
48554
|
-
static props = {
|
|
48555
|
-
onColorPicked: Function,
|
|
48556
|
-
currentColor: { type: String, optional: true },
|
|
48557
|
-
maxHeight: { type: Number, optional: true },
|
|
48558
|
-
anchorRect: Object,
|
|
48559
|
-
disableNoColor: { type: Boolean, optional: true },
|
|
48560
|
-
};
|
|
48561
|
-
static defaultProps = { currentColor: "" };
|
|
48562
|
-
static components = { Popover };
|
|
48563
|
-
COLORS = COLOR_PICKER_DEFAULTS;
|
|
48564
|
-
state = useState({
|
|
48565
|
-
showGradient: false,
|
|
48566
|
-
currentHslaColor: isColorValid(this.props.currentColor)
|
|
48567
|
-
? { ...hexToHSLA(this.props.currentColor), a: 1 }
|
|
48568
|
-
: { h: 0, s: 100, l: 100, a: 1 },
|
|
48569
|
-
customHexColor: isColorValid(this.props.currentColor) ? toHex(this.props.currentColor) : "",
|
|
48570
|
-
});
|
|
48571
|
-
get colorPickerStyle() {
|
|
48572
|
-
if (this.props.maxHeight !== undefined && this.props.maxHeight <= 0) {
|
|
48573
|
-
return cssPropertiesToCss({ display: "none" });
|
|
48574
|
-
}
|
|
48575
|
-
return "";
|
|
48576
|
-
}
|
|
48577
|
-
get popoverProps() {
|
|
48578
|
-
return {
|
|
48579
|
-
anchorRect: this.props.anchorRect,
|
|
48580
|
-
maxHeight: this.props.maxHeight,
|
|
48581
|
-
positioning: "bottom-left",
|
|
48582
|
-
verticalOffset: 0,
|
|
48583
|
-
};
|
|
48584
|
-
}
|
|
48585
|
-
get gradientHueStyle() {
|
|
48586
|
-
const hue = this.state.currentHslaColor?.h || 0;
|
|
48587
|
-
return cssPropertiesToCss({
|
|
48588
|
-
background: `hsl(${hue} 100% 50%)`,
|
|
49357
|
+
onLabelRangeChanged(ranges) {
|
|
49358
|
+
this.labelRange = ranges[0];
|
|
49359
|
+
this.state.labelsDispatchResult = this.props.canUpdateChart(this.props.figureId, {
|
|
49360
|
+
labelRange: this.labelRange,
|
|
48589
49361
|
});
|
|
48590
49362
|
}
|
|
48591
|
-
|
|
48592
|
-
|
|
48593
|
-
|
|
48594
|
-
const left = clip(delta, 1, INNER_GRADIENT_WIDTH) - ICON_EDGE_LENGTH / 2;
|
|
48595
|
-
return cssPropertiesToCss({
|
|
48596
|
-
"margin-left": `${left}px`,
|
|
49363
|
+
onLabelRangeConfirmed() {
|
|
49364
|
+
this.state.labelsDispatchResult = this.props.updateChart(this.props.figureId, {
|
|
49365
|
+
labelRange: this.labelRange,
|
|
48597
49366
|
});
|
|
48598
49367
|
}
|
|
48599
|
-
|
|
48600
|
-
|
|
48601
|
-
const left = Math.round(INNER_GRADIENT_WIDTH * clip(s / 100, 0, 1));
|
|
48602
|
-
const top = Math.round(INNER_GRADIENT_HEIGHT * clip(1 - (2 * l) / (200 - s), 0, 1));
|
|
48603
|
-
return cssPropertiesToCss({
|
|
48604
|
-
left: `${-MAGNIFIER_EDGE / 2 + left}px`,
|
|
48605
|
-
top: `${-MAGNIFIER_EDGE / 2 + top}px`,
|
|
48606
|
-
background: hslaToHex(this.state.currentHslaColor),
|
|
48607
|
-
});
|
|
49368
|
+
getLabelRange() {
|
|
49369
|
+
return this.labelRange || "";
|
|
48608
49370
|
}
|
|
48609
|
-
|
|
48610
|
-
|
|
48611
|
-
|
|
49371
|
+
onUpdateAggregated(aggregated) {
|
|
49372
|
+
this.props.updateChart(this.props.figureId, {
|
|
49373
|
+
aggregated,
|
|
48612
49374
|
});
|
|
48613
49375
|
}
|
|
48614
|
-
|
|
48615
|
-
|
|
49376
|
+
calculateHeaderPosition() {
|
|
49377
|
+
if (this.isDatasetInvalid || this.isLabelInvalid) {
|
|
49378
|
+
return undefined;
|
|
49379
|
+
}
|
|
49380
|
+
const getters = this.env.model.getters;
|
|
49381
|
+
const sheetId = getters.getActiveSheetId();
|
|
49382
|
+
const labelRange = createValidRange(getters, sheetId, this.labelRange);
|
|
49383
|
+
const dataSets = createDataSets(getters, this.dataSets, sheetId, this.props.definition.dataSetsHaveTitle);
|
|
49384
|
+
if (dataSets.length) {
|
|
49385
|
+
return this.datasetOrientation === "rows"
|
|
49386
|
+
? dataSets[0].dataRange.zone.left
|
|
49387
|
+
: dataSets[0].dataRange.zone.top + 1;
|
|
49388
|
+
}
|
|
49389
|
+
else if (labelRange) {
|
|
49390
|
+
return labelRange.zone.top + 1;
|
|
49391
|
+
}
|
|
49392
|
+
return undefined;
|
|
48616
49393
|
}
|
|
48617
|
-
get
|
|
48618
|
-
return
|
|
49394
|
+
get maxNumberOfUsedRanges() {
|
|
49395
|
+
return chartRegistry.get(this.props.definition.type).dataSeriesLimit;
|
|
48619
49396
|
}
|
|
48620
|
-
|
|
48621
|
-
const
|
|
48622
|
-
|
|
48623
|
-
|
|
48624
|
-
|
|
48625
|
-
const
|
|
48626
|
-
const
|
|
48627
|
-
|
|
49397
|
+
transposeDataSet(dataRanges, datasetOrientation) {
|
|
49398
|
+
const getters = this.env.model.getters;
|
|
49399
|
+
if (datasetOrientation === undefined) {
|
|
49400
|
+
return dataRanges.filter(isDefined).map((dataRange) => ({ dataRange }));
|
|
49401
|
+
}
|
|
49402
|
+
const zonesBySheetName = {};
|
|
49403
|
+
const transposedDatasets = [];
|
|
49404
|
+
const figureSheetId = getters.getFigureSheetId(this.props.figureId);
|
|
49405
|
+
let name = getters.getActiveSheet().name;
|
|
49406
|
+
if (figureSheetId) {
|
|
49407
|
+
name = getters.getSheet(figureSheetId).name;
|
|
49408
|
+
}
|
|
49409
|
+
for (const dataRange of dataRanges) {
|
|
49410
|
+
if (!dataRange) {
|
|
49411
|
+
continue;
|
|
49412
|
+
}
|
|
49413
|
+
if (!isXcRepresentation(dataRange)) {
|
|
49414
|
+
return dataRanges.filter(isDefined).map((dataRange) => ({ dataRange }));
|
|
49415
|
+
}
|
|
49416
|
+
let { sheetName, xc } = splitReference(dataRange);
|
|
49417
|
+
sheetName = sheetName ?? name;
|
|
49418
|
+
if (!zonesBySheetName[sheetName]) {
|
|
49419
|
+
zonesBySheetName[sheetName] = [];
|
|
49420
|
+
}
|
|
49421
|
+
zonesBySheetName[sheetName].push(toZone(xc));
|
|
49422
|
+
}
|
|
49423
|
+
for (const sheetName in zonesBySheetName) {
|
|
49424
|
+
const zones = zonesBySheetName[sheetName];
|
|
49425
|
+
const contiguousZones = mergeContiguousZones(zones);
|
|
49426
|
+
if (datasetOrientation === "columns") {
|
|
49427
|
+
for (const zone of contiguousZones) {
|
|
49428
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
49429
|
+
const dataRange = `${sheetName === name ? "" : sheetName + "!"}${zoneToXc({
|
|
49430
|
+
...zone,
|
|
49431
|
+
left: col,
|
|
49432
|
+
right: col,
|
|
49433
|
+
})}`;
|
|
49434
|
+
transposedDatasets.push({ dataRange });
|
|
49435
|
+
}
|
|
49436
|
+
}
|
|
49437
|
+
}
|
|
49438
|
+
else {
|
|
49439
|
+
for (const zone of contiguousZones) {
|
|
49440
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
49441
|
+
const dataRange = `${sheetName === name ? "" : sheetName + "!"}${zoneToXc({
|
|
49442
|
+
...zone,
|
|
49443
|
+
top: row,
|
|
49444
|
+
bottom: row,
|
|
49445
|
+
})}`;
|
|
49446
|
+
transposedDatasets.push({ dataRange });
|
|
49447
|
+
}
|
|
49448
|
+
}
|
|
49449
|
+
}
|
|
49450
|
+
}
|
|
49451
|
+
return transposedDatasets;
|
|
48628
49452
|
}
|
|
48629
|
-
|
|
48630
|
-
|
|
48631
|
-
|
|
48632
|
-
|
|
49453
|
+
}
|
|
49454
|
+
|
|
49455
|
+
class BarConfigPanel extends GenericChartConfigPanel {
|
|
49456
|
+
static template = "o-spreadsheet-BarConfigPanel";
|
|
49457
|
+
get stackedLabel() {
|
|
49458
|
+
const definition = this.props.definition;
|
|
49459
|
+
return definition.horizontal
|
|
49460
|
+
? this.chartTerms.StackedBarChart
|
|
49461
|
+
: this.chartTerms.StackedColumnChart;
|
|
48633
49462
|
}
|
|
48634
|
-
|
|
48635
|
-
this.
|
|
48636
|
-
|
|
49463
|
+
onUpdateStacked(stacked) {
|
|
49464
|
+
this.props.updateChart(this.props.figureId, {
|
|
49465
|
+
stacked,
|
|
49466
|
+
});
|
|
48637
49467
|
}
|
|
48638
|
-
|
|
48639
|
-
|
|
48640
|
-
|
|
48641
|
-
|
|
49468
|
+
}
|
|
49469
|
+
|
|
49470
|
+
css /* scss */ `
|
|
49471
|
+
.o-badge-selection {
|
|
49472
|
+
gap: 1px;
|
|
49473
|
+
button.o-button {
|
|
49474
|
+
border-radius: 0;
|
|
49475
|
+
&.selected {
|
|
49476
|
+
color: ${GRAY_900};
|
|
49477
|
+
border-color: ${ACTION_COLOR};
|
|
49478
|
+
background: ${BADGE_SELECTED_COLOR};
|
|
49479
|
+
font-weight: 600;
|
|
49480
|
+
}
|
|
49481
|
+
|
|
49482
|
+
&:first-child {
|
|
49483
|
+
border-radius: 4px 0 0 4px;
|
|
49484
|
+
}
|
|
49485
|
+
&:last-child {
|
|
49486
|
+
border-radius: 0 4px 4px 0;
|
|
49487
|
+
}
|
|
48642
49488
|
}
|
|
48643
|
-
|
|
48644
|
-
|
|
49489
|
+
}
|
|
49490
|
+
`;
|
|
49491
|
+
class BadgeSelection extends Component {
|
|
49492
|
+
static template = "o-spreadsheet.BadgeSelection";
|
|
49493
|
+
static props = {
|
|
49494
|
+
choices: Array,
|
|
49495
|
+
onChange: Function,
|
|
49496
|
+
selectedValue: String,
|
|
49497
|
+
};
|
|
49498
|
+
}
|
|
49499
|
+
|
|
49500
|
+
css /* scss */ `
|
|
49501
|
+
.o-menu-item-button {
|
|
49502
|
+
display: flex;
|
|
49503
|
+
justify-content: center;
|
|
49504
|
+
align-items: center;
|
|
49505
|
+
margin: 2px 1px;
|
|
49506
|
+
padding: 0px 1px;
|
|
49507
|
+
border-radius: 2px;
|
|
49508
|
+
min-width: 22px;
|
|
49509
|
+
}
|
|
49510
|
+
.o-disabled {
|
|
49511
|
+
opacity: 0.6;
|
|
49512
|
+
cursor: default;
|
|
49513
|
+
}
|
|
49514
|
+
`;
|
|
49515
|
+
class ActionButton extends Component {
|
|
49516
|
+
static template = "o-spreadsheet-ActionButton";
|
|
49517
|
+
static props = {
|
|
49518
|
+
action: Object,
|
|
49519
|
+
hasTriangleDownIcon: { type: Boolean, optional: true },
|
|
49520
|
+
selectedColor: { type: String, optional: true },
|
|
49521
|
+
class: { type: String, optional: true },
|
|
49522
|
+
onClick: { type: Function, optional: true },
|
|
49523
|
+
};
|
|
49524
|
+
actionButton = createAction(this.props.action);
|
|
49525
|
+
setup() {
|
|
49526
|
+
onWillUpdateProps((nextProps) => {
|
|
49527
|
+
if (nextProps.action !== this.props.action) {
|
|
49528
|
+
this.actionButton = createAction(nextProps.action);
|
|
49529
|
+
}
|
|
49530
|
+
});
|
|
48645
49531
|
}
|
|
48646
|
-
|
|
48647
|
-
this.
|
|
49532
|
+
get isVisible() {
|
|
49533
|
+
return this.actionButton.isVisible(this.env);
|
|
48648
49534
|
}
|
|
48649
|
-
|
|
48650
|
-
|
|
48651
|
-
this.setCustomGradient(initialGradientCoordinates);
|
|
48652
|
-
const initialMousePosition = { x: ev.clientX, y: ev.clientY };
|
|
48653
|
-
const onMouseMove = (ev) => {
|
|
48654
|
-
const currentMousePosition = { x: ev.clientX, y: ev.clientY };
|
|
48655
|
-
const deltaX = currentMousePosition.x - initialMousePosition.x;
|
|
48656
|
-
const deltaY = currentMousePosition.y - initialMousePosition.y;
|
|
48657
|
-
const currentGradientCoordinates = {
|
|
48658
|
-
x: initialGradientCoordinates.x + deltaX,
|
|
48659
|
-
y: initialGradientCoordinates.y + deltaY,
|
|
48660
|
-
};
|
|
48661
|
-
this.setCustomGradient(currentGradientCoordinates);
|
|
48662
|
-
};
|
|
48663
|
-
startDnd(onMouseMove, () => { });
|
|
49535
|
+
get isEnabled() {
|
|
49536
|
+
return this.actionButton.isEnabled(this.env);
|
|
48664
49537
|
}
|
|
48665
|
-
|
|
48666
|
-
|
|
48667
|
-
const initialMouseX = ev.clientX;
|
|
48668
|
-
this.setCustomHue(initialX);
|
|
48669
|
-
const onMouseMove = (ev) => {
|
|
48670
|
-
const currentMouseX = ev.clientX;
|
|
48671
|
-
const deltaX = currentMouseX - initialMouseX;
|
|
48672
|
-
const x = initialX + deltaX;
|
|
48673
|
-
this.setCustomHue(x);
|
|
48674
|
-
};
|
|
48675
|
-
startDnd(onMouseMove, () => { });
|
|
49538
|
+
get isActive() {
|
|
49539
|
+
return this.actionButton.isActive?.(this.env);
|
|
48676
49540
|
}
|
|
48677
|
-
|
|
48678
|
-
|
|
48679
|
-
const
|
|
48680
|
-
|
|
48681
|
-
if (!isColorValid(val)) ;
|
|
48682
|
-
else {
|
|
48683
|
-
this.state.currentHslaColor = { ...hexToHSLA(val), a: 1 };
|
|
48684
|
-
}
|
|
49541
|
+
get title() {
|
|
49542
|
+
const name = this.actionButton.name(this.env);
|
|
49543
|
+
const description = this.actionButton.description(this.env);
|
|
49544
|
+
return name + (description ? ` (${description})` : "");
|
|
48685
49545
|
}
|
|
48686
|
-
|
|
48687
|
-
|
|
48688
|
-
|
|
49546
|
+
get iconTitle() {
|
|
49547
|
+
return this.actionButton.icon(this.env);
|
|
49548
|
+
}
|
|
49549
|
+
onClick(ev) {
|
|
49550
|
+
if (this.isEnabled) {
|
|
49551
|
+
this.props.onClick?.(ev);
|
|
49552
|
+
this.actionButton.execute?.(this.env);
|
|
48689
49553
|
}
|
|
48690
|
-
this.props.onColorPicked(toHex(this.state.customHexColor));
|
|
48691
49554
|
}
|
|
48692
|
-
|
|
48693
|
-
|
|
49555
|
+
get buttonStyle() {
|
|
49556
|
+
if (this.props.selectedColor) {
|
|
49557
|
+
return cssPropertiesToCss({
|
|
49558
|
+
"border-bottom": `4px solid ${this.props.selectedColor}`,
|
|
49559
|
+
height: "16px",
|
|
49560
|
+
"margin-top": "2px",
|
|
49561
|
+
});
|
|
49562
|
+
}
|
|
49563
|
+
return "";
|
|
48694
49564
|
}
|
|
48695
49565
|
}
|
|
48696
49566
|
|
|
@@ -49161,57 +50031,6 @@ class RadioSelection extends Component {
|
|
|
49161
50031
|
};
|
|
49162
50032
|
}
|
|
49163
50033
|
|
|
49164
|
-
const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
|
|
49165
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
49166
|
-
<path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
|
|
49167
|
-
</svg>
|
|
49168
|
-
`;
|
|
49169
|
-
css /* scss */ `
|
|
49170
|
-
.o-round-color-picker-button {
|
|
49171
|
-
width: 20px;
|
|
49172
|
-
height: 20px;
|
|
49173
|
-
cursor: pointer;
|
|
49174
|
-
border: 1px solid ${GRAY_300};
|
|
49175
|
-
background-position: 1px 1px;
|
|
49176
|
-
background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
|
|
49177
|
-
}
|
|
49178
|
-
`;
|
|
49179
|
-
class RoundColorPicker extends Component {
|
|
49180
|
-
static template = "o-spreadsheet.RoundColorPicker";
|
|
49181
|
-
static components = { Section, ColorPicker };
|
|
49182
|
-
static props = {
|
|
49183
|
-
currentColor: { type: String, optional: true },
|
|
49184
|
-
title: { type: String, optional: true },
|
|
49185
|
-
onColorPicked: Function,
|
|
49186
|
-
disableNoColor: { type: Boolean, optional: true },
|
|
49187
|
-
};
|
|
49188
|
-
colorPickerButtonRef = useRef("colorPickerButton");
|
|
49189
|
-
state;
|
|
49190
|
-
setup() {
|
|
49191
|
-
this.state = useState({ pickerOpened: false });
|
|
49192
|
-
useExternalListener(window, "click", this.closePicker);
|
|
49193
|
-
}
|
|
49194
|
-
closePicker() {
|
|
49195
|
-
this.state.pickerOpened = false;
|
|
49196
|
-
}
|
|
49197
|
-
togglePicker() {
|
|
49198
|
-
this.state.pickerOpened = !this.state.pickerOpened;
|
|
49199
|
-
}
|
|
49200
|
-
onColorPicked(color) {
|
|
49201
|
-
this.props.onColorPicked(color);
|
|
49202
|
-
this.state.pickerOpened = false;
|
|
49203
|
-
}
|
|
49204
|
-
get colorPickerAnchorRect() {
|
|
49205
|
-
const button = this.colorPickerButtonRef.el;
|
|
49206
|
-
return getBoundingRectAsPOJO(button);
|
|
49207
|
-
}
|
|
49208
|
-
get buttonStyle() {
|
|
49209
|
-
return cssPropertiesToCss({
|
|
49210
|
-
background: this.props.currentColor,
|
|
49211
|
-
});
|
|
49212
|
-
}
|
|
49213
|
-
}
|
|
49214
|
-
|
|
49215
50034
|
class GeneralDesignEditor extends Component {
|
|
49216
50035
|
static template = "o-spreadsheet-GeneralDesignEditor";
|
|
49217
50036
|
static components = {
|
|
@@ -54739,7 +55558,7 @@ class SpreadsheetPivot {
|
|
|
54739
55558
|
}
|
|
54740
55559
|
getTypeFromZone(sheetId, zone) {
|
|
54741
55560
|
const cells = this.getters.getEvaluatedCellsInZone(sheetId, zone);
|
|
54742
|
-
const nonEmptyCells = cells.filter((cell) => cell.type
|
|
55561
|
+
const nonEmptyCells = cells.filter((cell) => !(cell.type === CellValueType.empty || cell.value === ""));
|
|
54743
55562
|
if (nonEmptyCells.length === 0) {
|
|
54744
55563
|
return "integer";
|
|
54745
55564
|
}
|
|
@@ -55347,7 +56166,7 @@ css /* scss */ `
|
|
|
55347
56166
|
`;
|
|
55348
56167
|
class SettingsPanel extends Component {
|
|
55349
56168
|
static template = "o-spreadsheet-SettingsPanel";
|
|
55350
|
-
static components = { Section, ValidationMessages };
|
|
56169
|
+
static components = { Section, ValidationMessages, BadgeSelection };
|
|
55351
56170
|
static props = { onCloseSidePanel: Function };
|
|
55352
56171
|
loadedLocales = [];
|
|
55353
56172
|
setup() {
|
|
@@ -56231,49 +57050,111 @@ class ScreenWidthStore {
|
|
|
56231
57050
|
}
|
|
56232
57051
|
|
|
56233
57052
|
const DEFAULT_SIDE_PANEL_SIZE = 350;
|
|
57053
|
+
const COLLAPSED_SIDE_PANEL_SIZE = 45;
|
|
56234
57054
|
const MIN_SHEET_VIEW_WIDTH = 150;
|
|
56235
57055
|
class SidePanelStore extends SpreadsheetStore {
|
|
56236
|
-
mutators = [
|
|
56237
|
-
|
|
56238
|
-
|
|
56239
|
-
|
|
57056
|
+
mutators = [
|
|
57057
|
+
"open",
|
|
57058
|
+
"toggle",
|
|
57059
|
+
"close",
|
|
57060
|
+
"changePanelSize",
|
|
57061
|
+
"resetPanelSize",
|
|
57062
|
+
"togglePinPanel",
|
|
57063
|
+
"closeMainPanel",
|
|
57064
|
+
"changeSpreadsheetWidth",
|
|
57065
|
+
"toggleCollapsePanel",
|
|
57066
|
+
];
|
|
57067
|
+
mainPanel = undefined;
|
|
57068
|
+
secondaryPanel;
|
|
57069
|
+
availableWidth = 0;
|
|
56240
57070
|
screenWidthStore = this.get(ScreenWidthStore);
|
|
56241
|
-
get
|
|
56242
|
-
|
|
56243
|
-
|
|
56244
|
-
|
|
56245
|
-
|
|
57071
|
+
get isMainPanelOpen() {
|
|
57072
|
+
return this.mainPanel && this.mainPanel.componentTag
|
|
57073
|
+
? this.computeState(this.mainPanel).isOpen
|
|
57074
|
+
: false;
|
|
57075
|
+
}
|
|
57076
|
+
get isSecondaryPanelOpen() {
|
|
57077
|
+
return this.secondaryPanel && this.secondaryPanel.componentTag
|
|
57078
|
+
? this.computeState(this.secondaryPanel).isOpen
|
|
57079
|
+
: false;
|
|
57080
|
+
}
|
|
57081
|
+
get mainPanelProps() {
|
|
57082
|
+
return this.mainPanel ? this.getPanelProps(this.mainPanel) : undefined;
|
|
57083
|
+
}
|
|
57084
|
+
get mainPanelKey() {
|
|
57085
|
+
return this.mainPanel ? this.getPanelKey(this.mainPanel) : undefined;
|
|
56246
57086
|
}
|
|
56247
|
-
get
|
|
56248
|
-
|
|
57087
|
+
get secondaryPanelProps() {
|
|
57088
|
+
return this.secondaryPanel ? this.getPanelProps(this.secondaryPanel) : undefined;
|
|
57089
|
+
}
|
|
57090
|
+
get secondaryPanelKey() {
|
|
57091
|
+
return this.secondaryPanel ? this.getPanelKey(this.secondaryPanel) : undefined;
|
|
57092
|
+
}
|
|
57093
|
+
get totalPanelSize() {
|
|
57094
|
+
return (this.mainPanel?.size || 0) + (this.secondaryPanel?.size ?? 0);
|
|
57095
|
+
}
|
|
57096
|
+
getPanelProps(panelInfo) {
|
|
57097
|
+
const state = this.computeState(panelInfo);
|
|
56249
57098
|
if (state.isOpen) {
|
|
56250
57099
|
return state.props ?? {};
|
|
56251
57100
|
}
|
|
56252
57101
|
return {};
|
|
56253
57102
|
}
|
|
56254
|
-
|
|
56255
|
-
const state = this.computeState(
|
|
57103
|
+
getPanelKey(panelInfo) {
|
|
57104
|
+
const state = this.computeState(panelInfo);
|
|
56256
57105
|
if (state.isOpen) {
|
|
56257
57106
|
return state.key;
|
|
56258
57107
|
}
|
|
56259
57108
|
return undefined;
|
|
56260
57109
|
}
|
|
56261
|
-
open(componentTag,
|
|
57110
|
+
open(componentTag, initialPanelProps = {}) {
|
|
56262
57111
|
if (this.screenWidthStore.isSmall) {
|
|
56263
57112
|
return;
|
|
56264
57113
|
}
|
|
56265
|
-
const
|
|
57114
|
+
const newPanelInfo = { initialPanelProps, componentTag, size: DEFAULT_SIDE_PANEL_SIZE };
|
|
57115
|
+
const state = this.computeState(newPanelInfo);
|
|
56266
57116
|
if (!state.isOpen) {
|
|
56267
57117
|
return;
|
|
56268
57118
|
}
|
|
56269
|
-
|
|
56270
|
-
|
|
57119
|
+
const mainPanelKey = this.mainPanel ? this.getPanelKey(this.mainPanel) : undefined;
|
|
57120
|
+
if (!this.mainPanel || !this.mainPanel.isPinned || mainPanelKey === state.key) {
|
|
57121
|
+
this._openPanel("mainPanel", newPanelInfo, state);
|
|
57122
|
+
return;
|
|
57123
|
+
}
|
|
57124
|
+
// Try to open secondary panel if main panel is pinned
|
|
57125
|
+
const nonCollapsedPanelSize = this.mainPanel.isCollapsed
|
|
57126
|
+
? DEFAULT_SIDE_PANEL_SIZE
|
|
57127
|
+
: this.mainPanel.size;
|
|
57128
|
+
if (!this.secondaryPanel &&
|
|
57129
|
+
nonCollapsedPanelSize + DEFAULT_SIDE_PANEL_SIZE > this.availableWidth) {
|
|
57130
|
+
this.get(NotificationStore).notifyUser({
|
|
57131
|
+
sticky: false,
|
|
57132
|
+
type: "warning",
|
|
57133
|
+
text: _t("The window is too small to display multiple side panels."),
|
|
57134
|
+
});
|
|
57135
|
+
return;
|
|
57136
|
+
}
|
|
57137
|
+
this._openPanel("secondaryPanel", newPanelInfo, state);
|
|
57138
|
+
}
|
|
57139
|
+
_openPanel(panel, newPanel, state) {
|
|
57140
|
+
const currentPanel = this[panel];
|
|
57141
|
+
if (currentPanel && newPanel.componentTag !== currentPanel.componentTag) {
|
|
57142
|
+
currentPanel.initialPanelProps?.onCloseSidePanel?.();
|
|
57143
|
+
}
|
|
57144
|
+
this[panel] = {
|
|
57145
|
+
initialPanelProps: state.props ?? {},
|
|
57146
|
+
componentTag: newPanel.componentTag,
|
|
57147
|
+
size: currentPanel?.size || DEFAULT_SIDE_PANEL_SIZE,
|
|
57148
|
+
isCollapsed: currentPanel?.isCollapsed || false,
|
|
57149
|
+
isPinned: currentPanel && "isPinned" in currentPanel ? currentPanel.isPinned : false,
|
|
57150
|
+
};
|
|
57151
|
+
if (this[panel].isCollapsed) {
|
|
57152
|
+
this.toggleCollapsePanel(panel);
|
|
56271
57153
|
}
|
|
56272
|
-
this.componentTag = componentTag;
|
|
56273
|
-
this.initialPanelProps = state.props ?? {};
|
|
56274
57154
|
}
|
|
56275
57155
|
toggle(componentTag, panelProps) {
|
|
56276
|
-
|
|
57156
|
+
const panel = this.mainPanel?.isPinned ? this.secondaryPanel : this.mainPanel;
|
|
57157
|
+
if (panel && componentTag === panel.componentTag) {
|
|
56277
57158
|
this.close();
|
|
56278
57159
|
}
|
|
56279
57160
|
else {
|
|
@@ -56281,34 +57162,85 @@ class SidePanelStore extends SpreadsheetStore {
|
|
|
56281
57162
|
}
|
|
56282
57163
|
}
|
|
56283
57164
|
close() {
|
|
56284
|
-
this.
|
|
56285
|
-
|
|
56286
|
-
|
|
57165
|
+
if (this.mainPanel?.isPinned) {
|
|
57166
|
+
if (this.secondaryPanel) {
|
|
57167
|
+
this.secondaryPanel.initialPanelProps.onCloseSidePanel?.();
|
|
57168
|
+
this.secondaryPanel = undefined;
|
|
57169
|
+
}
|
|
57170
|
+
return;
|
|
57171
|
+
}
|
|
57172
|
+
this.mainPanel?.initialPanelProps.onCloseSidePanel?.();
|
|
57173
|
+
this.mainPanel = undefined;
|
|
56287
57174
|
}
|
|
56288
|
-
|
|
56289
|
-
|
|
56290
|
-
|
|
57175
|
+
closeMainPanel() {
|
|
57176
|
+
this.mainPanel?.initialPanelProps.onCloseSidePanel?.();
|
|
57177
|
+
this.mainPanel = this.secondaryPanel || undefined;
|
|
57178
|
+
this.secondaryPanel = undefined;
|
|
57179
|
+
}
|
|
57180
|
+
changePanelSize(panel, size) {
|
|
57181
|
+
const panelInfo = this[panel];
|
|
57182
|
+
if (!panelInfo || ("isCollapsed" in panelInfo && panelInfo.isCollapsed)) {
|
|
57183
|
+
return;
|
|
56291
57184
|
}
|
|
56292
|
-
|
|
56293
|
-
|
|
57185
|
+
size = Math.max(size, DEFAULT_SIDE_PANEL_SIZE);
|
|
57186
|
+
let otherPanelSize = panel === "mainPanel" ? this.secondaryPanel?.size || 0 : this.mainPanel?.size || 0;
|
|
57187
|
+
if (size > this.availableWidth - otherPanelSize) {
|
|
57188
|
+
if (panel === "mainPanel" && this.secondaryPanel) {
|
|
57189
|
+
// reduce the secondary panel size to fit the main panel
|
|
57190
|
+
this.secondaryPanel.size = Math.max(this.availableWidth - size, DEFAULT_SIDE_PANEL_SIZE);
|
|
57191
|
+
otherPanelSize = this.secondaryPanel.size;
|
|
57192
|
+
}
|
|
57193
|
+
size = Math.max(this.availableWidth - otherPanelSize, DEFAULT_SIDE_PANEL_SIZE);
|
|
56294
57194
|
}
|
|
56295
|
-
|
|
56296
|
-
|
|
57195
|
+
panelInfo.size = size;
|
|
57196
|
+
}
|
|
57197
|
+
resetPanelSize(panel) {
|
|
57198
|
+
const panelInfo = this[panel];
|
|
57199
|
+
if (!panelInfo) {
|
|
57200
|
+
return;
|
|
56297
57201
|
}
|
|
57202
|
+
panelInfo.size = DEFAULT_SIDE_PANEL_SIZE;
|
|
56298
57203
|
}
|
|
56299
|
-
|
|
56300
|
-
this.
|
|
57204
|
+
togglePinPanel() {
|
|
57205
|
+
if (!this.mainPanel) {
|
|
57206
|
+
return;
|
|
57207
|
+
}
|
|
57208
|
+
this.mainPanel.isPinned = !this.mainPanel.isPinned;
|
|
57209
|
+
if (!this.mainPanel.isPinned && this.secondaryPanel) {
|
|
57210
|
+
this.secondaryPanel?.initialPanelProps.onCloseSidePanel?.();
|
|
57211
|
+
this.mainPanel = this.secondaryPanel;
|
|
57212
|
+
this.secondaryPanel = undefined;
|
|
57213
|
+
}
|
|
56301
57214
|
}
|
|
56302
|
-
|
|
56303
|
-
const
|
|
56304
|
-
if (!
|
|
56305
|
-
return
|
|
56306
|
-
|
|
56307
|
-
|
|
56308
|
-
|
|
57215
|
+
toggleCollapsePanel(panel) {
|
|
57216
|
+
const panelInfo = this[panel];
|
|
57217
|
+
if (!panelInfo) {
|
|
57218
|
+
return;
|
|
57219
|
+
}
|
|
57220
|
+
if (panelInfo.isCollapsed) {
|
|
57221
|
+
panelInfo.isCollapsed = false;
|
|
57222
|
+
this.changePanelSize(panel, DEFAULT_SIDE_PANEL_SIZE);
|
|
56309
57223
|
}
|
|
56310
57224
|
else {
|
|
56311
|
-
|
|
57225
|
+
panelInfo.isCollapsed = true;
|
|
57226
|
+
panelInfo.size = COLLAPSED_SIDE_PANEL_SIZE;
|
|
57227
|
+
}
|
|
57228
|
+
}
|
|
57229
|
+
computeState({ componentTag, initialPanelProps }) {
|
|
57230
|
+
const customComputeState = sidePanelRegistry.get(componentTag).computeState;
|
|
57231
|
+
const state = customComputeState
|
|
57232
|
+
? customComputeState(this.getters, initialPanelProps)
|
|
57233
|
+
: { isOpen: true, props: initialPanelProps };
|
|
57234
|
+
return state.isOpen ? { ...state, key: state.key || componentTag } : state;
|
|
57235
|
+
}
|
|
57236
|
+
changeSpreadsheetWidth(width) {
|
|
57237
|
+
this.availableWidth = width - MIN_SHEET_VIEW_WIDTH;
|
|
57238
|
+
if (this.secondaryPanel && width - this.totalPanelSize < MIN_SHEET_VIEW_WIDTH) {
|
|
57239
|
+
this.secondaryPanel?.initialPanelProps.onCloseSidePanel?.();
|
|
57240
|
+
this.secondaryPanel = undefined;
|
|
57241
|
+
}
|
|
57242
|
+
if (this.mainPanel && width - this.totalPanelSize < MIN_SHEET_VIEW_WIDTH) {
|
|
57243
|
+
this.mainPanel.size = Math.max(width - MIN_SHEET_VIEW_WIDTH, DEFAULT_SIDE_PANEL_SIZE);
|
|
56312
57244
|
}
|
|
56313
57245
|
}
|
|
56314
57246
|
}
|
|
@@ -56456,11 +57388,11 @@ class Grid extends Component {
|
|
|
56456
57388
|
this.hoveredCell.clear();
|
|
56457
57389
|
});
|
|
56458
57390
|
this.cellPopovers = useStore(CellPopoverStore);
|
|
56459
|
-
useEffect(() => {
|
|
56460
|
-
if (!
|
|
57391
|
+
useEffect((isMainPanelOpen, isSecondaryPanelOpen) => {
|
|
57392
|
+
if (!isMainPanelOpen && !isSecondaryPanelOpen) {
|
|
56461
57393
|
this.DOMFocusableElementStore.focus();
|
|
56462
57394
|
}
|
|
56463
|
-
}, () => [this.sidePanel.
|
|
57395
|
+
}, () => [this.sidePanel.isMainPanelOpen, this.sidePanel.isSecondaryPanelOpen]);
|
|
56464
57396
|
useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
|
|
56465
57397
|
const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
|
|
56466
57398
|
return scrollY > 0;
|
|
@@ -59542,7 +60474,7 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
59542
60474
|
if (!rule)
|
|
59543
60475
|
return false;
|
|
59544
60476
|
return ((rule.criterion.type === "isValueInList" || rule.criterion.type === "isValueInRange") &&
|
|
59545
|
-
rule.criterion.displayStyle === "arrow");
|
|
60477
|
+
(rule.criterion.displayStyle === "arrow" || rule.criterion.displayStyle === "chip"));
|
|
59546
60478
|
}
|
|
59547
60479
|
addDataValidationRule(sheetId, newRule) {
|
|
59548
60480
|
const rules = this.rules[sheetId];
|
|
@@ -59552,7 +60484,7 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
59552
60484
|
else if (newRule.criterion.type === "isValueInList") {
|
|
59553
60485
|
newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
|
|
59554
60486
|
}
|
|
59555
|
-
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
|
|
60487
|
+
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules, newRule.id);
|
|
59556
60488
|
const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
|
|
59557
60489
|
if (ruleIndex !== -1) {
|
|
59558
60490
|
adaptedRules[ruleIndex] = newRule;
|
|
@@ -59562,9 +60494,12 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
59562
60494
|
this.history.update("rules", sheetId, [...adaptedRules, newRule]);
|
|
59563
60495
|
}
|
|
59564
60496
|
}
|
|
59565
|
-
removeRangesFromRules(sheetId, ranges, rules) {
|
|
60497
|
+
removeRangesFromRules(sheetId, ranges, rules, editingRuleId) {
|
|
59566
60498
|
rules = deepCopy(rules);
|
|
59567
60499
|
for (const rule of rules) {
|
|
60500
|
+
if (rule.id === editingRuleId) {
|
|
60501
|
+
continue; // Skip the rule being edited to preserve its place in the list
|
|
60502
|
+
}
|
|
59568
60503
|
rule.ranges = this.getters.recomputeRanges(rule.ranges, ranges);
|
|
59569
60504
|
}
|
|
59570
60505
|
return rules.filter((rule) => rule.ranges.length > 0);
|
|
@@ -62972,7 +63907,7 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
62972
63907
|
break;
|
|
62973
63908
|
}
|
|
62974
63909
|
case "UPDATE_PIVOT": {
|
|
62975
|
-
this.history.update("pivots", cmd.pivotId, "definition",
|
|
63910
|
+
this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
|
|
62976
63911
|
this.compileCalculatedMeasures(cmd.pivot.measures);
|
|
62977
63912
|
break;
|
|
62978
63913
|
}
|
|
@@ -63043,10 +63978,7 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
63043
63978
|
// Private
|
|
63044
63979
|
// -------------------------------------------------------------------------
|
|
63045
63980
|
addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
|
|
63046
|
-
this.history.update("pivots", pivotId, {
|
|
63047
|
-
definition: this.repairSortedColumn(deepCopy(pivot)),
|
|
63048
|
-
formulaId,
|
|
63049
|
-
});
|
|
63981
|
+
this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
|
|
63050
63982
|
this.compileCalculatedMeasures(pivot.measures);
|
|
63051
63983
|
this.history.update("formulaIds", formulaId, pivotId);
|
|
63052
63984
|
this.history.update("nextFormulaId", this.nextFormulaId + 1);
|
|
@@ -63135,7 +64067,6 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
63135
64067
|
}
|
|
63136
64068
|
}
|
|
63137
64069
|
checkSortedColumnInMeasures(definition) {
|
|
63138
|
-
definition = this.repairSortedColumn(definition);
|
|
63139
64070
|
const measures = definition.measures.map((measure) => measure.id);
|
|
63140
64071
|
if (definition.sortedColumn && !measures.includes(definition.sortedColumn.measure)) {
|
|
63141
64072
|
return "InvalidDefinition" /* CommandResult.InvalidDefinition */;
|
|
@@ -63149,26 +64080,6 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
63149
64080
|
}
|
|
63150
64081
|
return "Success" /* CommandResult.Success */;
|
|
63151
64082
|
}
|
|
63152
|
-
repairSortedColumn(definition) {
|
|
63153
|
-
if (definition.sortedColumn) {
|
|
63154
|
-
// Fix for an upgrade issue: the sortedColumn measure was not updated
|
|
63155
|
-
// from using fieldName to using id. If the sortedColumn measure matches
|
|
63156
|
-
// a measure fieldName in the definition, update it to use the measure's id instead
|
|
63157
|
-
// of its fieldName.
|
|
63158
|
-
// TODO: add an upgrade step to fix this in master and remove this code
|
|
63159
|
-
const sortedMeasure = definition.measures.find((measure) => measure.fieldName === definition.sortedColumn?.measure);
|
|
63160
|
-
if (sortedMeasure) {
|
|
63161
|
-
return {
|
|
63162
|
-
...definition,
|
|
63163
|
-
sortedColumn: {
|
|
63164
|
-
...definition.sortedColumn,
|
|
63165
|
-
measure: sortedMeasure.id,
|
|
63166
|
-
},
|
|
63167
|
-
};
|
|
63168
|
-
}
|
|
63169
|
-
}
|
|
63170
|
-
return definition;
|
|
63171
|
-
}
|
|
63172
64083
|
// ---------------------------------------------------------------------
|
|
63173
64084
|
// Import/Export
|
|
63174
64085
|
// ---------------------------------------------------------------------
|
|
@@ -63250,9 +64161,7 @@ class SettingsPlugin extends CorePlugin {
|
|
|
63250
64161
|
this.locale = data.settings?.locale ?? DEFAULT_LOCALE;
|
|
63251
64162
|
}
|
|
63252
64163
|
export(data) {
|
|
63253
|
-
data.settings = {
|
|
63254
|
-
locale: this.locale,
|
|
63255
|
-
};
|
|
64164
|
+
data.settings = { locale: this.locale };
|
|
63256
64165
|
}
|
|
63257
64166
|
}
|
|
63258
64167
|
|
|
@@ -65908,7 +66817,10 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
65908
66817
|
"getDataValidationInvalidCriterionValueMessage",
|
|
65909
66818
|
"getInvalidDataValidationMessage",
|
|
65910
66819
|
"getValidationResultForCellValue",
|
|
66820
|
+
"getDataValidationRangeValues",
|
|
65911
66821
|
"isCellValidCheckbox",
|
|
66822
|
+
"getDataValidationCellStyle",
|
|
66823
|
+
"getDataValidationChipStyle",
|
|
65912
66824
|
"isDataValidationInvalid",
|
|
65913
66825
|
];
|
|
65914
66826
|
validationResults = {};
|
|
@@ -65929,6 +66841,18 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
65929
66841
|
isDataValidationInvalid(cellPosition) {
|
|
65930
66842
|
return !this.getValidationResultForCell(cellPosition).isValid;
|
|
65931
66843
|
}
|
|
66844
|
+
getDataValidationCellStyle(position) {
|
|
66845
|
+
if (this.hasChip(position)) {
|
|
66846
|
+
return undefined; // The style is not applied on the cell if it's a chip
|
|
66847
|
+
}
|
|
66848
|
+
return this.getDataValidationStyle(position);
|
|
66849
|
+
}
|
|
66850
|
+
getDataValidationChipStyle(position) {
|
|
66851
|
+
if (this.hasChip(position)) {
|
|
66852
|
+
return this.getDataValidationStyle(position) ?? { fillColor: GRAY_200 };
|
|
66853
|
+
}
|
|
66854
|
+
return undefined;
|
|
66855
|
+
}
|
|
65932
66856
|
getInvalidDataValidationMessage(cellPosition) {
|
|
65933
66857
|
const validationResult = this.getValidationResultForCell(cellPosition);
|
|
65934
66858
|
return validationResult.isValid ? undefined : validationResult.error;
|
|
@@ -65951,6 +66875,11 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
65951
66875
|
}
|
|
65952
66876
|
return evaluator.isCriterionValueValid(value) ? undefined : evaluator.criterionValueErrorString;
|
|
65953
66877
|
}
|
|
66878
|
+
getDataValidationRangeValues(sheetId, criterion) {
|
|
66879
|
+
const range = this.getters.getRangeFromSheetXC(sheetId, String(criterion.values[0]));
|
|
66880
|
+
const criterionValues = this.getters.getRangeValues(range);
|
|
66881
|
+
return criterionValues.map((value) => value?.toString()).filter(isDefined);
|
|
66882
|
+
}
|
|
65954
66883
|
isCellValidCheckbox(cellPosition) {
|
|
65955
66884
|
if (!this.getters.isMainCellPosition(cellPosition)) {
|
|
65956
66885
|
return false;
|
|
@@ -65970,6 +66899,38 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
65970
66899
|
const error = this.getRuleErrorForCellValue(cellValue, cellPosition, rule);
|
|
65971
66900
|
return error ? { error, rule, isValid: false } : VALID_RESULT;
|
|
65972
66901
|
}
|
|
66902
|
+
hasChip(position) {
|
|
66903
|
+
const rule = this.getters.getValidationRuleForCell(position);
|
|
66904
|
+
return ((rule?.criterion.type === "isValueInList" || rule?.criterion.type === "isValueInRange") &&
|
|
66905
|
+
rule.criterion.displayStyle === "chip");
|
|
66906
|
+
}
|
|
66907
|
+
getDataValidationStyle(position) {
|
|
66908
|
+
const rule = this.getters.getValidationRuleForCell(position);
|
|
66909
|
+
if (!rule || this.isDataValidationInvalid(position)) {
|
|
66910
|
+
return undefined;
|
|
66911
|
+
}
|
|
66912
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
66913
|
+
const color = this.getValueColor(rule, evaluatedCell.value);
|
|
66914
|
+
if (!color) {
|
|
66915
|
+
return undefined;
|
|
66916
|
+
}
|
|
66917
|
+
const style = {
|
|
66918
|
+
fillColor: color,
|
|
66919
|
+
textColor: chipTextColor(color),
|
|
66920
|
+
};
|
|
66921
|
+
return style;
|
|
66922
|
+
}
|
|
66923
|
+
getValueColor(rule, value) {
|
|
66924
|
+
if (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange") {
|
|
66925
|
+
return undefined;
|
|
66926
|
+
}
|
|
66927
|
+
for (const criterionValue in rule.criterion.colors) {
|
|
66928
|
+
if (criterionValue.toLowerCase() === String(value).toLowerCase()) {
|
|
66929
|
+
return rule.criterion.colors[criterionValue];
|
|
66930
|
+
}
|
|
66931
|
+
}
|
|
66932
|
+
return undefined;
|
|
66933
|
+
}
|
|
65973
66934
|
isValidFormula(value) {
|
|
65974
66935
|
return !compile(value).isBadExpression;
|
|
65975
66936
|
}
|
|
@@ -66066,12 +67027,35 @@ iconsOnCellRegistry.add("data_validation_checkbox", (getters, position) => {
|
|
|
66066
67027
|
}
|
|
66067
67028
|
return undefined;
|
|
66068
67029
|
});
|
|
67030
|
+
iconsOnCellRegistry.add("data_validation_chip_icon", (getters, position) => {
|
|
67031
|
+
const chipStyle = getters.getDataValidationChipStyle(position);
|
|
67032
|
+
if (chipStyle) {
|
|
67033
|
+
const cellStyle = getters.getCellComputedStyle(position);
|
|
67034
|
+
return {
|
|
67035
|
+
svg: getChipSvg(chipStyle),
|
|
67036
|
+
hoverSvg: getHoveredChipSvg(chipStyle),
|
|
67037
|
+
priority: 10,
|
|
67038
|
+
horizontalAlign: "right",
|
|
67039
|
+
size: computeTextFontSizeInPixels(cellStyle),
|
|
67040
|
+
margin: 4,
|
|
67041
|
+
position,
|
|
67042
|
+
onClick: (position, env) => {
|
|
67043
|
+
const { col, row } = position;
|
|
67044
|
+
env.model.selection.selectCell(col, row);
|
|
67045
|
+
env.startCellEdition();
|
|
67046
|
+
},
|
|
67047
|
+
type: "data_validation_chip_icon",
|
|
67048
|
+
};
|
|
67049
|
+
}
|
|
67050
|
+
return undefined;
|
|
67051
|
+
});
|
|
66069
67052
|
iconsOnCellRegistry.add("data_validation_list_icon", (getters, position) => {
|
|
66070
67053
|
const hasIcon = !getters.isReadonly() && getters.cellHasListDataValidationIcon(position);
|
|
66071
67054
|
if (hasIcon) {
|
|
67055
|
+
const cellStyle = getters.getCellComputedStyle(position);
|
|
66072
67056
|
return {
|
|
66073
|
-
svg:
|
|
66074
|
-
hoverSvg:
|
|
67057
|
+
svg: getCaretDownSvg(cellStyle),
|
|
67058
|
+
hoverSvg: getHoveredCaretDownSvg(cellStyle),
|
|
66075
67059
|
priority: 2,
|
|
66076
67060
|
horizontalAlign: "right",
|
|
66077
67061
|
size: GRID_ICON_EDGE_LENGTH,
|
|
@@ -66212,11 +67196,8 @@ class CellIconPlugin extends CoreViewPlugin {
|
|
|
66212
67196
|
}
|
|
66213
67197
|
return this.cellIconsCache[position.sheetId][position.col][position.row];
|
|
66214
67198
|
}
|
|
66215
|
-
getCellIconRect(icon) {
|
|
67199
|
+
getCellIconRect(icon, cellRect) {
|
|
66216
67200
|
const cellPosition = icon.position;
|
|
66217
|
-
const merge = this.getters.getMerge(cellPosition);
|
|
66218
|
-
const zone = merge || positionToZone(cellPosition);
|
|
66219
|
-
const cellRect = this.getters.getRect(zone);
|
|
66220
67201
|
const cell = this.getters.getCell(cellPosition);
|
|
66221
67202
|
const x = this.getIconHorizontalPosition(cellRect, icon.horizontalAlign, icon);
|
|
66222
67203
|
const y = this.getters.computeTextYCoordinate(cellRect, icon.size, cell?.style?.verticalAlign);
|
|
@@ -68613,11 +69594,11 @@ class OTRegistry extends Registry {
|
|
|
68613
69594
|
* transformation function given
|
|
68614
69595
|
*/
|
|
68615
69596
|
addTransformation(executed, toTransforms, fn) {
|
|
69597
|
+
if (!this.content[executed]) {
|
|
69598
|
+
this.content[executed] = new Map();
|
|
69599
|
+
}
|
|
68616
69600
|
for (const toTransform of toTransforms) {
|
|
68617
|
-
|
|
68618
|
-
this.content[toTransform] = new Map();
|
|
68619
|
-
}
|
|
68620
|
-
this.content[toTransform].set(executed, fn);
|
|
69601
|
+
this.content[executed].set(toTransform, fn);
|
|
68621
69602
|
}
|
|
68622
69603
|
return this;
|
|
68623
69604
|
}
|
|
@@ -68626,7 +69607,7 @@ class OTRegistry extends Registry {
|
|
|
68626
69607
|
* that the executed command happened.
|
|
68627
69608
|
*/
|
|
68628
69609
|
getTransformation(toTransform, executed) {
|
|
68629
|
-
return this.content[
|
|
69610
|
+
return this.content[executed] && this.content[executed].get(toTransform);
|
|
68630
69611
|
}
|
|
68631
69612
|
}
|
|
68632
69613
|
const otRegistry = new OTRegistry();
|
|
@@ -68956,10 +69937,20 @@ function adaptTransform(toTransform, executed) {
|
|
|
68956
69937
|
*/
|
|
68957
69938
|
function transformAll(toTransform, executed) {
|
|
68958
69939
|
let transformedCommands = [...toTransform];
|
|
69940
|
+
const possibleTransformations = new Set(otRegistry.getKeys());
|
|
68959
69941
|
for (const executedCommand of executed) {
|
|
68960
|
-
|
|
68961
|
-
|
|
68962
|
-
|
|
69942
|
+
// If the executed command is not in the registry, we skip it
|
|
69943
|
+
// because we know there won't be any transformation impacting the
|
|
69944
|
+
// commands to transform.
|
|
69945
|
+
if (possibleTransformations.has(executedCommand.type)) {
|
|
69946
|
+
transformedCommands = transformedCommands.reduce((acc, cmd) => {
|
|
69947
|
+
const transformed = transform(cmd, executedCommand);
|
|
69948
|
+
if (transformed) {
|
|
69949
|
+
acc.push(transformed);
|
|
69950
|
+
}
|
|
69951
|
+
return acc;
|
|
69952
|
+
}, []);
|
|
69953
|
+
}
|
|
68963
69954
|
}
|
|
68964
69955
|
return transformedCommands;
|
|
68965
69956
|
}
|
|
@@ -70516,6 +71507,9 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
70516
71507
|
for (const icon of this.getters.getCellIcons(position)) {
|
|
70517
71508
|
contentWidth += icon.margin + icon.size;
|
|
70518
71509
|
}
|
|
71510
|
+
if (this.getters.getDataValidationChipStyle(position)) {
|
|
71511
|
+
contentWidth += DATA_VALIDATION_CHIP_MARGIN * 2;
|
|
71512
|
+
}
|
|
70519
71513
|
if (contentWidth === 0) {
|
|
70520
71514
|
return 0;
|
|
70521
71515
|
}
|
|
@@ -70673,7 +71667,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
70673
71667
|
}
|
|
70674
71668
|
const position = this.getters.getCellPosition(cell.id);
|
|
70675
71669
|
const colSize = this.getters.getColSize(sheetId, position.col);
|
|
70676
|
-
if (cell.isFormula) {
|
|
71670
|
+
if (cell.isFormula || this.getters.getArrayFormulaSpreadingOn(position)) {
|
|
70677
71671
|
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
70678
71672
|
const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
|
|
70679
71673
|
if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
|
|
@@ -70876,6 +71870,8 @@ class CellComputedStylePlugin extends UIPlugin {
|
|
|
70876
71870
|
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
70877
71871
|
cmd.type === "UPDATE_CELL" ||
|
|
70878
71872
|
cmd.type === "SET_FORMATTING" ||
|
|
71873
|
+
cmd.type === "ADD_DATA_VALIDATION_RULE" ||
|
|
71874
|
+
cmd.type === "REMOVE_DATA_VALIDATION_RULE" ||
|
|
70879
71875
|
cmd.type === "EVALUATE_CELLS") {
|
|
70880
71876
|
this.styles = {};
|
|
70881
71877
|
this.borders = {};
|
|
@@ -70947,8 +71943,10 @@ class CellComputedStylePlugin extends UIPlugin {
|
|
|
70947
71943
|
const cell = this.getters.getCell(position);
|
|
70948
71944
|
const cfStyle = this.getters.getCellConditionalFormatStyle(position);
|
|
70949
71945
|
const tableStyle = this.getters.getCellTableStyle(position);
|
|
71946
|
+
const dataValidationStyle = this.getters.getDataValidationCellStyle(position);
|
|
70950
71947
|
const computedStyle = {
|
|
70951
71948
|
...removeFalsyAttributes(tableStyle),
|
|
71949
|
+
...removeFalsyAttributes(dataValidationStyle),
|
|
70952
71950
|
...removeFalsyAttributes(cell?.style),
|
|
70953
71951
|
...removeFalsyAttributes(cfStyle),
|
|
70954
71952
|
};
|
|
@@ -72032,49 +73030,17 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
72032
73030
|
if (!copiedData) {
|
|
72033
73031
|
return;
|
|
72034
73032
|
}
|
|
72035
|
-
let zone = undefined;
|
|
72036
|
-
const selectedZones = [];
|
|
72037
73033
|
const sheetId = this.getters.getActiveSheetId();
|
|
72038
|
-
const target = {
|
|
72039
|
-
sheetId,
|
|
72040
|
-
zones,
|
|
72041
|
-
};
|
|
72042
73034
|
const handlers = this.selectClipboardHandlers(copiedData);
|
|
72043
|
-
|
|
72044
|
-
const handlerData = copiedData[handlerName];
|
|
72045
|
-
if (!handlerData) {
|
|
72046
|
-
continue;
|
|
72047
|
-
}
|
|
72048
|
-
const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
|
|
72049
|
-
if (currentTarget.figureId) {
|
|
72050
|
-
target.figureId = currentTarget.figureId;
|
|
72051
|
-
}
|
|
72052
|
-
for (const targetZone of currentTarget.zones) {
|
|
72053
|
-
selectedZones.push(targetZone);
|
|
72054
|
-
if (zone === undefined) {
|
|
72055
|
-
zone = targetZone;
|
|
72056
|
-
continue;
|
|
72057
|
-
}
|
|
72058
|
-
zone = union(zone, targetZone);
|
|
72059
|
-
}
|
|
72060
|
-
}
|
|
73035
|
+
const { target, zone, selectedZones } = getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options);
|
|
72061
73036
|
if (zone !== undefined) {
|
|
72062
|
-
this.addMissingDimensions(
|
|
73037
|
+
this.addMissingDimensions(sheetId, zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
|
|
72063
73038
|
}
|
|
72064
|
-
handlers
|
|
72065
|
-
const handlerData = copiedData[handlerName];
|
|
72066
|
-
if (handlerData) {
|
|
72067
|
-
handler.paste(target, handlerData, options);
|
|
72068
|
-
}
|
|
72069
|
-
});
|
|
73039
|
+
applyClipboardHandlersPaste(handlers, copiedData, target, options);
|
|
72070
73040
|
if (!options?.selectTarget) {
|
|
72071
73041
|
return;
|
|
72072
73042
|
}
|
|
72073
|
-
|
|
72074
|
-
const col = selection.left;
|
|
72075
|
-
const row = selection.top;
|
|
72076
|
-
this.selection.getBackToDefault();
|
|
72077
|
-
this.selection.selectZone({ cell: { col, row }, zone: union(...selectedZones) }, { scrollIntoView: false });
|
|
73043
|
+
selectPastedZone(this.selection, zones, selectedZones);
|
|
72078
73044
|
}
|
|
72079
73045
|
/**
|
|
72080
73046
|
* Add columns and/or rows to ensure that col + width and row + height are still
|
|
@@ -74510,19 +75476,29 @@ autoCompleteProviders.add("dataValidation", {
|
|
|
74510
75476
|
(rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
|
|
74511
75477
|
return [];
|
|
74512
75478
|
}
|
|
74513
|
-
|
|
74514
|
-
|
|
74515
|
-
|
|
74516
|
-
|
|
74517
|
-
|
|
74518
|
-
|
|
74519
|
-
values
|
|
74520
|
-
|
|
74521
|
-
|
|
74522
|
-
|
|
74523
|
-
|
|
74524
|
-
|
|
74525
|
-
|
|
75479
|
+
const sheetId = this.composer.currentEditedCell.sheetId;
|
|
75480
|
+
const values = rule.criterion.type === "isValueInRange"
|
|
75481
|
+
? Array.from(new Set(this.getters.getDataValidationRangeValues(sheetId, rule.criterion)))
|
|
75482
|
+
: rule.criterion.values;
|
|
75483
|
+
const isChip = rule.criterion.displayStyle === "chip";
|
|
75484
|
+
if (!isChip) {
|
|
75485
|
+
return values.map((value) => ({ text: value }));
|
|
75486
|
+
}
|
|
75487
|
+
const colors = rule.criterion.colors;
|
|
75488
|
+
return values.map((value) => {
|
|
75489
|
+
const color = colors?.[value];
|
|
75490
|
+
return {
|
|
75491
|
+
text: value,
|
|
75492
|
+
htmlContent: [
|
|
75493
|
+
{
|
|
75494
|
+
value,
|
|
75495
|
+
color: color ? chipTextColor(color) : undefined,
|
|
75496
|
+
backgroundColor: color || GRAY_200,
|
|
75497
|
+
classes: ["badge rounded-pill fs-6 fw-normal w-100 mt-1 text-start"],
|
|
75498
|
+
},
|
|
75499
|
+
],
|
|
75500
|
+
};
|
|
75501
|
+
});
|
|
74526
75502
|
},
|
|
74527
75503
|
selectProposal(tokenAtCursor, value) {
|
|
74528
75504
|
this.composer.setCurrentContent(value);
|
|
@@ -74893,6 +75869,17 @@ clickableCellRegistry.add("link", {
|
|
|
74893
75869
|
return !!getters.getEvaluatedCell(position).link;
|
|
74894
75870
|
},
|
|
74895
75871
|
execute: (position, env, isMiddleClick) => openLink(env.model.getters.getEvaluatedCell(position).link, env, isMiddleClick),
|
|
75872
|
+
title: (position, getters) => {
|
|
75873
|
+
const link = getters.getEvaluatedCell(position).link;
|
|
75874
|
+
if (!link)
|
|
75875
|
+
return "";
|
|
75876
|
+
if (link.isExternal) {
|
|
75877
|
+
return _t("Go to url: %(url)s", { url: link.url });
|
|
75878
|
+
}
|
|
75879
|
+
else {
|
|
75880
|
+
return _t("Go to %(label)s", { label: link.label });
|
|
75881
|
+
}
|
|
75882
|
+
},
|
|
74896
75883
|
sequence: 5,
|
|
74897
75884
|
});
|
|
74898
75885
|
|
|
@@ -76604,12 +77591,13 @@ class ClickableCellsStore extends SpreadsheetStore {
|
|
|
76604
77591
|
if (!item) {
|
|
76605
77592
|
continue;
|
|
76606
77593
|
}
|
|
77594
|
+
const title = typeof item.title === "function" ? item.title(position, getters) : item.title;
|
|
76607
77595
|
const zone = getters.expandZone(sheetId, positionToZone(position));
|
|
76608
77596
|
cells.push({
|
|
76609
77597
|
coordinates: getters.getVisibleRect(zone),
|
|
76610
77598
|
position,
|
|
76611
77599
|
action: item.execute,
|
|
76612
|
-
title:
|
|
77600
|
+
title: title || "",
|
|
76613
77601
|
});
|
|
76614
77602
|
}
|
|
76615
77603
|
return cells;
|
|
@@ -76994,24 +77982,36 @@ css /* scss */ `
|
|
|
76994
77982
|
user-select: none;
|
|
76995
77983
|
color: ${TEXT_BODY};
|
|
76996
77984
|
|
|
77985
|
+
&.collapsed {
|
|
77986
|
+
padding: 8px;
|
|
77987
|
+
cursor: pointer;
|
|
77988
|
+
|
|
77989
|
+
.o-sidePanelTitle {
|
|
77990
|
+
writing-mode: vertical-rl;
|
|
77991
|
+
text-orientation: mixed;
|
|
77992
|
+
}
|
|
77993
|
+
}
|
|
77994
|
+
|
|
76997
77995
|
.o-sidePanelTitle {
|
|
76998
77996
|
line-height: 20px;
|
|
76999
77997
|
font-size: 16px;
|
|
77000
77998
|
}
|
|
77001
77999
|
|
|
77002
78000
|
.o-sidePanelHeader {
|
|
77003
|
-
padding: 8px
|
|
77004
|
-
display: flex;
|
|
77005
|
-
align-items: center;
|
|
77006
|
-
justify-content: space-between;
|
|
78001
|
+
padding: 8px;
|
|
77007
78002
|
border-bottom: 1px solid ${GRAY_300};
|
|
78003
|
+
}
|
|
77008
78004
|
|
|
77009
|
-
|
|
77010
|
-
|
|
77011
|
-
|
|
77012
|
-
|
|
77013
|
-
|
|
77014
|
-
}
|
|
78005
|
+
.o-sidePanelAction {
|
|
78006
|
+
padding: 5px 10px;
|
|
78007
|
+
cursor: pointer;
|
|
78008
|
+
|
|
78009
|
+
&.active {
|
|
78010
|
+
background-color: ${BUTTON_ACTIVE_BG};
|
|
78011
|
+
}
|
|
78012
|
+
|
|
78013
|
+
&:hover {
|
|
78014
|
+
background-color: ${BUTTON_HOVER_BG};
|
|
77015
78015
|
}
|
|
77016
78016
|
}
|
|
77017
78017
|
.o-sidePanelBody-container {
|
|
@@ -77088,43 +78088,114 @@ css /* scss */ `
|
|
|
77088
78088
|
`;
|
|
77089
78089
|
class SidePanel extends Component {
|
|
77090
78090
|
static template = "o-spreadsheet-SidePanel";
|
|
78091
|
+
static props = {
|
|
78092
|
+
panelContent: Object,
|
|
78093
|
+
panelProps: Object,
|
|
78094
|
+
onCloseSidePanel: Function,
|
|
78095
|
+
onStartHandleDrag: Function,
|
|
78096
|
+
onResetPanelSize: Function,
|
|
78097
|
+
isPinned: { type: Boolean, optional: true },
|
|
78098
|
+
onTogglePinPanel: { type: Function, optional: true },
|
|
78099
|
+
onToggleCollapsePanel: { type: Function, optional: true },
|
|
78100
|
+
isCollapsed: { type: Boolean, optional: true },
|
|
78101
|
+
};
|
|
78102
|
+
spreadsheetRect = useSpreadsheetRect();
|
|
78103
|
+
getTitle() {
|
|
78104
|
+
const panel = this.props.panelContent;
|
|
78105
|
+
return typeof panel.title === "function"
|
|
78106
|
+
? panel.title(this.env, this.props.panelProps)
|
|
78107
|
+
: panel.title;
|
|
78108
|
+
}
|
|
78109
|
+
get pinInfoMessage() {
|
|
78110
|
+
return _t("Pin this panel to allow to open another side panel beside it.");
|
|
78111
|
+
}
|
|
78112
|
+
}
|
|
78113
|
+
|
|
78114
|
+
class SidePanels extends Component {
|
|
78115
|
+
static template = "o-spreadsheet-SidePanels";
|
|
77091
78116
|
static props = {};
|
|
78117
|
+
static components = { SidePanel };
|
|
77092
78118
|
sidePanelStore;
|
|
77093
78119
|
spreadsheetRect = useSpreadsheetRect();
|
|
77094
78120
|
setup() {
|
|
77095
78121
|
this.sidePanelStore = useStore(SidePanelStore);
|
|
77096
|
-
useEffect((
|
|
77097
|
-
if (!
|
|
78122
|
+
useEffect(() => {
|
|
78123
|
+
if (this.sidePanelStore.mainPanel && !this.sidePanelStore.isMainPanelOpen) {
|
|
78124
|
+
this.sidePanelStore.closeMainPanel();
|
|
78125
|
+
}
|
|
78126
|
+
if (this.sidePanelStore.secondaryPanel && !this.sidePanelStore.isSecondaryPanelOpen) {
|
|
77098
78127
|
this.sidePanelStore.close();
|
|
77099
78128
|
}
|
|
77100
|
-
}, () => [this.sidePanelStore.
|
|
77101
|
-
}
|
|
77102
|
-
get panel() {
|
|
77103
|
-
return sidePanelRegistry.get(this.sidePanelStore.componentTag);
|
|
78129
|
+
}, () => [this.sidePanelStore.isMainPanelOpen, this.sidePanelStore.isSecondaryPanelOpen]);
|
|
77104
78130
|
}
|
|
77105
|
-
|
|
77106
|
-
this.sidePanelStore.close();
|
|
77107
|
-
}
|
|
77108
|
-
getTitle() {
|
|
77109
|
-
const panel = this.panel;
|
|
77110
|
-
return typeof panel.title === "function"
|
|
77111
|
-
? panel.title(this.env, this.sidePanelStore.panelProps)
|
|
77112
|
-
: panel.title;
|
|
77113
|
-
}
|
|
77114
|
-
startHandleDrag(ev) {
|
|
78131
|
+
startHandleDrag(panel, ev) {
|
|
77115
78132
|
const startingCursor = document.body.style.cursor;
|
|
77116
|
-
const
|
|
78133
|
+
const panelInfo = panel === "mainPanel" ? this.sidePanelStore.mainPanel : this.sidePanelStore.secondaryPanel;
|
|
78134
|
+
if (!panelInfo) {
|
|
78135
|
+
return;
|
|
78136
|
+
}
|
|
78137
|
+
const startSize = panelInfo.size;
|
|
77117
78138
|
const startPosition = ev.clientX;
|
|
77118
78139
|
const onMouseMove = (ev) => {
|
|
77119
78140
|
document.body.style.cursor = "col-resize";
|
|
77120
78141
|
const newSize = startSize + startPosition - ev.clientX;
|
|
77121
|
-
this.sidePanelStore.changePanelSize(
|
|
78142
|
+
this.sidePanelStore.changePanelSize(panel, newSize);
|
|
77122
78143
|
};
|
|
77123
78144
|
const cleanUp = () => {
|
|
77124
78145
|
document.body.style.cursor = startingCursor;
|
|
77125
78146
|
};
|
|
77126
78147
|
startDnd(onMouseMove, cleanUp);
|
|
77127
78148
|
}
|
|
78149
|
+
get mainPanelProps() {
|
|
78150
|
+
const panelProps = this.sidePanelStore.mainPanelProps;
|
|
78151
|
+
if (!this.sidePanelStore.mainPanel || !panelProps) {
|
|
78152
|
+
return undefined;
|
|
78153
|
+
}
|
|
78154
|
+
return {
|
|
78155
|
+
panelContent: sidePanelRegistry.get(this.sidePanelStore.mainPanel.componentTag),
|
|
78156
|
+
panelProps,
|
|
78157
|
+
onCloseSidePanel: () => this.sidePanelStore.closeMainPanel(),
|
|
78158
|
+
onTogglePinPanel: () => this.sidePanelStore.togglePinPanel(),
|
|
78159
|
+
onStartHandleDrag: (ev) => this.startHandleDrag("mainPanel", ev),
|
|
78160
|
+
onResetPanelSize: () => this.sidePanelStore.resetPanelSize("mainPanel"),
|
|
78161
|
+
isPinned: this.sidePanelStore.mainPanel?.isPinned,
|
|
78162
|
+
onToggleCollapsePanel: () => this.sidePanelStore.toggleCollapsePanel("mainPanel"),
|
|
78163
|
+
isCollapsed: this.sidePanelStore.mainPanel?.isCollapsed,
|
|
78164
|
+
};
|
|
78165
|
+
}
|
|
78166
|
+
get secondaryPanelProps() {
|
|
78167
|
+
const panelProps = this.sidePanelStore.secondaryPanelProps;
|
|
78168
|
+
if (!this.sidePanelStore.secondaryPanel || !panelProps) {
|
|
78169
|
+
return undefined;
|
|
78170
|
+
}
|
|
78171
|
+
return {
|
|
78172
|
+
panelContent: sidePanelRegistry.get(this.sidePanelStore.secondaryPanel.componentTag),
|
|
78173
|
+
panelProps,
|
|
78174
|
+
onCloseSidePanel: () => this.sidePanelStore.close(),
|
|
78175
|
+
onStartHandleDrag: (ev) => this.startHandleDrag("secondaryPanel", ev),
|
|
78176
|
+
onResetPanelSize: () => this.sidePanelStore.resetPanelSize("secondaryPanel"),
|
|
78177
|
+
onToggleCollapsePanel: () => this.sidePanelStore.toggleCollapsePanel("secondaryPanel"),
|
|
78178
|
+
isCollapsed: this.sidePanelStore.secondaryPanel?.isCollapsed,
|
|
78179
|
+
};
|
|
78180
|
+
}
|
|
78181
|
+
get panelList() {
|
|
78182
|
+
return [
|
|
78183
|
+
{
|
|
78184
|
+
key: this.sidePanelStore.secondaryPanelKey,
|
|
78185
|
+
props: this.secondaryPanelProps,
|
|
78186
|
+
style: this.sidePanelStore.secondaryPanel
|
|
78187
|
+
? cssPropertiesToCss({ width: `${this.sidePanelStore.secondaryPanel.size}px` })
|
|
78188
|
+
: "",
|
|
78189
|
+
},
|
|
78190
|
+
{
|
|
78191
|
+
key: this.sidePanelStore.mainPanelKey,
|
|
78192
|
+
props: this.mainPanelProps,
|
|
78193
|
+
style: this.sidePanelStore.mainPanel
|
|
78194
|
+
? cssPropertiesToCss({ width: `${this.sidePanelStore.mainPanel.size}px` })
|
|
78195
|
+
: "",
|
|
78196
|
+
},
|
|
78197
|
+
].filter((panel) => panel.key && panel.props);
|
|
78198
|
+
}
|
|
77128
78199
|
}
|
|
77129
78200
|
|
|
77130
78201
|
class RibbonMenu extends Component {
|
|
@@ -78686,7 +79757,7 @@ class Spreadsheet extends Component {
|
|
|
78686
79757
|
Grid,
|
|
78687
79758
|
BottomBar,
|
|
78688
79759
|
SmallBottomBar,
|
|
78689
|
-
|
|
79760
|
+
SidePanels,
|
|
78690
79761
|
SpreadsheetDashboard,
|
|
78691
79762
|
HeaderGroupContainer,
|
|
78692
79763
|
FullScreenChart,
|
|
@@ -78709,7 +79780,9 @@ class Spreadsheet extends Component {
|
|
|
78709
79780
|
else {
|
|
78710
79781
|
properties["grid-template-rows"] = `min-content auto min-content`;
|
|
78711
79782
|
}
|
|
78712
|
-
const columnWidth = this.sidePanel.
|
|
79783
|
+
const columnWidth = this.sidePanel.mainPanel
|
|
79784
|
+
? `${this.sidePanel.totalPanelSize || DEFAULT_SIDE_PANEL_SIZE}px`
|
|
79785
|
+
: "auto";
|
|
78713
79786
|
properties["grid-template-columns"] = `auto ${columnWidth}`;
|
|
78714
79787
|
return cssPropertiesToCss(properties);
|
|
78715
79788
|
}
|
|
@@ -78793,7 +79866,7 @@ class Spreadsheet extends Component {
|
|
|
78793
79866
|
this.checkViewportSize();
|
|
78794
79867
|
});
|
|
78795
79868
|
const resizeObserver = new ResizeObserver(() => {
|
|
78796
|
-
this.sidePanel.
|
|
79869
|
+
this.sidePanel.changeSpreadsheetWidth(this.spreadsheetRect.width);
|
|
78797
79870
|
});
|
|
78798
79871
|
}
|
|
78799
79872
|
bindModelEvents() {
|
|
@@ -83342,6 +84415,6 @@ const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
|
|
|
83342
84415
|
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, ClientDisconnectedError, CommandResult, CorePlugin, CoreViewPlugin, DispatchResult, EvaluationError, LocalTransportService, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, chartHelpers, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateChartEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
|
|
83343
84416
|
|
|
83344
84417
|
|
|
83345
|
-
__info__.version = "18.4.0
|
|
83346
|
-
__info__.date = "2025-06-
|
|
83347
|
-
__info__.hash = "
|
|
84418
|
+
__info__.version = "18.4.0";
|
|
84419
|
+
__info__.date = "2025-06-24T11:19:24.606Z";
|
|
84420
|
+
__info__.hash = "a5b7cad";
|