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