@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
|
(function (exports, owl) {
|
|
@@ -163,6 +163,7 @@
|
|
|
163
163
|
const FROZEN_PANE_BORDER_COLOR = "#DADFE8";
|
|
164
164
|
const COMPOSER_ASSISTANT_COLOR = "#9B359B";
|
|
165
165
|
const COLOR_TRANSPARENT = "#00000000";
|
|
166
|
+
const TABLE_HOVER_BACKGROUND_COLOR = "#017E8414";
|
|
166
167
|
const CHART_WATERFALL_POSITIVE_COLOR = "#4EA7F2";
|
|
167
168
|
const CHART_WATERFALL_NEGATIVE_COLOR = "#EA6175";
|
|
168
169
|
const CHART_WATERFALL_SUBTOTAL_COLOR = "#AAAAAA";
|
|
@@ -6848,19 +6849,17 @@
|
|
|
6848
6849
|
const textWidthCache = {};
|
|
6849
6850
|
function computeTextWidth(context, text, style, fontUnit = "pt") {
|
|
6850
6851
|
const font = computeTextFont(style, fontUnit);
|
|
6851
|
-
context
|
|
6852
|
-
context.font = font;
|
|
6853
|
-
const width = computeCachedTextWidth(context, text);
|
|
6854
|
-
context.restore();
|
|
6855
|
-
return width;
|
|
6852
|
+
return computeCachedTextWidth(context, text, font);
|
|
6856
6853
|
}
|
|
6857
|
-
function computeCachedTextWidth(context, text) {
|
|
6858
|
-
const font = context.font;
|
|
6854
|
+
function computeCachedTextWidth(context, text, font) {
|
|
6859
6855
|
if (!textWidthCache[font]) {
|
|
6860
6856
|
textWidthCache[font] = {};
|
|
6861
6857
|
}
|
|
6862
6858
|
if (textWidthCache[font][text] === undefined) {
|
|
6859
|
+
const oldFont = context.font;
|
|
6860
|
+
context.font = font;
|
|
6863
6861
|
textWidthCache[font][text] = context.measureText(text).width;
|
|
6862
|
+
context.font = oldFont;
|
|
6864
6863
|
}
|
|
6865
6864
|
return textWidthCache[font][text];
|
|
6866
6865
|
}
|
|
@@ -7025,19 +7024,19 @@
|
|
|
7025
7024
|
}
|
|
7026
7025
|
// Inspired from https://stackoverflow.com/a/10511598
|
|
7027
7026
|
function clipTextWithEllipsis(ctx, text, maxWidth) {
|
|
7028
|
-
let width = computeCachedTextWidth(ctx, text);
|
|
7027
|
+
let width = computeCachedTextWidth(ctx, text, ctx.font);
|
|
7029
7028
|
if (width <= maxWidth) {
|
|
7030
7029
|
return text;
|
|
7031
7030
|
}
|
|
7032
7031
|
const ellipsis = "…";
|
|
7033
|
-
const ellipsisWidth = computeCachedTextWidth(ctx, ellipsis);
|
|
7032
|
+
const ellipsisWidth = computeCachedTextWidth(ctx, ellipsis, ctx.font);
|
|
7034
7033
|
if (width <= ellipsisWidth) {
|
|
7035
7034
|
return text;
|
|
7036
7035
|
}
|
|
7037
7036
|
let len = text.length;
|
|
7038
7037
|
while (width >= maxWidth - ellipsisWidth && len-- > 0) {
|
|
7039
7038
|
text = text.substring(0, len);
|
|
7040
|
-
width = computeCachedTextWidth(ctx, text);
|
|
7039
|
+
width = computeCachedTextWidth(ctx, text, ctx.font);
|
|
7041
7040
|
}
|
|
7042
7041
|
return text + ellipsis;
|
|
7043
7042
|
}
|
|
@@ -7251,6 +7250,63 @@
|
|
|
7251
7250
|
};
|
|
7252
7251
|
return osClipboardContent;
|
|
7253
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
|
+
};
|
|
7254
7310
|
|
|
7255
7311
|
class ClipboardHandler {
|
|
7256
7312
|
getters;
|
|
@@ -9955,8 +10011,15 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
9955
10011
|
const ModelStore = createAbstractStore("Model");
|
|
9956
10012
|
|
|
9957
10013
|
class RendererStore {
|
|
9958
|
-
mutators = ["register", "unRegister", "
|
|
10014
|
+
mutators = ["register", "unRegister", "draw", "startAnimation", "stopAnimation"];
|
|
9959
10015
|
renderers = {};
|
|
10016
|
+
model;
|
|
10017
|
+
context = undefined;
|
|
10018
|
+
animationFrameId = null;
|
|
10019
|
+
registeredAnimations = new Set();
|
|
10020
|
+
constructor(get) {
|
|
10021
|
+
this.model = get(ModelStore);
|
|
10022
|
+
}
|
|
9960
10023
|
register(renderer) {
|
|
9961
10024
|
if (!renderer.renderingLayers.length) {
|
|
9962
10025
|
return;
|
|
@@ -9973,17 +10036,54 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
9973
10036
|
this.renderers[layer] = this.renderers[layer].filter((r) => r !== renderer);
|
|
9974
10037
|
}
|
|
9975
10038
|
}
|
|
9976
|
-
drawLayer(context, layer) {
|
|
10039
|
+
drawLayer(context, layer, timeStamp) {
|
|
9977
10040
|
const renderers = this.renderers[layer];
|
|
9978
10041
|
if (renderers) {
|
|
9979
10042
|
for (const renderer of renderers) {
|
|
9980
10043
|
context.ctx.save();
|
|
9981
|
-
renderer.drawLayer(context, layer);
|
|
10044
|
+
renderer.drawLayer(context, layer, timeStamp);
|
|
9982
10045
|
context.ctx.restore();
|
|
9983
10046
|
}
|
|
9984
10047
|
}
|
|
9985
10048
|
return "noStateChange";
|
|
9986
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
|
+
}
|
|
9987
10087
|
}
|
|
9988
10088
|
|
|
9989
10089
|
class SpreadsheetStore extends DisposableStore {
|
|
@@ -10007,7 +10107,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
10007
10107
|
}
|
|
10008
10108
|
handle(cmd) { }
|
|
10009
10109
|
finalize() { }
|
|
10010
|
-
drawLayer(ctx, layer) { }
|
|
10110
|
+
drawLayer(ctx, layer, timestamp) { }
|
|
10011
10111
|
}
|
|
10012
10112
|
|
|
10013
10113
|
const VOID_COMPOSER = {
|
|
@@ -23729,11 +23829,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23729
23829
|
function getGaugeRenderingConfig(boundingRect, runtime, ctx) {
|
|
23730
23830
|
const maxValue = runtime.maxValue;
|
|
23731
23831
|
const minValue = runtime.minValue;
|
|
23732
|
-
const gaugeValue = runtime
|
|
23832
|
+
const gaugeValue = getGaugeValue(runtime, "animated");
|
|
23733
23833
|
const gaugeRect = getGaugeRect(boundingRect, runtime.title.text);
|
|
23734
23834
|
const gaugeArcWidth = gaugeRect.width / 6;
|
|
23735
23835
|
const gaugePercentage = gaugeValue
|
|
23736
|
-
? (gaugeValue
|
|
23836
|
+
? (gaugeValue - minValue.value) / (maxValue.value - minValue.value)
|
|
23737
23837
|
: 0;
|
|
23738
23838
|
const gaugeValuePosition = {
|
|
23739
23839
|
x: boundingRect.width / 2,
|
|
@@ -23746,7 +23846,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23746
23846
|
}
|
|
23747
23847
|
// Scale down the font size if the text is too long
|
|
23748
23848
|
const maxTextWidth = gaugeRect.width / 2;
|
|
23749
|
-
const gaugeLabel = gaugeValue?.label || "-";
|
|
23849
|
+
const gaugeLabel = runtime.gaugeValue?.label || "-";
|
|
23750
23850
|
if (computeTextWidth(ctx, gaugeLabel, { fontSize: gaugeValueFontSize }, "px") > maxTextWidth) {
|
|
23751
23851
|
gaugeValueFontSize = getFontSizeMatchingWidth(maxTextWidth, gaugeValueFontSize, (fontSize) => computeTextWidth(ctx, gaugeLabel, { fontSize }, "px"));
|
|
23752
23852
|
}
|
|
@@ -23886,7 +23986,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23886
23986
|
return inflectionValues;
|
|
23887
23987
|
}
|
|
23888
23988
|
function getGaugeColor(runtime) {
|
|
23889
|
-
const gaugeValue = runtime
|
|
23989
|
+
const gaugeValue = getGaugeValue(runtime, "final");
|
|
23890
23990
|
if (gaugeValue === undefined) {
|
|
23891
23991
|
return GAUGE_BACKGROUND_COLOR;
|
|
23892
23992
|
}
|
|
@@ -23974,6 +24074,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
23974
24074
|
};
|
|
23975
24075
|
return { bottomLeft, bottomRight, topRight, topLeft };
|
|
23976
24076
|
}
|
|
24077
|
+
function getGaugeValue(runtime, mode) {
|
|
24078
|
+
return mode === "animated" && runtime.animationValue !== undefined
|
|
24079
|
+
? runtime.animationValue
|
|
24080
|
+
: runtime.gaugeValue?.value;
|
|
24081
|
+
}
|
|
23977
24082
|
|
|
23978
24083
|
const CHART_COMMON_OPTIONS = {
|
|
23979
24084
|
// https://www.chartjs.org/docs/latest/general/responsive.html
|
|
@@ -26328,6 +26433,320 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
26328
26433
|
return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
|
|
26329
26434
|
}
|
|
26330
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;
|
|
26331
26750
|
class GaugeChartComponent extends owl.Component {
|
|
26332
26751
|
static template = "o-spreadsheet-GaugeChartComponent";
|
|
26333
26752
|
static props = {
|
|
@@ -26335,16 +26754,101 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
26335
26754
|
isFullScreen: { type: Boolean, optional: true },
|
|
26336
26755
|
};
|
|
26337
26756
|
canvas = owl.useRef("chartContainer");
|
|
26757
|
+
animationStore;
|
|
26338
26758
|
get runtime() {
|
|
26339
26759
|
return this.env.model.getters.getChartRuntime(this.props.figureUI.id);
|
|
26340
26760
|
}
|
|
26341
26761
|
setup() {
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
|
|
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();
|
|
26345
26787
|
return [rect.width, rect.height, this.runtime, this.canvas.el, window.devicePixelRatio];
|
|
26346
26788
|
});
|
|
26347
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
|
+
}
|
|
26348
26852
|
}
|
|
26349
26853
|
|
|
26350
26854
|
class ComboChart extends AbstractChart {
|
|
@@ -36537,6 +37041,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36537
37041
|
// We need here the svg of the icons that we need to convert to images for the renderer
|
|
36538
37042
|
// -----------------------------------------------------------------------------
|
|
36539
37043
|
const ARROW_DOWN = {
|
|
37044
|
+
name: "ARROW_DOWN",
|
|
36540
37045
|
width: 448,
|
|
36541
37046
|
height: 512,
|
|
36542
37047
|
paths: [
|
|
@@ -36547,6 +37052,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36547
37052
|
],
|
|
36548
37053
|
};
|
|
36549
37054
|
const ARROW_UP = {
|
|
37055
|
+
name: "ARROW_UP",
|
|
36550
37056
|
width: 448,
|
|
36551
37057
|
height: 512,
|
|
36552
37058
|
paths: [
|
|
@@ -36557,6 +37063,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36557
37063
|
],
|
|
36558
37064
|
};
|
|
36559
37065
|
const ARROW_RIGHT = {
|
|
37066
|
+
name: "ARROW_RIGHT",
|
|
36560
37067
|
width: 448,
|
|
36561
37068
|
height: 512,
|
|
36562
37069
|
paths: [
|
|
@@ -36567,6 +37074,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36567
37074
|
],
|
|
36568
37075
|
};
|
|
36569
37076
|
const SMILE = {
|
|
37077
|
+
name: "SMILE",
|
|
36570
37078
|
width: 496,
|
|
36571
37079
|
height: 512,
|
|
36572
37080
|
paths: [
|
|
@@ -36577,6 +37085,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36577
37085
|
],
|
|
36578
37086
|
};
|
|
36579
37087
|
const MEH = {
|
|
37088
|
+
name: "MEH",
|
|
36580
37089
|
width: 496,
|
|
36581
37090
|
height: 512,
|
|
36582
37091
|
paths: [
|
|
@@ -36587,6 +37096,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36587
37096
|
],
|
|
36588
37097
|
};
|
|
36589
37098
|
const FROWN = {
|
|
37099
|
+
name: "FROWN",
|
|
36590
37100
|
width: 496,
|
|
36591
37101
|
height: 512,
|
|
36592
37102
|
paths: [
|
|
@@ -36598,22 +37108,26 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36598
37108
|
};
|
|
36599
37109
|
const DOT_PATH = "M256 9 a247 247 0 1 0.1 0 0";
|
|
36600
37110
|
const GREEN_DOT = {
|
|
37111
|
+
name: "GREEN_DOT",
|
|
36601
37112
|
width: 512,
|
|
36602
37113
|
height: 512,
|
|
36603
37114
|
paths: [{ fillColor: "#6AA84F", path: DOT_PATH }],
|
|
36604
37115
|
};
|
|
36605
37116
|
const YELLOW_DOT = {
|
|
37117
|
+
name: "YELLOW_DOT",
|
|
36606
37118
|
width: 512,
|
|
36607
37119
|
height: 512,
|
|
36608
37120
|
paths: [{ fillColor: "#F0AD4E", path: DOT_PATH }],
|
|
36609
37121
|
};
|
|
36610
37122
|
const RED_DOT = {
|
|
37123
|
+
name: "RED_DOT",
|
|
36611
37124
|
width: 512,
|
|
36612
37125
|
height: 512,
|
|
36613
37126
|
paths: [{ fillColor: "#E06666", path: DOT_PATH }],
|
|
36614
37127
|
};
|
|
36615
37128
|
function getCaretDownSvg(color) {
|
|
36616
37129
|
return {
|
|
37130
|
+
name: "CARET_DOWN",
|
|
36617
37131
|
width: 512,
|
|
36618
37132
|
height: 512,
|
|
36619
37133
|
paths: [{ fillColor: color.textColor || TEXT_BODY_MUTED, path: "M120 195 h270 l-135 130" }],
|
|
@@ -36621,6 +37135,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36621
37135
|
}
|
|
36622
37136
|
function getHoveredCaretDownSvg(color) {
|
|
36623
37137
|
return {
|
|
37138
|
+
name: "CARET_DOWN",
|
|
36624
37139
|
width: 512,
|
|
36625
37140
|
height: 512,
|
|
36626
37141
|
paths: [
|
|
@@ -36632,6 +37147,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36632
37147
|
const CHIP_CARET_DOWN_PATH = "M40 185 h270 l-135 128";
|
|
36633
37148
|
function getChipSvg(chipStyle) {
|
|
36634
37149
|
return {
|
|
37150
|
+
name: "CHIP",
|
|
36635
37151
|
width: 512,
|
|
36636
37152
|
height: 512,
|
|
36637
37153
|
paths: [{ fillColor: chipStyle.textColor || TEXT_BODY_MUTED, path: CHIP_CARET_DOWN_PATH }],
|
|
@@ -36639,6 +37155,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36639
37155
|
}
|
|
36640
37156
|
function getHoveredChipSvg(chipStyle) {
|
|
36641
37157
|
return {
|
|
37158
|
+
name: "CHIP",
|
|
36642
37159
|
width: 512,
|
|
36643
37160
|
height: 512,
|
|
36644
37161
|
paths: [
|
|
@@ -36651,16 +37168,19 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36651
37168
|
};
|
|
36652
37169
|
}
|
|
36653
37170
|
const CHECKBOX_UNCHECKED = {
|
|
37171
|
+
name: "CHECKBOX_UNCHECKED",
|
|
36654
37172
|
width: 512,
|
|
36655
37173
|
height: 512,
|
|
36656
37174
|
paths: [{ fillColor: GRAY_300, path: "M45,45 h422 v422 h-422 v-422 m30,30 v362 h362 v-362" }],
|
|
36657
37175
|
};
|
|
36658
37176
|
const CHECKBOX_UNCHECKED_HOVERED = {
|
|
37177
|
+
name: "CHECKBOX_UNCHECKED",
|
|
36659
37178
|
width: 512,
|
|
36660
37179
|
height: 512,
|
|
36661
37180
|
paths: [{ fillColor: ACTION_COLOR, path: "M45,45 h422 v422 h-422 v-422 m30,30 v362 h362 v-362" }],
|
|
36662
37181
|
};
|
|
36663
37182
|
const CHECKBOX_CHECKED = {
|
|
37183
|
+
name: "CHECKBOX_CHECKED",
|
|
36664
37184
|
width: 512,
|
|
36665
37185
|
height: 512,
|
|
36666
37186
|
paths: [
|
|
@@ -36673,6 +37193,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36673
37193
|
? "M149,235 h213 v43 h-213 M235,149 h43 v213 h-43" // +
|
|
36674
37194
|
: "M149,235 h213 v43 h-213"; // -
|
|
36675
37195
|
return {
|
|
37196
|
+
name: "PIVOT_ICON",
|
|
36676
37197
|
width: 512,
|
|
36677
37198
|
height: 512,
|
|
36678
37199
|
paths: [
|
|
@@ -36699,6 +37220,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
36699
37220
|
colors.hoverBackgroundColor = "#fff";
|
|
36700
37221
|
}
|
|
36701
37222
|
return {
|
|
37223
|
+
name: "DATA_FILTER_ICON",
|
|
36702
37224
|
width: isActive ? 24 : 850,
|
|
36703
37225
|
height: isActive ? 24 : 850,
|
|
36704
37226
|
paths: [
|
|
@@ -40894,6 +41416,23 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
40894
41416
|
}
|
|
40895
41417
|
return data;
|
|
40896
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
|
+
},
|
|
40897
41436
|
});
|
|
40898
41437
|
function fixOverlappingFilters(data) {
|
|
40899
41438
|
for (const sheet of data.sheets || []) {
|
|
@@ -45960,15 +46499,16 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45960
46499
|
return state;
|
|
45961
46500
|
}
|
|
45962
46501
|
|
|
46502
|
+
const PAINT_FORMAT_HANDLER_KEYS = [
|
|
46503
|
+
"cell",
|
|
46504
|
+
"border",
|
|
46505
|
+
"table",
|
|
46506
|
+
"conditionalFormat",
|
|
46507
|
+
"merge",
|
|
46508
|
+
];
|
|
45963
46509
|
class PaintFormatStore extends SpreadsheetStore {
|
|
45964
46510
|
mutators = ["activate", "cancel", "pasteFormat"];
|
|
45965
46511
|
highlightStore = this.get(HighlightStore);
|
|
45966
|
-
clipboardHandlers = [
|
|
45967
|
-
new CellClipboardHandler(this.getters, this.model.dispatch),
|
|
45968
|
-
new BorderClipboardHandler(this.getters, this.model.dispatch),
|
|
45969
|
-
new TableClipboardHandler(this.getters, this.model.dispatch),
|
|
45970
|
-
new ConditionalFormatClipboardHandler(this.getters, this.model.dispatch),
|
|
45971
|
-
];
|
|
45972
46512
|
status = "inactive";
|
|
45973
46513
|
copiedData;
|
|
45974
46514
|
constructor(get) {
|
|
@@ -45999,24 +46539,38 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
45999
46539
|
get isActive() {
|
|
46000
46540
|
return this.status !== "inactive";
|
|
46001
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
|
+
}
|
|
46002
46551
|
copyFormats() {
|
|
46003
46552
|
const sheetId = this.getters.getActiveSheetId();
|
|
46004
46553
|
const zones = this.getters.getSelectedZones();
|
|
46005
|
-
const copiedData = {};
|
|
46006
|
-
for (const handler of this.clipboardHandlers) {
|
|
46007
|
-
|
|
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
|
+
}
|
|
46008
46560
|
}
|
|
46009
46561
|
return copiedData;
|
|
46010
46562
|
}
|
|
46011
46563
|
paintFormat(sheetId, target) {
|
|
46012
|
-
if (this.copiedData) {
|
|
46013
|
-
|
|
46014
|
-
handler.paste({ zones: target, sheetId }, this.copiedData, {
|
|
46015
|
-
isCutOperation: false,
|
|
46016
|
-
pasteOption: "onlyFormat",
|
|
46017
|
-
});
|
|
46018
|
-
}
|
|
46564
|
+
if (!this.copiedData) {
|
|
46565
|
+
return;
|
|
46019
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);
|
|
46020
46574
|
if (this.status === "oneOff") {
|
|
46021
46575
|
this.cancel();
|
|
46022
46576
|
}
|
|
@@ -46063,12 +46617,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46063
46617
|
this.row = undefined;
|
|
46064
46618
|
}
|
|
46065
46619
|
computeOverlay() {
|
|
46066
|
-
if (!this.getters.isDashboard()) {
|
|
46067
|
-
return;
|
|
46068
|
-
}
|
|
46069
46620
|
this.overlayColors = new PositionMap();
|
|
46070
|
-
const col = this
|
|
46071
|
-
const row = this.row;
|
|
46621
|
+
const { col, row } = this;
|
|
46072
46622
|
if (col === undefined || row === undefined) {
|
|
46073
46623
|
return;
|
|
46074
46624
|
}
|
|
@@ -46077,9 +46627,16 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46077
46627
|
if (!table) {
|
|
46078
46628
|
return;
|
|
46079
46629
|
}
|
|
46080
|
-
const { left, right } = table.range.zone;
|
|
46081
|
-
|
|
46082
|
-
|
|
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
|
+
}
|
|
46083
46640
|
}
|
|
46084
46641
|
}
|
|
46085
46642
|
}
|
|
@@ -46327,7 +46884,10 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
46327
46884
|
}
|
|
46328
46885
|
const icons = this.env.model.getters.getCellIcons(position);
|
|
46329
46886
|
const icon = icons.find((icon) => {
|
|
46330
|
-
|
|
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));
|
|
46331
46891
|
});
|
|
46332
46892
|
return icon?.onClick ? icon : undefined;
|
|
46333
46893
|
}
|
|
@@ -47064,19 +47624,56 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47064
47624
|
}
|
|
47065
47625
|
}
|
|
47066
47626
|
|
|
47067
|
-
|
|
47068
|
-
|
|
47069
|
-
renderer;
|
|
47627
|
+
const CELL_ANIMATION_DURATION = 200;
|
|
47628
|
+
class GridRenderer extends SpreadsheetStore {
|
|
47070
47629
|
fingerprints;
|
|
47071
47630
|
hoveredTables;
|
|
47072
47631
|
hoveredIcon;
|
|
47632
|
+
lastRenderBoxes = new Map();
|
|
47633
|
+
preventNewAnimationsInNextFrame = false;
|
|
47634
|
+
zonesWithPreventedAnimationsInNextFrame = [];
|
|
47635
|
+
animations = new Map();
|
|
47073
47636
|
constructor(get) {
|
|
47637
|
+
super(get);
|
|
47074
47638
|
this.getters = get(ModelStore).getters;
|
|
47075
|
-
this.renderer = get(RendererStore);
|
|
47076
47639
|
this.fingerprints = get(FormulaFingerprintStore);
|
|
47077
47640
|
this.hoveredTables = get(HoveredTableStore);
|
|
47078
47641
|
this.hoveredIcon = get(HoveredIconStore);
|
|
47079
|
-
|
|
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
|
+
}
|
|
47080
47677
|
}
|
|
47081
47678
|
get renderingLayers() {
|
|
47082
47679
|
return ["Background", "Headers"];
|
|
@@ -47084,17 +47681,20 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47084
47681
|
// ---------------------------------------------------------------------------
|
|
47085
47682
|
// Grid rendering
|
|
47086
47683
|
// ---------------------------------------------------------------------------
|
|
47087
|
-
drawLayer(renderingContext, layer) {
|
|
47684
|
+
drawLayer(renderingContext, layer, timeStamp) {
|
|
47088
47685
|
switch (layer) {
|
|
47089
47686
|
case "Background":
|
|
47090
47687
|
this.drawGlobalBackground(renderingContext);
|
|
47688
|
+
const oldBoxes = this.lastRenderBoxes;
|
|
47689
|
+
this.lastRenderBoxes = new Map();
|
|
47091
47690
|
for (const { zone, rect } of this.getters.getAllActiveViewportsZonesAndRect()) {
|
|
47092
47691
|
const { ctx } = renderingContext;
|
|
47093
47692
|
ctx.save();
|
|
47094
47693
|
ctx.beginPath();
|
|
47095
47694
|
ctx.rect(rect.x, rect.y, rect.width, rect.height);
|
|
47096
47695
|
ctx.clip();
|
|
47097
|
-
const
|
|
47696
|
+
const boxesWithoutAnimations = this.getGridBoxes(zone);
|
|
47697
|
+
const boxes = this.getBoxesWithAnimations(boxesWithoutAnimations, oldBoxes, timeStamp);
|
|
47098
47698
|
this.drawBackground(renderingContext, boxes);
|
|
47099
47699
|
this.drawOverflowingCellBackground(renderingContext, boxes);
|
|
47100
47700
|
this.drawCellBackground(renderingContext, boxes);
|
|
@@ -47104,6 +47704,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47104
47704
|
ctx.restore();
|
|
47105
47705
|
}
|
|
47106
47706
|
this.drawFrozenPanes(renderingContext);
|
|
47707
|
+
this.preventNewAnimationsInNextFrame = false;
|
|
47708
|
+
this.zonesWithPreventedAnimationsInNextFrame = [];
|
|
47107
47709
|
break;
|
|
47108
47710
|
case "Headers":
|
|
47109
47711
|
if (!this.getters.isDashboard()) {
|
|
@@ -47127,6 +47729,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47127
47729
|
const inset = areGridLinesVisible ? 0.1 * thinLineWidth : 0;
|
|
47128
47730
|
if (areGridLinesVisible) {
|
|
47129
47731
|
for (const box of boxes) {
|
|
47732
|
+
if (box.skipCellGridLines)
|
|
47733
|
+
continue;
|
|
47130
47734
|
ctx.strokeStyle = CELL_BORDER_COLOR;
|
|
47131
47735
|
ctx.lineWidth = thinLineWidth;
|
|
47132
47736
|
ctx.strokeRect(box.x + inset, box.y + inset, box.width - 2 * inset, box.height - 2 * inset);
|
|
@@ -47234,7 +47838,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47234
47838
|
* each line and adding 1 pixel to the end of each line (depending on the direction of the
|
|
47235
47839
|
* line).
|
|
47236
47840
|
*/
|
|
47237
|
-
function drawBorder({ style,
|
|
47841
|
+
function drawBorder({ color, style, opacity }, x1, y1, x2, y2) {
|
|
47842
|
+
ctx.globalAlpha = opacity ?? 1;
|
|
47238
47843
|
ctx.strokeStyle = color;
|
|
47239
47844
|
switch (style) {
|
|
47240
47845
|
case "medium":
|
|
@@ -47282,6 +47887,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47282
47887
|
ctx.stroke();
|
|
47283
47888
|
ctx.lineWidth = 1;
|
|
47284
47889
|
ctx.setLineDash([]);
|
|
47890
|
+
ctx.globalAlpha = 1;
|
|
47285
47891
|
}
|
|
47286
47892
|
}
|
|
47287
47893
|
drawTexts(renderingContext, boxes) {
|
|
@@ -47290,6 +47896,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47290
47896
|
let currentFont;
|
|
47291
47897
|
for (const box of boxes) {
|
|
47292
47898
|
if (box.content) {
|
|
47899
|
+
ctx.globalAlpha = box.textOpacity ?? 1;
|
|
47293
47900
|
const style = box.style || {};
|
|
47294
47901
|
const align = box.content.align || "left";
|
|
47295
47902
|
// compute font and textColor
|
|
@@ -47320,6 +47927,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47320
47927
|
if (box.clipRect) {
|
|
47321
47928
|
ctx.restore();
|
|
47322
47929
|
}
|
|
47930
|
+
ctx.globalAlpha = 1;
|
|
47323
47931
|
}
|
|
47324
47932
|
}
|
|
47325
47933
|
}
|
|
@@ -47337,10 +47945,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47337
47945
|
}
|
|
47338
47946
|
ctx.save();
|
|
47339
47947
|
ctx.beginPath();
|
|
47340
|
-
|
|
47948
|
+
const clipRect = icon.clipRect || box;
|
|
47949
|
+
ctx.rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
|
|
47341
47950
|
ctx.clip();
|
|
47342
47951
|
const iconSize = icon.size;
|
|
47343
|
-
const { x, y } = this.getters.getCellIconRect(icon);
|
|
47952
|
+
const { x, y } = this.getters.getCellIconRect(icon, box);
|
|
47344
47953
|
ctx.translate(x, y);
|
|
47345
47954
|
ctx.scale(iconSize / svg.width, iconSize / svg.height);
|
|
47346
47955
|
for (const path of svg.paths) {
|
|
@@ -47549,7 +48158,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47549
48158
|
const cell = this.getters.getEvaluatedCell(position);
|
|
47550
48159
|
const showFormula = this.getters.shouldShowFormulas();
|
|
47551
48160
|
const { x, y, width, height } = this.getters.getRect(zone);
|
|
47552
|
-
const { verticalAlign } = this.getters.getCellStyle(position);
|
|
47553
48161
|
const chipStyle = this.getters.getDataValidationChipStyle(position);
|
|
47554
48162
|
let style = this.getters.getCellComputedStyle(position);
|
|
47555
48163
|
if (this.fingerprints.isEnabled) {
|
|
@@ -47569,6 +48177,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47569
48177
|
center: iconsList.find((icon) => icon?.horizontalAlign === "center"),
|
|
47570
48178
|
};
|
|
47571
48179
|
const box = {
|
|
48180
|
+
id: zoneToXc(zone),
|
|
47572
48181
|
x,
|
|
47573
48182
|
y,
|
|
47574
48183
|
width,
|
|
@@ -47576,11 +48185,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47576
48185
|
border: this.getters.getCellComputedBorder(position) || undefined,
|
|
47577
48186
|
style,
|
|
47578
48187
|
dataBarFill,
|
|
47579
|
-
verticalAlign,
|
|
47580
48188
|
overlayColor: this.hoveredTables.overlayColors.get(position),
|
|
47581
48189
|
isError: (cell.type === CellValueType.error && !!cell.message) ||
|
|
47582
48190
|
this.getters.isDataValidationInvalid(position),
|
|
47583
48191
|
icons: cellIcons,
|
|
48192
|
+
disabledAnimation: this.zonesWithPreventedAnimationsInNextFrame.some((z) => isZoneInside(zone, z) || overlap(zone, z)),
|
|
47584
48193
|
};
|
|
47585
48194
|
const fontSizePX = computeTextFontSizeInPixels(box.style);
|
|
47586
48195
|
if (cell.type === CellValueType.empty || box.icons.center) {
|
|
@@ -47750,6 +48359,77 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47750
48359
|
}
|
|
47751
48360
|
return boxes;
|
|
47752
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
|
+
}
|
|
47753
48433
|
}
|
|
47754
48434
|
|
|
47755
48435
|
function useGridDrawing(refName, model, canvasSize) {
|
|
@@ -47784,10 +48464,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
47784
48464
|
// http://diveintohtml5.info/canvas.html#pixel-madness
|
|
47785
48465
|
ctx.translate(-CANVAS_SHIFT, -CANVAS_SHIFT);
|
|
47786
48466
|
ctx.scale(dpr, dpr);
|
|
47787
|
-
|
|
47788
|
-
model.drawLayer(renderingContext, layer);
|
|
47789
|
-
rendererStore.drawLayer(renderingContext, layer);
|
|
47790
|
-
}
|
|
48467
|
+
rendererStore.draw(renderingContext);
|
|
47791
48468
|
}
|
|
47792
48469
|
}
|
|
47793
48470
|
|
|
@@ -54882,7 +55559,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
54882
55559
|
}
|
|
54883
55560
|
getTypeFromZone(sheetId, zone) {
|
|
54884
55561
|
const cells = this.getters.getEvaluatedCellsInZone(sheetId, zone);
|
|
54885
|
-
const nonEmptyCells = cells.filter((cell) => cell.type
|
|
55562
|
+
const nonEmptyCells = cells.filter((cell) => !(cell.type === CellValueType.empty || cell.value === ""));
|
|
54886
55563
|
if (nonEmptyCells.length === 0) {
|
|
54887
55564
|
return "integer";
|
|
54888
55565
|
}
|
|
@@ -55490,7 +56167,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
55490
56167
|
`;
|
|
55491
56168
|
class SettingsPanel extends owl.Component {
|
|
55492
56169
|
static template = "o-spreadsheet-SettingsPanel";
|
|
55493
|
-
static components = { Section, ValidationMessages };
|
|
56170
|
+
static components = { Section, ValidationMessages, BadgeSelection };
|
|
55494
56171
|
static props = { onCloseSidePanel: Function };
|
|
55495
56172
|
loadedLocales = [];
|
|
55496
56173
|
setup() {
|
|
@@ -56374,49 +57051,111 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
56374
57051
|
}
|
|
56375
57052
|
|
|
56376
57053
|
const DEFAULT_SIDE_PANEL_SIZE = 350;
|
|
57054
|
+
const COLLAPSED_SIDE_PANEL_SIZE = 45;
|
|
56377
57055
|
const MIN_SHEET_VIEW_WIDTH = 150;
|
|
56378
57056
|
class SidePanelStore extends SpreadsheetStore {
|
|
56379
|
-
mutators = [
|
|
56380
|
-
|
|
56381
|
-
|
|
56382
|
-
|
|
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;
|
|
56383
57071
|
screenWidthStore = this.get(ScreenWidthStore);
|
|
56384
|
-
get
|
|
56385
|
-
|
|
56386
|
-
|
|
56387
|
-
|
|
56388
|
-
|
|
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;
|
|
56389
57087
|
}
|
|
56390
|
-
get
|
|
56391
|
-
|
|
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;
|
|
57093
|
+
}
|
|
57094
|
+
get totalPanelSize() {
|
|
57095
|
+
return (this.mainPanel?.size || 0) + (this.secondaryPanel?.size ?? 0);
|
|
57096
|
+
}
|
|
57097
|
+
getPanelProps(panelInfo) {
|
|
57098
|
+
const state = this.computeState(panelInfo);
|
|
56392
57099
|
if (state.isOpen) {
|
|
56393
57100
|
return state.props ?? {};
|
|
56394
57101
|
}
|
|
56395
57102
|
return {};
|
|
56396
57103
|
}
|
|
56397
|
-
|
|
56398
|
-
const state = this.computeState(
|
|
57104
|
+
getPanelKey(panelInfo) {
|
|
57105
|
+
const state = this.computeState(panelInfo);
|
|
56399
57106
|
if (state.isOpen) {
|
|
56400
57107
|
return state.key;
|
|
56401
57108
|
}
|
|
56402
57109
|
return undefined;
|
|
56403
57110
|
}
|
|
56404
|
-
open(componentTag,
|
|
57111
|
+
open(componentTag, initialPanelProps = {}) {
|
|
56405
57112
|
if (this.screenWidthStore.isSmall) {
|
|
56406
57113
|
return;
|
|
56407
57114
|
}
|
|
56408
|
-
const
|
|
57115
|
+
const newPanelInfo = { initialPanelProps, componentTag, size: DEFAULT_SIDE_PANEL_SIZE };
|
|
57116
|
+
const state = this.computeState(newPanelInfo);
|
|
56409
57117
|
if (!state.isOpen) {
|
|
56410
57118
|
return;
|
|
56411
57119
|
}
|
|
56412
|
-
|
|
56413
|
-
|
|
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);
|
|
56414
57154
|
}
|
|
56415
|
-
this.componentTag = componentTag;
|
|
56416
|
-
this.initialPanelProps = state.props ?? {};
|
|
56417
57155
|
}
|
|
56418
57156
|
toggle(componentTag, panelProps) {
|
|
56419
|
-
|
|
57157
|
+
const panel = this.mainPanel?.isPinned ? this.secondaryPanel : this.mainPanel;
|
|
57158
|
+
if (panel && componentTag === panel.componentTag) {
|
|
56420
57159
|
this.close();
|
|
56421
57160
|
}
|
|
56422
57161
|
else {
|
|
@@ -56424,34 +57163,85 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
56424
57163
|
}
|
|
56425
57164
|
}
|
|
56426
57165
|
close() {
|
|
56427
|
-
this.
|
|
56428
|
-
|
|
56429
|
-
|
|
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;
|
|
56430
57175
|
}
|
|
56431
|
-
|
|
56432
|
-
|
|
56433
|
-
|
|
57176
|
+
closeMainPanel() {
|
|
57177
|
+
this.mainPanel?.initialPanelProps.onCloseSidePanel?.();
|
|
57178
|
+
this.mainPanel = this.secondaryPanel || undefined;
|
|
57179
|
+
this.secondaryPanel = undefined;
|
|
57180
|
+
}
|
|
57181
|
+
changePanelSize(panel, size) {
|
|
57182
|
+
const panelInfo = this[panel];
|
|
57183
|
+
if (!panelInfo || ("isCollapsed" in panelInfo && panelInfo.isCollapsed)) {
|
|
57184
|
+
return;
|
|
56434
57185
|
}
|
|
56435
|
-
|
|
56436
|
-
|
|
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);
|
|
56437
57195
|
}
|
|
56438
|
-
|
|
56439
|
-
|
|
57196
|
+
panelInfo.size = size;
|
|
57197
|
+
}
|
|
57198
|
+
resetPanelSize(panel) {
|
|
57199
|
+
const panelInfo = this[panel];
|
|
57200
|
+
if (!panelInfo) {
|
|
57201
|
+
return;
|
|
56440
57202
|
}
|
|
57203
|
+
panelInfo.size = DEFAULT_SIDE_PANEL_SIZE;
|
|
56441
57204
|
}
|
|
56442
|
-
|
|
56443
|
-
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
|
+
}
|
|
56444
57215
|
}
|
|
56445
|
-
|
|
56446
|
-
const
|
|
56447
|
-
if (!
|
|
56448
|
-
return
|
|
56449
|
-
|
|
56450
|
-
|
|
56451
|
-
|
|
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);
|
|
56452
57224
|
}
|
|
56453
57225
|
else {
|
|
56454
|
-
|
|
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);
|
|
56455
57245
|
}
|
|
56456
57246
|
}
|
|
56457
57247
|
}
|
|
@@ -56599,11 +57389,11 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
56599
57389
|
this.hoveredCell.clear();
|
|
56600
57390
|
});
|
|
56601
57391
|
this.cellPopovers = useStore(CellPopoverStore);
|
|
56602
|
-
owl.useEffect(() => {
|
|
56603
|
-
if (!
|
|
57392
|
+
owl.useEffect((isMainPanelOpen, isSecondaryPanelOpen) => {
|
|
57393
|
+
if (!isMainPanelOpen && !isSecondaryPanelOpen) {
|
|
56604
57394
|
this.DOMFocusableElementStore.focus();
|
|
56605
57395
|
}
|
|
56606
|
-
}, () => [this.sidePanel.
|
|
57396
|
+
}, () => [this.sidePanel.isMainPanelOpen, this.sidePanel.isSecondaryPanelOpen]);
|
|
56607
57397
|
useTouchScroll(this.gridRef, this.moveCanvas.bind(this), () => {
|
|
56608
57398
|
const { scrollY } = this.env.model.getters.getActiveSheetScrollInfo();
|
|
56609
57399
|
return scrollY > 0;
|
|
@@ -59695,7 +60485,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59695
60485
|
else if (newRule.criterion.type === "isValueInList") {
|
|
59696
60486
|
newRule.criterion.values = Array.from(new Set(newRule.criterion.values));
|
|
59697
60487
|
}
|
|
59698
|
-
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules);
|
|
60488
|
+
const adaptedRules = this.removeRangesFromRules(sheetId, newRule.ranges, rules, newRule.id);
|
|
59699
60489
|
const ruleIndex = adaptedRules.findIndex((rule) => rule.id === newRule.id);
|
|
59700
60490
|
if (ruleIndex !== -1) {
|
|
59701
60491
|
adaptedRules[ruleIndex] = newRule;
|
|
@@ -59705,9 +60495,12 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
59705
60495
|
this.history.update("rules", sheetId, [...adaptedRules, newRule]);
|
|
59706
60496
|
}
|
|
59707
60497
|
}
|
|
59708
|
-
removeRangesFromRules(sheetId, ranges, rules) {
|
|
60498
|
+
removeRangesFromRules(sheetId, ranges, rules, editingRuleId) {
|
|
59709
60499
|
rules = deepCopy(rules);
|
|
59710
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
|
+
}
|
|
59711
60504
|
rule.ranges = this.getters.recomputeRanges(rule.ranges, ranges);
|
|
59712
60505
|
}
|
|
59713
60506
|
return rules.filter((rule) => rule.ranges.length > 0);
|
|
@@ -63115,7 +63908,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
63115
63908
|
break;
|
|
63116
63909
|
}
|
|
63117
63910
|
case "UPDATE_PIVOT": {
|
|
63118
|
-
this.history.update("pivots", cmd.pivotId, "definition",
|
|
63911
|
+
this.history.update("pivots", cmd.pivotId, "definition", deepCopy(cmd.pivot));
|
|
63119
63912
|
this.compileCalculatedMeasures(cmd.pivot.measures);
|
|
63120
63913
|
break;
|
|
63121
63914
|
}
|
|
@@ -63186,10 +63979,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
63186
63979
|
// Private
|
|
63187
63980
|
// -------------------------------------------------------------------------
|
|
63188
63981
|
addPivot(pivotId, pivot, formulaId = this.nextFormulaId.toString()) {
|
|
63189
|
-
this.history.update("pivots", pivotId, {
|
|
63190
|
-
definition: this.repairSortedColumn(deepCopy(pivot)),
|
|
63191
|
-
formulaId,
|
|
63192
|
-
});
|
|
63982
|
+
this.history.update("pivots", pivotId, { definition: deepCopy(pivot), formulaId });
|
|
63193
63983
|
this.compileCalculatedMeasures(pivot.measures);
|
|
63194
63984
|
this.history.update("formulaIds", formulaId, pivotId);
|
|
63195
63985
|
this.history.update("nextFormulaId", this.nextFormulaId + 1);
|
|
@@ -63278,7 +64068,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
63278
64068
|
}
|
|
63279
64069
|
}
|
|
63280
64070
|
checkSortedColumnInMeasures(definition) {
|
|
63281
|
-
definition = this.repairSortedColumn(definition);
|
|
63282
64071
|
const measures = definition.measures.map((measure) => measure.id);
|
|
63283
64072
|
if (definition.sortedColumn && !measures.includes(definition.sortedColumn.measure)) {
|
|
63284
64073
|
return "InvalidDefinition" /* CommandResult.InvalidDefinition */;
|
|
@@ -63292,26 +64081,6 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
63292
64081
|
}
|
|
63293
64082
|
return "Success" /* CommandResult.Success */;
|
|
63294
64083
|
}
|
|
63295
|
-
repairSortedColumn(definition) {
|
|
63296
|
-
if (definition.sortedColumn) {
|
|
63297
|
-
// Fix for an upgrade issue: the sortedColumn measure was not updated
|
|
63298
|
-
// from using fieldName to using id. If the sortedColumn measure matches
|
|
63299
|
-
// a measure fieldName in the definition, update it to use the measure's id instead
|
|
63300
|
-
// of its fieldName.
|
|
63301
|
-
// TODO: add an upgrade step to fix this in master and remove this code
|
|
63302
|
-
const sortedMeasure = definition.measures.find((measure) => measure.fieldName === definition.sortedColumn?.measure);
|
|
63303
|
-
if (sortedMeasure) {
|
|
63304
|
-
return {
|
|
63305
|
-
...definition,
|
|
63306
|
-
sortedColumn: {
|
|
63307
|
-
...definition.sortedColumn,
|
|
63308
|
-
measure: sortedMeasure.id,
|
|
63309
|
-
},
|
|
63310
|
-
};
|
|
63311
|
-
}
|
|
63312
|
-
}
|
|
63313
|
-
return definition;
|
|
63314
|
-
}
|
|
63315
64084
|
// ---------------------------------------------------------------------
|
|
63316
64085
|
// Import/Export
|
|
63317
64086
|
// ---------------------------------------------------------------------
|
|
@@ -63393,9 +64162,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
63393
64162
|
this.locale = data.settings?.locale ?? DEFAULT_LOCALE;
|
|
63394
64163
|
}
|
|
63395
64164
|
export(data) {
|
|
63396
|
-
data.settings = {
|
|
63397
|
-
locale: this.locale,
|
|
63398
|
-
};
|
|
64165
|
+
data.settings = { locale: this.locale };
|
|
63399
64166
|
}
|
|
63400
64167
|
}
|
|
63401
64168
|
|
|
@@ -66430,11 +67197,8 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
66430
67197
|
}
|
|
66431
67198
|
return this.cellIconsCache[position.sheetId][position.col][position.row];
|
|
66432
67199
|
}
|
|
66433
|
-
getCellIconRect(icon) {
|
|
67200
|
+
getCellIconRect(icon, cellRect) {
|
|
66434
67201
|
const cellPosition = icon.position;
|
|
66435
|
-
const merge = this.getters.getMerge(cellPosition);
|
|
66436
|
-
const zone = merge || positionToZone(cellPosition);
|
|
66437
|
-
const cellRect = this.getters.getRect(zone);
|
|
66438
67202
|
const cell = this.getters.getCell(cellPosition);
|
|
66439
67203
|
const x = this.getIconHorizontalPosition(cellRect, icon.horizontalAlign, icon);
|
|
66440
67204
|
const y = this.getters.computeTextYCoordinate(cellRect, icon.size, cell?.style?.verticalAlign);
|
|
@@ -72267,49 +73031,17 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
72267
73031
|
if (!copiedData) {
|
|
72268
73032
|
return;
|
|
72269
73033
|
}
|
|
72270
|
-
let zone = undefined;
|
|
72271
|
-
const selectedZones = [];
|
|
72272
73034
|
const sheetId = this.getters.getActiveSheetId();
|
|
72273
|
-
const target = {
|
|
72274
|
-
sheetId,
|
|
72275
|
-
zones,
|
|
72276
|
-
};
|
|
72277
73035
|
const handlers = this.selectClipboardHandlers(copiedData);
|
|
72278
|
-
|
|
72279
|
-
const handlerData = copiedData[handlerName];
|
|
72280
|
-
if (!handlerData) {
|
|
72281
|
-
continue;
|
|
72282
|
-
}
|
|
72283
|
-
const currentTarget = handler.getPasteTarget(sheetId, zones, handlerData, options);
|
|
72284
|
-
if (currentTarget.figureId) {
|
|
72285
|
-
target.figureId = currentTarget.figureId;
|
|
72286
|
-
}
|
|
72287
|
-
for (const targetZone of currentTarget.zones) {
|
|
72288
|
-
selectedZones.push(targetZone);
|
|
72289
|
-
if (zone === undefined) {
|
|
72290
|
-
zone = targetZone;
|
|
72291
|
-
continue;
|
|
72292
|
-
}
|
|
72293
|
-
zone = union(zone, targetZone);
|
|
72294
|
-
}
|
|
72295
|
-
}
|
|
73036
|
+
const { target, zone, selectedZones } = getPasteTargetFromHandlers(sheetId, zones, copiedData, handlers, options);
|
|
72296
73037
|
if (zone !== undefined) {
|
|
72297
|
-
this.addMissingDimensions(
|
|
73038
|
+
this.addMissingDimensions(sheetId, zone.right - zone.left + 1, zone.bottom - zone.top + 1, zone.left, zone.top);
|
|
72298
73039
|
}
|
|
72299
|
-
handlers
|
|
72300
|
-
const handlerData = copiedData[handlerName];
|
|
72301
|
-
if (handlerData) {
|
|
72302
|
-
handler.paste(target, handlerData, options);
|
|
72303
|
-
}
|
|
72304
|
-
});
|
|
73040
|
+
applyClipboardHandlersPaste(handlers, copiedData, target, options);
|
|
72305
73041
|
if (!options?.selectTarget) {
|
|
72306
73042
|
return;
|
|
72307
73043
|
}
|
|
72308
|
-
|
|
72309
|
-
const col = selection.left;
|
|
72310
|
-
const row = selection.top;
|
|
72311
|
-
this.selection.getBackToDefault();
|
|
72312
|
-
this.selection.selectZone({ cell: { col, row }, zone: union(...selectedZones) }, { scrollIntoView: false });
|
|
73044
|
+
selectPastedZone(this.selection, zones, selectedZones);
|
|
72313
73045
|
}
|
|
72314
73046
|
/**
|
|
72315
73047
|
* Add columns and/or rows to ensure that col + width and row + height are still
|
|
@@ -75138,6 +75870,17 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
75138
75870
|
return !!getters.getEvaluatedCell(position).link;
|
|
75139
75871
|
},
|
|
75140
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
|
+
},
|
|
75141
75884
|
sequence: 5,
|
|
75142
75885
|
});
|
|
75143
75886
|
|
|
@@ -76849,12 +77592,13 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
76849
77592
|
if (!item) {
|
|
76850
77593
|
continue;
|
|
76851
77594
|
}
|
|
77595
|
+
const title = typeof item.title === "function" ? item.title(position, getters) : item.title;
|
|
76852
77596
|
const zone = getters.expandZone(sheetId, positionToZone(position));
|
|
76853
77597
|
cells.push({
|
|
76854
77598
|
coordinates: getters.getVisibleRect(zone),
|
|
76855
77599
|
position,
|
|
76856
77600
|
action: item.execute,
|
|
76857
|
-
title:
|
|
77601
|
+
title: title || "",
|
|
76858
77602
|
});
|
|
76859
77603
|
}
|
|
76860
77604
|
return cells;
|
|
@@ -77239,24 +77983,36 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
77239
77983
|
user-select: none;
|
|
77240
77984
|
color: ${TEXT_BODY};
|
|
77241
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
|
+
|
|
77242
77996
|
.o-sidePanelTitle {
|
|
77243
77997
|
line-height: 20px;
|
|
77244
77998
|
font-size: 16px;
|
|
77245
77999
|
}
|
|
77246
78000
|
|
|
77247
78001
|
.o-sidePanelHeader {
|
|
77248
|
-
padding: 8px
|
|
77249
|
-
display: flex;
|
|
77250
|
-
align-items: center;
|
|
77251
|
-
justify-content: space-between;
|
|
78002
|
+
padding: 8px;
|
|
77252
78003
|
border-bottom: 1px solid ${GRAY_300};
|
|
78004
|
+
}
|
|
77253
78005
|
|
|
77254
|
-
|
|
77255
|
-
|
|
77256
|
-
|
|
77257
|
-
|
|
77258
|
-
|
|
77259
|
-
}
|
|
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};
|
|
77260
78016
|
}
|
|
77261
78017
|
}
|
|
77262
78018
|
.o-sidePanelBody-container {
|
|
@@ -77333,43 +78089,114 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
77333
78089
|
`;
|
|
77334
78090
|
class SidePanel extends owl.Component {
|
|
77335
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";
|
|
77336
78117
|
static props = {};
|
|
78118
|
+
static components = { SidePanel };
|
|
77337
78119
|
sidePanelStore;
|
|
77338
78120
|
spreadsheetRect = useSpreadsheetRect();
|
|
77339
78121
|
setup() {
|
|
77340
78122
|
this.sidePanelStore = useStore(SidePanelStore);
|
|
77341
|
-
owl.useEffect((
|
|
77342
|
-
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) {
|
|
77343
78128
|
this.sidePanelStore.close();
|
|
77344
78129
|
}
|
|
77345
|
-
}, () => [this.sidePanelStore.
|
|
77346
|
-
}
|
|
77347
|
-
get panel() {
|
|
77348
|
-
return sidePanelRegistry.get(this.sidePanelStore.componentTag);
|
|
77349
|
-
}
|
|
77350
|
-
close() {
|
|
77351
|
-
this.sidePanelStore.close();
|
|
78130
|
+
}, () => [this.sidePanelStore.isMainPanelOpen, this.sidePanelStore.isSecondaryPanelOpen]);
|
|
77352
78131
|
}
|
|
77353
|
-
|
|
77354
|
-
const panel = this.panel;
|
|
77355
|
-
return typeof panel.title === "function"
|
|
77356
|
-
? panel.title(this.env, this.sidePanelStore.panelProps)
|
|
77357
|
-
: panel.title;
|
|
77358
|
-
}
|
|
77359
|
-
startHandleDrag(ev) {
|
|
78132
|
+
startHandleDrag(panel, ev) {
|
|
77360
78133
|
const startingCursor = document.body.style.cursor;
|
|
77361
|
-
const
|
|
78134
|
+
const panelInfo = panel === "mainPanel" ? this.sidePanelStore.mainPanel : this.sidePanelStore.secondaryPanel;
|
|
78135
|
+
if (!panelInfo) {
|
|
78136
|
+
return;
|
|
78137
|
+
}
|
|
78138
|
+
const startSize = panelInfo.size;
|
|
77362
78139
|
const startPosition = ev.clientX;
|
|
77363
78140
|
const onMouseMove = (ev) => {
|
|
77364
78141
|
document.body.style.cursor = "col-resize";
|
|
77365
78142
|
const newSize = startSize + startPosition - ev.clientX;
|
|
77366
|
-
this.sidePanelStore.changePanelSize(
|
|
78143
|
+
this.sidePanelStore.changePanelSize(panel, newSize);
|
|
77367
78144
|
};
|
|
77368
78145
|
const cleanUp = () => {
|
|
77369
78146
|
document.body.style.cursor = startingCursor;
|
|
77370
78147
|
};
|
|
77371
78148
|
startDnd(onMouseMove, cleanUp);
|
|
77372
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
|
+
}
|
|
77373
78200
|
}
|
|
77374
78201
|
|
|
77375
78202
|
class RibbonMenu extends owl.Component {
|
|
@@ -78931,7 +79758,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
78931
79758
|
Grid,
|
|
78932
79759
|
BottomBar,
|
|
78933
79760
|
SmallBottomBar,
|
|
78934
|
-
|
|
79761
|
+
SidePanels,
|
|
78935
79762
|
SpreadsheetDashboard,
|
|
78936
79763
|
HeaderGroupContainer,
|
|
78937
79764
|
FullScreenChart,
|
|
@@ -78954,7 +79781,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
78954
79781
|
else {
|
|
78955
79782
|
properties["grid-template-rows"] = `min-content auto min-content`;
|
|
78956
79783
|
}
|
|
78957
|
-
const columnWidth = this.sidePanel.
|
|
79784
|
+
const columnWidth = this.sidePanel.mainPanel
|
|
79785
|
+
? `${this.sidePanel.totalPanelSize || DEFAULT_SIDE_PANEL_SIZE}px`
|
|
79786
|
+
: "auto";
|
|
78958
79787
|
properties["grid-template-columns"] = `auto ${columnWidth}`;
|
|
78959
79788
|
return cssPropertiesToCss(properties);
|
|
78960
79789
|
}
|
|
@@ -79038,7 +79867,7 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
79038
79867
|
this.checkViewportSize();
|
|
79039
79868
|
});
|
|
79040
79869
|
const resizeObserver = new ResizeObserver(() => {
|
|
79041
|
-
this.sidePanel.
|
|
79870
|
+
this.sidePanel.changeSpreadsheetWidth(this.spreadsheetRect.width);
|
|
79042
79871
|
});
|
|
79043
79872
|
}
|
|
79044
79873
|
bindModelEvents() {
|
|
@@ -83635,9 +84464,9 @@ stores.inject(MyMetaStore, storeInstance);
|
|
|
83635
84464
|
exports.tokenize = tokenize;
|
|
83636
84465
|
|
|
83637
84466
|
|
|
83638
|
-
__info__.version = "18.4.0
|
|
83639
|
-
__info__.date = "2025-06-
|
|
83640
|
-
__info__.hash = "
|
|
84467
|
+
__info__.version = "18.4.0";
|
|
84468
|
+
__info__.date = "2025-06-24T11:19:24.606Z";
|
|
84469
|
+
__info__.hash = "a5b7cad";
|
|
83641
84470
|
|
|
83642
84471
|
|
|
83643
84472
|
})(this.o_spreadsheet = this.o_spreadsheet || {}, owl);
|