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