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