@odoo/o-spreadsheet 18.4.0-alpha.9 → 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 +1045 -216
- package/dist/o-spreadsheet.d.ts +152 -51
- package/dist/o-spreadsheet.esm.js +1045 -216
- package/dist/o-spreadsheet.iife.js +1045 -216
- package/dist/o-spreadsheet.iife.min.js +412 -400
- package/dist/o_spreadsheet.xml +76 -22
- package/package.json +2 -2
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* This file is generated by o-spreadsheet build tools. Do not edit it.
|
|
4
4
|
* @see https://github.com/odoo/o-spreadsheet
|
|
5
|
-
* @version 18.4.0
|
|
6
|
-
* @date 2025-06-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.4.0
|
|
6
|
+
* @date 2025-06-24T11:19:24.606Z
|
|
7
|
+
* @hash a5b7cad
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
@@ -164,6 +164,7 @@ const FROZEN_PANE_HEADER_BORDER_COLOR = "#BCBCBC";
|
|
|
164
164
|
const FROZEN_PANE_BORDER_COLOR = "#DADFE8";
|
|
165
165
|
const COMPOSER_ASSISTANT_COLOR = "#9B359B";
|
|
166
166
|
const COLOR_TRANSPARENT = "#00000000";
|
|
167
|
+
const TABLE_HOVER_BACKGROUND_COLOR = "#017E8414";
|
|
167
168
|
const CHART_WATERFALL_POSITIVE_COLOR = "#4EA7F2";
|
|
168
169
|
const CHART_WATERFALL_NEGATIVE_COLOR = "#EA6175";
|
|
169
170
|
const CHART_WATERFALL_SUBTOTAL_COLOR = "#AAAAAA";
|
|
@@ -6849,19 +6850,17 @@ function getDefaultContextFont(fontSize, bold = false, italic = false) {
|
|
|
6849
6850
|
const textWidthCache = {};
|
|
6850
6851
|
function computeTextWidth(context, text, style, fontUnit = "pt") {
|
|
6851
6852
|
const font = computeTextFont(style, fontUnit);
|
|
6852
|
-
context
|
|
6853
|
-
context.font = font;
|
|
6854
|
-
const width = computeCachedTextWidth(context, text);
|
|
6855
|
-
context.restore();
|
|
6856
|
-
return width;
|
|
6853
|
+
return computeCachedTextWidth(context, text, font);
|
|
6857
6854
|
}
|
|
6858
|
-
function computeCachedTextWidth(context, text) {
|
|
6859
|
-
const font = context.font;
|
|
6855
|
+
function computeCachedTextWidth(context, text, font) {
|
|
6860
6856
|
if (!textWidthCache[font]) {
|
|
6861
6857
|
textWidthCache[font] = {};
|
|
6862
6858
|
}
|
|
6863
6859
|
if (textWidthCache[font][text] === undefined) {
|
|
6860
|
+
const oldFont = context.font;
|
|
6861
|
+
context.font = font;
|
|
6864
6862
|
textWidthCache[font][text] = context.measureText(text).width;
|
|
6863
|
+
context.font = oldFont;
|
|
6865
6864
|
}
|
|
6866
6865
|
return textWidthCache[font][text];
|
|
6867
6866
|
}
|
|
@@ -7026,19 +7025,19 @@ function getContextFontSize(font) {
|
|
|
7026
7025
|
}
|
|
7027
7026
|
// Inspired from https://stackoverflow.com/a/10511598
|
|
7028
7027
|
function clipTextWithEllipsis(ctx, text, maxWidth) {
|
|
7029
|
-
let width = computeCachedTextWidth(ctx, text);
|
|
7028
|
+
let width = computeCachedTextWidth(ctx, text, ctx.font);
|
|
7030
7029
|
if (width <= maxWidth) {
|
|
7031
7030
|
return text;
|
|
7032
7031
|
}
|
|
7033
7032
|
const ellipsis = "…";
|
|
7034
|
-
const ellipsisWidth = computeCachedTextWidth(ctx, ellipsis);
|
|
7033
|
+
const ellipsisWidth = computeCachedTextWidth(ctx, ellipsis, ctx.font);
|
|
7035
7034
|
if (width <= ellipsisWidth) {
|
|
7036
7035
|
return text;
|
|
7037
7036
|
}
|
|
7038
7037
|
let len = text.length;
|
|
7039
7038
|
while (width >= maxWidth - ellipsisWidth && len-- > 0) {
|
|
7040
7039
|
text = text.substring(0, len);
|
|
7041
|
-
width = computeCachedTextWidth(ctx, text);
|
|
7040
|
+
width = computeCachedTextWidth(ctx, text, ctx.font);
|
|
7042
7041
|
}
|
|
7043
7042
|
return text + ellipsis;
|
|
7044
7043
|
}
|
|
@@ -7252,6 +7251,63 @@ function parseOSClipboardContent(content, clipboardId) {
|
|
|
7252
7251
|
};
|
|
7253
7252
|
return osClipboardContent;
|
|
7254
7253
|
}
|
|
7254
|
+
/**
|
|
7255
|
+
* Applies each clipboard handler to paste its corresponding data into the target.
|
|
7256
|
+
*/
|
|
7257
|
+
const applyClipboardHandlersPaste = (handlers, copiedData, target, options) => {
|
|
7258
|
+
handlers.forEach(({ handlerName, handler }) => {
|
|
7259
|
+
const data = copiedData[handlerName];
|
|
7260
|
+
if (data) {
|
|
7261
|
+
handler.paste(target, data, options);
|
|
7262
|
+
}
|
|
7263
|
+
});
|
|
7264
|
+
};
|
|
7265
|
+
/**
|
|
7266
|
+
* Returns the paste target based on clipboard handlers.
|
|
7267
|
+
* Also includes the full affected zone and the list of pasted zones for selection.
|
|
7268
|
+
*/
|
|
7269
|
+
function getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options) {
|
|
7270
|
+
let zone = undefined;
|
|
7271
|
+
const selectedZones = [];
|
|
7272
|
+
const target = {
|
|
7273
|
+
sheetId,
|
|
7274
|
+
zones,
|
|
7275
|
+
};
|
|
7276
|
+
for (const { handlerName, handler } of handlers) {
|
|
7277
|
+
const handlerData = copiedData[handlerName];
|
|
7278
|
+
if (!handlerData) {
|
|
7279
|
+
continue;
|
|
7280
|
+
}
|
|
7281
|
+
const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
|
|
7282
|
+
if (currentTarget.figureId) {
|
|
7283
|
+
target.figureId = currentTarget.figureId;
|
|
7284
|
+
}
|
|
7285
|
+
for (const targetZone of currentTarget.zones) {
|
|
7286
|
+
selectedZones.push(targetZone);
|
|
7287
|
+
if (zone === undefined) {
|
|
7288
|
+
zone = targetZone;
|
|
7289
|
+
continue;
|
|
7290
|
+
}
|
|
7291
|
+
zone = union(zone, targetZone);
|
|
7292
|
+
}
|
|
7293
|
+
}
|
|
7294
|
+
return {
|
|
7295
|
+
target,
|
|
7296
|
+
zone,
|
|
7297
|
+
selectedZones,
|
|
7298
|
+
};
|
|
7299
|
+
}
|
|
7300
|
+
/**
|
|
7301
|
+
* Updates the selection after a paste operation.
|
|
7302
|
+
*/
|
|
7303
|
+
const selectPastedZone = (selection, sourceZones, pastedZones) => {
|
|
7304
|
+
const anchorCell = {
|
|
7305
|
+
col: sourceZones[0].left,
|
|
7306
|
+
row: sourceZones[0].top,
|
|
7307
|
+
};
|
|
7308
|
+
selection.getBackToDefault();
|
|
7309
|
+
selection.selectZone({ cell: anchorCell, zone: union(...pastedZones) }, { scrollIntoView: false });
|
|
7310
|
+
};
|
|
7255
7311
|
|
|
7256
7312
|
class ClipboardHandler {
|
|
7257
7313
|
getters;
|
|
@@ -9956,8 +10012,15 @@ function getDependencyContainer(env) {
|
|
|
9956
10012
|
const ModelStore = createAbstractStore("Model");
|
|
9957
10013
|
|
|
9958
10014
|
class RendererStore {
|
|
9959
|
-
mutators = ["register", "unRegister", "
|
|
10015
|
+
mutators = ["register", "unRegister", "draw", "startAnimation", "stopAnimation"];
|
|
9960
10016
|
renderers = {};
|
|
10017
|
+
model;
|
|
10018
|
+
context = undefined;
|
|
10019
|
+
animationFrameId = null;
|
|
10020
|
+
registeredAnimations = new Set();
|
|
10021
|
+
constructor(get) {
|
|
10022
|
+
this.model = get(ModelStore);
|
|
10023
|
+
}
|
|
9961
10024
|
register(renderer) {
|
|
9962
10025
|
if (!renderer.renderingLayers.length) {
|
|
9963
10026
|
return;
|
|
@@ -9974,17 +10037,54 @@ class RendererStore {
|
|
|
9974
10037
|
this.renderers[layer] = this.renderers[layer].filter((r) => r !== renderer);
|
|
9975
10038
|
}
|
|
9976
10039
|
}
|
|
9977
|
-
drawLayer(context, layer) {
|
|
10040
|
+
drawLayer(context, layer, timeStamp) {
|
|
9978
10041
|
const renderers = this.renderers[layer];
|
|
9979
10042
|
if (renderers) {
|
|
9980
10043
|
for (const renderer of renderers) {
|
|
9981
10044
|
context.ctx.save();
|
|
9982
|
-
renderer.drawLayer(context, layer);
|
|
10045
|
+
renderer.drawLayer(context, layer, timeStamp);
|
|
9983
10046
|
context.ctx.restore();
|
|
9984
10047
|
}
|
|
9985
10048
|
}
|
|
9986
10049
|
return "noStateChange";
|
|
9987
10050
|
}
|
|
10051
|
+
draw(context, timestamp) {
|
|
10052
|
+
context = context || this.context;
|
|
10053
|
+
if (!context) {
|
|
10054
|
+
throw new Error("Rendering context is not defined");
|
|
10055
|
+
}
|
|
10056
|
+
this.context = context;
|
|
10057
|
+
for (const layer of OrderedLayers()) {
|
|
10058
|
+
this.model.drawLayer(context, layer);
|
|
10059
|
+
this.drawLayer(context, layer, timestamp);
|
|
10060
|
+
}
|
|
10061
|
+
return "noStateChange";
|
|
10062
|
+
}
|
|
10063
|
+
startAnimation(animationId) {
|
|
10064
|
+
this.registeredAnimations.add(animationId);
|
|
10065
|
+
if (!this.animationFrameId) {
|
|
10066
|
+
const animationCallback = (timestamp) => {
|
|
10067
|
+
this.animationFrameId = requestAnimationFrame(animationCallback);
|
|
10068
|
+
this.draw(undefined, timestamp);
|
|
10069
|
+
};
|
|
10070
|
+
this.animationFrameId = requestAnimationFrame(animationCallback);
|
|
10071
|
+
}
|
|
10072
|
+
return "noStateChange";
|
|
10073
|
+
}
|
|
10074
|
+
stopAnimation(animationId) {
|
|
10075
|
+
this.registeredAnimations.delete(animationId);
|
|
10076
|
+
if (this.registeredAnimations.size === 0 && this.animationFrameId !== null) {
|
|
10077
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
10078
|
+
this.animationFrameId = null;
|
|
10079
|
+
}
|
|
10080
|
+
return "noStateChange";
|
|
10081
|
+
}
|
|
10082
|
+
dispose() {
|
|
10083
|
+
if (this.animationFrameId) {
|
|
10084
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
10085
|
+
this.animationFrameId = null;
|
|
10086
|
+
}
|
|
10087
|
+
}
|
|
9988
10088
|
}
|
|
9989
10089
|
|
|
9990
10090
|
class SpreadsheetStore extends DisposableStore {
|
|
@@ -10008,7 +10108,7 @@ class SpreadsheetStore extends DisposableStore {
|
|
|
10008
10108
|
}
|
|
10009
10109
|
handle(cmd) { }
|
|
10010
10110
|
finalize() { }
|
|
10011
|
-
drawLayer(ctx, layer) { }
|
|
10111
|
+
drawLayer(ctx, layer, timestamp) { }
|
|
10012
10112
|
}
|
|
10013
10113
|
|
|
10014
10114
|
const VOID_COMPOSER = {
|
|
@@ -23730,11 +23830,11 @@ function drawTitle(ctx, config) {
|
|
|
23730
23830
|
function getGaugeRenderingConfig(boundingRect, runtime, ctx) {
|
|
23731
23831
|
const maxValue = runtime.maxValue;
|
|
23732
23832
|
const minValue = runtime.minValue;
|
|
23733
|
-
const gaugeValue = runtime
|
|
23833
|
+
const gaugeValue = getGaugeValue(runtime, "animated");
|
|
23734
23834
|
const gaugeRect = getGaugeRect(boundingRect, runtime.title.text);
|
|
23735
23835
|
const gaugeArcWidth = gaugeRect.width / 6;
|
|
23736
23836
|
const gaugePercentage = gaugeValue
|
|
23737
|
-
? (gaugeValue
|
|
23837
|
+
? (gaugeValue - minValue.value) / (maxValue.value - minValue.value)
|
|
23738
23838
|
: 0;
|
|
23739
23839
|
const gaugeValuePosition = {
|
|
23740
23840
|
x: boundingRect.width / 2,
|
|
@@ -23747,7 +23847,7 @@ function getGaugeRenderingConfig(boundingRect, runtime, ctx) {
|
|
|
23747
23847
|
}
|
|
23748
23848
|
// Scale down the font size if the text is too long
|
|
23749
23849
|
const maxTextWidth = gaugeRect.width / 2;
|
|
23750
|
-
const gaugeLabel = gaugeValue?.label || "-";
|
|
23850
|
+
const gaugeLabel = runtime.gaugeValue?.label || "-";
|
|
23751
23851
|
if (computeTextWidth(ctx, gaugeLabel, { fontSize: gaugeValueFontSize }, "px") > maxTextWidth) {
|
|
23752
23852
|
gaugeValueFontSize = getFontSizeMatchingWidth(maxTextWidth, gaugeValueFontSize, (fontSize) => computeTextWidth(ctx, gaugeLabel, { fontSize }, "px"));
|
|
23753
23853
|
}
|
|
@@ -23887,7 +23987,7 @@ function getInflectionValues(runtime, gaugeRect, textColor, ctx) {
|
|
|
23887
23987
|
return inflectionValues;
|
|
23888
23988
|
}
|
|
23889
23989
|
function getGaugeColor(runtime) {
|
|
23890
|
-
const gaugeValue = runtime
|
|
23990
|
+
const gaugeValue = getGaugeValue(runtime, "final");
|
|
23891
23991
|
if (gaugeValue === undefined) {
|
|
23892
23992
|
return GAUGE_BACKGROUND_COLOR;
|
|
23893
23993
|
}
|
|
@@ -23975,6 +24075,11 @@ function getRectangleTangentToCircle(angle, radius, circleCenterX, circleCenterY
|
|
|
23975
24075
|
};
|
|
23976
24076
|
return { bottomLeft, bottomRight, topRight, topLeft };
|
|
23977
24077
|
}
|
|
24078
|
+
function getGaugeValue(runtime, mode) {
|
|
24079
|
+
return mode === "animated" && runtime.animationValue !== undefined
|
|
24080
|
+
? runtime.animationValue
|
|
24081
|
+
: runtime.gaugeValue?.value;
|
|
24082
|
+
}
|
|
23978
24083
|
|
|
23979
24084
|
const CHART_COMMON_OPTIONS = {
|
|
23980
24085
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
@@ -26329,6 +26434,320 @@ function createBarChartRuntime(chart, getters) {
|
|
|
26329
26434
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
26330
26435
|
}
|
|
26331
26436
|
|
|
26437
|
+
const cellAnimationRegistry = new Registry();
|
|
26438
|
+
cellAnimationRegistry.add("animatedBackgroundColorChange", {
|
|
26439
|
+
id: "animatedBackgroundColorChange",
|
|
26440
|
+
easingFn: "easeOutCubic",
|
|
26441
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26442
|
+
return oldBox?.style?.fillColor !== newBox?.style?.fillColor;
|
|
26443
|
+
},
|
|
26444
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26445
|
+
const colorScale = getColorScale([
|
|
26446
|
+
{ value: 0, color: oldBox.style.fillColor || "#ffffff" },
|
|
26447
|
+
{ value: 1, color: newBox.style.fillColor || "#ffffff" },
|
|
26448
|
+
]);
|
|
26449
|
+
animatedBox.style.fillColor = colorScale(EASING_FN[this.easingFn](progress));
|
|
26450
|
+
},
|
|
26451
|
+
});
|
|
26452
|
+
cellAnimationRegistry.add("animatedTextColorChange", {
|
|
26453
|
+
id: "animatedTextColorChange",
|
|
26454
|
+
easingFn: "easeOutCubic",
|
|
26455
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26456
|
+
return oldBox?.style?.textColor !== newBox?.style?.textColor;
|
|
26457
|
+
},
|
|
26458
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26459
|
+
const colorScale = getColorScale([
|
|
26460
|
+
{ value: 0, color: oldBox.style.textColor || "#000000" },
|
|
26461
|
+
{ value: 1, color: newBox.style.textColor || "#000000" },
|
|
26462
|
+
]);
|
|
26463
|
+
animatedBox.style.textColor = colorScale(EASING_FN[this.easingFn](progress));
|
|
26464
|
+
},
|
|
26465
|
+
});
|
|
26466
|
+
cellAnimationRegistry.add("animatedDataBar", {
|
|
26467
|
+
id: "animatedDataBar",
|
|
26468
|
+
easingFn: "easeOutCubic",
|
|
26469
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26470
|
+
return oldBox?.dataBarFill?.percentage !== newBox?.dataBarFill?.percentage;
|
|
26471
|
+
},
|
|
26472
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26473
|
+
const startingPercentage = oldBox?.dataBarFill?.percentage || 0;
|
|
26474
|
+
const endingPercentage = newBox?.dataBarFill?.percentage || 0;
|
|
26475
|
+
const value = EASING_FN[this.easingFn](progress);
|
|
26476
|
+
const percentage = startingPercentage + (endingPercentage - startingPercentage) * value;
|
|
26477
|
+
animatedBox.dataBarFill = {
|
|
26478
|
+
color: newBox.dataBarFill?.color || oldBox.dataBarFill?.color || "#ffffff",
|
|
26479
|
+
percentage: percentage,
|
|
26480
|
+
};
|
|
26481
|
+
},
|
|
26482
|
+
});
|
|
26483
|
+
cellAnimationRegistry.add("textFadeIn", {
|
|
26484
|
+
id: "textFadeIn",
|
|
26485
|
+
easingFn: "easeInCubic",
|
|
26486
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26487
|
+
const oldText = oldBox?.content?.textLines?.join("\n");
|
|
26488
|
+
const newText = newBox?.content?.textLines?.join("\n");
|
|
26489
|
+
return Boolean(!oldText && newText);
|
|
26490
|
+
},
|
|
26491
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26492
|
+
animatedBox.textOpacity = EASING_FN[this.easingFn](progress);
|
|
26493
|
+
},
|
|
26494
|
+
});
|
|
26495
|
+
cellAnimationRegistry.add("textFadeOut", {
|
|
26496
|
+
id: "textFadeOut",
|
|
26497
|
+
easingFn: "easeOutCubic",
|
|
26498
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26499
|
+
const oldText = oldBox?.content?.textLines?.join("\n");
|
|
26500
|
+
const newText = newBox?.content?.textLines?.join("\n");
|
|
26501
|
+
return Boolean(oldText && !newText);
|
|
26502
|
+
},
|
|
26503
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26504
|
+
const textOpacity = 1 - EASING_FN[this.easingFn](progress);
|
|
26505
|
+
const style = { ...oldBox.style };
|
|
26506
|
+
delete style.fillColor;
|
|
26507
|
+
animatedBox.textOpacity = textOpacity;
|
|
26508
|
+
animatedBox.content = oldBox.content;
|
|
26509
|
+
animatedBox.clipRect = oldBox.clipRect;
|
|
26510
|
+
Object.assign(animatedBox.style, style);
|
|
26511
|
+
},
|
|
26512
|
+
});
|
|
26513
|
+
cellAnimationRegistry.add("textChange", {
|
|
26514
|
+
id: "textChange",
|
|
26515
|
+
easingFn: "easeOutCubic",
|
|
26516
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26517
|
+
const oldText = oldBox?.content?.textLines?.join("\n");
|
|
26518
|
+
const newText = newBox?.content?.textLines?.join("\n");
|
|
26519
|
+
// Note: here, we also animate changes to icons layout (margins/size change, or icon appearing/disappearing)
|
|
26520
|
+
// because a change to the icon layout will impact where the text is positioned.
|
|
26521
|
+
return (Boolean(oldText && newText && oldText !== newText) || hasIconLayoutChange(newBox, oldBox));
|
|
26522
|
+
},
|
|
26523
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26524
|
+
const value = EASING_FN[this.easingFn](progress);
|
|
26525
|
+
const slideInY = newBox.y + (value - 1) * newBox.height;
|
|
26526
|
+
const slideOutY = newBox.y + value * newBox.height;
|
|
26527
|
+
const iconLayoutChange = hasIconLayoutChange(newBox, oldBox);
|
|
26528
|
+
const slideInBox = {
|
|
26529
|
+
id: newBox.id + "-text-slide-in",
|
|
26530
|
+
x: newBox.x,
|
|
26531
|
+
y: slideInY,
|
|
26532
|
+
width: newBox.width,
|
|
26533
|
+
height: newBox.height,
|
|
26534
|
+
style: { ...newBox.style },
|
|
26535
|
+
skipCellGridLines: true,
|
|
26536
|
+
content: newBox.content,
|
|
26537
|
+
clipRect: newBox.clipRect || {
|
|
26538
|
+
...newBox,
|
|
26539
|
+
// large width to avoid clipping the text it it didn't have a clipRect before,
|
|
26540
|
+
// we mainly want to clip the Y for the animation
|
|
26541
|
+
x: Math.max(0, newBox.x - (newBox.content?.width || 0)),
|
|
26542
|
+
width: newBox.width + (newBox.content?.width || 0) * 2,
|
|
26543
|
+
},
|
|
26544
|
+
icons: iconLayoutChange
|
|
26545
|
+
? addClipRectToIcons(newBox.icons, newBox)
|
|
26546
|
+
: makeIconsEmpty(newBox.icons),
|
|
26547
|
+
};
|
|
26548
|
+
const slideOutBox = {
|
|
26549
|
+
id: oldBox.id + "-text-slide-out",
|
|
26550
|
+
x: newBox.x,
|
|
26551
|
+
y: slideOutY,
|
|
26552
|
+
width: newBox.width,
|
|
26553
|
+
height: newBox.height,
|
|
26554
|
+
style: { ...oldBox.style },
|
|
26555
|
+
skipCellGridLines: true,
|
|
26556
|
+
content: oldBox.content,
|
|
26557
|
+
clipRect: oldBox.clipRect || {
|
|
26558
|
+
...newBox,
|
|
26559
|
+
x: Math.max(0, newBox.x - (oldBox.content?.width || 0)),
|
|
26560
|
+
width: newBox.width + (oldBox.content?.width || 0) * 2,
|
|
26561
|
+
},
|
|
26562
|
+
icons: iconLayoutChange
|
|
26563
|
+
? addClipRectToIcons(oldBox.icons, newBox)
|
|
26564
|
+
: makeIconsEmpty(oldBox.icons),
|
|
26565
|
+
};
|
|
26566
|
+
if (newBox.content && oldBox.content && slideInBox.content && slideOutBox.content) {
|
|
26567
|
+
const slideInContentY = newBox.content.y + (value - 1) * newBox.height;
|
|
26568
|
+
const slideOutContentY = newBox.content.y + value * newBox.height;
|
|
26569
|
+
slideInBox.content.y = slideInContentY;
|
|
26570
|
+
slideOutBox.content.y = slideOutContentY;
|
|
26571
|
+
}
|
|
26572
|
+
slideOutBox.style.fillColor = slideInBox.style.fillColor = undefined;
|
|
26573
|
+
animatedBox.content = undefined;
|
|
26574
|
+
animatedBox.icons = iconLayoutChange ? {} : animatedBox.icons;
|
|
26575
|
+
return { newBoxes: [slideInBox, slideOutBox] };
|
|
26576
|
+
},
|
|
26577
|
+
});
|
|
26578
|
+
cellAnimationRegistry.add("borderFadeIn", {
|
|
26579
|
+
id: "borderFadeIn",
|
|
26580
|
+
easingFn: "easeInCubic",
|
|
26581
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26582
|
+
return Boolean((!oldBox?.border?.bottom && newBox?.border?.bottom) ||
|
|
26583
|
+
(!oldBox?.border?.top && newBox?.border?.top) ||
|
|
26584
|
+
(!oldBox?.border?.left && newBox?.border?.left) ||
|
|
26585
|
+
(!oldBox?.border?.right && newBox?.border?.right));
|
|
26586
|
+
},
|
|
26587
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26588
|
+
const borderOpacity = EASING_FN[this.easingFn](progress);
|
|
26589
|
+
if (animatedBox.border?.top && newBox.border?.top && !oldBox.border?.top) {
|
|
26590
|
+
animatedBox.border.top.opacity = borderOpacity;
|
|
26591
|
+
}
|
|
26592
|
+
if (animatedBox.border?.bottom && newBox.border?.bottom && !oldBox.border?.bottom) {
|
|
26593
|
+
animatedBox.border.bottom.opacity = borderOpacity;
|
|
26594
|
+
}
|
|
26595
|
+
if (animatedBox.border?.left && newBox.border?.left && !oldBox.border?.left) {
|
|
26596
|
+
animatedBox.border.left.opacity = borderOpacity;
|
|
26597
|
+
}
|
|
26598
|
+
if (animatedBox.border?.right && newBox.border?.right && !oldBox.border?.right) {
|
|
26599
|
+
animatedBox.border.right.opacity = borderOpacity;
|
|
26600
|
+
}
|
|
26601
|
+
},
|
|
26602
|
+
});
|
|
26603
|
+
cellAnimationRegistry.add("borderFadeOut", {
|
|
26604
|
+
id: "borderFadeOut",
|
|
26605
|
+
easingFn: "easeOutCubic",
|
|
26606
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26607
|
+
return Boolean((oldBox?.border?.bottom && !newBox?.border?.bottom) ||
|
|
26608
|
+
(oldBox?.border?.top && !newBox?.border?.top) ||
|
|
26609
|
+
(oldBox?.border?.left && !newBox?.border?.left) ||
|
|
26610
|
+
(oldBox?.border?.right && !newBox?.border?.right));
|
|
26611
|
+
},
|
|
26612
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26613
|
+
const borderOpacity = 1 - EASING_FN[this.easingFn](progress);
|
|
26614
|
+
if (!animatedBox.border) {
|
|
26615
|
+
animatedBox.border = {};
|
|
26616
|
+
}
|
|
26617
|
+
if (oldBox.border?.top && !newBox.border?.top) {
|
|
26618
|
+
animatedBox.border.top = { ...oldBox.border.top, opacity: borderOpacity };
|
|
26619
|
+
}
|
|
26620
|
+
if (oldBox.border?.bottom && !newBox.border?.bottom) {
|
|
26621
|
+
animatedBox.border.bottom = { ...oldBox.border.bottom, opacity: borderOpacity };
|
|
26622
|
+
}
|
|
26623
|
+
if (oldBox.border?.left && !newBox.border?.left) {
|
|
26624
|
+
animatedBox.border.left = { ...oldBox.border.left, opacity: borderOpacity };
|
|
26625
|
+
}
|
|
26626
|
+
if (oldBox.border?.right && !newBox.border?.right) {
|
|
26627
|
+
animatedBox.border.right = { ...oldBox.border.right, opacity: borderOpacity };
|
|
26628
|
+
}
|
|
26629
|
+
},
|
|
26630
|
+
});
|
|
26631
|
+
cellAnimationRegistry.add("borderColorChange", {
|
|
26632
|
+
id: "borderColorChange",
|
|
26633
|
+
easingFn: "easeOutCubic",
|
|
26634
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26635
|
+
const oldBorder = oldBox?.border;
|
|
26636
|
+
const newBorder = newBox?.border;
|
|
26637
|
+
if (!oldBorder || !newBorder) {
|
|
26638
|
+
return false;
|
|
26639
|
+
}
|
|
26640
|
+
return Boolean(oldBorder.bottom?.color !== newBorder.bottom?.color ||
|
|
26641
|
+
oldBorder.top?.color !== newBorder.top?.color ||
|
|
26642
|
+
oldBorder.left?.color !== newBorder.left?.color ||
|
|
26643
|
+
oldBorder.right?.color !== newBorder.right?.color);
|
|
26644
|
+
},
|
|
26645
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26646
|
+
const animateBorderColor = (side) => {
|
|
26647
|
+
const oldBorder = oldBox?.border?.[side];
|
|
26648
|
+
const newBorder = newBox?.border?.[side];
|
|
26649
|
+
const animatedBorder = animatedBox.border?.[side];
|
|
26650
|
+
if (oldBorder && newBorder && animatedBorder) {
|
|
26651
|
+
const colorScale = getColorScale([
|
|
26652
|
+
{ value: 0, color: oldBorder.color || "#000000" },
|
|
26653
|
+
{ value: 1, color: newBorder.color || "#000000" },
|
|
26654
|
+
]);
|
|
26655
|
+
animatedBorder.color = colorScale(EASING_FN[this.easingFn](progress));
|
|
26656
|
+
}
|
|
26657
|
+
};
|
|
26658
|
+
animateBorderColor("top");
|
|
26659
|
+
animateBorderColor("bottom");
|
|
26660
|
+
animateBorderColor("left");
|
|
26661
|
+
animateBorderColor("right");
|
|
26662
|
+
},
|
|
26663
|
+
});
|
|
26664
|
+
cellAnimationRegistry.add("iconChange", {
|
|
26665
|
+
id: "iconChange",
|
|
26666
|
+
easingFn: "easeOutCubic",
|
|
26667
|
+
hasAnimation: (oldBox, newBox) => {
|
|
26668
|
+
return (!hasIconLayoutChange(newBox, oldBox) &&
|
|
26669
|
+
Boolean(oldBox?.icons?.center?.svg?.name !== newBox?.icons?.center?.svg?.name ||
|
|
26670
|
+
oldBox?.icons?.left?.svg?.name !== newBox?.icons?.left?.svg?.name ||
|
|
26671
|
+
oldBox?.icons?.right?.svg?.name !== newBox?.icons?.right?.svg?.name));
|
|
26672
|
+
},
|
|
26673
|
+
updateAnimation: function (progress, animatedBox, oldBox, newBox) {
|
|
26674
|
+
const value = EASING_FN[this.easingFn](progress);
|
|
26675
|
+
const slideInY = newBox.y + (value - 1) * newBox.height;
|
|
26676
|
+
const slideOutY = newBox.y + value * newBox.height;
|
|
26677
|
+
const newBoxes = [];
|
|
26678
|
+
const animateIconChange = (side) => {
|
|
26679
|
+
const oldIcon = oldBox.icons?.[side];
|
|
26680
|
+
const newIcon = newBox.icons?.[side];
|
|
26681
|
+
const slideInBox = {
|
|
26682
|
+
id: `${newBox.id}-icon-${side}-slide-in`,
|
|
26683
|
+
style: { verticalAlign: newBox.style.verticalAlign },
|
|
26684
|
+
x: newBox.x,
|
|
26685
|
+
y: slideInY,
|
|
26686
|
+
width: newBox.width,
|
|
26687
|
+
height: newBox.height,
|
|
26688
|
+
skipCellGridLines: true,
|
|
26689
|
+
icons: { [side]: { ...newIcon, clipRect: newBox } },
|
|
26690
|
+
};
|
|
26691
|
+
const slideOutBox = {
|
|
26692
|
+
id: `${newBox.id}-icon-${side}-slide-out`,
|
|
26693
|
+
style: { verticalAlign: oldBox.style.verticalAlign },
|
|
26694
|
+
x: newBox.x,
|
|
26695
|
+
y: slideOutY,
|
|
26696
|
+
width: newBox.width,
|
|
26697
|
+
height: newBox.height,
|
|
26698
|
+
skipCellGridLines: true,
|
|
26699
|
+
icons: { [side]: { ...oldIcon, clipRect: newBox } },
|
|
26700
|
+
};
|
|
26701
|
+
animatedBox.icons[side] = makeIconsEmpty(newBox.icons)[side];
|
|
26702
|
+
newBoxes.push(slideInBox, slideOutBox);
|
|
26703
|
+
};
|
|
26704
|
+
animateIconChange("left");
|
|
26705
|
+
animateIconChange("right");
|
|
26706
|
+
animateIconChange("center");
|
|
26707
|
+
return { newBoxes };
|
|
26708
|
+
},
|
|
26709
|
+
});
|
|
26710
|
+
const EASING_FN = {
|
|
26711
|
+
linear: (t) => t,
|
|
26712
|
+
easeInCubic: (t) => t * t * t,
|
|
26713
|
+
easeOutCubic: (t) => (t -= 1) * t * t + 1,
|
|
26714
|
+
easeInOutCubic: (t) => ((t /= 0.5) < 1 ? 0.5 * t * t * t : 0.5 * ((t -= 2) * t * t + 2)),
|
|
26715
|
+
easeOutQuart: (t) => -((t -= 1) * t * t * t - 1),
|
|
26716
|
+
};
|
|
26717
|
+
function makeIconsEmpty(icons) {
|
|
26718
|
+
return {
|
|
26719
|
+
left: icons.left ? { ...icons.left, svg: undefined } : undefined,
|
|
26720
|
+
right: icons.right ? { ...icons.right, svg: undefined } : undefined,
|
|
26721
|
+
center: icons.center ? { ...icons.center, svg: undefined } : undefined,
|
|
26722
|
+
};
|
|
26723
|
+
}
|
|
26724
|
+
function addClipRectToIcons(icons, clipRect) {
|
|
26725
|
+
return {
|
|
26726
|
+
left: icons.left ? { ...icons.left, clipRect } : undefined,
|
|
26727
|
+
right: icons.right ? { ...icons.right, clipRect } : undefined,
|
|
26728
|
+
center: icons.center ? { ...icons.center, clipRect } : undefined,
|
|
26729
|
+
};
|
|
26730
|
+
}
|
|
26731
|
+
/**
|
|
26732
|
+
* Check if the icons have appeared, disappeared or changed margin/size/align. Those changes affect where the text is positioned.
|
|
26733
|
+
*/
|
|
26734
|
+
function hasIconLayoutChange(newBox, oldBox) {
|
|
26735
|
+
const hasLayoutChange = (newIcon, oldIcon) => {
|
|
26736
|
+
if (oldIcon && newIcon) {
|
|
26737
|
+
return !!(newIcon.horizontalAlign !== oldIcon.horizontalAlign ||
|
|
26738
|
+
newIcon.size !== oldIcon.size ||
|
|
26739
|
+
newIcon.margin !== oldIcon.margin ||
|
|
26740
|
+
(newIcon.svg && !oldIcon.svg) ||
|
|
26741
|
+
(!newIcon.svg && oldIcon.svg));
|
|
26742
|
+
}
|
|
26743
|
+
return !!((newIcon && !oldIcon) || (!newIcon && oldIcon));
|
|
26744
|
+
};
|
|
26745
|
+
return (hasLayoutChange(newBox?.icons.left, oldBox?.icons.left) ||
|
|
26746
|
+
hasLayoutChange(newBox?.icons.right, oldBox?.icons.right) ||
|
|
26747
|
+
hasLayoutChange(newBox?.icons.center, oldBox?.icons.center));
|
|
26748
|
+
}
|
|
26749
|
+
|
|
26750
|
+
const ANIMATION_DURATION = 1000;
|
|
26332
26751
|
class GaugeChartComponent extends owl.Component {
|
|
26333
26752
|
static template = "o-spreadsheet-GaugeChartComponent";
|
|
26334
26753
|
static props = {
|
|
@@ -26336,16 +26755,101 @@ class GaugeChartComponent extends owl.Component {
|
|
|
26336
26755
|
isFullScreen: { type: Boolean, optional: true },
|
|
26337
26756
|
};
|
|
26338
26757
|
canvas = owl.useRef("chartContainer");
|
|
26758
|
+
animationStore;
|
|
26339
26759
|
get runtime() {
|
|
26340
26760
|
return this.env.model.getters.getChartRuntime(this.props.figureUI.id);
|
|
26341
26761
|
}
|
|
26342
26762
|
setup() {
|
|
26343
|
-
|
|
26344
|
-
|
|
26345
|
-
|
|
26763
|
+
if (this.env.model.getters.isDashboard()) {
|
|
26764
|
+
this.animationStore = useStore(ChartAnimationStore);
|
|
26765
|
+
}
|
|
26766
|
+
let animation = null;
|
|
26767
|
+
let lastRuntime = undefined;
|
|
26768
|
+
owl.useEffect(() => {
|
|
26769
|
+
if (this.env.isDashboard() &&
|
|
26770
|
+
lastRuntime === undefined && // first render
|
|
26771
|
+
this.animationStore?.animationPlayed[this.animationFigureId] !== "gauge") {
|
|
26772
|
+
animation = this.drawGaugeWithAnimation();
|
|
26773
|
+
this.animationStore?.disableAnimationForChart(this.animationFigureId, "gauge");
|
|
26774
|
+
}
|
|
26775
|
+
else if (this.env.isDashboard() &&
|
|
26776
|
+
lastRuntime !== undefined && // not first render
|
|
26777
|
+
!deepEquals(this.runtime, lastRuntime)) {
|
|
26778
|
+
animation = this.drawGaugeWithAnimation();
|
|
26779
|
+
this.animationStore?.disableAnimationForChart(this.animationFigureId, "gauge");
|
|
26780
|
+
}
|
|
26781
|
+
else {
|
|
26782
|
+
drawGaugeChart(this.canvasEl, this.runtime);
|
|
26783
|
+
}
|
|
26784
|
+
lastRuntime = this.runtime;
|
|
26785
|
+
return () => animation?.stop();
|
|
26786
|
+
}, () => {
|
|
26787
|
+
const rect = this.canvasEl.getBoundingClientRect();
|
|
26346
26788
|
return [rect.width, rect.height, this.runtime, this.canvas.el, window.devicePixelRatio];
|
|
26347
26789
|
});
|
|
26348
26790
|
}
|
|
26791
|
+
drawGaugeWithAnimation() {
|
|
26792
|
+
drawGaugeChart(this.canvasEl, { ...this.runtime, animationValue: 0 });
|
|
26793
|
+
const gaugeValue = this.runtime.gaugeValue?.value || 0;
|
|
26794
|
+
const upperBound = this.runtime.maxValue.value;
|
|
26795
|
+
const finalValue = Math.sign(gaugeValue) * Math.min(Math.abs(gaugeValue), Math.abs(upperBound));
|
|
26796
|
+
if (finalValue === 0) {
|
|
26797
|
+
return null;
|
|
26798
|
+
}
|
|
26799
|
+
const lowerBound = this.runtime.minValue.value;
|
|
26800
|
+
const animation = new Animation(lowerBound, finalValue, ANIMATION_DURATION, (animationValue) => drawGaugeChart(this.canvasEl, { ...this.runtime, animationValue }));
|
|
26801
|
+
animation.start();
|
|
26802
|
+
return animation;
|
|
26803
|
+
}
|
|
26804
|
+
get canvasEl() {
|
|
26805
|
+
return this.canvas.el;
|
|
26806
|
+
}
|
|
26807
|
+
get animationFigureId() {
|
|
26808
|
+
return this.props.isFullScreen
|
|
26809
|
+
? this.props.figureUI.id + "-fullscreen"
|
|
26810
|
+
: this.props.figureUI.id;
|
|
26811
|
+
}
|
|
26812
|
+
}
|
|
26813
|
+
/**
|
|
26814
|
+
* Animation interpolating values using the ease-out quartic curve function (chartJS default easing)
|
|
26815
|
+
*/
|
|
26816
|
+
class Animation {
|
|
26817
|
+
startValue;
|
|
26818
|
+
endValue;
|
|
26819
|
+
duration;
|
|
26820
|
+
callback;
|
|
26821
|
+
startTime = undefined;
|
|
26822
|
+
animationFrameId = null;
|
|
26823
|
+
constructor(startValue, endValue, duration, callback) {
|
|
26824
|
+
this.startValue = startValue;
|
|
26825
|
+
this.endValue = endValue;
|
|
26826
|
+
this.duration = duration;
|
|
26827
|
+
this.callback = callback;
|
|
26828
|
+
}
|
|
26829
|
+
start() {
|
|
26830
|
+
this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
|
|
26831
|
+
}
|
|
26832
|
+
stop() {
|
|
26833
|
+
if (this.animationFrameId) {
|
|
26834
|
+
cancelAnimationFrame(this.animationFrameId);
|
|
26835
|
+
this.animationFrameId = null;
|
|
26836
|
+
}
|
|
26837
|
+
}
|
|
26838
|
+
animate(timestamp) {
|
|
26839
|
+
if (!this.startTime) {
|
|
26840
|
+
this.startTime = timestamp;
|
|
26841
|
+
}
|
|
26842
|
+
const elapsed = timestamp - this.startTime;
|
|
26843
|
+
const progress = Math.min(elapsed / this.duration, 1);
|
|
26844
|
+
const currentValue = this.startValue + (this.endValue - this.startValue) * EASING_FN.easeOutQuart(progress);
|
|
26845
|
+
this.callback(currentValue);
|
|
26846
|
+
if (progress < 1) {
|
|
26847
|
+
this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
|
|
26848
|
+
}
|
|
26849
|
+
else {
|
|
26850
|
+
this.stop();
|
|
26851
|
+
}
|
|
26852
|
+
}
|
|
26349
26853
|
}
|
|
26350
26854
|
|
|
26351
26855
|
class ComboChart extends AbstractChart {
|
|
@@ -36538,6 +37042,7 @@ css /* scss */ `
|
|
|
36538
37042
|
// We need here the svg of the icons that we need to convert to images for the renderer
|
|
36539
37043
|
// -----------------------------------------------------------------------------
|
|
36540
37044
|
const ARROW_DOWN = {
|
|
37045
|
+
name: "ARROW_DOWN",
|
|
36541
37046
|
width: 448,
|
|
36542
37047
|
height: 512,
|
|
36543
37048
|
paths: [
|
|
@@ -36548,6 +37053,7 @@ const ARROW_DOWN = {
|
|
|
36548
37053
|
],
|
|
36549
37054
|
};
|
|
36550
37055
|
const ARROW_UP = {
|
|
37056
|
+
name: "ARROW_UP",
|
|
36551
37057
|
width: 448,
|
|
36552
37058
|
height: 512,
|
|
36553
37059
|
paths: [
|
|
@@ -36558,6 +37064,7 @@ const ARROW_UP = {
|
|
|
36558
37064
|
],
|
|
36559
37065
|
};
|
|
36560
37066
|
const ARROW_RIGHT = {
|
|
37067
|
+
name: "ARROW_RIGHT",
|
|
36561
37068
|
width: 448,
|
|
36562
37069
|
height: 512,
|
|
36563
37070
|
paths: [
|
|
@@ -36568,6 +37075,7 @@ const ARROW_RIGHT = {
|
|
|
36568
37075
|
],
|
|
36569
37076
|
};
|
|
36570
37077
|
const SMILE = {
|
|
37078
|
+
name: "SMILE",
|
|
36571
37079
|
width: 496,
|
|
36572
37080
|
height: 512,
|
|
36573
37081
|
paths: [
|
|
@@ -36578,6 +37086,7 @@ const SMILE = {
|
|
|
36578
37086
|
],
|
|
36579
37087
|
};
|
|
36580
37088
|
const MEH = {
|
|
37089
|
+
name: "MEH",
|
|
36581
37090
|
width: 496,
|
|
36582
37091
|
height: 512,
|
|
36583
37092
|
paths: [
|
|
@@ -36588,6 +37097,7 @@ const MEH = {
|
|
|
36588
37097
|
],
|
|
36589
37098
|
};
|
|
36590
37099
|
const FROWN = {
|
|
37100
|
+
name: "FROWN",
|
|
36591
37101
|
width: 496,
|
|
36592
37102
|
height: 512,
|
|
36593
37103
|
paths: [
|
|
@@ -36599,22 +37109,26 @@ const FROWN = {
|
|
|
36599
37109
|
};
|
|
36600
37110
|
const DOT_PATH = "M256 9 a247 247 0 1 0.1 0 0";
|
|
36601
37111
|
const GREEN_DOT = {
|
|
37112
|
+
name: "GREEN_DOT",
|
|
36602
37113
|
width: 512,
|
|
36603
37114
|
height: 512,
|
|
36604
37115
|
paths: [{ fillColor: "#6AA84F", path: DOT_PATH }],
|
|
36605
37116
|
};
|
|
36606
37117
|
const YELLOW_DOT = {
|
|
37118
|
+
name: "YELLOW_DOT",
|
|
36607
37119
|
width: 512,
|
|
36608
37120
|
height: 512,
|
|
36609
37121
|
paths: [{ fillColor: "#F0AD4E", path: DOT_PATH }],
|
|
36610
37122
|
};
|
|
36611
37123
|
const RED_DOT = {
|
|
37124
|
+
name: "RED_DOT",
|
|
36612
37125
|
width: 512,
|
|
36613
37126
|
height: 512,
|
|
36614
37127
|
paths: [{ fillColor: "#E06666", path: DOT_PATH }],
|
|
36615
37128
|
};
|
|
36616
37129
|
function getCaretDownSvg(color) {
|
|
36617
37130
|
return {
|
|
37131
|
+
name: "CARET_DOWN",
|
|
36618
37132
|
width: 512,
|
|
36619
37133
|
height: 512,
|
|
36620
37134
|
paths: [{ fillColor: color.textColor || TEXT_BODY_MUTED, path: "M120 195 h270 l-135 130" }],
|
|
@@ -36622,6 +37136,7 @@ function getCaretDownSvg(color) {
|
|
|
36622
37136
|
}
|
|
36623
37137
|
function getHoveredCaretDownSvg(color) {
|
|
36624
37138
|
return {
|
|
37139
|
+
name: "CARET_DOWN",
|
|
36625
37140
|
width: 512,
|
|
36626
37141
|
height: 512,
|
|
36627
37142
|
paths: [
|
|
@@ -36633,6 +37148,7 @@ function getHoveredCaretDownSvg(color) {
|
|
|
36633
37148
|
const CHIP_CARET_DOWN_PATH = "M40 185 h270 l-135 128";
|
|
36634
37149
|
function getChipSvg(chipStyle) {
|
|
36635
37150
|
return {
|
|
37151
|
+
name: "CHIP",
|
|
36636
37152
|
width: 512,
|
|
36637
37153
|
height: 512,
|
|
36638
37154
|
paths: [{ fillColor: chipStyle.textColor || TEXT_BODY_MUTED, path: CHIP_CARET_DOWN_PATH }],
|
|
@@ -36640,6 +37156,7 @@ function getChipSvg(chipStyle) {
|
|
|
36640
37156
|
}
|
|
36641
37157
|
function getHoveredChipSvg(chipStyle) {
|
|
36642
37158
|
return {
|
|
37159
|
+
name: "CHIP",
|
|
36643
37160
|
width: 512,
|
|
36644
37161
|
height: 512,
|
|
36645
37162
|
paths: [
|
|
@@ -36652,16 +37169,19 @@ function getHoveredChipSvg(chipStyle) {
|
|
|
36652
37169
|
};
|
|
36653
37170
|
}
|
|
36654
37171
|
const CHECKBOX_UNCHECKED = {
|
|
37172
|
+
name: "CHECKBOX_UNCHECKED",
|
|
36655
37173
|
width: 512,
|
|
36656
37174
|
height: 512,
|
|
36657
37175
|
paths: [{ fillColor: GRAY_300, path: "M45,45 h422 v422 h-422 v-422 m30,30 v362 h362 v-362" }],
|
|
36658
37176
|
};
|
|
36659
37177
|
const CHECKBOX_UNCHECKED_HOVERED = {
|
|
37178
|
+
name: "CHECKBOX_UNCHECKED",
|
|
36660
37179
|
width: 512,
|
|
36661
37180
|
height: 512,
|
|
36662
37181
|
paths: [{ fillColor: ACTION_COLOR, path: "M45,45 h422 v422 h-422 v-422 m30,30 v362 h362 v-362" }],
|
|
36663
37182
|
};
|
|
36664
37183
|
const CHECKBOX_CHECKED = {
|
|
37184
|
+
name: "CHECKBOX_CHECKED",
|
|
36665
37185
|
width: 512,
|
|
36666
37186
|
height: 512,
|
|
36667
37187
|
paths: [
|
|
@@ -36674,6 +37194,7 @@ function getPivotIconSvg(isCollapsed, isHovered) {
|
|
|
36674
37194
|
? "M149,235 h213 v43 h-213 M235,149 h43 v213 h-43" // +
|
|
36675
37195
|
: "M149,235 h213 v43 h-213"; // -
|
|
36676
37196
|
return {
|
|
37197
|
+
name: "PIVOT_ICON",
|
|
36677
37198
|
width: 512,
|
|
36678
37199
|
height: 512,
|
|
36679
37200
|
paths: [
|
|
@@ -36700,6 +37221,7 @@ function getDataFilterIcon(isActive, isHighContrast, isHovered) {
|
|
|
36700
37221
|
colors.hoverBackgroundColor = "#fff";
|
|
36701
37222
|
}
|
|
36702
37223
|
return {
|
|
37224
|
+
name: "DATA_FILTER_ICON",
|
|
36703
37225
|
width: isActive ? 24 : 850,
|
|
36704
37226
|
height: isActive ? 24 : 850,
|
|
36705
37227
|
paths: [
|
|
@@ -40895,6 +41417,23 @@ migrationStepRegistry
|
|
|
40895
41417
|
}
|
|
40896
41418
|
return data;
|
|
40897
41419
|
},
|
|
41420
|
+
})
|
|
41421
|
+
.add("18.4.3", {
|
|
41422
|
+
migrate(data) {
|
|
41423
|
+
if (!data.pivots) {
|
|
41424
|
+
return data;
|
|
41425
|
+
}
|
|
41426
|
+
for (const pivotId in data.pivots) {
|
|
41427
|
+
const pivot = data.pivots[pivotId];
|
|
41428
|
+
if (pivot.sortedColumn) {
|
|
41429
|
+
const measure = pivot.measures.find((measure) => measure.fieldName === pivot.sortedColumn?.measure);
|
|
41430
|
+
if (measure) {
|
|
41431
|
+
pivot.sortedColumn.measure = measure.id;
|
|
41432
|
+
}
|
|
41433
|
+
}
|
|
41434
|
+
}
|
|
41435
|
+
return data;
|
|
41436
|
+
},
|
|
40898
41437
|
});
|
|
40899
41438
|
function fixOverlappingFilters(data) {
|
|
40900
41439
|
for (const sheet of data.sheets || []) {
|
|
@@ -45961,15 +46500,16 @@ function useHoveredElement(ref) {
|
|
|
45961
46500
|
return state;
|
|
45962
46501
|
}
|
|
45963
46502
|
|
|
46503
|
+
const PAINT_FORMAT_HANDLER_KEYS = [
|
|
46504
|
+
"cell",
|
|
46505
|
+
"border",
|
|
46506
|
+
"table",
|
|
46507
|
+
"conditionalFormat",
|
|
46508
|
+
"merge",
|
|
46509
|
+
];
|
|
45964
46510
|
class PaintFormatStore extends SpreadsheetStore {
|
|
45965
46511
|
mutators = ["activate", "cancel", "pasteFormat"];
|
|
45966
46512
|
highlightStore = this.get(HighlightStore);
|
|
45967
|
-
clipboardHandlers = [
|
|
45968
|
-
new CellClipboardHandler(this.getters, this.model.dispatch),
|
|
45969
|
-
new BorderClipboardHandler(this.getters, this.model.dispatch),
|
|
45970
|
-
new TableClipboardHandler(this.getters, this.model.dispatch),
|
|
45971
|
-
new ConditionalFormatClipboardHandler(this.getters, this.model.dispatch),
|
|
45972
|
-
];
|
|
45973
46513
|
status = "inactive";
|
|
45974
46514
|
copiedData;
|
|
45975
46515
|
constructor(get) {
|
|
@@ -46000,24 +46540,38 @@ class PaintFormatStore extends SpreadsheetStore {
|
|
|
46000
46540
|
get isActive() {
|
|
46001
46541
|
return this.status !== "inactive";
|
|
46002
46542
|
}
|
|
46543
|
+
get clipboardHandlers() {
|
|
46544
|
+
return PAINT_FORMAT_HANDLER_KEYS.map((handlerName) => {
|
|
46545
|
+
const HandlerClass = clipboardHandlersRegistries.cellHandlers.get(handlerName);
|
|
46546
|
+
return {
|
|
46547
|
+
handlerName,
|
|
46548
|
+
handler: new HandlerClass(this.getters, this.model.dispatch),
|
|
46549
|
+
};
|
|
46550
|
+
});
|
|
46551
|
+
}
|
|
46003
46552
|
copyFormats() {
|
|
46004
46553
|
const sheetId = this.getters.getActiveSheetId();
|
|
46005
46554
|
const zones = this.getters.getSelectedZones();
|
|
46006
|
-
const copiedData = {};
|
|
46007
|
-
for (const handler of this.clipboardHandlers) {
|
|
46008
|
-
|
|
46555
|
+
const copiedData = { zones, sheetId };
|
|
46556
|
+
for (const { handlerName, handler } of this.clipboardHandlers) {
|
|
46557
|
+
const handlerResult = handler.copy(getClipboardDataPositions(sheetId, zones), false);
|
|
46558
|
+
if (handlerResult !== undefined) {
|
|
46559
|
+
copiedData[handlerName] = handlerResult;
|
|
46560
|
+
}
|
|
46009
46561
|
}
|
|
46010
46562
|
return copiedData;
|
|
46011
46563
|
}
|
|
46012
46564
|
paintFormat(sheetId, target) {
|
|
46013
|
-
if (this.copiedData) {
|
|
46014
|
-
|
|
46015
|
-
handler.paste({ zones: target, sheetId }, this.copiedData, {
|
|
46016
|
-
isCutOperation: false,
|
|
46017
|
-
pasteOption: "onlyFormat",
|
|
46018
|
-
});
|
|
46019
|
-
}
|
|
46565
|
+
if (!this.copiedData) {
|
|
46566
|
+
return;
|
|
46020
46567
|
}
|
|
46568
|
+
const options = {
|
|
46569
|
+
isCutOperation: false,
|
|
46570
|
+
pasteOption: "onlyFormat",
|
|
46571
|
+
};
|
|
46572
|
+
const { target: pasteTarget, selectedZones } = getPasteTargetFromHandlers(sheetId, target, this.copiedData, this.clipboardHandlers, options);
|
|
46573
|
+
applyClipboardHandlersPaste(this.clipboardHandlers, this.copiedData, pasteTarget, options);
|
|
46574
|
+
selectPastedZone(this.model.selection, target, selectedZones);
|
|
46021
46575
|
if (this.status === "oneOff") {
|
|
46022
46576
|
this.cancel();
|
|
46023
46577
|
}
|
|
@@ -46064,12 +46618,8 @@ class HoveredTableStore extends SpreadsheetStore {
|
|
|
46064
46618
|
this.row = undefined;
|
|
46065
46619
|
}
|
|
46066
46620
|
computeOverlay() {
|
|
46067
|
-
if (!this.getters.isDashboard()) {
|
|
46068
|
-
return;
|
|
46069
|
-
}
|
|
46070
46621
|
this.overlayColors = new PositionMap();
|
|
46071
|
-
const col = this
|
|
46072
|
-
const row = this.row;
|
|
46622
|
+
const { col, row } = this;
|
|
46073
46623
|
if (col === undefined || row === undefined) {
|
|
46074
46624
|
return;
|
|
46075
46625
|
}
|
|
@@ -46078,9 +46628,16 @@ class HoveredTableStore extends SpreadsheetStore {
|
|
|
46078
46628
|
if (!table) {
|
|
46079
46629
|
return;
|
|
46080
46630
|
}
|
|
46081
|
-
const { left, right } = table.range.zone;
|
|
46082
|
-
|
|
46083
|
-
|
|
46631
|
+
const { left, right, top } = table.range.zone;
|
|
46632
|
+
const isTableHeader = row < top + table.config.numberOfHeaders;
|
|
46633
|
+
const doesTableRowHaveContent = range(left, right + 1).some((col) => {
|
|
46634
|
+
return (!this.getters.isColHidden(sheetId, col) &&
|
|
46635
|
+
this.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
|
|
46636
|
+
});
|
|
46637
|
+
if (!isTableHeader && doesTableRowHaveContent) {
|
|
46638
|
+
for (let col = left; col <= right; col++) {
|
|
46639
|
+
this.overlayColors.set({ sheetId, col, row }, TABLE_HOVER_BACKGROUND_COLOR);
|
|
46640
|
+
}
|
|
46084
46641
|
}
|
|
46085
46642
|
}
|
|
46086
46643
|
}
|
|
@@ -46328,7 +46885,10 @@ class GridOverlay extends owl.Component {
|
|
|
46328
46885
|
}
|
|
46329
46886
|
const icons = this.env.model.getters.getCellIcons(position);
|
|
46330
46887
|
const icon = icons.find((icon) => {
|
|
46331
|
-
|
|
46888
|
+
const merge = this.env.model.getters.getMerge(position);
|
|
46889
|
+
const zone = merge || positionToZone(position);
|
|
46890
|
+
const cellRect = this.env.model.getters.getRect(zone);
|
|
46891
|
+
return isPointInsideRect(x, y, this.env.model.getters.getCellIconRect(icon, cellRect));
|
|
46332
46892
|
});
|
|
46333
46893
|
return icon?.onClick ? icon : undefined;
|
|
46334
46894
|
}
|
|
@@ -47065,19 +47625,56 @@ class HeadersOverlay extends owl.Component {
|
|
|
47065
47625
|
}
|
|
47066
47626
|
}
|
|
47067
47627
|
|
|
47068
|
-
|
|
47069
|
-
|
|
47070
|
-
renderer;
|
|
47628
|
+
const CELL_ANIMATION_DURATION = 200;
|
|
47629
|
+
class GridRenderer extends SpreadsheetStore {
|
|
47071
47630
|
fingerprints;
|
|
47072
47631
|
hoveredTables;
|
|
47073
47632
|
hoveredIcon;
|
|
47633
|
+
lastRenderBoxes = new Map();
|
|
47634
|
+
preventNewAnimationsInNextFrame = false;
|
|
47635
|
+
zonesWithPreventedAnimationsInNextFrame = [];
|
|
47636
|
+
animations = new Map();
|
|
47074
47637
|
constructor(get) {
|
|
47638
|
+
super(get);
|
|
47075
47639
|
this.getters = get(ModelStore).getters;
|
|
47076
|
-
this.renderer = get(RendererStore);
|
|
47077
47640
|
this.fingerprints = get(FormulaFingerprintStore);
|
|
47078
47641
|
this.hoveredTables = get(HoveredTableStore);
|
|
47079
47642
|
this.hoveredIcon = get(HoveredIconStore);
|
|
47080
|
-
|
|
47643
|
+
}
|
|
47644
|
+
handle(cmd) {
|
|
47645
|
+
switch (cmd.type) {
|
|
47646
|
+
case "START":
|
|
47647
|
+
case "ACTIVATE_SHEET":
|
|
47648
|
+
case "ADD_COLUMNS_ROWS":
|
|
47649
|
+
case "REMOVE_COLUMNS_ROWS":
|
|
47650
|
+
this.animations.clear();
|
|
47651
|
+
this.preventNewAnimationsInNextFrame = true;
|
|
47652
|
+
break;
|
|
47653
|
+
case "RESIZE_COLUMNS_ROWS":
|
|
47654
|
+
this.preventNewAnimationsInNextFrame = true;
|
|
47655
|
+
break;
|
|
47656
|
+
case "REDO":
|
|
47657
|
+
this.zonesWithPreventedAnimationsInNextFrame = [];
|
|
47658
|
+
break;
|
|
47659
|
+
case "UNDO":
|
|
47660
|
+
for (const command of cmd.commands) {
|
|
47661
|
+
if (command.type === "ADD_COLUMNS_ROWS" ||
|
|
47662
|
+
command.type === "REMOVE_COLUMNS_ROWS" ||
|
|
47663
|
+
command.type === "RESIZE_COLUMNS_ROWS") {
|
|
47664
|
+
this.animations.clear();
|
|
47665
|
+
this.preventNewAnimationsInNextFrame = true;
|
|
47666
|
+
break;
|
|
47667
|
+
}
|
|
47668
|
+
}
|
|
47669
|
+
break;
|
|
47670
|
+
case "PASTE":
|
|
47671
|
+
this.zonesWithPreventedAnimationsInNextFrame.push(...this.getters.getSelectedZones());
|
|
47672
|
+
break;
|
|
47673
|
+
case "UPDATE_CELL":
|
|
47674
|
+
const zones = this.getters.getCommandZones(cmd);
|
|
47675
|
+
this.zonesWithPreventedAnimationsInNextFrame.push(...zones);
|
|
47676
|
+
break;
|
|
47677
|
+
}
|
|
47081
47678
|
}
|
|
47082
47679
|
get renderingLayers() {
|
|
47083
47680
|
return ["Background", "Headers"];
|
|
@@ -47085,17 +47682,20 @@ class GridRenderer {
|
|
|
47085
47682
|
// ---------------------------------------------------------------------------
|
|
47086
47683
|
// Grid rendering
|
|
47087
47684
|
// ---------------------------------------------------------------------------
|
|
47088
|
-
drawLayer(renderingContext, layer) {
|
|
47685
|
+
drawLayer(renderingContext, layer, timeStamp) {
|
|
47089
47686
|
switch (layer) {
|
|
47090
47687
|
case "Background":
|
|
47091
47688
|
this.drawGlobalBackground(renderingContext);
|
|
47689
|
+
const oldBoxes = this.lastRenderBoxes;
|
|
47690
|
+
this.lastRenderBoxes = new Map();
|
|
47092
47691
|
for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
|
|
47093
47692
|
const { ctx } = renderingContext;
|
|
47094
47693
|
ctx.save();
|
|
47095
47694
|
ctx.beginPath();
|
|
47096
47695
|
ctx.rect(rect.x, rect.y, rect.width, rect.height);
|
|
47097
47696
|
ctx.clip();
|
|
47098
|
-
const
|
|
47697
|
+
const boxesWithoutAnimations = this.getGridBoxes(zone);
|
|
47698
|
+
const boxes = this.getBoxesWithAnimations(boxesWithoutAnimations, oldBoxes, timeStamp);
|
|
47099
47699
|
this.drawBackground(renderingContext, boxes);
|
|
47100
47700
|
this.drawOverflowingCellBackground(renderingContext, boxes);
|
|
47101
47701
|
this.drawCellBackground(renderingContext, boxes);
|
|
@@ -47105,6 +47705,8 @@ class GridRenderer {
|
|
|
47105
47705
|
ctx.restore();
|
|
47106
47706
|
}
|
|
47107
47707
|
this.drawFrozenPanes(renderingContext);
|
|
47708
|
+
this.preventNewAnimationsInNextFrame = false;
|
|
47709
|
+
this.zonesWithPreventedAnimationsInNextFrame = [];
|
|
47108
47710
|
break;
|
|
47109
47711
|
case "Headers":
|
|
47110
47712
|
if (!this.getters.isDashboard()) {
|
|
@@ -47128,6 +47730,8 @@ class GridRenderer {
|
|
|
47128
47730
|
const inset = areGridLinesVisible ? 0.1 * thinLineWidth : 0;
|
|
47129
47731
|
if (areGridLinesVisible) {
|
|
47130
47732
|
for (const box of boxes) {
|
|
47733
|
+
if (box.skipCellGridLines)
|
|
47734
|
+
continue;
|
|
47131
47735
|
ctx.strokeStyle = CELL_BORDER_COLOR;
|
|
47132
47736
|
ctx.lineWidth = thinLineWidth;
|
|
47133
47737
|
ctx.strokeRect(box.x + inset, box.y + inset, box.width - 2 * inset, box.height - 2 * inset);
|
|
@@ -47235,7 +47839,8 @@ class GridRenderer {
|
|
|
47235
47839
|
* each line and adding 1 pixel to the end of each line (depending on the direction of the
|
|
47236
47840
|
* line).
|
|
47237
47841
|
*/
|
|
47238
|
-
function drawBorder({ style,
|
|
47842
|
+
function drawBorder({ color, style, opacity }, x1, y1, x2, y2) {
|
|
47843
|
+
ctx.globalAlpha = opacity ?? 1;
|
|
47239
47844
|
ctx.strokeStyle = color;
|
|
47240
47845
|
switch (style) {
|
|
47241
47846
|
case "medium":
|
|
@@ -47283,6 +47888,7 @@ class GridRenderer {
|
|
|
47283
47888
|
ctx.stroke();
|
|
47284
47889
|
ctx.lineWidth = 1;
|
|
47285
47890
|
ctx.setLineDash([]);
|
|
47891
|
+
ctx.globalAlpha = 1;
|
|
47286
47892
|
}
|
|
47287
47893
|
}
|
|
47288
47894
|
drawTexts(renderingContext, boxes) {
|
|
@@ -47291,6 +47897,7 @@ class GridRenderer {
|
|
|
47291
47897
|
let currentFont;
|
|
47292
47898
|
for (const box of boxes) {
|
|
47293
47899
|
if (box.content) {
|
|
47900
|
+
ctx.globalAlpha = box.textOpacity ?? 1;
|
|
47294
47901
|
const style = box.style || {};
|
|
47295
47902
|
const align = box.content.align || "left";
|
|
47296
47903
|
// compute font and textColor
|
|
@@ -47321,6 +47928,7 @@ class GridRenderer {
|
|
|
47321
47928
|
if (box.clipRect) {
|
|
47322
47929
|
ctx.restore();
|
|
47323
47930
|
}
|
|
47931
|
+
ctx.globalAlpha = 1;
|
|
47324
47932
|
}
|
|
47325
47933
|
}
|
|
47326
47934
|
}
|
|
@@ -47338,10 +47946,11 @@ class GridRenderer {
|
|
|
47338
47946
|
}
|
|
47339
47947
|
ctx.save();
|
|
47340
47948
|
ctx.beginPath();
|
|
47341
|
-
|
|
47949
|
+
const clipRect = icon.clipRect || box;
|
|
47950
|
+
ctx.rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
|
|
47342
47951
|
ctx.clip();
|
|
47343
47952
|
const iconSize = icon.size;
|
|
47344
|
-
const { x, y } = this.getters.getCellIconRect(icon);
|
|
47953
|
+
const { x, y } = this.getters.getCellIconRect(icon, box);
|
|
47345
47954
|
ctx.translate(x, y);
|
|
47346
47955
|
ctx.scale(iconSize / svg.width, iconSize / svg.height);
|
|
47347
47956
|
for (const path of svg.paths) {
|
|
@@ -47550,7 +48159,6 @@ class GridRenderer {
|
|
|
47550
48159
|
const cell = this.getters.getEvaluatedCell(position);
|
|
47551
48160
|
const showFormula = this.getters.shouldShowFormulas();
|
|
47552
48161
|
const { x, y, width, height } = this.getters.getRect(zone);
|
|
47553
|
-
const { verticalAlign } = this.getters.getCellStyle(position);
|
|
47554
48162
|
const chipStyle = this.getters.getDataValidationChipStyle(position);
|
|
47555
48163
|
let style = this.getters.getCellComputedStyle(position);
|
|
47556
48164
|
if (this.fingerprints.isEnabled) {
|
|
@@ -47570,6 +48178,7 @@ class GridRenderer {
|
|
|
47570
48178
|
center: iconsList.find((icon) => icon?.horizontalAlign === "center"),
|
|
47571
48179
|
};
|
|
47572
48180
|
const box = {
|
|
48181
|
+
id: zoneToXc(zone),
|
|
47573
48182
|
x,
|
|
47574
48183
|
y,
|
|
47575
48184
|
width,
|
|
@@ -47577,11 +48186,11 @@ class GridRenderer {
|
|
|
47577
48186
|
border: this.getters.getCellComputedBorder(position) || undefined,
|
|
47578
48187
|
style,
|
|
47579
48188
|
dataBarFill,
|
|
47580
|
-
verticalAlign,
|
|
47581
48189
|
overlayColor: this.hoveredTables.overlayColors.get(position),
|
|
47582
48190
|
isError: (cell.type === CellValueType.error && !!cell.message) ||
|
|
47583
48191
|
this.getters.isDataValidationInvalid(position),
|
|
47584
48192
|
icons: cellIcons,
|
|
48193
|
+
disabledAnimation: this.zonesWithPreventedAnimationsInNextFrame.some((z) => isZoneInside(zone, z) || overlap(zone, z)),
|
|
47585
48194
|
};
|
|
47586
48195
|
const fontSizePX = computeTextFontSizeInPixels(box.style);
|
|
47587
48196
|
if (cell.type === CellValueType.empty || box.icons.center) {
|
|
@@ -47751,6 +48360,77 @@ class GridRenderer {
|
|
|
47751
48360
|
}
|
|
47752
48361
|
return boxes;
|
|
47753
48362
|
}
|
|
48363
|
+
getBoxesWithAnimations(boxes, oldBoxes, timeStamp) {
|
|
48364
|
+
this.updateAnimationsProgress(timeStamp);
|
|
48365
|
+
this.addNewAnimations(boxes, oldBoxes, timeStamp);
|
|
48366
|
+
if (this.animations.size > 0) {
|
|
48367
|
+
this.renderer.startAnimation("grid_renderer_animation");
|
|
48368
|
+
return this.updateBoxesWithAnimations(boxes);
|
|
48369
|
+
}
|
|
48370
|
+
else {
|
|
48371
|
+
this.renderer.stopAnimation("grid_renderer_animation");
|
|
48372
|
+
return boxes;
|
|
48373
|
+
}
|
|
48374
|
+
}
|
|
48375
|
+
updateBoxesWithAnimations(boxes) {
|
|
48376
|
+
const boxesWithAnimations = [];
|
|
48377
|
+
for (const box of boxes) {
|
|
48378
|
+
const animation = this.animations.get(box.id);
|
|
48379
|
+
if (!animation) {
|
|
48380
|
+
boxesWithAnimations.push(box);
|
|
48381
|
+
continue;
|
|
48382
|
+
}
|
|
48383
|
+
const animatedBox = deepCopy(box);
|
|
48384
|
+
boxesWithAnimations.push(animatedBox);
|
|
48385
|
+
for (const animationId of animation.animationTypes) {
|
|
48386
|
+
const animationItem = cellAnimationRegistry.get(animationId);
|
|
48387
|
+
const newBoxes = animationItem.updateAnimation(animation.progress, animatedBox, animation.oldBox, box);
|
|
48388
|
+
if (newBoxes) {
|
|
48389
|
+
boxesWithAnimations.push(...newBoxes.newBoxes);
|
|
48390
|
+
}
|
|
48391
|
+
}
|
|
48392
|
+
}
|
|
48393
|
+
return boxesWithAnimations;
|
|
48394
|
+
}
|
|
48395
|
+
updateAnimationsProgress(timeStamp) {
|
|
48396
|
+
if (timeStamp === undefined) {
|
|
48397
|
+
return;
|
|
48398
|
+
}
|
|
48399
|
+
for (const boxId of this.animations.keys()) {
|
|
48400
|
+
const animation = this.animations.get(boxId);
|
|
48401
|
+
if (animation.startTime === undefined) {
|
|
48402
|
+
animation.startTime = timeStamp;
|
|
48403
|
+
continue;
|
|
48404
|
+
}
|
|
48405
|
+
const elapsedTime = timeStamp - animation.startTime;
|
|
48406
|
+
const progress = Math.min(1, elapsedTime / CELL_ANIMATION_DURATION);
|
|
48407
|
+
if (progress >= 1) {
|
|
48408
|
+
this.animations.delete(boxId);
|
|
48409
|
+
}
|
|
48410
|
+
animation.progress = progress;
|
|
48411
|
+
}
|
|
48412
|
+
}
|
|
48413
|
+
addNewAnimations(boxes, oldBoxes, timeStamp) {
|
|
48414
|
+
for (const box of boxes) {
|
|
48415
|
+
this.lastRenderBoxes.set(box.id, box);
|
|
48416
|
+
const oldBox = oldBoxes.get(box.id);
|
|
48417
|
+
if (this.preventNewAnimationsInNextFrame || !oldBox || box.disabledAnimation) {
|
|
48418
|
+
continue;
|
|
48419
|
+
}
|
|
48420
|
+
const animationTypes = [];
|
|
48421
|
+
for (const animationItem of cellAnimationRegistry.getAll()) {
|
|
48422
|
+
if (animationItem.hasAnimation(oldBox, box)) {
|
|
48423
|
+
animationTypes.push(animationItem.id);
|
|
48424
|
+
}
|
|
48425
|
+
}
|
|
48426
|
+
const animation = animationTypes.length > 0
|
|
48427
|
+
? { animationTypes, oldBox, progress: 0, startTime: timeStamp }
|
|
48428
|
+
: undefined;
|
|
48429
|
+
if (animation) {
|
|
48430
|
+
this.animations.set(box.id, animation);
|
|
48431
|
+
}
|
|
48432
|
+
}
|
|
48433
|
+
}
|
|
47754
48434
|
}
|
|
47755
48435
|
|
|
47756
48436
|
function useGridDrawing(refName, model, canvasSize) {
|
|
@@ -47785,10 +48465,7 @@ function useGridDrawing(refName, model, canvasSize) {
|
|
|
47785
48465
|
// http://diveintohtml5.info/canvas.html#pixel-madness
|
|
47786
48466
|
ctx.translate(-CANVAS_SHIFT, -CANVAS_SHIFT);
|
|
47787
48467
|
ctx.scale(dpr, dpr);
|
|
47788
|
-
|
|
47789
|
-
model.drawLayer(renderingContext, layer);
|
|
47790
|
-
rendererStore.drawLayer(renderingContext, layer);
|
|
47791
|
-
}
|
|
48468
|
+
rendererStore.draw(renderingContext);
|
|
47792
48469
|
}
|
|
47793
48470
|
}
|
|
47794
48471
|
|
|
@@ -54883,7 +55560,7 @@ class SpreadsheetPivot {
|
|
|
54883
55560
|
}
|
|
54884
55561
|
getTypeFromZone(sheetId, zone) {
|
|
54885
55562
|
const cells = this.getters.getEvaluatedCellsInZone(sheetId, zone);
|
|
54886
|
-
const nonEmptyCells = cells.filter((cell) => cell.type
|
|
55563
|
+
const nonEmptyCells = cells.filter((cell) => !(cell.type === CellValueType.empty || cell.value === ""));
|
|
54887
55564
|
if (nonEmptyCells.length === 0) {
|
|
54888
55565
|
return "integer";
|
|
54889
55566
|
}
|
|
@@ -55491,7 +56168,7 @@ css /* scss */ `
|
|
|
55491
56168
|
`;
|
|
55492
56169
|
class SettingsPanel extends owl.Component {
|
|
55493
56170
|
static template = "o-spreadsheet-SettingsPanel";
|
|
55494
|
-
static components = { Section, ValidationMessages };
|
|
56171
|
+
static components = { Section, ValidationMessages, BadgeSelection };
|
|
55495
56172
|
static props = { onCloseSidePanel: Function };
|
|
55496
56173
|
loadedLocales = [];
|
|
55497
56174
|
setup() {
|
|
@@ -56375,49 +57052,111 @@ class ScreenWidthStore {
|
|
|
56375
57052
|
}
|
|
56376
57053
|
|
|
56377
57054
|
const DEFAULT_SIDE_PANEL_SIZE = 350;
|
|
57055
|
+
const COLLAPSED_SIDE_PANEL_SIZE = 45;
|
|
56378
57056
|
const MIN_SHEET_VIEW_WIDTH = 150;
|
|
56379
57057
|
class SidePanelStore extends SpreadsheetStore {
|
|
56380
|
-
mutators = [
|
|
56381
|
-
|
|
56382
|
-
|
|
56383
|
-
|
|
57058
|
+
mutators = [
|
|
57059
|
+
"open",
|
|
57060
|
+
"toggle",
|
|
57061
|
+
"close",
|
|
57062
|
+
"changePanelSize",
|
|
57063
|
+
"resetPanelSize",
|
|
57064
|
+
"togglePinPanel",
|
|
57065
|
+
"closeMainPanel",
|
|
57066
|
+
"changeSpreadsheetWidth",
|
|
57067
|
+
"toggleCollapsePanel",
|
|
57068
|
+
];
|
|
57069
|
+
mainPanel = undefined;
|
|
57070
|
+
secondaryPanel;
|
|
57071
|
+
availableWidth = 0;
|
|
56384
57072
|
screenWidthStore = this.get(ScreenWidthStore);
|
|
56385
|
-
get
|
|
56386
|
-
|
|
56387
|
-
|
|
56388
|
-
|
|
56389
|
-
|
|
57073
|
+
get isMainPanelOpen() {
|
|
57074
|
+
return this.mainPanel && this.mainPanel.componentTag
|
|
57075
|
+
? this.computeState(this.mainPanel).isOpen
|
|
57076
|
+
: false;
|
|
57077
|
+
}
|
|
57078
|
+
get isSecondaryPanelOpen() {
|
|
57079
|
+
return this.secondaryPanel && this.secondaryPanel.componentTag
|
|
57080
|
+
? this.computeState(this.secondaryPanel).isOpen
|
|
57081
|
+
: false;
|
|
57082
|
+
}
|
|
57083
|
+
get mainPanelProps() {
|
|
57084
|
+
return this.mainPanel ? this.getPanelProps(this.mainPanel) : undefined;
|
|
57085
|
+
}
|
|
57086
|
+
get mainPanelKey() {
|
|
57087
|
+
return this.mainPanel ? this.getPanelKey(this.mainPanel) : undefined;
|
|
56390
57088
|
}
|
|
56391
|
-
get
|
|
56392
|
-
|
|
57089
|
+
get secondaryPanelProps() {
|
|
57090
|
+
return this.secondaryPanel ? this.getPanelProps(this.secondaryPanel) : undefined;
|
|
57091
|
+
}
|
|
57092
|
+
get secondaryPanelKey() {
|
|
57093
|
+
return this.secondaryPanel ? this.getPanelKey(this.secondaryPanel) : undefined;
|
|
57094
|
+
}
|
|
57095
|
+
get totalPanelSize() {
|
|
57096
|
+
return (this.mainPanel?.size || 0) + (this.secondaryPanel?.size ?? 0);
|
|
57097
|
+
}
|
|
57098
|
+
getPanelProps(panelInfo) {
|
|
57099
|
+
const state = this.computeState(panelInfo);
|
|
56393
57100
|
if (state.isOpen) {
|
|
56394
57101
|
return state.props ?? {};
|
|
56395
57102
|
}
|
|
56396
57103
|
return {};
|
|
56397
57104
|
}
|
|
56398
|
-
|
|
56399
|
-
const state = this.computeState(
|
|
57105
|
+
getPanelKey(panelInfo) {
|
|
57106
|
+
const state = this.computeState(panelInfo);
|
|
56400
57107
|
if (state.isOpen) {
|
|
56401
57108
|
return state.key;
|
|
56402
57109
|
}
|
|
56403
57110
|
return undefined;
|
|
56404
57111
|
}
|
|
56405
|
-
open(componentTag,
|
|
57112
|
+
open(componentTag, initialPanelProps = {}) {
|
|
56406
57113
|
if (this.screenWidthStore.isSmall) {
|
|
56407
57114
|
return;
|
|
56408
57115
|
}
|
|
56409
|
-
const
|
|
57116
|
+
const newPanelInfo = { initialPanelProps, componentTag, size: DEFAULT_SIDE_PANEL_SIZE };
|
|
57117
|
+
const state = this.computeState(newPanelInfo);
|
|
56410
57118
|
if (!state.isOpen) {
|
|
56411
57119
|
return;
|
|
56412
57120
|
}
|
|
56413
|
-
|
|
56414
|
-
|
|
57121
|
+
const mainPanelKey = this.mainPanel ? this.getPanelKey(this.mainPanel) : undefined;
|
|
57122
|
+
if (!this.mainPanel || !this.mainPanel.isPinned || mainPanelKey === state.key) {
|
|
57123
|
+
this._openPanel("mainPanel", newPanelInfo, state);
|
|
57124
|
+
return;
|
|
57125
|
+
}
|
|
57126
|
+
// Try to open secondary panel if main panel is pinned
|
|
57127
|
+
const nonCollapsedPanelSize = this.mainPanel.isCollapsed
|
|
57128
|
+
? DEFAULT_SIDE_PANEL_SIZE
|
|
57129
|
+
: this.mainPanel.size;
|
|
57130
|
+
if (!this.secondaryPanel &&
|
|
57131
|
+
nonCollapsedPanelSize + DEFAULT_SIDE_PANEL_SIZE > this.availableWidth) {
|
|
57132
|
+
this.get(NotificationStore).notifyUser({
|
|
57133
|
+
sticky: false,
|
|
57134
|
+
type: "warning",
|
|
57135
|
+
text: _t("The window is too small to display multiple side panels."),
|
|
57136
|
+
});
|
|
57137
|
+
return;
|
|
57138
|
+
}
|
|
57139
|
+
this._openPanel("secondaryPanel", newPanelInfo, state);
|
|
57140
|
+
}
|
|
57141
|
+
_openPanel(panel, newPanel, state) {
|
|
57142
|
+
const currentPanel = this[panel];
|
|
57143
|
+
if (currentPanel && newPanel.componentTag !== currentPanel.componentTag) {
|
|
57144
|
+
currentPanel.initialPanelProps?.onCloseSidePanel?.();
|
|
57145
|
+
}
|
|
57146
|
+
this[panel] = {
|
|
57147
|
+
initialPanelProps: state.props ?? {},
|
|
57148
|
+
componentTag: newPanel.componentTag,
|
|
57149
|
+
size: currentPanel?.size || DEFAULT_SIDE_PANEL_SIZE,
|
|
57150
|
+
isCollapsed: currentPanel?.isCollapsed || false,
|
|
57151
|
+
isPinned: currentPanel && "isPinned" in currentPanel ? currentPanel.isPinned : false,
|
|
57152
|
+
};
|
|
57153
|
+
if (this[panel].isCollapsed) {
|
|
57154
|
+
this.toggleCollapsePanel(panel);
|
|
56415
57155
|
}
|
|
56416
|
-
this.componentTag = componentTag;
|
|
56417
|
-
this.initialPanelProps = state.props ?? {};
|
|
56418
57156
|
}
|
|
56419
57157
|
toggle(componentTag, panelProps) {
|
|
56420
|
-
|
|
57158
|
+
const panel = this.mainPanel?.isPinned ? this.secondaryPanel : this.mainPanel;
|
|
57159
|
+
if (panel && componentTag === panel.componentTag) {
|
|
56421
57160
|
this.close();
|
|
56422
57161
|
}
|
|
56423
57162
|
else {
|
|
@@ -56425,34 +57164,85 @@ class SidePanelStore extends SpreadsheetStore {
|
|
|
56425
57164
|
}
|
|
56426
57165
|
}
|
|
56427
57166
|
close() {
|
|
56428
|
-
this.
|
|
56429
|
-
|
|
56430
|
-
|
|
57167
|
+
if (this.mainPanel?.isPinned) {
|
|
57168
|
+
if (this.secondaryPanel) {
|
|
57169
|
+
this.secondaryPanel.initialPanelProps.onCloseSidePanel?.();
|
|
57170
|
+
this.secondaryPanel = undefined;
|
|
57171
|
+
}
|
|
57172
|
+
return;
|
|
57173
|
+
}
|
|
57174
|
+
this.mainPanel?.initialPanelProps.onCloseSidePanel?.();
|
|
57175
|
+
this.mainPanel = undefined;
|
|
56431
57176
|
}
|
|
56432
|
-
|
|
56433
|
-
|
|
56434
|
-
|
|
57177
|
+
closeMainPanel() {
|
|
57178
|
+
this.mainPanel?.initialPanelProps.onCloseSidePanel?.();
|
|
57179
|
+
this.mainPanel = this.secondaryPanel || undefined;
|
|
57180
|
+
this.secondaryPanel = undefined;
|
|
57181
|
+
}
|
|
57182
|
+
changePanelSize(panel, size) {
|
|
57183
|
+
const panelInfo = this[panel];
|
|
57184
|
+
if (!panelInfo || ("isCollapsed" in panelInfo && panelInfo.isCollapsed)) {
|
|
57185
|
+
return;
|
|
56435
57186
|
}
|
|
56436
|
-
|
|
56437
|
-
|
|
57187
|
+
size = Math.max(size, DEFAULT_SIDE_PANEL_SIZE);
|
|
57188
|
+
let otherPanelSize = panel === "mainPanel" ? this.secondaryPanel?.size || 0 : this.mainPanel?.size || 0;
|
|
57189
|
+
if (size > this.availableWidth - otherPanelSize) {
|
|
57190
|
+
if (panel === "mainPanel" && this.secondaryPanel) {
|
|
57191
|
+
// reduce the secondary panel size to fit the main panel
|
|
57192
|
+
this.secondaryPanel.size = Math.max(this.availableWidth - size, DEFAULT_SIDE_PANEL_SIZE);
|
|
57193
|
+
otherPanelSize = this.secondaryPanel.size;
|
|
57194
|
+
}
|
|
57195
|
+
size = Math.max(this.availableWidth - otherPanelSize, DEFAULT_SIDE_PANEL_SIZE);
|
|
56438
57196
|
}
|
|
56439
|
-
|
|
56440
|
-
|
|
57197
|
+
panelInfo.size = size;
|
|
57198
|
+
}
|
|
57199
|
+
resetPanelSize(panel) {
|
|
57200
|
+
const panelInfo = this[panel];
|
|
57201
|
+
if (!panelInfo) {
|
|
57202
|
+
return;
|
|
56441
57203
|
}
|
|
57204
|
+
panelInfo.size = DEFAULT_SIDE_PANEL_SIZE;
|
|
56442
57205
|
}
|
|
56443
|
-
|
|
56444
|
-
this.
|
|
57206
|
+
togglePinPanel() {
|
|
57207
|
+
if (!this.mainPanel) {
|
|
57208
|
+
return;
|
|
57209
|
+
}
|
|
57210
|
+
this.mainPanel.isPinned = !this.mainPanel.isPinned;
|
|
57211
|
+
if (!this.mainPanel.isPinned && this.secondaryPanel) {
|
|
57212
|
+
this.secondaryPanel?.initialPanelProps.onCloseSidePanel?.();
|
|
57213
|
+
this.mainPanel = this.secondaryPanel;
|
|
57214
|
+
this.secondaryPanel = undefined;
|
|
57215
|
+
}
|
|
56445
57216
|
}
|
|
56446
|
-
|
|
56447
|
-
const
|
|
56448
|
-
if (!
|
|
56449
|
-
return
|
|
56450
|
-
|
|
56451
|
-
|
|
56452
|
-
|
|
57217
|
+
toggleCollapsePanel(panel) {
|
|
57218
|
+
const panelInfo = this[panel];
|
|
57219
|
+
if (!panelInfo) {
|
|
57220
|
+
return;
|
|
57221
|
+
}
|
|
57222
|
+
if (panelInfo.isCollapsed) {
|
|
57223
|
+
panelInfo.isCollapsed = false;
|
|
57224
|
+
this.changePanelSize(panel, DEFAULT_SIDE_PANEL_SIZE);
|
|
56453
57225
|
}
|
|
56454
57226
|
else {
|
|
56455
|
-
|
|
57227
|
+
panelInfo.isCollapsed = true;
|
|
57228
|
+
panelInfo.size = COLLAPSED_SIDE_PANEL_SIZE;
|
|
57229
|
+
}
|
|
57230
|
+
}
|
|
57231
|
+
computeState({ componentTag, initialPanelProps }) {
|
|
57232
|
+
const customComputeState = sidePanelRegistry.get(componentTag).computeState;
|
|
57233
|
+
const state = customComputeState
|
|
57234
|
+
? customComputeState(this.getters, initialPanelProps)
|
|
57235
|
+
: { isOpen: true, props: initialPanelProps };
|
|
57236
|
+
return state.isOpen ? { ...state, key: state.key || componentTag } : state;
|
|
57237
|
+
}
|
|
57238
|
+
changeSpreadsheetWidth(width) {
|
|
57239
|
+
this.availableWidth = width - MIN_SHEET_VIEW_WIDTH;
|
|
57240
|
+
if (this.secondaryPanel && width - this.totalPanelSize < MIN_SHEET_VIEW_WIDTH) {
|
|
57241
|
+
this.secondaryPanel?.initialPanelProps.onCloseSidePanel?.();
|
|
57242
|
+
this.secondaryPanel = undefined;
|
|
57243
|
+
}
|
|
57244
|
+
if (this.mainPanel && width - this.totalPanelSize < MIN_SHEET_VIEW_WIDTH) {
|
|
57245
|
+
this.mainPanel.size = Math.max(width - MIN_SHEET_VIEW_WIDTH, DEFAULT_SIDE_PANEL_SIZE);
|
|
56456
57246
|
}
|
|
56457
57247
|
}
|
|
56458
57248
|
}
|
|
@@ -56600,11 +57390,11 @@ class Grid extends owl.Component {
|
|
|
56600
57390
|
this.hoveredCell.clear();
|
|
56601
57391
|
});
|
|
56602
57392
|
this.cellPopovers = useStore(CellPopoverStore);
|
|
56603
|
-
owl.useEffect(() => {
|
|
56604
|
-
if (!
|
|
57393
|
+
owl.useEffect((isMainPanelOpen, isSecondaryPanelOpen) => {
|
|
57394
|
+
if (!isMainPanelOpen && !isSecondaryPanelOpen) {
|
|
56605
57395
|
this.DOMFocusableElementStore.focus();
|
|
56606
57396
|
}
|
|
56607
|
-
}, () => [this.sidePanel.
|
|
57397
|
+
}, () => [this.sidePanel.isMainPanelOpen, this.sidePanel.isSecondaryPanelOpen]);
|
|
56608
57398
|
useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
|
|
56609
57399
|
const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
|
|
56610
57400
|
return scrollY > 0;
|
|
@@ -59696,7 +60486,7 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
59696
60486
|
else if (newRule.criterion.type === "isValueInList") {
|
|
59697
60487
|
newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
|
|
59698
60488
|
}
|
|
59699
|
-
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
|
|
60489
|
+
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules, newRule.id);
|
|
59700
60490
|
const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
|
|
59701
60491
|
if (ruleIndex !== -1) {
|
|
59702
60492
|
adaptedRules[ruleIndex] = newRule;
|
|
@@ -59706,9 +60496,12 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
59706
60496
|
this.history.update("rules", sheetId, [...adaptedRules, newRule]);
|
|
59707
60497
|
}
|
|
59708
60498
|
}
|
|
59709
|
-
removeRangesFromRules(sheetId, ranges, rules) {
|
|
60499
|
+
removeRangesFromRules(sheetId, ranges, rules, editingRuleId) {
|
|
59710
60500
|
rules = deepCopy(rules);
|
|
59711
60501
|
for (const rule of rules) {
|
|
60502
|
+
if (rule.id === editingRuleId) {
|
|
60503
|
+
continue; // Skip the rule being edited to preserve its place in the list
|
|
60504
|
+
}
|
|
59712
60505
|
rule.ranges = this.getters.recomputeRanges(rule.ranges, ranges);
|
|
59713
60506
|
}
|
|
59714
60507
|
return rules.filter((rule) => rule.ranges.length > 0);
|
|
@@ -63116,7 +63909,7 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
63116
63909
|
break;
|
|
63117
63910
|
}
|
|
63118
63911
|
case "UPDATE_PIVOT": {
|
|
63119
|
-
this.history.update("pivots", cmd.pivotId, "definition",
|
|
63912
|
+
this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
|
|
63120
63913
|
this.compileCalculatedMeasures(cmd.pivot.measures);
|
|
63121
63914
|
break;
|
|
63122
63915
|
}
|
|
@@ -63187,10 +63980,7 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
63187
63980
|
// Private
|
|
63188
63981
|
// -------------------------------------------------------------------------
|
|
63189
63982
|
addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
|
|
63190
|
-
this.history.update("pivots", pivotId, {
|
|
63191
|
-
definition: this.repairSortedColumn(deepCopy(pivot)),
|
|
63192
|
-
formulaId,
|
|
63193
|
-
});
|
|
63983
|
+
this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
|
|
63194
63984
|
this.compileCalculatedMeasures(pivot.measures);
|
|
63195
63985
|
this.history.update("formulaIds", formulaId, pivotId);
|
|
63196
63986
|
this.history.update("nextFormulaId", this.nextFormulaId + 1);
|
|
@@ -63279,7 +64069,6 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
63279
64069
|
}
|
|
63280
64070
|
}
|
|
63281
64071
|
checkSortedColumnInMeasures(definition) {
|
|
63282
|
-
definition = this.repairSortedColumn(definition);
|
|
63283
64072
|
const measures = definition.measures.map((measure) => measure.id);
|
|
63284
64073
|
if (definition.sortedColumn && !measures.includes(definition.sortedColumn.measure)) {
|
|
63285
64074
|
return "InvalidDefinition" /* CommandResult.InvalidDefinition */;
|
|
@@ -63293,26 +64082,6 @@ class PivotCorePlugin extends CorePlugin {
|
|
|
63293
64082
|
}
|
|
63294
64083
|
return "Success" /* CommandResult.Success */;
|
|
63295
64084
|
}
|
|
63296
|
-
repairSortedColumn(definition) {
|
|
63297
|
-
if (definition.sortedColumn) {
|
|
63298
|
-
// Fix for an upgrade issue: the sortedColumn measure was not updated
|
|
63299
|
-
// from using fieldName to using id. If the sortedColumn measure matches
|
|
63300
|
-
// a measure fieldName in the definition, update it to use the measure's id instead
|
|
63301
|
-
// of its fieldName.
|
|
63302
|
-
// TODO: add an upgrade step to fix this in master and remove this code
|
|
63303
|
-
const sortedMeasure = definition.measures.find((measure) => measure.fieldName === definition.sortedColumn?.measure);
|
|
63304
|
-
if (sortedMeasure) {
|
|
63305
|
-
return {
|
|
63306
|
-
...definition,
|
|
63307
|
-
sortedColumn: {
|
|
63308
|
-
...definition.sortedColumn,
|
|
63309
|
-
measure: sortedMeasure.id,
|
|
63310
|
-
},
|
|
63311
|
-
};
|
|
63312
|
-
}
|
|
63313
|
-
}
|
|
63314
|
-
return definition;
|
|
63315
|
-
}
|
|
63316
64085
|
// ---------------------------------------------------------------------
|
|
63317
64086
|
// Import/Export
|
|
63318
64087
|
// ---------------------------------------------------------------------
|
|
@@ -63394,9 +64163,7 @@ class SettingsPlugin extends CorePlugin {
|
|
|
63394
64163
|
this.locale = data.settings?.locale ?? DEFAULT_LOCALE;
|
|
63395
64164
|
}
|
|
63396
64165
|
export(data) {
|
|
63397
|
-
data.settings = {
|
|
63398
|
-
locale: this.locale,
|
|
63399
|
-
};
|
|
64166
|
+
data.settings = { locale: this.locale };
|
|
63400
64167
|
}
|
|
63401
64168
|
}
|
|
63402
64169
|
|
|
@@ -66431,11 +67198,8 @@ class CellIconPlugin extends CoreViewPlugin {
|
|
|
66431
67198
|
}
|
|
66432
67199
|
return this.cellIconsCache[position.sheetId][position.col][position.row];
|
|
66433
67200
|
}
|
|
66434
|
-
getCellIconRect(icon) {
|
|
67201
|
+
getCellIconRect(icon, cellRect) {
|
|
66435
67202
|
const cellPosition = icon.position;
|
|
66436
|
-
const merge = this.getters.getMerge(cellPosition);
|
|
66437
|
-
const zone = merge || positionToZone(cellPosition);
|
|
66438
|
-
const cellRect = this.getters.getRect(zone);
|
|
66439
67203
|
const cell = this.getters.getCell(cellPosition);
|
|
66440
67204
|
const x = this.getIconHorizontalPosition(cellRect, icon.horizontalAlign, icon);
|
|
66441
67205
|
const y = this.getters.computeTextYCoordinate(cellRect, icon.size, cell?.style?.verticalAlign);
|
|
@@ -72268,49 +73032,17 @@ class ClipboardPlugin extends UIPlugin {
|
|
|
72268
73032
|
if (!copiedData) {
|
|
72269
73033
|
return;
|
|
72270
73034
|
}
|
|
72271
|
-
let zone = undefined;
|
|
72272
|
-
const selectedZones = [];
|
|
72273
73035
|
const sheetId = this.getters.getActiveSheetId();
|
|
72274
|
-
const target = {
|
|
72275
|
-
sheetId,
|
|
72276
|
-
zones,
|
|
72277
|
-
};
|
|
72278
73036
|
const handlers = this.selectClipboardHandlers(copiedData);
|
|
72279
|
-
|
|
72280
|
-
const handlerData = copiedData[handlerName];
|
|
72281
|
-
if (!handlerData) {
|
|
72282
|
-
continue;
|
|
72283
|
-
}
|
|
72284
|
-
const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
|
|
72285
|
-
if (currentTarget.figureId) {
|
|
72286
|
-
target.figureId = currentTarget.figureId;
|
|
72287
|
-
}
|
|
72288
|
-
for (const targetZone of currentTarget.zones) {
|
|
72289
|
-
selectedZones.push(targetZone);
|
|
72290
|
-
if (zone === undefined) {
|
|
72291
|
-
zone = targetZone;
|
|
72292
|
-
continue;
|
|
72293
|
-
}
|
|
72294
|
-
zone = union(zone, targetZone);
|
|
72295
|
-
}
|
|
72296
|
-
}
|
|
73037
|
+
const { target, zone, selectedZones } = getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options);
|
|
72297
73038
|
if (zone !== undefined) {
|
|
72298
|
-
this.addMissingDimensions(
|
|
73039
|
+
this.addMissingDimensions(sheetId, zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
|
|
72299
73040
|
}
|
|
72300
|
-
handlers
|
|
72301
|
-
const handlerData = copiedData[handlerName];
|
|
72302
|
-
if (handlerData) {
|
|
72303
|
-
handler.paste(target, handlerData, options);
|
|
72304
|
-
}
|
|
72305
|
-
});
|
|
73041
|
+
applyClipboardHandlersPaste(handlers, copiedData, target, options);
|
|
72306
73042
|
if (!options?.selectTarget) {
|
|
72307
73043
|
return;
|
|
72308
73044
|
}
|
|
72309
|
-
|
|
72310
|
-
const col = selection.left;
|
|
72311
|
-
const row = selection.top;
|
|
72312
|
-
this.selection.getBackToDefault();
|
|
72313
|
-
this.selection.selectZone({ cell: { col, row }, zone: union(...selectedZones) }, { scrollIntoView: false });
|
|
73045
|
+
selectPastedZone(this.selection, zones, selectedZones);
|
|
72314
73046
|
}
|
|
72315
73047
|
/**
|
|
72316
73048
|
* Add columns and/or rows to ensure that col + width and row + height are still
|
|
@@ -75139,6 +75871,17 @@ clickableCellRegistry.add("link", {
|
|
|
75139
75871
|
return !!getters.getEvaluatedCell(position).link;
|
|
75140
75872
|
},
|
|
75141
75873
|
execute: (position, env, isMiddleClick) => openLink(env.model.getters.getEvaluatedCell(position).link, env, isMiddleClick),
|
|
75874
|
+
title: (position, getters) => {
|
|
75875
|
+
const link = getters.getEvaluatedCell(position).link;
|
|
75876
|
+
if (!link)
|
|
75877
|
+
return "";
|
|
75878
|
+
if (link.isExternal) {
|
|
75879
|
+
return _t("Go to url: %(url)s", { url: link.url });
|
|
75880
|
+
}
|
|
75881
|
+
else {
|
|
75882
|
+
return _t("Go to %(label)s", { label: link.label });
|
|
75883
|
+
}
|
|
75884
|
+
},
|
|
75142
75885
|
sequence: 5,
|
|
75143
75886
|
});
|
|
75144
75887
|
|
|
@@ -76850,12 +77593,13 @@ class ClickableCellsStore extends SpreadsheetStore {
|
|
|
76850
77593
|
if (!item) {
|
|
76851
77594
|
continue;
|
|
76852
77595
|
}
|
|
77596
|
+
const title = typeof item.title === "function" ? item.title(position, getters) : item.title;
|
|
76853
77597
|
const zone = getters.expandZone(sheetId, positionToZone(position));
|
|
76854
77598
|
cells.push({
|
|
76855
77599
|
coordinates: getters.getVisibleRect(zone),
|
|
76856
77600
|
position,
|
|
76857
77601
|
action: item.execute,
|
|
76858
|
-
title:
|
|
77602
|
+
title: title || "",
|
|
76859
77603
|
});
|
|
76860
77604
|
}
|
|
76861
77605
|
return cells;
|
|
@@ -77240,24 +77984,36 @@ css /* scss */ `
|
|
|
77240
77984
|
user-select: none;
|
|
77241
77985
|
color: ${TEXT_BODY};
|
|
77242
77986
|
|
|
77987
|
+
&.collapsed {
|
|
77988
|
+
padding: 8px;
|
|
77989
|
+
cursor: pointer;
|
|
77990
|
+
|
|
77991
|
+
.o-sidePanelTitle {
|
|
77992
|
+
writing-mode: vertical-rl;
|
|
77993
|
+
text-orientation: mixed;
|
|
77994
|
+
}
|
|
77995
|
+
}
|
|
77996
|
+
|
|
77243
77997
|
.o-sidePanelTitle {
|
|
77244
77998
|
line-height: 20px;
|
|
77245
77999
|
font-size: 16px;
|
|
77246
78000
|
}
|
|
77247
78001
|
|
|
77248
78002
|
.o-sidePanelHeader {
|
|
77249
|
-
padding: 8px
|
|
77250
|
-
display: flex;
|
|
77251
|
-
align-items: center;
|
|
77252
|
-
justify-content: space-between;
|
|
78003
|
+
padding: 8px;
|
|
77253
78004
|
border-bottom: 1px solid ${GRAY_300};
|
|
78005
|
+
}
|
|
77254
78006
|
|
|
77255
|
-
|
|
77256
|
-
|
|
77257
|
-
|
|
77258
|
-
|
|
77259
|
-
|
|
77260
|
-
}
|
|
78007
|
+
.o-sidePanelAction {
|
|
78008
|
+
padding: 5px 10px;
|
|
78009
|
+
cursor: pointer;
|
|
78010
|
+
|
|
78011
|
+
&.active {
|
|
78012
|
+
background-color: ${BUTTON_ACTIVE_BG};
|
|
78013
|
+
}
|
|
78014
|
+
|
|
78015
|
+
&:hover {
|
|
78016
|
+
background-color: ${BUTTON_HOVER_BG};
|
|
77261
78017
|
}
|
|
77262
78018
|
}
|
|
77263
78019
|
.o-sidePanelBody-container {
|
|
@@ -77334,43 +78090,114 @@ css /* scss */ `
|
|
|
77334
78090
|
`;
|
|
77335
78091
|
class SidePanel extends owl.Component {
|
|
77336
78092
|
static template = "o-spreadsheet-SidePanel";
|
|
78093
|
+
static props = {
|
|
78094
|
+
panelContent: Object,
|
|
78095
|
+
panelProps: Object,
|
|
78096
|
+
onCloseSidePanel: Function,
|
|
78097
|
+
onStartHandleDrag: Function,
|
|
78098
|
+
onResetPanelSize: Function,
|
|
78099
|
+
isPinned: { type: Boolean, optional: true },
|
|
78100
|
+
onTogglePinPanel: { type: Function, optional: true },
|
|
78101
|
+
onToggleCollapsePanel: { type: Function, optional: true },
|
|
78102
|
+
isCollapsed: { type: Boolean, optional: true },
|
|
78103
|
+
};
|
|
78104
|
+
spreadsheetRect = useSpreadsheetRect();
|
|
78105
|
+
getTitle() {
|
|
78106
|
+
const panel = this.props.panelContent;
|
|
78107
|
+
return typeof panel.title === "function"
|
|
78108
|
+
? panel.title(this.env, this.props.panelProps)
|
|
78109
|
+
: panel.title;
|
|
78110
|
+
}
|
|
78111
|
+
get pinInfoMessage() {
|
|
78112
|
+
return _t("Pin this panel to allow to open another side panel beside it.");
|
|
78113
|
+
}
|
|
78114
|
+
}
|
|
78115
|
+
|
|
78116
|
+
class SidePanels extends owl.Component {
|
|
78117
|
+
static template = "o-spreadsheet-SidePanels";
|
|
77337
78118
|
static props = {};
|
|
78119
|
+
static components = { SidePanel };
|
|
77338
78120
|
sidePanelStore;
|
|
77339
78121
|
spreadsheetRect = useSpreadsheetRect();
|
|
77340
78122
|
setup() {
|
|
77341
78123
|
this.sidePanelStore = useStore(SidePanelStore);
|
|
77342
|
-
owl.useEffect((
|
|
77343
|
-
if (!
|
|
78124
|
+
owl.useEffect(() => {
|
|
78125
|
+
if (this.sidePanelStore.mainPanel && !this.sidePanelStore.isMainPanelOpen) {
|
|
78126
|
+
this.sidePanelStore.closeMainPanel();
|
|
78127
|
+
}
|
|
78128
|
+
if (this.sidePanelStore.secondaryPanel && !this.sidePanelStore.isSecondaryPanelOpen) {
|
|
77344
78129
|
this.sidePanelStore.close();
|
|
77345
78130
|
}
|
|
77346
|
-
}, () => [this.sidePanelStore.
|
|
77347
|
-
}
|
|
77348
|
-
get panel() {
|
|
77349
|
-
return sidePanelRegistry.get(this.sidePanelStore.componentTag);
|
|
77350
|
-
}
|
|
77351
|
-
close() {
|
|
77352
|
-
this.sidePanelStore.close();
|
|
78131
|
+
}, () => [this.sidePanelStore.isMainPanelOpen, this.sidePanelStore.isSecondaryPanelOpen]);
|
|
77353
78132
|
}
|
|
77354
|
-
|
|
77355
|
-
const panel = this.panel;
|
|
77356
|
-
return typeof panel.title === "function"
|
|
77357
|
-
? panel.title(this.env, this.sidePanelStore.panelProps)
|
|
77358
|
-
: panel.title;
|
|
77359
|
-
}
|
|
77360
|
-
startHandleDrag(ev) {
|
|
78133
|
+
startHandleDrag(panel, ev) {
|
|
77361
78134
|
const startingCursor = document.body.style.cursor;
|
|
77362
|
-
const
|
|
78135
|
+
const panelInfo = panel === "mainPanel" ? this.sidePanelStore.mainPanel : this.sidePanelStore.secondaryPanel;
|
|
78136
|
+
if (!panelInfo) {
|
|
78137
|
+
return;
|
|
78138
|
+
}
|
|
78139
|
+
const startSize = panelInfo.size;
|
|
77363
78140
|
const startPosition = ev.clientX;
|
|
77364
78141
|
const onMouseMove = (ev) => {
|
|
77365
78142
|
document.body.style.cursor = "col-resize";
|
|
77366
78143
|
const newSize = startSize + startPosition - ev.clientX;
|
|
77367
|
-
this.sidePanelStore.changePanelSize(
|
|
78144
|
+
this.sidePanelStore.changePanelSize(panel, newSize);
|
|
77368
78145
|
};
|
|
77369
78146
|
const cleanUp = () => {
|
|
77370
78147
|
document.body.style.cursor = startingCursor;
|
|
77371
78148
|
};
|
|
77372
78149
|
startDnd(onMouseMove, cleanUp);
|
|
77373
78150
|
}
|
|
78151
|
+
get mainPanelProps() {
|
|
78152
|
+
const panelProps = this.sidePanelStore.mainPanelProps;
|
|
78153
|
+
if (!this.sidePanelStore.mainPanel || !panelProps) {
|
|
78154
|
+
return undefined;
|
|
78155
|
+
}
|
|
78156
|
+
return {
|
|
78157
|
+
panelContent: sidePanelRegistry.get(this.sidePanelStore.mainPanel.componentTag),
|
|
78158
|
+
panelProps,
|
|
78159
|
+
onCloseSidePanel: () => this.sidePanelStore.closeMainPanel(),
|
|
78160
|
+
onTogglePinPanel: () => this.sidePanelStore.togglePinPanel(),
|
|
78161
|
+
onStartHandleDrag: (ev) => this.startHandleDrag("mainPanel", ev),
|
|
78162
|
+
onResetPanelSize: () => this.sidePanelStore.resetPanelSize("mainPanel"),
|
|
78163
|
+
isPinned: this.sidePanelStore.mainPanel?.isPinned,
|
|
78164
|
+
onToggleCollapsePanel: () => this.sidePanelStore.toggleCollapsePanel("mainPanel"),
|
|
78165
|
+
isCollapsed: this.sidePanelStore.mainPanel?.isCollapsed,
|
|
78166
|
+
};
|
|
78167
|
+
}
|
|
78168
|
+
get secondaryPanelProps() {
|
|
78169
|
+
const panelProps = this.sidePanelStore.secondaryPanelProps;
|
|
78170
|
+
if (!this.sidePanelStore.secondaryPanel || !panelProps) {
|
|
78171
|
+
return undefined;
|
|
78172
|
+
}
|
|
78173
|
+
return {
|
|
78174
|
+
panelContent: sidePanelRegistry.get(this.sidePanelStore.secondaryPanel.componentTag),
|
|
78175
|
+
panelProps,
|
|
78176
|
+
onCloseSidePanel: () => this.sidePanelStore.close(),
|
|
78177
|
+
onStartHandleDrag: (ev) => this.startHandleDrag("secondaryPanel", ev),
|
|
78178
|
+
onResetPanelSize: () => this.sidePanelStore.resetPanelSize("secondaryPanel"),
|
|
78179
|
+
onToggleCollapsePanel: () => this.sidePanelStore.toggleCollapsePanel("secondaryPanel"),
|
|
78180
|
+
isCollapsed: this.sidePanelStore.secondaryPanel?.isCollapsed,
|
|
78181
|
+
};
|
|
78182
|
+
}
|
|
78183
|
+
get panelList() {
|
|
78184
|
+
return [
|
|
78185
|
+
{
|
|
78186
|
+
key: this.sidePanelStore.secondaryPanelKey,
|
|
78187
|
+
props: this.secondaryPanelProps,
|
|
78188
|
+
style: this.sidePanelStore.secondaryPanel
|
|
78189
|
+
? cssPropertiesToCss({ width: `${this.sidePanelStore.secondaryPanel.size}px` })
|
|
78190
|
+
: "",
|
|
78191
|
+
},
|
|
78192
|
+
{
|
|
78193
|
+
key: this.sidePanelStore.mainPanelKey,
|
|
78194
|
+
props: this.mainPanelProps,
|
|
78195
|
+
style: this.sidePanelStore.mainPanel
|
|
78196
|
+
? cssPropertiesToCss({ width: `${this.sidePanelStore.mainPanel.size}px` })
|
|
78197
|
+
: "",
|
|
78198
|
+
},
|
|
78199
|
+
].filter((panel) => panel.key && panel.props);
|
|
78200
|
+
}
|
|
77374
78201
|
}
|
|
77375
78202
|
|
|
77376
78203
|
class RibbonMenu extends owl.Component {
|
|
@@ -78932,7 +79759,7 @@ class Spreadsheet extends owl.Component {
|
|
|
78932
79759
|
Grid,
|
|
78933
79760
|
BottomBar,
|
|
78934
79761
|
SmallBottomBar,
|
|
78935
|
-
|
|
79762
|
+
SidePanels,
|
|
78936
79763
|
SpreadsheetDashboard,
|
|
78937
79764
|
HeaderGroupContainer,
|
|
78938
79765
|
FullScreenChart,
|
|
@@ -78955,7 +79782,9 @@ class Spreadsheet extends owl.Component {
|
|
|
78955
79782
|
else {
|
|
78956
79783
|
properties["grid-template-rows"] = `min-content auto min-content`;
|
|
78957
79784
|
}
|
|
78958
|
-
const columnWidth = this.sidePanel.
|
|
79785
|
+
const columnWidth = this.sidePanel.mainPanel
|
|
79786
|
+
? `${this.sidePanel.totalPanelSize || DEFAULT_SIDE_PANEL_SIZE}px`
|
|
79787
|
+
: "auto";
|
|
78959
79788
|
properties["grid-template-columns"] = `auto ${columnWidth}`;
|
|
78960
79789
|
return cssPropertiesToCss(properties);
|
|
78961
79790
|
}
|
|
@@ -79039,7 +79868,7 @@ class Spreadsheet extends owl.Component {
|
|
|
79039
79868
|
this.checkViewportSize();
|
|
79040
79869
|
});
|
|
79041
79870
|
const resizeObserver = new ResizeObserver(() => {
|
|
79042
|
-
this.sidePanel.
|
|
79871
|
+
this.sidePanel.changeSpreadsheetWidth(this.spreadsheetRect.width);
|
|
79043
79872
|
});
|
|
79044
79873
|
}
|
|
79045
79874
|
bindModelEvents() {
|
|
@@ -83636,6 +84465,6 @@ exports.tokenColors = tokenColors;
|
|
|
83636
84465
|
exports.tokenize = tokenize;
|
|
83637
84466
|
|
|
83638
84467
|
|
|
83639
|
-
__info__.version = "18.4.0
|
|
83640
|
-
__info__.date = "2025-06-
|
|
83641
|
-
__info__.hash = "
|
|
84468
|
+
__info__.version = "18.4.0";
|
|
84469
|
+
__info__.date = "2025-06-24T11:19:24.606Z";
|
|
84470
|
+
__info__.hash = "a5b7cad";
|