@odoo/o-spreadsheet 18.4.0-alpha.7 → 18.4.0-alpha.9
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 +916 -586
- package/dist/o-spreadsheet.d.ts +40 -9
- package/dist/o-spreadsheet.esm.js +916 -587
- package/dist/o-spreadsheet.iife.js +916 -586
- package/dist/o-spreadsheet.iife.min.js +563 -562
- package/dist/o_spreadsheet.xml +34 -9
- package/package.json +1 -1
|
@@ -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-alpha.
|
|
6
|
-
* @date 2025-06-
|
|
7
|
-
* @hash
|
|
5
|
+
* @version 18.4.0-alpha.9
|
|
6
|
+
* @date 2025-06-19T18:23:22.025Z
|
|
7
|
+
* @hash 6d4d685
|
|
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';
|
|
@@ -20,13 +20,21 @@ function createAction(item) {
|
|
|
20
20
|
const icon = item.icon;
|
|
21
21
|
const secondaryIcon = item.secondaryIcon;
|
|
22
22
|
const itemId = item.id || nextItemId++;
|
|
23
|
+
const isEnabled = item.isEnabled ? item.isEnabled : () => true;
|
|
23
24
|
return {
|
|
24
25
|
id: itemId.toString(),
|
|
25
26
|
name: typeof name === "function" ? name : () => name,
|
|
26
27
|
isVisible: item.isVisible ? item.isVisible : () => true,
|
|
27
|
-
isEnabled:
|
|
28
|
+
isEnabled: isEnabled,
|
|
28
29
|
isActive: item.isActive,
|
|
29
|
-
execute: item.execute
|
|
30
|
+
execute: item.execute
|
|
31
|
+
? (env, isMiddleClick) => {
|
|
32
|
+
if (isEnabled(env)) {
|
|
33
|
+
return item.execute(env, isMiddleClick);
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
: undefined,
|
|
30
38
|
children: children
|
|
31
39
|
? (env) => {
|
|
32
40
|
return children
|
|
@@ -130,7 +138,7 @@ class Registry {
|
|
|
130
138
|
|
|
131
139
|
const CANVAS_SHIFT = 0.5;
|
|
132
140
|
// Colors
|
|
133
|
-
const HIGHLIGHT_COLOR = "#
|
|
141
|
+
const HIGHLIGHT_COLOR = "#017E84";
|
|
134
142
|
const BACKGROUND_GRAY_COLOR = "#f5f5f5";
|
|
135
143
|
const BACKGROUND_HEADER_COLOR = "#F8F9FA";
|
|
136
144
|
const BACKGROUND_HEADER_SELECTED_COLOR = "#E8EAED";
|
|
@@ -143,7 +151,7 @@ const CELL_BORDER_COLOR = "#E2E3E3";
|
|
|
143
151
|
const BACKGROUND_CHART_COLOR = "#FFFFFF";
|
|
144
152
|
const DISABLED_TEXT_COLOR = "#CACACA";
|
|
145
153
|
const DEFAULT_COLOR_SCALE_MIDPOINT_COLOR = 0xb6d7a8;
|
|
146
|
-
const LINK_COLOR =
|
|
154
|
+
const LINK_COLOR = HIGHLIGHT_COLOR;
|
|
147
155
|
const FILTERS_COLOR = "#188038";
|
|
148
156
|
const SEPARATOR_COLOR = "#E0E2E4";
|
|
149
157
|
const ICONS_COLOR = "#4A4F59";
|
|
@@ -172,7 +180,7 @@ const BUTTON_HOVER_BG = GRAY_300;
|
|
|
172
180
|
const BUTTON_HOVER_TEXT_COLOR = "#111827";
|
|
173
181
|
const BUTTON_ACTIVE_BG = "#e6f2f3";
|
|
174
182
|
const BUTTON_ACTIVE_TEXT_COLOR = "#111827";
|
|
175
|
-
const ACTION_COLOR =
|
|
183
|
+
const ACTION_COLOR = HIGHLIGHT_COLOR;
|
|
176
184
|
const ACTION_COLOR_HOVER = "#01585c";
|
|
177
185
|
const ALERT_WARNING_BG = "#FBEBCC";
|
|
178
186
|
const ALERT_WARNING_BORDER = "#F8E2B3";
|
|
@@ -297,6 +305,7 @@ const GROUP_LAYER_WIDTH = 21;
|
|
|
297
305
|
const GRID_ICON_MARGIN = 2;
|
|
298
306
|
const GRID_ICON_EDGE_LENGTH = 17;
|
|
299
307
|
const FOOTER_HEIGHT = 2 * DEFAULT_CELL_HEIGHT;
|
|
308
|
+
const DATA_VALIDATION_CHIP_MARGIN = 5;
|
|
300
309
|
// 768px is a common breakpoint for small screens
|
|
301
310
|
// Typically inside Odoo, it is the threshold for switching to mobile view
|
|
302
311
|
const MOBILE_WIDTH_BREAKPOINT = 768;
|
|
@@ -642,9 +651,6 @@ function parseSheetUrl(sheetLink) {
|
|
|
642
651
|
function isDefined(argument) {
|
|
643
652
|
return argument !== undefined;
|
|
644
653
|
}
|
|
645
|
-
function isNotNull(argument) {
|
|
646
|
-
return argument !== null;
|
|
647
|
-
}
|
|
648
654
|
/**
|
|
649
655
|
* Check if all the values of an object, and all the values of the objects inside of it, are undefined.
|
|
650
656
|
*/
|
|
@@ -1355,9 +1361,16 @@ function darkenColor(color, percentage) {
|
|
|
1355
1361
|
if (percentage === 1) {
|
|
1356
1362
|
return "#000";
|
|
1357
1363
|
}
|
|
1364
|
+
// increase saturation to compensate and make it more vivid
|
|
1365
|
+
hsla.s = Math.min(100, percentage * hsla.s + hsla.s);
|
|
1358
1366
|
hsla.l = hsla.l - percentage * hsla.l;
|
|
1359
1367
|
return hslaToHex(hsla);
|
|
1360
1368
|
}
|
|
1369
|
+
function chipTextColor(chipBackgroundColor) {
|
|
1370
|
+
return relativeLuminance(chipBackgroundColor) < 0.6
|
|
1371
|
+
? lightenColor(chipBackgroundColor, 0.9)
|
|
1372
|
+
: darkenColor(chipBackgroundColor, 0.75);
|
|
1373
|
+
}
|
|
1361
1374
|
const COLORS_SM = [
|
|
1362
1375
|
"#4EA7F2", // Blue
|
|
1363
1376
|
"#EA6175", // Red
|
|
@@ -1566,6 +1579,19 @@ class AlternatingColorGenerator extends ColorGenerator {
|
|
|
1566
1579
|
this.palette = getAlternatingColorsPalette(paletteSize).filter((c) => !preferredColors.includes(c));
|
|
1567
1580
|
}
|
|
1568
1581
|
}
|
|
1582
|
+
class AlternatingColorMap {
|
|
1583
|
+
availableColors;
|
|
1584
|
+
colors = {};
|
|
1585
|
+
constructor(paletteSize = 12) {
|
|
1586
|
+
this.availableColors = new AlternatingColorGenerator(paletteSize);
|
|
1587
|
+
}
|
|
1588
|
+
get(id) {
|
|
1589
|
+
if (!this.colors[id]) {
|
|
1590
|
+
this.colors[id] = this.availableColors.next();
|
|
1591
|
+
}
|
|
1592
|
+
return this.colors[id];
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1569
1595
|
/**
|
|
1570
1596
|
* Returns a function that maps a value to a color using a color scale defined by the given
|
|
1571
1597
|
* color/threshold values pairs.
|
|
@@ -5005,7 +5031,9 @@ function isTextFormat(format) {
|
|
|
5005
5031
|
}
|
|
5006
5032
|
|
|
5007
5033
|
function evaluateLiteral(literalCell, localeFormat) {
|
|
5008
|
-
const value = isTextFormat(localeFormat.format)
|
|
5034
|
+
const value = isTextFormat(localeFormat.format) && literalCell.parsedValue !== null
|
|
5035
|
+
? literalCell.content
|
|
5036
|
+
: literalCell.parsedValue;
|
|
5009
5037
|
const functionResult = { value, format: localeFormat.format };
|
|
5010
5038
|
return createEvaluatedCell(functionResult, localeFormat.locale);
|
|
5011
5039
|
}
|
|
@@ -5054,6 +5082,9 @@ function _createEvaluatedCell(functionResult, locale, cell) {
|
|
|
5054
5082
|
if (isEvaluationError(value)) {
|
|
5055
5083
|
return errorCell(value, message);
|
|
5056
5084
|
}
|
|
5085
|
+
if (value === null) {
|
|
5086
|
+
return emptyCell(format);
|
|
5087
|
+
}
|
|
5057
5088
|
if (isTextFormat(format)) {
|
|
5058
5089
|
// TO DO:
|
|
5059
5090
|
// with the next line, the value of the cell is transformed depending on the format.
|
|
@@ -5061,9 +5092,6 @@ function _createEvaluatedCell(functionResult, locale, cell) {
|
|
|
5061
5092
|
// to interpret the value as a number.
|
|
5062
5093
|
return textCell(toString(value), format, formattedValue);
|
|
5063
5094
|
}
|
|
5064
|
-
if (value === null) {
|
|
5065
|
-
return emptyCell(format);
|
|
5066
|
-
}
|
|
5067
5095
|
if (typeof value === "number") {
|
|
5068
5096
|
if (isDateTimeFormat(format || "")) {
|
|
5069
5097
|
return dateTimeCell(value, format, formattedValue);
|
|
@@ -19351,8 +19379,9 @@ const PIVOT = {
|
|
|
19351
19379
|
arg("include_total (boolean, default=TRUE)", _t("Whether to include total/sub-totals or not.")),
|
|
19352
19380
|
arg("include_column_titles (boolean, default=TRUE)", _t("Whether to include the column titles or not.")),
|
|
19353
19381
|
arg("column_count (number, optional)", _t("number of columns")),
|
|
19382
|
+
arg("include_measure_titles (boolean, default=TRUE)", _t("Whether to include the measure titles row or not.")),
|
|
19354
19383
|
],
|
|
19355
|
-
compute: function (pivotFormulaId, rowCount = { value: 10000 }, includeTotal = { value: true }, includeColumnHeaders = { value: true }, columnCount = { value: Number.MAX_VALUE }) {
|
|
19384
|
+
compute: function (pivotFormulaId, rowCount = { value: 10000 }, includeTotal = { value: true }, includeColumnHeaders = { value: true }, columnCount = { value: Number.MAX_VALUE }, includeMeasureTitles = { value: true }) {
|
|
19356
19385
|
const _pivotFormulaId = toString(pivotFormulaId);
|
|
19357
19386
|
const _rowCount = toNumber(rowCount, this.locale);
|
|
19358
19387
|
if (_rowCount < 0) {
|
|
@@ -19362,8 +19391,11 @@ const PIVOT = {
|
|
|
19362
19391
|
if (_columnCount < 0) {
|
|
19363
19392
|
return new EvaluationError(_t("The number of columns must be positive."));
|
|
19364
19393
|
}
|
|
19365
|
-
const
|
|
19366
|
-
|
|
19394
|
+
const visibilityOptions = {
|
|
19395
|
+
displayColumnHeaders: toBoolean(includeColumnHeaders),
|
|
19396
|
+
displayTotals: toBoolean(includeTotal),
|
|
19397
|
+
displayMeasuresRow: toBoolean(includeMeasureTitles),
|
|
19398
|
+
};
|
|
19367
19399
|
const pivotId = getPivotId(_pivotFormulaId, this.getters);
|
|
19368
19400
|
const pivot = this.getters.getPivot(pivotId);
|
|
19369
19401
|
const coreDefinition = this.getters.getPivotCoreDefinition(pivotId);
|
|
@@ -19374,9 +19406,15 @@ const PIVOT = {
|
|
|
19374
19406
|
return error;
|
|
19375
19407
|
}
|
|
19376
19408
|
const table = pivot.getCollapsedTableStructure();
|
|
19377
|
-
const cells = table.getPivotCells(
|
|
19378
|
-
|
|
19379
|
-
|
|
19409
|
+
const cells = table.getPivotCells(visibilityOptions);
|
|
19410
|
+
let headerRows = 0;
|
|
19411
|
+
if (visibilityOptions.displayColumnHeaders) {
|
|
19412
|
+
headerRows = table.columns.length - 1;
|
|
19413
|
+
}
|
|
19414
|
+
if (visibilityOptions.displayMeasuresRow) {
|
|
19415
|
+
headerRows++;
|
|
19416
|
+
}
|
|
19417
|
+
const pivotTitle = this.getters.getPivotName(pivotId);
|
|
19380
19418
|
const tableHeight = Math.min(headerRows + _rowCount, cells[0].length);
|
|
19381
19419
|
if (tableHeight === 0) {
|
|
19382
19420
|
return [[{ value: pivotTitle }]];
|
|
@@ -19404,7 +19442,7 @@ const PIVOT = {
|
|
|
19404
19442
|
}
|
|
19405
19443
|
}
|
|
19406
19444
|
}
|
|
19407
|
-
if (
|
|
19445
|
+
if (visibilityOptions.displayColumnHeaders || visibilityOptions.displayMeasuresRow) {
|
|
19408
19446
|
result[0][0] = { value: pivotTitle };
|
|
19409
19447
|
}
|
|
19410
19448
|
return result;
|
|
@@ -21661,6 +21699,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
|
|
|
21661
21699
|
if (isTrendLineAxis(dataset.xAxisID) || dataset.hidden) {
|
|
21662
21700
|
continue;
|
|
21663
21701
|
}
|
|
21702
|
+
const yAxisScale = chart.scales[dataset.yAxisID];
|
|
21664
21703
|
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
21665
21704
|
const parsedValue = dataset._parsed[i];
|
|
21666
21705
|
const value = Number(chart.config.type === "radar" ? parsedValue.r : parsedValue.y);
|
|
@@ -21671,10 +21710,18 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
|
|
|
21671
21710
|
const xPosition = point.x;
|
|
21672
21711
|
let yPosition = 0;
|
|
21673
21712
|
if (chart.config.type === "line" || chart.config.type === "radar") {
|
|
21674
|
-
yPosition = point.y - 10;
|
|
21713
|
+
yPosition = value < 0 ? point.y + 10 : point.y - 10;
|
|
21675
21714
|
}
|
|
21676
21715
|
else {
|
|
21677
|
-
|
|
21716
|
+
const yZeroLine = yAxisScale.getPixelForValue(0);
|
|
21717
|
+
const distanceFromAxisOrigin = Math.abs(yZeroLine - point.y);
|
|
21718
|
+
const textHeight = 12; // ChartJS default text height
|
|
21719
|
+
if (distanceFromAxisOrigin < textHeight) {
|
|
21720
|
+
yPosition = value < 0 ? yZeroLine + textHeight / 2 : yZeroLine - textHeight / 2;
|
|
21721
|
+
}
|
|
21722
|
+
else {
|
|
21723
|
+
yPosition = value < 0 ? point.y - point.height / 2 : point.y + point.height / 2;
|
|
21724
|
+
}
|
|
21678
21725
|
}
|
|
21679
21726
|
yPosition = Math.min(yPosition, yMax);
|
|
21680
21727
|
yPosition = Math.max(yPosition, yMin);
|
|
@@ -21684,7 +21731,7 @@ function drawLineOrBarOrRadarChartValues(chart, options, ctx) {
|
|
|
21684
21731
|
}
|
|
21685
21732
|
for (const otherPosition of textsPositions[xPosition] || []) {
|
|
21686
21733
|
if (Math.abs(otherPosition - yPosition) < 13) {
|
|
21687
|
-
yPosition = otherPosition - 13;
|
|
21734
|
+
yPosition = value < 0 ? otherPosition + 13 : otherPosition - 13;
|
|
21688
21735
|
}
|
|
21689
21736
|
}
|
|
21690
21737
|
textsPositions[xPosition].push(yPosition);
|
|
@@ -21703,6 +21750,8 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
|
|
|
21703
21750
|
if (isTrendLineAxis(dataset.xAxisID)) {
|
|
21704
21751
|
return; // ignore trend lines
|
|
21705
21752
|
}
|
|
21753
|
+
const xAxisScale = chart.scales[dataset.xAxisID];
|
|
21754
|
+
const xZeroLine = xAxisScale.getPixelForValue(0);
|
|
21706
21755
|
for (let i = 0; i < dataset._parsed.length; i++) {
|
|
21707
21756
|
const value = Number(dataset._parsed[i].x);
|
|
21708
21757
|
if (isNaN(value)) {
|
|
@@ -21711,17 +21760,27 @@ function drawHorizontalBarChartValues(chart, options, ctx) {
|
|
|
21711
21760
|
const displayValue = options.callback(value, dataset, i);
|
|
21712
21761
|
const point = dataset.data[i];
|
|
21713
21762
|
const yPosition = point.y;
|
|
21714
|
-
|
|
21715
|
-
|
|
21716
|
-
|
|
21763
|
+
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
|
|
21764
|
+
const distanceFromAxisOrigin = Math.abs(point.x - xZeroLine);
|
|
21765
|
+
const PADDING = 3;
|
|
21766
|
+
let xPosition;
|
|
21767
|
+
if (distanceFromAxisOrigin < textWidth) {
|
|
21768
|
+
xPosition =
|
|
21769
|
+
value < 0 ? xZeroLine - textWidth / 2 - PADDING : xZeroLine + textWidth / 2 + PADDING;
|
|
21770
|
+
}
|
|
21771
|
+
else {
|
|
21772
|
+
xPosition = value < 0 ? point.x + point.width / 2 : point.x - point.width / 2;
|
|
21773
|
+
xPosition = Math.min(xPosition, xMax);
|
|
21774
|
+
xPosition = Math.max(xPosition, xMin);
|
|
21775
|
+
}
|
|
21717
21776
|
// Avoid overlapping texts with same Y
|
|
21718
21777
|
if (!textsPositions[yPosition]) {
|
|
21719
21778
|
textsPositions[yPosition] = [];
|
|
21720
21779
|
}
|
|
21721
|
-
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: 12 }, "px");
|
|
21722
21780
|
for (const otherPosition of textsPositions[yPosition]) {
|
|
21723
21781
|
if (Math.abs(otherPosition - xPosition) < textWidth) {
|
|
21724
|
-
xPosition =
|
|
21782
|
+
xPosition =
|
|
21783
|
+
value < 0 ? otherPosition - textWidth - PADDING : otherPosition + textWidth + PADDING;
|
|
21725
21784
|
}
|
|
21726
21785
|
}
|
|
21727
21786
|
textsPositions[yPosition].push(xPosition);
|
|
@@ -21743,10 +21802,22 @@ function drawPieChartValues(chart, options, ctx) {
|
|
|
21743
21802
|
const midAngle = (startAngle + endAngle) / 2;
|
|
21744
21803
|
const midRadius = (innerRadius + outerRadius) / 2;
|
|
21745
21804
|
const x = bar.x + midRadius * Math.cos(midAngle);
|
|
21746
|
-
const y = bar.y + midRadius * Math.sin(midAngle)
|
|
21805
|
+
const y = bar.y + midRadius * Math.sin(midAngle);
|
|
21806
|
+
const displayValue = options.callback(value, dataset, i);
|
|
21807
|
+
const textHeight = 12; // ChartJS default
|
|
21808
|
+
const textWidth = computeTextWidth(ctx, displayValue, { fontSize: textHeight }, "px");
|
|
21809
|
+
const radius = outerRadius - innerRadius;
|
|
21810
|
+
// Check if the text fits in the slice. Not perfect, but good enough heuristic.
|
|
21811
|
+
if (textWidth >= radius || radius < textHeight) {
|
|
21812
|
+
continue;
|
|
21813
|
+
}
|
|
21814
|
+
const sliceAngle = endAngle - startAngle;
|
|
21815
|
+
const midWidth = 2 * midRadius * Math.tan(sliceAngle / 2);
|
|
21816
|
+
if (sliceAngle < Math.PI / 2 && (textWidth >= midWidth || midWidth < textHeight)) {
|
|
21817
|
+
continue;
|
|
21818
|
+
}
|
|
21747
21819
|
ctx.fillStyle = chartFontColor(options.background);
|
|
21748
21820
|
ctx.strokeStyle = options.background || "#ffffff";
|
|
21749
|
-
const displayValue = options.callback(value, dataset, i);
|
|
21750
21821
|
drawTextWithBackground(displayValue, x, y, ctx);
|
|
21751
21822
|
}
|
|
21752
21823
|
}
|
|
@@ -25643,7 +25714,9 @@ function getPyramidChartShowValues(definition, args) {
|
|
|
25643
25714
|
background: definition.background,
|
|
25644
25715
|
callback: (value, dataset) => {
|
|
25645
25716
|
value = Math.abs(Number(value));
|
|
25646
|
-
return
|
|
25717
|
+
return value === 0
|
|
25718
|
+
? ""
|
|
25719
|
+
: formatChartDatasetValue(axisFormats, locale)(value, dataset.xAxisID || "x");
|
|
25647
25720
|
},
|
|
25648
25721
|
};
|
|
25649
25722
|
}
|
|
@@ -29304,6 +29377,12 @@ class Menu extends Component {
|
|
|
29304
29377
|
menu.onStopHover?.(this.env);
|
|
29305
29378
|
this.props.onMouseLeave?.(menu, ev);
|
|
29306
29379
|
}
|
|
29380
|
+
onClickMenu(menu, ev) {
|
|
29381
|
+
if (!this.isEnabled(menu)) {
|
|
29382
|
+
return;
|
|
29383
|
+
}
|
|
29384
|
+
this.props.onClickMenu?.(menu, ev);
|
|
29385
|
+
}
|
|
29307
29386
|
}
|
|
29308
29387
|
|
|
29309
29388
|
/**
|
|
@@ -29802,9 +29881,6 @@ class MenuPopover extends Component {
|
|
|
29802
29881
|
this.subMenu.parentMenu = undefined;
|
|
29803
29882
|
}
|
|
29804
29883
|
onClickMenu(menu, ev) {
|
|
29805
|
-
if (!this.isEnabled(menu)) {
|
|
29806
|
-
return;
|
|
29807
|
-
}
|
|
29808
29884
|
if (this.isRoot(menu)) {
|
|
29809
29885
|
this.openSubMenu(menu, ev.currentTarget);
|
|
29810
29886
|
}
|
|
@@ -31055,11 +31131,9 @@ criterionEvaluatorRegistry.add("isValueInRange", {
|
|
|
31055
31131
|
if (!value) {
|
|
31056
31132
|
return false;
|
|
31057
31133
|
}
|
|
31058
|
-
const
|
|
31059
|
-
const criterionValues = getters.getRangeValues(range);
|
|
31134
|
+
const criterionValues = getters.getDataValidationRangeValues(sheetId, criterion);
|
|
31060
31135
|
return criterionValues
|
|
31061
|
-
.
|
|
31062
|
-
.map((value) => value.toString().toLowerCase())
|
|
31136
|
+
.map((value) => value.toLowerCase())
|
|
31063
31137
|
.includes(value.toString().toLowerCase());
|
|
31064
31138
|
},
|
|
31065
31139
|
getErrorString: (criterion) => _t("The value must be a value in the range %s", String(criterion.values[0])),
|
|
@@ -31231,6 +31305,12 @@ class TextValueProvider extends Component {
|
|
|
31231
31305
|
selectedElement?.scrollIntoView?.({ block: "nearest" });
|
|
31232
31306
|
}, () => [this.props.selectedIndex, this.autoCompleteListRef.el]);
|
|
31233
31307
|
}
|
|
31308
|
+
getCss(html) {
|
|
31309
|
+
return cssPropertiesToCss({
|
|
31310
|
+
color: html.color || "#000000",
|
|
31311
|
+
background: html.backgroundColor,
|
|
31312
|
+
});
|
|
31313
|
+
}
|
|
31234
31314
|
}
|
|
31235
31315
|
|
|
31236
31316
|
class ContentEditableHelper {
|
|
@@ -31369,7 +31449,6 @@ class ContentEditableHelper {
|
|
|
31369
31449
|
// We can only modify a node in place if it has the same type as the content
|
|
31370
31450
|
// that we would insert, which are spans.
|
|
31371
31451
|
// Otherwise, it means that the node has been input by the user, through the keyboard or a copy/paste
|
|
31372
|
-
// @ts-ignore (somehow required because jest does not like child.tagName despite the prior check)
|
|
31373
31452
|
const childIsSpan = child && "tagName" in child && child.tagName === "SPAN";
|
|
31374
31453
|
if (childIsSpan && compareContentToSpanElement(content, child)) {
|
|
31375
31454
|
continue;
|
|
@@ -31404,9 +31483,7 @@ class ContentEditableHelper {
|
|
|
31404
31483
|
}
|
|
31405
31484
|
// Empty line
|
|
31406
31485
|
if (!p.hasChildNodes()) {
|
|
31407
|
-
|
|
31408
|
-
span.appendChild(document.createElement("br"));
|
|
31409
|
-
p.appendChild(span);
|
|
31486
|
+
p.appendChild(document.createElement("span"));
|
|
31410
31487
|
}
|
|
31411
31488
|
// replace p if necessary
|
|
31412
31489
|
if (newChild) {
|
|
@@ -31453,13 +31530,10 @@ class ContentEditableHelper {
|
|
|
31453
31530
|
}
|
|
31454
31531
|
getText() {
|
|
31455
31532
|
let text = "";
|
|
31456
|
-
const it = iterateChildren(this.el);
|
|
31457
|
-
let current = it.next();
|
|
31458
31533
|
let isFirstParagraph = true;
|
|
31459
|
-
|
|
31460
|
-
|
|
31461
|
-
|
|
31462
|
-
}
|
|
31534
|
+
let emptyParagraph = false;
|
|
31535
|
+
const it = iterateChildren(this.el);
|
|
31536
|
+
for (let current = it.next(); !current.done; current = it.next()) {
|
|
31463
31537
|
if (current.value.nodeName === "P" ||
|
|
31464
31538
|
(current.value.nodeName === "DIV" && current.value !== this.el) // On paste, the HTML may contain <div> instead of <p>
|
|
31465
31539
|
) {
|
|
@@ -31469,8 +31543,15 @@ class ContentEditableHelper {
|
|
|
31469
31543
|
else {
|
|
31470
31544
|
text += NEWLINE;
|
|
31471
31545
|
}
|
|
31546
|
+
emptyParagraph = ["<br>", "<span><br></span>"].includes(current.value.innerHTML);
|
|
31547
|
+
continue;
|
|
31548
|
+
}
|
|
31549
|
+
if (!current.value.hasChildNodes()) {
|
|
31550
|
+
if (current.value.nodeName === "BR" && !emptyParagraph) {
|
|
31551
|
+
text += NEWLINE;
|
|
31552
|
+
}
|
|
31553
|
+
text += current.value.textContent;
|
|
31472
31554
|
}
|
|
31473
|
-
current = it.next();
|
|
31474
31555
|
}
|
|
31475
31556
|
return text;
|
|
31476
31557
|
}
|
|
@@ -32820,6 +32901,12 @@ class Composer extends Component {
|
|
|
32820
32901
|
useEffect(() => {
|
|
32821
32902
|
this.processTokenAtCursor();
|
|
32822
32903
|
}, () => [this.props.composerStore.editionMode !== "inactive"]);
|
|
32904
|
+
useEffect(() => {
|
|
32905
|
+
this.contentHelper.scrollSelectionIntoView();
|
|
32906
|
+
}, () => [
|
|
32907
|
+
this.props.composerStore.composerSelection.start,
|
|
32908
|
+
this.props.composerStore.composerSelection.end,
|
|
32909
|
+
]);
|
|
32823
32910
|
}
|
|
32824
32911
|
// ---------------------------------------------------------------------------
|
|
32825
32912
|
// Handlers
|
|
@@ -33030,6 +33117,7 @@ class Composer extends Component {
|
|
|
33030
33117
|
if (this.env.isMobile() && !isIOS()) {
|
|
33031
33118
|
return;
|
|
33032
33119
|
}
|
|
33120
|
+
this.debouncedHover.stopDebounce();
|
|
33033
33121
|
this.contentHelper.removeSelection();
|
|
33034
33122
|
}
|
|
33035
33123
|
onMouseup() {
|
|
@@ -33108,7 +33196,6 @@ class Composer extends Component {
|
|
|
33108
33196
|
const { start, end } = this.props.composerStore.composerSelection;
|
|
33109
33197
|
this.contentHelper.selectRange(start, end);
|
|
33110
33198
|
}
|
|
33111
|
-
this.contentHelper.scrollSelectionIntoView();
|
|
33112
33199
|
}
|
|
33113
33200
|
this.shouldProcessInputEvents = true;
|
|
33114
33201
|
}
|
|
@@ -33584,6 +33671,414 @@ class SingleInputCriterionForm extends CriterionForm {
|
|
|
33584
33671
|
}
|
|
33585
33672
|
}
|
|
33586
33673
|
|
|
33674
|
+
/**
|
|
33675
|
+
* Start listening to pointer events and apply the given callbacks.
|
|
33676
|
+
*
|
|
33677
|
+
* @returns A function to remove the listeners.
|
|
33678
|
+
*/
|
|
33679
|
+
function startDnd(onPointerMove, onPointerUp) {
|
|
33680
|
+
const removeListeners = () => {
|
|
33681
|
+
window.removeEventListener("pointerup", _onPointerUp, { capture: true });
|
|
33682
|
+
window.removeEventListener("dragstart", _onDragStart);
|
|
33683
|
+
window.removeEventListener("pointermove", onPointerMove);
|
|
33684
|
+
window.removeEventListener("wheel", onPointerMove);
|
|
33685
|
+
};
|
|
33686
|
+
const _onPointerUp = (ev) => {
|
|
33687
|
+
onPointerUp(ev);
|
|
33688
|
+
removeListeners();
|
|
33689
|
+
};
|
|
33690
|
+
function _onDragStart(ev) {
|
|
33691
|
+
ev.preventDefault();
|
|
33692
|
+
}
|
|
33693
|
+
window.addEventListener("pointerup", _onPointerUp, { capture: true });
|
|
33694
|
+
window.addEventListener("dragstart", _onDragStart);
|
|
33695
|
+
window.addEventListener("pointermove", onPointerMove);
|
|
33696
|
+
// mouse wheel on window is by default a passive event.
|
|
33697
|
+
// preventDefault() is not allowed in passive event handler.
|
|
33698
|
+
// https://chromestatus.com/feature/6662647093133312
|
|
33699
|
+
window.addEventListener("wheel", onPointerMove, { passive: false });
|
|
33700
|
+
return removeListeners;
|
|
33701
|
+
}
|
|
33702
|
+
|
|
33703
|
+
const LINE_VERTICAL_PADDING = 1;
|
|
33704
|
+
const PICKER_PADDING = 8;
|
|
33705
|
+
const ITEM_BORDER_WIDTH = 1;
|
|
33706
|
+
const ITEM_EDGE_LENGTH = 18;
|
|
33707
|
+
const ITEMS_PER_LINE = 10;
|
|
33708
|
+
const MAGNIFIER_EDGE = 16;
|
|
33709
|
+
const ITEM_GAP = 2;
|
|
33710
|
+
const CONTENT_WIDTH = ITEMS_PER_LINE * (ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH) + (ITEMS_PER_LINE - 1) * ITEM_GAP;
|
|
33711
|
+
const INNER_GRADIENT_WIDTH = CONTENT_WIDTH - 2 * ITEM_BORDER_WIDTH;
|
|
33712
|
+
const INNER_GRADIENT_HEIGHT = CONTENT_WIDTH - 30 - 2 * ITEM_BORDER_WIDTH;
|
|
33713
|
+
const CONTAINER_WIDTH = CONTENT_WIDTH + 2 * PICKER_PADDING;
|
|
33714
|
+
css /* scss */ `
|
|
33715
|
+
.o-color-picker {
|
|
33716
|
+
padding: ${PICKER_PADDING}px 0;
|
|
33717
|
+
/* FIXME: this is useless, overiden by the popover container */
|
|
33718
|
+
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
|
|
33719
|
+
background-color: white;
|
|
33720
|
+
line-height: 1.2;
|
|
33721
|
+
overflow-y: auto;
|
|
33722
|
+
overflow-x: hidden;
|
|
33723
|
+
width: ${CONTAINER_WIDTH}px;
|
|
33724
|
+
|
|
33725
|
+
.o-color-picker-section-name {
|
|
33726
|
+
margin: 0px ${ITEM_BORDER_WIDTH}px;
|
|
33727
|
+
padding: 4px ${PICKER_PADDING}px;
|
|
33728
|
+
}
|
|
33729
|
+
.colors-grid {
|
|
33730
|
+
display: grid;
|
|
33731
|
+
padding: ${LINE_VERTICAL_PADDING}px ${PICKER_PADDING}px;
|
|
33732
|
+
grid-template-columns: repeat(${ITEMS_PER_LINE}, 1fr);
|
|
33733
|
+
grid-gap: ${ITEM_GAP}px;
|
|
33734
|
+
}
|
|
33735
|
+
.o-color-picker-toggler-button {
|
|
33736
|
+
display: flex;
|
|
33737
|
+
.o-color-picker-toggler-sign {
|
|
33738
|
+
display: flex;
|
|
33739
|
+
margin: auto auto;
|
|
33740
|
+
width: 55%;
|
|
33741
|
+
height: 55%;
|
|
33742
|
+
.o-icon {
|
|
33743
|
+
width: 100%;
|
|
33744
|
+
height: 100%;
|
|
33745
|
+
}
|
|
33746
|
+
}
|
|
33747
|
+
}
|
|
33748
|
+
.o-color-picker-line-item {
|
|
33749
|
+
width: ${ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
33750
|
+
height: ${ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
33751
|
+
margin: 0px;
|
|
33752
|
+
border-radius: 50px;
|
|
33753
|
+
border: ${ITEM_BORDER_WIDTH}px solid #666666;
|
|
33754
|
+
padding: 0px;
|
|
33755
|
+
font-size: 16px;
|
|
33756
|
+
background: white;
|
|
33757
|
+
&:hover {
|
|
33758
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
33759
|
+
outline: 1px solid gray;
|
|
33760
|
+
cursor: pointer;
|
|
33761
|
+
}
|
|
33762
|
+
}
|
|
33763
|
+
.o-buttons {
|
|
33764
|
+
padding: ${PICKER_PADDING}px;
|
|
33765
|
+
display: flex;
|
|
33766
|
+
.o-cancel {
|
|
33767
|
+
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
33768
|
+
width: 100%;
|
|
33769
|
+
padding: 5px;
|
|
33770
|
+
font-size: 14px;
|
|
33771
|
+
background: white;
|
|
33772
|
+
border-radius: 4px;
|
|
33773
|
+
&:hover:enabled {
|
|
33774
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
33775
|
+
}
|
|
33776
|
+
}
|
|
33777
|
+
}
|
|
33778
|
+
.o-add-button {
|
|
33779
|
+
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
33780
|
+
padding: 4px;
|
|
33781
|
+
background: white;
|
|
33782
|
+
border-radius: 4px;
|
|
33783
|
+
&:hover:enabled {
|
|
33784
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
33785
|
+
}
|
|
33786
|
+
}
|
|
33787
|
+
.o-separator {
|
|
33788
|
+
border-bottom: ${MENU_SEPARATOR_BORDER_WIDTH}px solid ${SEPARATOR_COLOR};
|
|
33789
|
+
margin-top: ${MENU_SEPARATOR_PADDING}px;
|
|
33790
|
+
margin-bottom: ${MENU_SEPARATOR_PADDING}px;
|
|
33791
|
+
}
|
|
33792
|
+
|
|
33793
|
+
.o-custom-selector {
|
|
33794
|
+
padding: ${PICKER_PADDING + 2}px ${PICKER_PADDING}px;
|
|
33795
|
+
position: relative;
|
|
33796
|
+
.o-gradient {
|
|
33797
|
+
margin-bottom: ${MAGNIFIER_EDGE / 2}px;
|
|
33798
|
+
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
33799
|
+
width: ${INNER_GRADIENT_WIDTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
33800
|
+
height: ${INNER_GRADIENT_HEIGHT + 2 * ITEM_BORDER_WIDTH}px;
|
|
33801
|
+
position: relative;
|
|
33802
|
+
}
|
|
33803
|
+
|
|
33804
|
+
.magnifier {
|
|
33805
|
+
height: ${MAGNIFIER_EDGE}px;
|
|
33806
|
+
width: ${MAGNIFIER_EDGE}px;
|
|
33807
|
+
border-radius: 50%;
|
|
33808
|
+
border: 2px solid #fff;
|
|
33809
|
+
box-shadow: 0px 0px 3px #c0c0c0;
|
|
33810
|
+
position: absolute;
|
|
33811
|
+
z-index: 2;
|
|
33812
|
+
}
|
|
33813
|
+
.saturation {
|
|
33814
|
+
background: linear-gradient(to right, #fff 0%, transparent 100%);
|
|
33815
|
+
}
|
|
33816
|
+
.lightness {
|
|
33817
|
+
background: linear-gradient(to top, #000 0%, transparent 100%);
|
|
33818
|
+
}
|
|
33819
|
+
.o-hue-picker {
|
|
33820
|
+
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
33821
|
+
width: 100%;
|
|
33822
|
+
height: 12px;
|
|
33823
|
+
border-radius: 4px;
|
|
33824
|
+
background: linear-gradient(
|
|
33825
|
+
to right,
|
|
33826
|
+
hsl(0 100% 50%) 0%,
|
|
33827
|
+
hsl(0.2turn 100% 50%) 20%,
|
|
33828
|
+
hsl(0.3turn 100% 50%) 30%,
|
|
33829
|
+
hsl(0.4turn 100% 50%) 40%,
|
|
33830
|
+
hsl(0.5turn 100% 50%) 50%,
|
|
33831
|
+
hsl(0.6turn 100% 50%) 60%,
|
|
33832
|
+
hsl(0.7turn 100% 50%) 70%,
|
|
33833
|
+
hsl(0.8turn 100% 50%) 80%,
|
|
33834
|
+
hsl(0.9turn 100% 50%) 90%,
|
|
33835
|
+
hsl(1turn 100% 50%) 100%
|
|
33836
|
+
);
|
|
33837
|
+
position: relative;
|
|
33838
|
+
cursor: crosshair;
|
|
33839
|
+
}
|
|
33840
|
+
.o-hue-slider {
|
|
33841
|
+
margin-top: -3px;
|
|
33842
|
+
}
|
|
33843
|
+
.o-custom-input-preview {
|
|
33844
|
+
padding: 2px 0px;
|
|
33845
|
+
display: flex;
|
|
33846
|
+
input {
|
|
33847
|
+
width: 50%;
|
|
33848
|
+
border-radius: 4px;
|
|
33849
|
+
padding: 4px 23px 4px 10px;
|
|
33850
|
+
height: 24px;
|
|
33851
|
+
border: 1px solid #c0c0c0;
|
|
33852
|
+
margin-right: 2px;
|
|
33853
|
+
}
|
|
33854
|
+
.o-wrong-color {
|
|
33855
|
+
/* FIXME bootstrap class instead? */
|
|
33856
|
+
outline-color: red;
|
|
33857
|
+
border-color: red;
|
|
33858
|
+
&:focus {
|
|
33859
|
+
outline-style: solid;
|
|
33860
|
+
outline-width: 1px;
|
|
33861
|
+
}
|
|
33862
|
+
}
|
|
33863
|
+
}
|
|
33864
|
+
.o-custom-input-buttons {
|
|
33865
|
+
padding: 2px 0px;
|
|
33866
|
+
display: flex;
|
|
33867
|
+
justify-content: end;
|
|
33868
|
+
}
|
|
33869
|
+
.o-color-preview {
|
|
33870
|
+
border: 1px solid #c0c0c0;
|
|
33871
|
+
border-radius: 4px;
|
|
33872
|
+
width: 50%;
|
|
33873
|
+
}
|
|
33874
|
+
}
|
|
33875
|
+
}
|
|
33876
|
+
`;
|
|
33877
|
+
class ColorPicker extends Component {
|
|
33878
|
+
static template = "o-spreadsheet-ColorPicker";
|
|
33879
|
+
static props = {
|
|
33880
|
+
onColorPicked: Function,
|
|
33881
|
+
currentColor: { type: String, optional: true },
|
|
33882
|
+
maxHeight: { type: Number, optional: true },
|
|
33883
|
+
anchorRect: Object,
|
|
33884
|
+
disableNoColor: { type: Boolean, optional: true },
|
|
33885
|
+
};
|
|
33886
|
+
static defaultProps = { currentColor: "" };
|
|
33887
|
+
static components = { Popover };
|
|
33888
|
+
COLORS = COLOR_PICKER_DEFAULTS;
|
|
33889
|
+
state = useState({
|
|
33890
|
+
showGradient: false,
|
|
33891
|
+
currentHslaColor: isColorValid(this.props.currentColor)
|
|
33892
|
+
? { ...hexToHSLA(this.props.currentColor), a: 1 }
|
|
33893
|
+
: { h: 0, s: 100, l: 100, a: 1 },
|
|
33894
|
+
customHexColor: isColorValid(this.props.currentColor) ? toHex(this.props.currentColor) : "",
|
|
33895
|
+
});
|
|
33896
|
+
get colorPickerStyle() {
|
|
33897
|
+
if (this.props.maxHeight !== undefined && this.props.maxHeight <= 0) {
|
|
33898
|
+
return cssPropertiesToCss({ display: "none" });
|
|
33899
|
+
}
|
|
33900
|
+
return "";
|
|
33901
|
+
}
|
|
33902
|
+
get popoverProps() {
|
|
33903
|
+
return {
|
|
33904
|
+
anchorRect: this.props.anchorRect,
|
|
33905
|
+
maxHeight: this.props.maxHeight,
|
|
33906
|
+
positioning: "bottom-left",
|
|
33907
|
+
verticalOffset: 0,
|
|
33908
|
+
};
|
|
33909
|
+
}
|
|
33910
|
+
get gradientHueStyle() {
|
|
33911
|
+
const hue = this.state.currentHslaColor?.h || 0;
|
|
33912
|
+
return cssPropertiesToCss({
|
|
33913
|
+
background: `hsl(${hue} 100% 50%)`,
|
|
33914
|
+
});
|
|
33915
|
+
}
|
|
33916
|
+
get sliderStyle() {
|
|
33917
|
+
const hue = this.state.currentHslaColor?.h || 0;
|
|
33918
|
+
const delta = Math.round((hue / 360) * INNER_GRADIENT_WIDTH);
|
|
33919
|
+
const left = clip(delta, 1, INNER_GRADIENT_WIDTH) - ICON_EDGE_LENGTH / 2;
|
|
33920
|
+
return cssPropertiesToCss({
|
|
33921
|
+
"margin-left": `${left}px`,
|
|
33922
|
+
});
|
|
33923
|
+
}
|
|
33924
|
+
get pointerStyle() {
|
|
33925
|
+
const { s, l } = this.state.currentHslaColor || { s: 0, l: 0 };
|
|
33926
|
+
const left = Math.round(INNER_GRADIENT_WIDTH * clip(s / 100, 0, 1));
|
|
33927
|
+
const top = Math.round(INNER_GRADIENT_HEIGHT * clip(1 - (2 * l) / (200 - s), 0, 1));
|
|
33928
|
+
return cssPropertiesToCss({
|
|
33929
|
+
left: `${-MAGNIFIER_EDGE / 2 + left}px`,
|
|
33930
|
+
top: `${-MAGNIFIER_EDGE / 2 + top}px`,
|
|
33931
|
+
background: hslaToHex(this.state.currentHslaColor),
|
|
33932
|
+
});
|
|
33933
|
+
}
|
|
33934
|
+
get colorPreviewStyle() {
|
|
33935
|
+
return cssPropertiesToCss({
|
|
33936
|
+
"background-color": hslaToHex(this.state.currentHslaColor),
|
|
33937
|
+
});
|
|
33938
|
+
}
|
|
33939
|
+
get checkmarkColor() {
|
|
33940
|
+
return chartFontColor(this.props.currentColor);
|
|
33941
|
+
}
|
|
33942
|
+
get isHexColorInputValid() {
|
|
33943
|
+
return !this.state.customHexColor || isColorValid(this.state.customHexColor);
|
|
33944
|
+
}
|
|
33945
|
+
setCustomGradient({ x, y }) {
|
|
33946
|
+
const offsetX = clip(x, 0, INNER_GRADIENT_WIDTH);
|
|
33947
|
+
const offsetY = clip(y, 0, INNER_GRADIENT_HEIGHT);
|
|
33948
|
+
const deltaX = offsetX / INNER_GRADIENT_WIDTH;
|
|
33949
|
+
const deltaY = offsetY / INNER_GRADIENT_HEIGHT;
|
|
33950
|
+
const s = 100 * deltaX;
|
|
33951
|
+
const l = 100 * (1 - deltaY) * (1 - 0.5 * deltaX);
|
|
33952
|
+
this.updateColor({ s, l });
|
|
33953
|
+
}
|
|
33954
|
+
setCustomHue(x) {
|
|
33955
|
+
// needs to be capped such that h is in [0°, 359°]
|
|
33956
|
+
const h = Math.round(clip((360 * x) / INNER_GRADIENT_WIDTH, 0, 359));
|
|
33957
|
+
this.updateColor({ h });
|
|
33958
|
+
}
|
|
33959
|
+
updateColor(newHsl) {
|
|
33960
|
+
this.state.currentHslaColor = { ...this.state.currentHslaColor, ...newHsl };
|
|
33961
|
+
this.state.customHexColor = hslaToHex(this.state.currentHslaColor);
|
|
33962
|
+
}
|
|
33963
|
+
onColorClick(color) {
|
|
33964
|
+
if (color) {
|
|
33965
|
+
this.props.onColorPicked(toHex(color));
|
|
33966
|
+
}
|
|
33967
|
+
}
|
|
33968
|
+
resetColor() {
|
|
33969
|
+
this.props.onColorPicked("");
|
|
33970
|
+
}
|
|
33971
|
+
toggleColorPicker() {
|
|
33972
|
+
this.state.showGradient = !this.state.showGradient;
|
|
33973
|
+
}
|
|
33974
|
+
dragGradientPointer(ev) {
|
|
33975
|
+
const initialGradientCoordinates = { x: ev.offsetX, y: ev.offsetY };
|
|
33976
|
+
this.setCustomGradient(initialGradientCoordinates);
|
|
33977
|
+
const initialMousePosition = { x: ev.clientX, y: ev.clientY };
|
|
33978
|
+
const onMouseMove = (ev) => {
|
|
33979
|
+
const currentMousePosition = { x: ev.clientX, y: ev.clientY };
|
|
33980
|
+
const deltaX = currentMousePosition.x - initialMousePosition.x;
|
|
33981
|
+
const deltaY = currentMousePosition.y - initialMousePosition.y;
|
|
33982
|
+
const currentGradientCoordinates = {
|
|
33983
|
+
x: initialGradientCoordinates.x + deltaX,
|
|
33984
|
+
y: initialGradientCoordinates.y + deltaY,
|
|
33985
|
+
};
|
|
33986
|
+
this.setCustomGradient(currentGradientCoordinates);
|
|
33987
|
+
};
|
|
33988
|
+
startDnd(onMouseMove, () => { });
|
|
33989
|
+
}
|
|
33990
|
+
dragHuePointer(ev) {
|
|
33991
|
+
const initialX = ev.offsetX;
|
|
33992
|
+
const initialMouseX = ev.clientX;
|
|
33993
|
+
this.setCustomHue(initialX);
|
|
33994
|
+
const onMouseMove = (ev) => {
|
|
33995
|
+
const currentMouseX = ev.clientX;
|
|
33996
|
+
const deltaX = currentMouseX - initialMouseX;
|
|
33997
|
+
const x = initialX + deltaX;
|
|
33998
|
+
this.setCustomHue(x);
|
|
33999
|
+
};
|
|
34000
|
+
startDnd(onMouseMove, () => { });
|
|
34001
|
+
}
|
|
34002
|
+
setHexColor(ev) {
|
|
34003
|
+
// only support HEX code input
|
|
34004
|
+
const val = ev.target.value.replace("##", "#").slice(0, 7);
|
|
34005
|
+
this.state.customHexColor = val;
|
|
34006
|
+
if (!isColorValid(val)) ;
|
|
34007
|
+
else {
|
|
34008
|
+
this.state.currentHslaColor = { ...hexToHSLA(val), a: 1 };
|
|
34009
|
+
}
|
|
34010
|
+
}
|
|
34011
|
+
addCustomColor(ev) {
|
|
34012
|
+
if (!isHSLAValid(this.state.currentHslaColor) || !isColorValid(this.state.customHexColor)) {
|
|
34013
|
+
return;
|
|
34014
|
+
}
|
|
34015
|
+
this.props.onColorPicked(toHex(this.state.customHexColor));
|
|
34016
|
+
}
|
|
34017
|
+
isSameColor(color1, color2) {
|
|
34018
|
+
return isSameColor(color1, color2);
|
|
34019
|
+
}
|
|
34020
|
+
}
|
|
34021
|
+
|
|
34022
|
+
class Section extends Component {
|
|
34023
|
+
static template = "o_spreadsheet.Section";
|
|
34024
|
+
static props = {
|
|
34025
|
+
class: { type: String, optional: true },
|
|
34026
|
+
title: { type: String, optional: true },
|
|
34027
|
+
slots: Object,
|
|
34028
|
+
};
|
|
34029
|
+
}
|
|
34030
|
+
|
|
34031
|
+
const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
|
|
34032
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
34033
|
+
<path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
|
|
34034
|
+
</svg>
|
|
34035
|
+
`;
|
|
34036
|
+
css /* scss */ `
|
|
34037
|
+
.o-round-color-picker-button {
|
|
34038
|
+
width: 20px;
|
|
34039
|
+
height: 20px;
|
|
34040
|
+
cursor: pointer;
|
|
34041
|
+
border: 1px solid ${GRAY_300};
|
|
34042
|
+
background-position: 1px 1px;
|
|
34043
|
+
background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
|
|
34044
|
+
}
|
|
34045
|
+
`;
|
|
34046
|
+
class RoundColorPicker extends Component {
|
|
34047
|
+
static template = "o-spreadsheet.RoundColorPicker";
|
|
34048
|
+
static components = { Section, ColorPicker };
|
|
34049
|
+
static props = {
|
|
34050
|
+
currentColor: { type: String, optional: true },
|
|
34051
|
+
title: { type: String, optional: true },
|
|
34052
|
+
onColorPicked: Function,
|
|
34053
|
+
disableNoColor: { type: Boolean, optional: true },
|
|
34054
|
+
};
|
|
34055
|
+
colorPickerButtonRef = useRef("colorPickerButton");
|
|
34056
|
+
state;
|
|
34057
|
+
setup() {
|
|
34058
|
+
this.state = useState({ pickerOpened: false });
|
|
34059
|
+
useExternalListener(window, "click", this.closePicker);
|
|
34060
|
+
}
|
|
34061
|
+
closePicker() {
|
|
34062
|
+
this.state.pickerOpened = false;
|
|
34063
|
+
}
|
|
34064
|
+
togglePicker() {
|
|
34065
|
+
this.state.pickerOpened = !this.state.pickerOpened;
|
|
34066
|
+
}
|
|
34067
|
+
onColorPicked(color) {
|
|
34068
|
+
this.props.onColorPicked(color);
|
|
34069
|
+
this.state.pickerOpened = false;
|
|
34070
|
+
}
|
|
34071
|
+
get colorPickerAnchorRect() {
|
|
34072
|
+
const button = this.colorPickerButtonRef.el;
|
|
34073
|
+
return getBoundingRectAsPOJO(button);
|
|
34074
|
+
}
|
|
34075
|
+
get buttonStyle() {
|
|
34076
|
+
return cssPropertiesToCss({
|
|
34077
|
+
background: this.props.currentColor,
|
|
34078
|
+
});
|
|
34079
|
+
}
|
|
34080
|
+
}
|
|
34081
|
+
|
|
33587
34082
|
css /* scss */ `
|
|
33588
34083
|
.o-dv-list-item-delete {
|
|
33589
34084
|
color: #666666;
|
|
@@ -33592,7 +34087,7 @@ css /* scss */ `
|
|
|
33592
34087
|
`;
|
|
33593
34088
|
class ListCriterionForm extends CriterionForm {
|
|
33594
34089
|
static template = "o-spreadsheet-ListCriterionForm";
|
|
33595
|
-
static components = { CriterionInput };
|
|
34090
|
+
static components = { CriterionInput, RoundColorPicker };
|
|
33596
34091
|
state = useState({
|
|
33597
34092
|
numberOfValues: Math.max(this.props.criterion.values.length, 2),
|
|
33598
34093
|
});
|
|
@@ -33600,7 +34095,7 @@ class ListCriterionForm extends CriterionForm {
|
|
|
33600
34095
|
super.setup();
|
|
33601
34096
|
const setupDefault = (props) => {
|
|
33602
34097
|
if (props.criterion.displayStyle === undefined) {
|
|
33603
|
-
this.updateCriterion({ displayStyle: "
|
|
34098
|
+
this.updateCriterion({ displayStyle: "chip" });
|
|
33604
34099
|
}
|
|
33605
34100
|
};
|
|
33606
34101
|
onWillUpdateProps(setupDefault);
|
|
@@ -33611,6 +34106,11 @@ class ListCriterionForm extends CriterionForm {
|
|
|
33611
34106
|
values[index] = value;
|
|
33612
34107
|
this.updateCriterion({ values });
|
|
33613
34108
|
}
|
|
34109
|
+
onColorChanged(color, value) {
|
|
34110
|
+
const colors = { ...this.props.criterion.colors };
|
|
34111
|
+
colors[value] = color || undefined;
|
|
34112
|
+
this.updateCriterion({ colors });
|
|
34113
|
+
}
|
|
33614
34114
|
onAddAnotherValue() {
|
|
33615
34115
|
this.state.numberOfValues++;
|
|
33616
34116
|
}
|
|
@@ -33646,35 +34146,6 @@ class ListCriterionForm extends CriterionForm {
|
|
|
33646
34146
|
}
|
|
33647
34147
|
}
|
|
33648
34148
|
|
|
33649
|
-
/**
|
|
33650
|
-
* Start listening to pointer events and apply the given callbacks.
|
|
33651
|
-
*
|
|
33652
|
-
* @returns A function to remove the listeners.
|
|
33653
|
-
*/
|
|
33654
|
-
function startDnd(onPointerMove, onPointerUp) {
|
|
33655
|
-
const removeListeners = () => {
|
|
33656
|
-
window.removeEventListener("pointerup", _onPointerUp, { capture: true });
|
|
33657
|
-
window.removeEventListener("dragstart", _onDragStart);
|
|
33658
|
-
window.removeEventListener("pointermove", onPointerMove);
|
|
33659
|
-
window.removeEventListener("wheel", onPointerMove);
|
|
33660
|
-
};
|
|
33661
|
-
const _onPointerUp = (ev) => {
|
|
33662
|
-
onPointerUp(ev);
|
|
33663
|
-
removeListeners();
|
|
33664
|
-
};
|
|
33665
|
-
function _onDragStart(ev) {
|
|
33666
|
-
ev.preventDefault();
|
|
33667
|
-
}
|
|
33668
|
-
window.addEventListener("pointerup", _onPointerUp, { capture: true });
|
|
33669
|
-
window.addEventListener("dragstart", _onDragStart);
|
|
33670
|
-
window.addEventListener("pointermove", onPointerMove);
|
|
33671
|
-
// mouse wheel on window is by default a passive event.
|
|
33672
|
-
// preventDefault() is not allowed in passive event handler.
|
|
33673
|
-
// https://chromestatus.com/feature/6662647093133312
|
|
33674
|
-
window.addEventListener("wheel", onPointerMove, { passive: false });
|
|
33675
|
-
return removeListeners;
|
|
33676
|
-
}
|
|
33677
|
-
|
|
33678
34149
|
function useDragAndDropListItems() {
|
|
33679
34150
|
let dndHelper;
|
|
33680
34151
|
const previousCursor = document.body.style.cursor;
|
|
@@ -34534,12 +35005,12 @@ class SelectionInput extends Component {
|
|
|
34534
35005
|
|
|
34535
35006
|
class ValueInRangeCriterionForm extends CriterionForm {
|
|
34536
35007
|
static template = "o-spreadsheet-ValueInRangeCriterionForm";
|
|
34537
|
-
static components = { SelectionInput };
|
|
35008
|
+
static components = { RoundColorPicker, SelectionInput };
|
|
34538
35009
|
setup() {
|
|
34539
35010
|
super.setup();
|
|
34540
35011
|
const setupDefault = (props) => {
|
|
34541
35012
|
if (props.criterion.displayStyle === undefined) {
|
|
34542
|
-
this.updateCriterion({ displayStyle: "
|
|
35013
|
+
this.updateCriterion({ displayStyle: "chip" });
|
|
34543
35014
|
}
|
|
34544
35015
|
};
|
|
34545
35016
|
onWillUpdateProps(setupDefault);
|
|
@@ -34552,6 +35023,16 @@ class ValueInRangeCriterionForm extends CriterionForm {
|
|
|
34552
35023
|
const displayStyle = ev.target.value;
|
|
34553
35024
|
this.updateCriterion({ displayStyle });
|
|
34554
35025
|
}
|
|
35026
|
+
onColorChanged(color, value) {
|
|
35027
|
+
const colors = { ...this.props.criterion.colors };
|
|
35028
|
+
colors[value] = color || undefined;
|
|
35029
|
+
this.updateCriterion({ colors });
|
|
35030
|
+
}
|
|
35031
|
+
get values() {
|
|
35032
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
35033
|
+
const values = this.env.model.getters.getDataValidationRangeValues(sheetId, this.props.criterion);
|
|
35034
|
+
return new Set(values);
|
|
35035
|
+
}
|
|
34555
35036
|
}
|
|
34556
35037
|
|
|
34557
35038
|
const criterionCategoriesSequences = {
|
|
@@ -34939,17 +35420,22 @@ class FilterMenuValueList extends Component {
|
|
|
34939
35420
|
static components = { FilterMenuValueItem };
|
|
34940
35421
|
state = useState({
|
|
34941
35422
|
values: [],
|
|
35423
|
+
displayedValues: [],
|
|
34942
35424
|
textFilter: "",
|
|
34943
35425
|
selectedValue: undefined,
|
|
35426
|
+
numberOfDisplayedValues: 50,
|
|
35427
|
+
hasMoreValues: false,
|
|
34944
35428
|
});
|
|
34945
35429
|
searchBar = useRef("filterMenuSearchBar");
|
|
34946
35430
|
setup() {
|
|
34947
35431
|
onWillUpdateProps((nextProps) => {
|
|
34948
35432
|
if (!deepEquals(nextProps.filterPosition, this.props.filterPosition)) {
|
|
34949
35433
|
this.state.values = this.getFilterHiddenValues(nextProps.filterPosition);
|
|
35434
|
+
this.computeDisplayedValues();
|
|
34950
35435
|
}
|
|
34951
35436
|
});
|
|
34952
35437
|
this.state.values = this.getFilterHiddenValues(this.props.filterPosition);
|
|
35438
|
+
this.computeDisplayedValues();
|
|
34953
35439
|
}
|
|
34954
35440
|
getFilterHiddenValues(position) {
|
|
34955
35441
|
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
@@ -34967,21 +35453,28 @@ class FilterMenuValueList extends Component {
|
|
|
34967
35453
|
}
|
|
34968
35454
|
const cellValues = cells.map((val) => val.cellValue);
|
|
34969
35455
|
const filterValues = filterValue?.filterType === "values" ? filterValue.hiddenValues : [];
|
|
34970
|
-
const
|
|
34971
|
-
const
|
|
34972
|
-
|
|
34973
|
-
const
|
|
34974
|
-
|
|
34975
|
-
|
|
34976
|
-
|
|
34977
|
-
|
|
34978
|
-
|
|
35456
|
+
const normalizedFilteredValues = new Set(filterValues.map(toLowerCase));
|
|
35457
|
+
const set = new Set();
|
|
35458
|
+
const values = [];
|
|
35459
|
+
const addValue = (value) => {
|
|
35460
|
+
const normalizedValue = toLowerCase(value);
|
|
35461
|
+
if (!set.has(normalizedValue)) {
|
|
35462
|
+
values.push({
|
|
35463
|
+
string: value || "",
|
|
35464
|
+
checked: filterValue?.filterType !== "criterion"
|
|
35465
|
+
? !normalizedFilteredValues.has(normalizedValue)
|
|
35466
|
+
: false,
|
|
35467
|
+
normalizedValue,
|
|
35468
|
+
});
|
|
35469
|
+
set.add(normalizedValue);
|
|
34979
35470
|
}
|
|
34980
|
-
|
|
34981
|
-
|
|
34982
|
-
|
|
34983
|
-
|
|
34984
|
-
|
|
35471
|
+
};
|
|
35472
|
+
cellValues.forEach(addValue);
|
|
35473
|
+
filterValues.forEach(addValue);
|
|
35474
|
+
return values.sort((val1, val2) => val1.normalizedValue.localeCompare(val2.normalizedValue, undefined, {
|
|
35475
|
+
numeric: true,
|
|
35476
|
+
sensitivity: "base",
|
|
35477
|
+
}));
|
|
34985
35478
|
}
|
|
34986
35479
|
checkValue(value) {
|
|
34987
35480
|
this.state.selectedValue = value.string;
|
|
@@ -34993,25 +35486,37 @@ class FilterMenuValueList extends Component {
|
|
|
34993
35486
|
this.state.selectedValue = value.string;
|
|
34994
35487
|
}
|
|
34995
35488
|
selectAll() {
|
|
34996
|
-
this.displayedValues.forEach((value) => (value.checked = true));
|
|
34997
|
-
this.
|
|
35489
|
+
this.state.displayedValues.forEach((value) => (value.checked = true));
|
|
35490
|
+
this.props.onUpdateHiddenValues([]);
|
|
34998
35491
|
}
|
|
34999
35492
|
clearAll() {
|
|
35000
|
-
this.displayedValues.forEach((value) => (value.checked = false));
|
|
35001
|
-
this.
|
|
35493
|
+
this.state.displayedValues.forEach((value) => (value.checked = false));
|
|
35494
|
+
const hiddenValues = this.state.values.map((val) => val.string);
|
|
35495
|
+
this.props.onUpdateHiddenValues(hiddenValues);
|
|
35002
35496
|
}
|
|
35003
35497
|
updateHiddenValues() {
|
|
35004
35498
|
const hiddenValues = this.state.values.filter((val) => !val.checked).map((val) => val.string);
|
|
35005
35499
|
this.props.onUpdateHiddenValues(hiddenValues);
|
|
35006
35500
|
}
|
|
35007
|
-
|
|
35008
|
-
|
|
35009
|
-
|
|
35010
|
-
|
|
35011
|
-
|
|
35501
|
+
updateSearch(ev) {
|
|
35502
|
+
const target = ev.target;
|
|
35503
|
+
this.state.textFilter = target.value;
|
|
35504
|
+
this.state.selectedValue = undefined;
|
|
35505
|
+
this.computeDisplayedValues();
|
|
35506
|
+
}
|
|
35507
|
+
computeDisplayedValues() {
|
|
35508
|
+
const values = !this.state.textFilter
|
|
35509
|
+
? this.state.values
|
|
35510
|
+
: fuzzyLookup(this.state.textFilter, this.state.values, (val) => val.string);
|
|
35511
|
+
this.state.displayedValues = values.slice(0, this.state.numberOfDisplayedValues);
|
|
35512
|
+
this.state.hasMoreValues = values.length > this.state.numberOfDisplayedValues;
|
|
35513
|
+
}
|
|
35514
|
+
loadMoreValues() {
|
|
35515
|
+
this.state.numberOfDisplayedValues += 100;
|
|
35516
|
+
this.computeDisplayedValues();
|
|
35012
35517
|
}
|
|
35013
35518
|
onKeyDown(ev) {
|
|
35014
|
-
const displayedValues = this.displayedValues;
|
|
35519
|
+
const displayedValues = this.state.displayedValues;
|
|
35015
35520
|
if (displayedValues.length === 0)
|
|
35016
35521
|
return;
|
|
35017
35522
|
let selectedIndex = undefined;
|
|
@@ -36106,19 +36611,44 @@ const RED_DOT = {
|
|
|
36106
36611
|
height: 512,
|
|
36107
36612
|
paths: [{ fillColor: "#E06666", path: DOT_PATH }],
|
|
36108
36613
|
};
|
|
36109
|
-
|
|
36110
|
-
|
|
36111
|
-
|
|
36112
|
-
|
|
36113
|
-
}
|
|
36114
|
-
|
|
36115
|
-
|
|
36116
|
-
|
|
36117
|
-
|
|
36118
|
-
|
|
36119
|
-
|
|
36120
|
-
|
|
36121
|
-
}
|
|
36614
|
+
function getCaretDownSvg(color) {
|
|
36615
|
+
return {
|
|
36616
|
+
width: 512,
|
|
36617
|
+
height: 512,
|
|
36618
|
+
paths: [{ fillColor: color.textColor || TEXT_BODY_MUTED, path: "M120 195 h270 l-135 130" }],
|
|
36619
|
+
};
|
|
36620
|
+
}
|
|
36621
|
+
function getHoveredCaretDownSvg(color) {
|
|
36622
|
+
return {
|
|
36623
|
+
width: 512,
|
|
36624
|
+
height: 512,
|
|
36625
|
+
paths: [
|
|
36626
|
+
{ fillColor: color.textColor || TEXT_BODY_MUTED, path: "M15 15 h482 v482 h-482" },
|
|
36627
|
+
{ fillColor: color.fillColor || "#fff", path: "M120 195 h270 l-135 130" },
|
|
36628
|
+
],
|
|
36629
|
+
};
|
|
36630
|
+
}
|
|
36631
|
+
const CHIP_CARET_DOWN_PATH = "M40 185 h270 l-135 128";
|
|
36632
|
+
function getChipSvg(chipStyle) {
|
|
36633
|
+
return {
|
|
36634
|
+
width: 512,
|
|
36635
|
+
height: 512,
|
|
36636
|
+
paths: [{ fillColor: chipStyle.textColor || TEXT_BODY_MUTED, path: CHIP_CARET_DOWN_PATH }],
|
|
36637
|
+
};
|
|
36638
|
+
}
|
|
36639
|
+
function getHoveredChipSvg(chipStyle) {
|
|
36640
|
+
return {
|
|
36641
|
+
width: 512,
|
|
36642
|
+
height: 512,
|
|
36643
|
+
paths: [
|
|
36644
|
+
{
|
|
36645
|
+
fillColor: chipStyle.textColor || TEXT_BODY_MUTED,
|
|
36646
|
+
path: "M0,225 A175,175 0 1,0 350,225 A175,175 0 1,0 0,225",
|
|
36647
|
+
},
|
|
36648
|
+
{ fillColor: chipStyle.fillColor || TEXT_BODY_MUTED, path: CHIP_CARET_DOWN_PATH },
|
|
36649
|
+
],
|
|
36650
|
+
};
|
|
36651
|
+
}
|
|
36122
36652
|
const CHECKBOX_UNCHECKED = {
|
|
36123
36653
|
width: 512,
|
|
36124
36654
|
height: 512,
|
|
@@ -38484,7 +39014,7 @@ class XlsxBaseExtractor {
|
|
|
38484
39014
|
*/
|
|
38485
39015
|
handleMissingValue(parentElement, missingElementName, optionalArgs) {
|
|
38486
39016
|
if (optionalArgs?.required) {
|
|
38487
|
-
if (optionalArgs?.default) {
|
|
39017
|
+
if (optionalArgs?.default !== undefined) {
|
|
38488
39018
|
this.warningManager.addParsingWarning(`Missing required ${missingElementName} in element <${parentElement.tagName}> of ${this.currentFile}, replacing it by the default value ${optionalArgs.default}`);
|
|
38489
39019
|
}
|
|
38490
39020
|
else {
|
|
@@ -41026,6 +41556,10 @@ const REMOVE_ROWS_ACTION = (env) => {
|
|
|
41026
41556
|
});
|
|
41027
41557
|
};
|
|
41028
41558
|
const CAN_REMOVE_COLUMNS_ROWS = (dimension, env) => {
|
|
41559
|
+
if ((dimension === "COL" && env.model.getters.getActiveRows().size > 0) ||
|
|
41560
|
+
(dimension === "ROW" && env.model.getters.getActiveCols().size > 0)) {
|
|
41561
|
+
return false;
|
|
41562
|
+
}
|
|
41029
41563
|
const sheetId = env.model.getters.getActiveSheetId();
|
|
41030
41564
|
const selectedElements = env.model.getters.getElementsFromSelection(dimension);
|
|
41031
41565
|
const includesAllVisibleHeaders = env.model.getters.checkElementsIncludeAllVisibleHeaders(sheetId, dimension, selectedElements);
|
|
@@ -42580,7 +43114,7 @@ const insertDropdown = {
|
|
|
42580
43114
|
criterion: {
|
|
42581
43115
|
type: "isValueInList",
|
|
42582
43116
|
values: [],
|
|
42583
|
-
displayStyle: "
|
|
43117
|
+
displayStyle: "chip",
|
|
42584
43118
|
},
|
|
42585
43119
|
},
|
|
42586
43120
|
});
|
|
@@ -44495,6 +45029,11 @@ class GridComposer extends Component {
|
|
|
44495
45029
|
rect = this.defaultRect;
|
|
44496
45030
|
isEditing = false;
|
|
44497
45031
|
isCellReferenceVisible = false;
|
|
45032
|
+
currentEditedCell = {
|
|
45033
|
+
col: 0,
|
|
45034
|
+
row: 0,
|
|
45035
|
+
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
45036
|
+
};
|
|
44498
45037
|
composerStore;
|
|
44499
45038
|
composerFocusStore;
|
|
44500
45039
|
composerInterface;
|
|
@@ -44619,12 +45158,17 @@ class GridComposer extends Component {
|
|
|
44619
45158
|
if (!isEditing && this.composerFocusStore.activeComposer !== this.composerInterface) {
|
|
44620
45159
|
this.composerFocusStore.focusComposer(this.composerInterface, { focusMode: "inactive" });
|
|
44621
45160
|
}
|
|
45161
|
+
let shouldRecomputeRect = !deepEquals(this.currentEditedCell, this.composerStore.currentEditedCell);
|
|
44622
45162
|
if (this.isEditing !== isEditing) {
|
|
44623
45163
|
this.isEditing = isEditing;
|
|
44624
45164
|
if (!isEditing) {
|
|
44625
45165
|
this.rect = this.defaultRect;
|
|
44626
45166
|
return;
|
|
44627
45167
|
}
|
|
45168
|
+
this.currentEditedCell = this.composerStore.currentEditedCell;
|
|
45169
|
+
shouldRecomputeRect = true;
|
|
45170
|
+
}
|
|
45171
|
+
if (shouldRecomputeRect) {
|
|
44628
45172
|
const position = this.env.model.getters.getActivePosition();
|
|
44629
45173
|
const zone = this.env.model.getters.expandZone(position.sheetId, positionToZone(position));
|
|
44630
45174
|
this.rect = this.env.model.getters.getVisibleRect(zone);
|
|
@@ -45730,11 +46274,14 @@ class GridOverlay extends Component {
|
|
|
45730
46274
|
onCellClicked(ev) {
|
|
45731
46275
|
const openedPopover = this.cellPopovers.persistentCellPopover;
|
|
45732
46276
|
const [col, row] = this.getCartesianCoordinates(ev);
|
|
46277
|
+
const clickedIcon = this.getInteractiveIconAtEvent(ev);
|
|
46278
|
+
if (clickedIcon) {
|
|
46279
|
+
this.env.model.selection.getBackToDefault();
|
|
46280
|
+
}
|
|
45733
46281
|
this.props.onCellClicked(col, row, {
|
|
45734
46282
|
expandZone: ev.shiftKey,
|
|
45735
46283
|
addZone: isCtrlKey(ev),
|
|
45736
46284
|
}, ev);
|
|
45737
|
-
const clickedIcon = this.getInteractiveIconAtEvent(ev);
|
|
45738
46285
|
if (clickedIcon?.onClick) {
|
|
45739
46286
|
clickedIcon.onClick(clickedIcon.position, this.env);
|
|
45740
46287
|
}
|
|
@@ -46599,6 +47146,19 @@ class GridRenderer {
|
|
|
46599
47146
|
const width = box.width * (percentage / 100);
|
|
46600
47147
|
ctx.fillRect(box.x, box.y, width, box.height);
|
|
46601
47148
|
}
|
|
47149
|
+
if (box?.chip) {
|
|
47150
|
+
ctx.save();
|
|
47151
|
+
ctx.beginPath();
|
|
47152
|
+
ctx.rect(box.x, box.y, box.width, box.height);
|
|
47153
|
+
ctx.clip();
|
|
47154
|
+
const chip = box.chip;
|
|
47155
|
+
ctx.fillStyle = chip.color;
|
|
47156
|
+
const radius = 10;
|
|
47157
|
+
ctx.beginPath();
|
|
47158
|
+
ctx.roundRect(chip.x, chip.y, chip.width, chip.height, radius);
|
|
47159
|
+
ctx.fill();
|
|
47160
|
+
ctx.restore();
|
|
47161
|
+
}
|
|
46602
47162
|
if (box.overlayColor) {
|
|
46603
47163
|
ctx.fillStyle = box.overlayColor;
|
|
46604
47164
|
ctx.fillRect(box.x, box.y, box.width, box.height);
|
|
@@ -46738,19 +47298,6 @@ class GridRenderer {
|
|
|
46738
47298
|
ctx.font = font;
|
|
46739
47299
|
}
|
|
46740
47300
|
ctx.fillStyle = style.textColor || "#000";
|
|
46741
|
-
// compute horizontal align start point parameter
|
|
46742
|
-
let x = box.x;
|
|
46743
|
-
if (align === "left") {
|
|
46744
|
-
const leftIconSize = box.icons.left ? box.icons.left.size + box.icons.left.margin : 0;
|
|
46745
|
-
x += MIN_CELL_TEXT_MARGIN + leftIconSize;
|
|
46746
|
-
}
|
|
46747
|
-
else if (align === "right") {
|
|
46748
|
-
const rightIconSize = box.icons.right ? box.icons.right.size + box.icons.right.margin : 0;
|
|
46749
|
-
x += box.width - MIN_CELL_TEXT_MARGIN - rightIconSize;
|
|
46750
|
-
}
|
|
46751
|
-
else {
|
|
46752
|
-
x += box.width / 2;
|
|
46753
|
-
}
|
|
46754
47301
|
// horizontal align text direction
|
|
46755
47302
|
ctx.textAlign = align;
|
|
46756
47303
|
// clip rect if needed
|
|
@@ -46761,15 +47308,13 @@ class GridRenderer {
|
|
|
46761
47308
|
ctx.rect(x, y, width, height);
|
|
46762
47309
|
ctx.clip();
|
|
46763
47310
|
}
|
|
46764
|
-
|
|
46765
|
-
|
|
46766
|
-
const numberOfLines = box.content.textLines.length;
|
|
46767
|
-
let y = this.getters.computeTextYCoordinate(box, textLineHeight, style.verticalAlign, numberOfLines);
|
|
47311
|
+
const x = box.content.x;
|
|
47312
|
+
let y = box.content.y;
|
|
46768
47313
|
// use the horizontal and the vertical start points to:
|
|
46769
47314
|
// fill text / fill strikethrough / fill underline
|
|
46770
47315
|
for (const brokenLine of box.content.textLines) {
|
|
46771
|
-
drawDecoratedText(ctx, brokenLine, { x
|
|
46772
|
-
y += MIN_CELL_TEXT_MARGIN +
|
|
47316
|
+
drawDecoratedText(ctx, brokenLine, { x, y }, style.underline, style.strikethrough);
|
|
47317
|
+
y += MIN_CELL_TEXT_MARGIN + box.content.fontSizePx;
|
|
46773
47318
|
}
|
|
46774
47319
|
if (box.clipRect) {
|
|
46775
47320
|
ctx.restore();
|
|
@@ -47004,11 +47549,15 @@ class GridRenderer {
|
|
|
47004
47549
|
const showFormula = this.getters.shouldShowFormulas();
|
|
47005
47550
|
const { x, y, width, height } = this.getters.getRect(zone);
|
|
47006
47551
|
const { verticalAlign } = this.getters.getCellStyle(position);
|
|
47552
|
+
const chipStyle = this.getters.getDataValidationChipStyle(position);
|
|
47007
47553
|
let style = this.getters.getCellComputedStyle(position);
|
|
47008
47554
|
if (this.fingerprints.isEnabled) {
|
|
47009
47555
|
const fingerprintColor = this.fingerprints.colors.get(position);
|
|
47010
47556
|
style = { ...style, fillColor: fingerprintColor };
|
|
47011
47557
|
}
|
|
47558
|
+
if (chipStyle?.textColor) {
|
|
47559
|
+
style = { ...style, textColor: chipStyle.textColor };
|
|
47560
|
+
}
|
|
47012
47561
|
const dataBarFill = this.fingerprints.isEnabled
|
|
47013
47562
|
? undefined
|
|
47014
47563
|
: this.getters.getConditionalDataBar(position);
|
|
@@ -47042,22 +47591,55 @@ class GridRenderer {
|
|
|
47042
47591
|
const maxWidth = width - 2 * MIN_CELL_TEXT_MARGIN;
|
|
47043
47592
|
const multiLineText = this.getters.getCellMultiLineText(position, { maxWidth, wrapText });
|
|
47044
47593
|
const textWidth = Math.max(...multiLineText.map((line) => this.getters.getTextWidth(line, style) + MIN_CELL_TEXT_MARGIN));
|
|
47594
|
+
const chipMargin = chipStyle ? DATA_VALIDATION_CHIP_MARGIN : 0;
|
|
47045
47595
|
const leftIconWidth = box.icons.left ? box.icons.left.size + box.icons.left.margin : 0;
|
|
47596
|
+
const leftMargin = leftIconWidth + chipMargin;
|
|
47046
47597
|
const rightIconWidth = box.icons.right ? box.icons.right.size + box.icons.right.margin : 0;
|
|
47047
|
-
const
|
|
47598
|
+
const rightMargin = rightIconWidth + chipMargin;
|
|
47599
|
+
const contentWidth = leftMargin + textWidth + rightMargin;
|
|
47048
47600
|
const align = this.computeCellAlignment(position, contentWidth > width);
|
|
47601
|
+
// compute vertical align start point parameter:
|
|
47602
|
+
const numberOfLines = multiLineText.length;
|
|
47603
|
+
const contentY = Math.round(this.getters.computeTextYCoordinate(box, fontSizePX, style.verticalAlign, numberOfLines));
|
|
47604
|
+
// compute horizontal align start point parameter
|
|
47605
|
+
let contentX = box.x;
|
|
47606
|
+
if (align === "left") {
|
|
47607
|
+
contentX += MIN_CELL_TEXT_MARGIN + leftMargin;
|
|
47608
|
+
}
|
|
47609
|
+
else if (align === "right") {
|
|
47610
|
+
contentX += box.width - MIN_CELL_TEXT_MARGIN - rightMargin;
|
|
47611
|
+
}
|
|
47612
|
+
else {
|
|
47613
|
+
contentX += box.width / 2;
|
|
47614
|
+
}
|
|
47615
|
+
contentX = Math.round(contentX);
|
|
47616
|
+
const textHeight = computeTextLinesHeight(fontSizePX, numberOfLines);
|
|
47049
47617
|
box.content = {
|
|
47050
47618
|
textLines: multiLineText,
|
|
47051
47619
|
width: wrapping === "overflow" ? textWidth : width,
|
|
47052
47620
|
align,
|
|
47621
|
+
x: contentX,
|
|
47622
|
+
y: contentY,
|
|
47623
|
+
fontSizePx: fontSizePX,
|
|
47053
47624
|
};
|
|
47625
|
+
if (chipStyle?.fillColor) {
|
|
47626
|
+
const chipMarginLeft = leftMargin;
|
|
47627
|
+
const chipMarginRight = DATA_VALIDATION_CHIP_MARGIN;
|
|
47628
|
+
box.chip = {
|
|
47629
|
+
color: chipStyle.fillColor,
|
|
47630
|
+
width: box.width - chipMarginLeft - chipMarginRight,
|
|
47631
|
+
height: textHeight + 2,
|
|
47632
|
+
x: box.x + chipMarginLeft,
|
|
47633
|
+
y: contentY - 2,
|
|
47634
|
+
};
|
|
47635
|
+
}
|
|
47054
47636
|
/** ClipRect */
|
|
47055
47637
|
const isOverflowing = contentWidth > width || fontSizePX > height;
|
|
47056
|
-
if (box.icons.left || box.icons.right) {
|
|
47638
|
+
if (box.icons.left || box.icons.right || box.chip) {
|
|
47057
47639
|
box.clipRect = {
|
|
47058
|
-
x: box.x +
|
|
47640
|
+
x: box.x + leftMargin,
|
|
47059
47641
|
y: box.y,
|
|
47060
|
-
width: Math.max(0, width -
|
|
47642
|
+
width: Math.max(0, width - leftMargin - rightMargin),
|
|
47061
47643
|
height,
|
|
47062
47644
|
};
|
|
47063
47645
|
}
|
|
@@ -47757,15 +48339,6 @@ class Selection extends Component {
|
|
|
47757
48339
|
}
|
|
47758
48340
|
}
|
|
47759
48341
|
|
|
47760
|
-
class Section extends Component {
|
|
47761
|
-
static template = "o_spreadsheet.Section";
|
|
47762
|
-
static props = {
|
|
47763
|
-
class: { type: String, optional: true },
|
|
47764
|
-
title: { type: String, optional: true },
|
|
47765
|
-
slots: Object,
|
|
47766
|
-
};
|
|
47767
|
-
}
|
|
47768
|
-
|
|
47769
48342
|
class ChartDataSeries extends Component {
|
|
47770
48343
|
static template = "o-spreadsheet.ChartDataSeries";
|
|
47771
48344
|
static components = { SelectionInput, Section };
|
|
@@ -48314,325 +48887,6 @@ class ActionButton extends Component {
|
|
|
48314
48887
|
}
|
|
48315
48888
|
}
|
|
48316
48889
|
|
|
48317
|
-
const LINE_VERTICAL_PADDING = 1;
|
|
48318
|
-
const PICKER_PADDING = 8;
|
|
48319
|
-
const ITEM_BORDER_WIDTH = 1;
|
|
48320
|
-
const ITEM_EDGE_LENGTH = 18;
|
|
48321
|
-
const ITEMS_PER_LINE = 10;
|
|
48322
|
-
const MAGNIFIER_EDGE = 16;
|
|
48323
|
-
const ITEM_GAP = 2;
|
|
48324
|
-
const CONTENT_WIDTH = ITEMS_PER_LINE * (ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH) + (ITEMS_PER_LINE - 1) * ITEM_GAP;
|
|
48325
|
-
const INNER_GRADIENT_WIDTH = CONTENT_WIDTH - 2 * ITEM_BORDER_WIDTH;
|
|
48326
|
-
const INNER_GRADIENT_HEIGHT = CONTENT_WIDTH - 30 - 2 * ITEM_BORDER_WIDTH;
|
|
48327
|
-
const CONTAINER_WIDTH = CONTENT_WIDTH + 2 * PICKER_PADDING;
|
|
48328
|
-
css /* scss */ `
|
|
48329
|
-
.o-color-picker {
|
|
48330
|
-
padding: ${PICKER_PADDING}px 0;
|
|
48331
|
-
/* FIXME: this is useless, overiden by the popover container */
|
|
48332
|
-
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
|
|
48333
|
-
background-color: white;
|
|
48334
|
-
line-height: 1.2;
|
|
48335
|
-
overflow-y: auto;
|
|
48336
|
-
overflow-x: hidden;
|
|
48337
|
-
width: ${CONTAINER_WIDTH}px;
|
|
48338
|
-
|
|
48339
|
-
.o-color-picker-section-name {
|
|
48340
|
-
margin: 0px ${ITEM_BORDER_WIDTH}px;
|
|
48341
|
-
padding: 4px ${PICKER_PADDING}px;
|
|
48342
|
-
}
|
|
48343
|
-
.colors-grid {
|
|
48344
|
-
display: grid;
|
|
48345
|
-
padding: ${LINE_VERTICAL_PADDING}px ${PICKER_PADDING}px;
|
|
48346
|
-
grid-template-columns: repeat(${ITEMS_PER_LINE}, 1fr);
|
|
48347
|
-
grid-gap: ${ITEM_GAP}px;
|
|
48348
|
-
}
|
|
48349
|
-
.o-color-picker-toggler-button {
|
|
48350
|
-
display: flex;
|
|
48351
|
-
.o-color-picker-toggler-sign {
|
|
48352
|
-
display: flex;
|
|
48353
|
-
margin: auto auto;
|
|
48354
|
-
width: 55%;
|
|
48355
|
-
height: 55%;
|
|
48356
|
-
.o-icon {
|
|
48357
|
-
width: 100%;
|
|
48358
|
-
height: 100%;
|
|
48359
|
-
}
|
|
48360
|
-
}
|
|
48361
|
-
}
|
|
48362
|
-
.o-color-picker-line-item {
|
|
48363
|
-
width: ${ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
48364
|
-
height: ${ITEM_EDGE_LENGTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
48365
|
-
margin: 0px;
|
|
48366
|
-
border-radius: 50px;
|
|
48367
|
-
border: ${ITEM_BORDER_WIDTH}px solid #666666;
|
|
48368
|
-
padding: 0px;
|
|
48369
|
-
font-size: 16px;
|
|
48370
|
-
background: white;
|
|
48371
|
-
&:hover {
|
|
48372
|
-
background-color: rgba(0, 0, 0, 0.08);
|
|
48373
|
-
outline: 1px solid gray;
|
|
48374
|
-
cursor: pointer;
|
|
48375
|
-
}
|
|
48376
|
-
}
|
|
48377
|
-
.o-buttons {
|
|
48378
|
-
padding: ${PICKER_PADDING}px;
|
|
48379
|
-
display: flex;
|
|
48380
|
-
.o-cancel {
|
|
48381
|
-
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
48382
|
-
width: 100%;
|
|
48383
|
-
padding: 5px;
|
|
48384
|
-
font-size: 14px;
|
|
48385
|
-
background: white;
|
|
48386
|
-
border-radius: 4px;
|
|
48387
|
-
&:hover:enabled {
|
|
48388
|
-
background-color: rgba(0, 0, 0, 0.08);
|
|
48389
|
-
}
|
|
48390
|
-
}
|
|
48391
|
-
}
|
|
48392
|
-
.o-add-button {
|
|
48393
|
-
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
48394
|
-
padding: 4px;
|
|
48395
|
-
background: white;
|
|
48396
|
-
border-radius: 4px;
|
|
48397
|
-
&:hover:enabled {
|
|
48398
|
-
background-color: rgba(0, 0, 0, 0.08);
|
|
48399
|
-
}
|
|
48400
|
-
}
|
|
48401
|
-
.o-separator {
|
|
48402
|
-
border-bottom: ${MENU_SEPARATOR_BORDER_WIDTH}px solid ${SEPARATOR_COLOR};
|
|
48403
|
-
margin-top: ${MENU_SEPARATOR_PADDING}px;
|
|
48404
|
-
margin-bottom: ${MENU_SEPARATOR_PADDING}px;
|
|
48405
|
-
}
|
|
48406
|
-
|
|
48407
|
-
.o-custom-selector {
|
|
48408
|
-
padding: ${PICKER_PADDING + 2}px ${PICKER_PADDING}px;
|
|
48409
|
-
position: relative;
|
|
48410
|
-
.o-gradient {
|
|
48411
|
-
margin-bottom: ${MAGNIFIER_EDGE / 2}px;
|
|
48412
|
-
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
48413
|
-
width: ${INNER_GRADIENT_WIDTH + 2 * ITEM_BORDER_WIDTH}px;
|
|
48414
|
-
height: ${INNER_GRADIENT_HEIGHT + 2 * ITEM_BORDER_WIDTH}px;
|
|
48415
|
-
position: relative;
|
|
48416
|
-
}
|
|
48417
|
-
|
|
48418
|
-
.magnifier {
|
|
48419
|
-
height: ${MAGNIFIER_EDGE}px;
|
|
48420
|
-
width: ${MAGNIFIER_EDGE}px;
|
|
48421
|
-
border-radius: 50%;
|
|
48422
|
-
border: 2px solid #fff;
|
|
48423
|
-
box-shadow: 0px 0px 3px #c0c0c0;
|
|
48424
|
-
position: absolute;
|
|
48425
|
-
z-index: 2;
|
|
48426
|
-
}
|
|
48427
|
-
.saturation {
|
|
48428
|
-
background: linear-gradient(to right, #fff 0%, transparent 100%);
|
|
48429
|
-
}
|
|
48430
|
-
.lightness {
|
|
48431
|
-
background: linear-gradient(to top, #000 0%, transparent 100%);
|
|
48432
|
-
}
|
|
48433
|
-
.o-hue-picker {
|
|
48434
|
-
border: ${ITEM_BORDER_WIDTH}px solid #c0c0c0;
|
|
48435
|
-
width: 100%;
|
|
48436
|
-
height: 12px;
|
|
48437
|
-
border-radius: 4px;
|
|
48438
|
-
background: linear-gradient(
|
|
48439
|
-
to right,
|
|
48440
|
-
hsl(0 100% 50%) 0%,
|
|
48441
|
-
hsl(0.2turn 100% 50%) 20%,
|
|
48442
|
-
hsl(0.3turn 100% 50%) 30%,
|
|
48443
|
-
hsl(0.4turn 100% 50%) 40%,
|
|
48444
|
-
hsl(0.5turn 100% 50%) 50%,
|
|
48445
|
-
hsl(0.6turn 100% 50%) 60%,
|
|
48446
|
-
hsl(0.7turn 100% 50%) 70%,
|
|
48447
|
-
hsl(0.8turn 100% 50%) 80%,
|
|
48448
|
-
hsl(0.9turn 100% 50%) 90%,
|
|
48449
|
-
hsl(1turn 100% 50%) 100%
|
|
48450
|
-
);
|
|
48451
|
-
position: relative;
|
|
48452
|
-
cursor: crosshair;
|
|
48453
|
-
}
|
|
48454
|
-
.o-hue-slider {
|
|
48455
|
-
margin-top: -3px;
|
|
48456
|
-
}
|
|
48457
|
-
.o-custom-input-preview {
|
|
48458
|
-
padding: 2px 0px;
|
|
48459
|
-
display: flex;
|
|
48460
|
-
input {
|
|
48461
|
-
width: 50%;
|
|
48462
|
-
border-radius: 4px;
|
|
48463
|
-
padding: 4px 23px 4px 10px;
|
|
48464
|
-
height: 24px;
|
|
48465
|
-
border: 1px solid #c0c0c0;
|
|
48466
|
-
margin-right: 2px;
|
|
48467
|
-
}
|
|
48468
|
-
.o-wrong-color {
|
|
48469
|
-
/* FIXME bootstrap class instead? */
|
|
48470
|
-
outline-color: red;
|
|
48471
|
-
border-color: red;
|
|
48472
|
-
&:focus {
|
|
48473
|
-
outline-style: solid;
|
|
48474
|
-
outline-width: 1px;
|
|
48475
|
-
}
|
|
48476
|
-
}
|
|
48477
|
-
}
|
|
48478
|
-
.o-custom-input-buttons {
|
|
48479
|
-
padding: 2px 0px;
|
|
48480
|
-
display: flex;
|
|
48481
|
-
justify-content: end;
|
|
48482
|
-
}
|
|
48483
|
-
.o-color-preview {
|
|
48484
|
-
border: 1px solid #c0c0c0;
|
|
48485
|
-
border-radius: 4px;
|
|
48486
|
-
width: 50%;
|
|
48487
|
-
}
|
|
48488
|
-
}
|
|
48489
|
-
}
|
|
48490
|
-
`;
|
|
48491
|
-
class ColorPicker extends Component {
|
|
48492
|
-
static template = "o-spreadsheet-ColorPicker";
|
|
48493
|
-
static props = {
|
|
48494
|
-
onColorPicked: Function,
|
|
48495
|
-
currentColor: { type: String, optional: true },
|
|
48496
|
-
maxHeight: { type: Number, optional: true },
|
|
48497
|
-
anchorRect: Object,
|
|
48498
|
-
disableNoColor: { type: Boolean, optional: true },
|
|
48499
|
-
};
|
|
48500
|
-
static defaultProps = { currentColor: "" };
|
|
48501
|
-
static components = { Popover };
|
|
48502
|
-
COLORS = COLOR_PICKER_DEFAULTS;
|
|
48503
|
-
state = useState({
|
|
48504
|
-
showGradient: false,
|
|
48505
|
-
currentHslaColor: isColorValid(this.props.currentColor)
|
|
48506
|
-
? { ...hexToHSLA(this.props.currentColor), a: 1 }
|
|
48507
|
-
: { h: 0, s: 100, l: 100, a: 1 },
|
|
48508
|
-
customHexColor: isColorValid(this.props.currentColor) ? toHex(this.props.currentColor) : "",
|
|
48509
|
-
});
|
|
48510
|
-
get colorPickerStyle() {
|
|
48511
|
-
if (this.props.maxHeight !== undefined && this.props.maxHeight <= 0) {
|
|
48512
|
-
return cssPropertiesToCss({ display: "none" });
|
|
48513
|
-
}
|
|
48514
|
-
return "";
|
|
48515
|
-
}
|
|
48516
|
-
get popoverProps() {
|
|
48517
|
-
return {
|
|
48518
|
-
anchorRect: this.props.anchorRect,
|
|
48519
|
-
maxHeight: this.props.maxHeight,
|
|
48520
|
-
positioning: "bottom-left",
|
|
48521
|
-
verticalOffset: 0,
|
|
48522
|
-
};
|
|
48523
|
-
}
|
|
48524
|
-
get gradientHueStyle() {
|
|
48525
|
-
const hue = this.state.currentHslaColor?.h || 0;
|
|
48526
|
-
return cssPropertiesToCss({
|
|
48527
|
-
background: `hsl(${hue} 100% 50%)`,
|
|
48528
|
-
});
|
|
48529
|
-
}
|
|
48530
|
-
get sliderStyle() {
|
|
48531
|
-
const hue = this.state.currentHslaColor?.h || 0;
|
|
48532
|
-
const delta = Math.round((hue / 360) * INNER_GRADIENT_WIDTH);
|
|
48533
|
-
const left = clip(delta, 1, INNER_GRADIENT_WIDTH) - ICON_EDGE_LENGTH / 2;
|
|
48534
|
-
return cssPropertiesToCss({
|
|
48535
|
-
"margin-left": `${left}px`,
|
|
48536
|
-
});
|
|
48537
|
-
}
|
|
48538
|
-
get pointerStyle() {
|
|
48539
|
-
const { s, l } = this.state.currentHslaColor || { s: 0, l: 0 };
|
|
48540
|
-
const left = Math.round(INNER_GRADIENT_WIDTH * clip(s / 100, 0, 1));
|
|
48541
|
-
const top = Math.round(INNER_GRADIENT_HEIGHT * clip(1 - (2 * l) / (200 - s), 0, 1));
|
|
48542
|
-
return cssPropertiesToCss({
|
|
48543
|
-
left: `${-MAGNIFIER_EDGE / 2 + left}px`,
|
|
48544
|
-
top: `${-MAGNIFIER_EDGE / 2 + top}px`,
|
|
48545
|
-
background: hslaToHex(this.state.currentHslaColor),
|
|
48546
|
-
});
|
|
48547
|
-
}
|
|
48548
|
-
get colorPreviewStyle() {
|
|
48549
|
-
return cssPropertiesToCss({
|
|
48550
|
-
"background-color": hslaToHex(this.state.currentHslaColor),
|
|
48551
|
-
});
|
|
48552
|
-
}
|
|
48553
|
-
get checkmarkColor() {
|
|
48554
|
-
return chartFontColor(this.props.currentColor);
|
|
48555
|
-
}
|
|
48556
|
-
get isHexColorInputValid() {
|
|
48557
|
-
return !this.state.customHexColor || isColorValid(this.state.customHexColor);
|
|
48558
|
-
}
|
|
48559
|
-
setCustomGradient({ x, y }) {
|
|
48560
|
-
const offsetX = clip(x, 0, INNER_GRADIENT_WIDTH);
|
|
48561
|
-
const offsetY = clip(y, 0, INNER_GRADIENT_HEIGHT);
|
|
48562
|
-
const deltaX = offsetX / INNER_GRADIENT_WIDTH;
|
|
48563
|
-
const deltaY = offsetY / INNER_GRADIENT_HEIGHT;
|
|
48564
|
-
const s = 100 * deltaX;
|
|
48565
|
-
const l = 100 * (1 - deltaY) * (1 - 0.5 * deltaX);
|
|
48566
|
-
this.updateColor({ s, l });
|
|
48567
|
-
}
|
|
48568
|
-
setCustomHue(x) {
|
|
48569
|
-
// needs to be capped such that h is in [0°, 359°]
|
|
48570
|
-
const h = Math.round(clip((360 * x) / INNER_GRADIENT_WIDTH, 0, 359));
|
|
48571
|
-
this.updateColor({ h });
|
|
48572
|
-
}
|
|
48573
|
-
updateColor(newHsl) {
|
|
48574
|
-
this.state.currentHslaColor = { ...this.state.currentHslaColor, ...newHsl };
|
|
48575
|
-
this.state.customHexColor = hslaToHex(this.state.currentHslaColor);
|
|
48576
|
-
}
|
|
48577
|
-
onColorClick(color) {
|
|
48578
|
-
if (color) {
|
|
48579
|
-
this.props.onColorPicked(toHex(color));
|
|
48580
|
-
}
|
|
48581
|
-
}
|
|
48582
|
-
resetColor() {
|
|
48583
|
-
this.props.onColorPicked("");
|
|
48584
|
-
}
|
|
48585
|
-
toggleColorPicker() {
|
|
48586
|
-
this.state.showGradient = !this.state.showGradient;
|
|
48587
|
-
}
|
|
48588
|
-
dragGradientPointer(ev) {
|
|
48589
|
-
const initialGradientCoordinates = { x: ev.offsetX, y: ev.offsetY };
|
|
48590
|
-
this.setCustomGradient(initialGradientCoordinates);
|
|
48591
|
-
const initialMousePosition = { x: ev.clientX, y: ev.clientY };
|
|
48592
|
-
const onMouseMove = (ev) => {
|
|
48593
|
-
const currentMousePosition = { x: ev.clientX, y: ev.clientY };
|
|
48594
|
-
const deltaX = currentMousePosition.x - initialMousePosition.x;
|
|
48595
|
-
const deltaY = currentMousePosition.y - initialMousePosition.y;
|
|
48596
|
-
const currentGradientCoordinates = {
|
|
48597
|
-
x: initialGradientCoordinates.x + deltaX,
|
|
48598
|
-
y: initialGradientCoordinates.y + deltaY,
|
|
48599
|
-
};
|
|
48600
|
-
this.setCustomGradient(currentGradientCoordinates);
|
|
48601
|
-
};
|
|
48602
|
-
startDnd(onMouseMove, () => { });
|
|
48603
|
-
}
|
|
48604
|
-
dragHuePointer(ev) {
|
|
48605
|
-
const initialX = ev.offsetX;
|
|
48606
|
-
const initialMouseX = ev.clientX;
|
|
48607
|
-
this.setCustomHue(initialX);
|
|
48608
|
-
const onMouseMove = (ev) => {
|
|
48609
|
-
const currentMouseX = ev.clientX;
|
|
48610
|
-
const deltaX = currentMouseX - initialMouseX;
|
|
48611
|
-
const x = initialX + deltaX;
|
|
48612
|
-
this.setCustomHue(x);
|
|
48613
|
-
};
|
|
48614
|
-
startDnd(onMouseMove, () => { });
|
|
48615
|
-
}
|
|
48616
|
-
setHexColor(ev) {
|
|
48617
|
-
// only support HEX code input
|
|
48618
|
-
const val = ev.target.value.replace("##", "#").slice(0, 7);
|
|
48619
|
-
this.state.customHexColor = val;
|
|
48620
|
-
if (!isColorValid(val)) ;
|
|
48621
|
-
else {
|
|
48622
|
-
this.state.currentHslaColor = { ...hexToHSLA(val), a: 1 };
|
|
48623
|
-
}
|
|
48624
|
-
}
|
|
48625
|
-
addCustomColor(ev) {
|
|
48626
|
-
if (!isHSLAValid(this.state.currentHslaColor) || !isColorValid(this.state.customHexColor)) {
|
|
48627
|
-
return;
|
|
48628
|
-
}
|
|
48629
|
-
this.props.onColorPicked(toHex(this.state.customHexColor));
|
|
48630
|
-
}
|
|
48631
|
-
isSameColor(color1, color2) {
|
|
48632
|
-
return isSameColor(color1, color2);
|
|
48633
|
-
}
|
|
48634
|
-
}
|
|
48635
|
-
|
|
48636
48890
|
css /* scss */ `
|
|
48637
48891
|
.o-color-picker-widget {
|
|
48638
48892
|
display: flex;
|
|
@@ -48707,7 +48961,8 @@ css /* scss */ `
|
|
|
48707
48961
|
input.o-font-size {
|
|
48708
48962
|
outline: none;
|
|
48709
48963
|
height: 20px;
|
|
48710
|
-
width:
|
|
48964
|
+
width: 31px;
|
|
48965
|
+
text-align: center;
|
|
48711
48966
|
}
|
|
48712
48967
|
}
|
|
48713
48968
|
.o-text-options > div {
|
|
@@ -49099,57 +49354,6 @@ class RadioSelection extends Component {
|
|
|
49099
49354
|
};
|
|
49100
49355
|
}
|
|
49101
49356
|
|
|
49102
|
-
const TRANSPARENT_BACKGROUND_SVG = /*xml*/ `
|
|
49103
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10">
|
|
49104
|
-
<path fill="#d9d9d9" d="M5 5h5v5H5zH0V0h5"/>
|
|
49105
|
-
</svg>
|
|
49106
|
-
`;
|
|
49107
|
-
css /* scss */ `
|
|
49108
|
-
.o-round-color-picker-button {
|
|
49109
|
-
width: 20px;
|
|
49110
|
-
height: 20px;
|
|
49111
|
-
cursor: pointer;
|
|
49112
|
-
border: 1px solid ${GRAY_300};
|
|
49113
|
-
background-position: 1px 1px;
|
|
49114
|
-
background-image: url("data:image/svg+xml,${encodeURIComponent(TRANSPARENT_BACKGROUND_SVG)}");
|
|
49115
|
-
}
|
|
49116
|
-
`;
|
|
49117
|
-
class RoundColorPicker extends Component {
|
|
49118
|
-
static template = "o-spreadsheet.RoundColorPicker";
|
|
49119
|
-
static components = { Section, ColorPicker };
|
|
49120
|
-
static props = {
|
|
49121
|
-
currentColor: { type: String, optional: true },
|
|
49122
|
-
title: { type: String, optional: true },
|
|
49123
|
-
onColorPicked: Function,
|
|
49124
|
-
disableNoColor: { type: Boolean, optional: true },
|
|
49125
|
-
};
|
|
49126
|
-
colorPickerButtonRef = useRef("colorPickerButton");
|
|
49127
|
-
state;
|
|
49128
|
-
setup() {
|
|
49129
|
-
this.state = useState({ pickerOpened: false });
|
|
49130
|
-
useExternalListener(window, "click", this.closePicker);
|
|
49131
|
-
}
|
|
49132
|
-
closePicker() {
|
|
49133
|
-
this.state.pickerOpened = false;
|
|
49134
|
-
}
|
|
49135
|
-
togglePicker() {
|
|
49136
|
-
this.state.pickerOpened = !this.state.pickerOpened;
|
|
49137
|
-
}
|
|
49138
|
-
onColorPicked(color) {
|
|
49139
|
-
this.props.onColorPicked(color);
|
|
49140
|
-
this.state.pickerOpened = false;
|
|
49141
|
-
}
|
|
49142
|
-
get colorPickerAnchorRect() {
|
|
49143
|
-
const button = this.colorPickerButtonRef.el;
|
|
49144
|
-
return getBoundingRectAsPOJO(button);
|
|
49145
|
-
}
|
|
49146
|
-
get buttonStyle() {
|
|
49147
|
-
return cssPropertiesToCss({
|
|
49148
|
-
background: this.props.currentColor,
|
|
49149
|
-
});
|
|
49150
|
-
}
|
|
49151
|
-
}
|
|
49152
|
-
|
|
49153
49357
|
class GeneralDesignEditor extends Component {
|
|
49154
49358
|
static template = "o-spreadsheet-GeneralDesignEditor";
|
|
49155
49359
|
static components = {
|
|
@@ -53785,28 +53989,45 @@ class SpreadsheetPivotTable {
|
|
|
53785
53989
|
getNumberOfDataColumns() {
|
|
53786
53990
|
return this.columns.at(-1)?.length || 0;
|
|
53787
53991
|
}
|
|
53788
|
-
|
|
53789
|
-
const
|
|
53992
|
+
getSkippedRows(visibilityOptions) {
|
|
53993
|
+
const skippedRows = new Set();
|
|
53994
|
+
if (!visibilityOptions.displayColumnHeaders) {
|
|
53995
|
+
for (let i = 0; i < this.columns.length - 1; i++) {
|
|
53996
|
+
skippedRows.add(i);
|
|
53997
|
+
}
|
|
53998
|
+
}
|
|
53999
|
+
if (!visibilityOptions.displayMeasuresRow) {
|
|
54000
|
+
skippedRows.add(this.columns.length - 1);
|
|
54001
|
+
}
|
|
54002
|
+
return skippedRows;
|
|
54003
|
+
}
|
|
54004
|
+
getPivotCells(visibilityOptions = {
|
|
54005
|
+
displayColumnHeaders: true,
|
|
54006
|
+
displayTotals: true,
|
|
54007
|
+
displayMeasuresRow: true,
|
|
54008
|
+
}) {
|
|
54009
|
+
const key = JSON.stringify(visibilityOptions);
|
|
53790
54010
|
if (!this.pivotCells[key]) {
|
|
54011
|
+
const { displayTotals } = visibilityOptions;
|
|
53791
54012
|
const numberOfDataRows = this.rows.length;
|
|
53792
54013
|
const numberOfDataColumns = this.getNumberOfDataColumns();
|
|
53793
54014
|
let pivotHeight = this.columns.length + numberOfDataRows;
|
|
53794
54015
|
let pivotWidth = 1 /*(row headers)*/ + numberOfDataColumns;
|
|
53795
|
-
if (!
|
|
54016
|
+
if (!displayTotals && numberOfDataRows !== 1) {
|
|
53796
54017
|
pivotHeight -= 1;
|
|
53797
54018
|
}
|
|
53798
|
-
if (!
|
|
54019
|
+
if (!displayTotals && numberOfDataColumns !== this.measures.length) {
|
|
53799
54020
|
pivotWidth -= this.measures.length;
|
|
53800
54021
|
}
|
|
53801
54022
|
const domainArray = [];
|
|
53802
|
-
const
|
|
54023
|
+
const skippedRows = this.getSkippedRows(visibilityOptions);
|
|
53803
54024
|
for (let col = 0; col < pivotWidth; col++) {
|
|
53804
54025
|
domainArray.push([]);
|
|
53805
|
-
for (let row =
|
|
53806
|
-
if (
|
|
54026
|
+
for (let row = 0; row < pivotHeight; row++) {
|
|
54027
|
+
if (skippedRows.has(row)) {
|
|
53807
54028
|
continue;
|
|
53808
54029
|
}
|
|
53809
|
-
domainArray[col].push(this.getPivotCell(col, row,
|
|
54030
|
+
domainArray[col].push(this.getPivotCell(col, row, displayTotals));
|
|
53810
54031
|
}
|
|
53811
54032
|
}
|
|
53812
54033
|
this.pivotCells[key] = domainArray;
|
|
@@ -59463,7 +59684,7 @@ class DataValidationPlugin extends CorePlugin {
|
|
|
59463
59684
|
if (!rule)
|
|
59464
59685
|
return false;
|
|
59465
59686
|
return ((rule.criterion.type === "isValueInList" || rule.criterion.type === "isValueInRange") &&
|
|
59466
|
-
rule.criterion.displayStyle === "arrow");
|
|
59687
|
+
(rule.criterion.displayStyle === "arrow" || rule.criterion.displayStyle === "chip"));
|
|
59467
59688
|
}
|
|
59468
59689
|
addDataValidationRule(sheetId, newRule) {
|
|
59469
59690
|
const rules = this.rules[sheetId];
|
|
@@ -61997,7 +62218,9 @@ class TablePlugin extends CorePlugin {
|
|
|
61997
62218
|
const ranges = cmd.ranges.map((rangeData) => this.getters.getRangeFromRangeData(rangeData));
|
|
61998
62219
|
const union = this.getters.getRangesUnion(ranges);
|
|
61999
62220
|
const mergesInTarget = this.getters.getMergesInZone(cmd.sheetId, union.zone);
|
|
62000
|
-
|
|
62221
|
+
if (mergesInTarget.length) {
|
|
62222
|
+
this.dispatch("REMOVE_MERGE", { sheetId: cmd.sheetId, target: mergesInTarget });
|
|
62223
|
+
}
|
|
62001
62224
|
const id = this.consumeNextId();
|
|
62002
62225
|
const config = cmd.config || DEFAULT_TABLE_CONFIG;
|
|
62003
62226
|
const newTable = cmd.tableType === "dynamic"
|
|
@@ -62096,14 +62319,16 @@ class TablePlugin extends CorePlugin {
|
|
|
62096
62319
|
const zoneToCheckIfEmpty = direction === "down"
|
|
62097
62320
|
? { ...zone, bottom: zone.bottom + 1, top: zone.bottom + 1 }
|
|
62098
62321
|
: { ...zone, right: zone.right + 1, left: zone.right + 1 };
|
|
62099
|
-
for (
|
|
62100
|
-
|
|
62101
|
-
|
|
62102
|
-
|
|
62103
|
-
|
|
62104
|
-
|
|
62105
|
-
|
|
62106
|
-
|
|
62322
|
+
for (let row = zoneToCheckIfEmpty.top; row <= zoneToCheckIfEmpty.bottom; row++) {
|
|
62323
|
+
for (let col = zoneToCheckIfEmpty.left; col <= zoneToCheckIfEmpty.right; col++) {
|
|
62324
|
+
const cellPosition = { sheetId, col, row };
|
|
62325
|
+
// Since this plugin is loaded before CellPlugin, the getters still give us the old cell content
|
|
62326
|
+
const cellContent = this.getters.getCell(cellPosition)?.content;
|
|
62327
|
+
if (cellContent ||
|
|
62328
|
+
this.getters.isInMerge(cellPosition) ||
|
|
62329
|
+
this.getTablesOverlappingZones(sheetId, [positionToZone(cellPosition)]).length) {
|
|
62330
|
+
return "none";
|
|
62331
|
+
}
|
|
62107
62332
|
}
|
|
62108
62333
|
}
|
|
62109
62334
|
return direction;
|
|
@@ -65825,7 +66050,10 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
65825
66050
|
"getDataValidationInvalidCriterionValueMessage",
|
|
65826
66051
|
"getInvalidDataValidationMessage",
|
|
65827
66052
|
"getValidationResultForCellValue",
|
|
66053
|
+
"getDataValidationRangeValues",
|
|
65828
66054
|
"isCellValidCheckbox",
|
|
66055
|
+
"getDataValidationCellStyle",
|
|
66056
|
+
"getDataValidationChipStyle",
|
|
65829
66057
|
"isDataValidationInvalid",
|
|
65830
66058
|
];
|
|
65831
66059
|
validationResults = {};
|
|
@@ -65846,6 +66074,18 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
65846
66074
|
isDataValidationInvalid(cellPosition) {
|
|
65847
66075
|
return !this.getValidationResultForCell(cellPosition).isValid;
|
|
65848
66076
|
}
|
|
66077
|
+
getDataValidationCellStyle(position) {
|
|
66078
|
+
if (this.hasChip(position)) {
|
|
66079
|
+
return undefined; // The style is not applied on the cell if it's a chip
|
|
66080
|
+
}
|
|
66081
|
+
return this.getDataValidationStyle(position);
|
|
66082
|
+
}
|
|
66083
|
+
getDataValidationChipStyle(position) {
|
|
66084
|
+
if (this.hasChip(position)) {
|
|
66085
|
+
return this.getDataValidationStyle(position) ?? { fillColor: GRAY_200 };
|
|
66086
|
+
}
|
|
66087
|
+
return undefined;
|
|
66088
|
+
}
|
|
65849
66089
|
getInvalidDataValidationMessage(cellPosition) {
|
|
65850
66090
|
const validationResult = this.getValidationResultForCell(cellPosition);
|
|
65851
66091
|
return validationResult.isValid ? undefined : validationResult.error;
|
|
@@ -65868,6 +66108,11 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
65868
66108
|
}
|
|
65869
66109
|
return evaluator.isCriterionValueValid(value) ? undefined : evaluator.criterionValueErrorString;
|
|
65870
66110
|
}
|
|
66111
|
+
getDataValidationRangeValues(sheetId, criterion) {
|
|
66112
|
+
const range = this.getters.getRangeFromSheetXC(sheetId, String(criterion.values[0]));
|
|
66113
|
+
const criterionValues = this.getters.getRangeValues(range);
|
|
66114
|
+
return criterionValues.map((value) => value?.toString()).filter(isDefined);
|
|
66115
|
+
}
|
|
65871
66116
|
isCellValidCheckbox(cellPosition) {
|
|
65872
66117
|
if (!this.getters.isMainCellPosition(cellPosition)) {
|
|
65873
66118
|
return false;
|
|
@@ -65887,6 +66132,38 @@ class EvaluationDataValidationPlugin extends CoreViewPlugin {
|
|
|
65887
66132
|
const error = this.getRuleErrorForCellValue(cellValue, cellPosition, rule);
|
|
65888
66133
|
return error ? { error, rule, isValid: false } : VALID_RESULT;
|
|
65889
66134
|
}
|
|
66135
|
+
hasChip(position) {
|
|
66136
|
+
const rule = this.getters.getValidationRuleForCell(position);
|
|
66137
|
+
return ((rule?.criterion.type === "isValueInList" || rule?.criterion.type === "isValueInRange") &&
|
|
66138
|
+
rule.criterion.displayStyle === "chip");
|
|
66139
|
+
}
|
|
66140
|
+
getDataValidationStyle(position) {
|
|
66141
|
+
const rule = this.getters.getValidationRuleForCell(position);
|
|
66142
|
+
if (!rule || this.isDataValidationInvalid(position)) {
|
|
66143
|
+
return undefined;
|
|
66144
|
+
}
|
|
66145
|
+
const evaluatedCell = this.getters.getEvaluatedCell(position);
|
|
66146
|
+
const color = this.getValueColor(rule, evaluatedCell.value);
|
|
66147
|
+
if (!color) {
|
|
66148
|
+
return undefined;
|
|
66149
|
+
}
|
|
66150
|
+
const style = {
|
|
66151
|
+
fillColor: color,
|
|
66152
|
+
textColor: chipTextColor(color),
|
|
66153
|
+
};
|
|
66154
|
+
return style;
|
|
66155
|
+
}
|
|
66156
|
+
getValueColor(rule, value) {
|
|
66157
|
+
if (rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange") {
|
|
66158
|
+
return undefined;
|
|
66159
|
+
}
|
|
66160
|
+
for (const criterionValue in rule.criterion.colors) {
|
|
66161
|
+
if (criterionValue.toLowerCase() === String(value).toLowerCase()) {
|
|
66162
|
+
return rule.criterion.colors[criterionValue];
|
|
66163
|
+
}
|
|
66164
|
+
}
|
|
66165
|
+
return undefined;
|
|
66166
|
+
}
|
|
65890
66167
|
isValidFormula(value) {
|
|
65891
66168
|
return !compile(value).isBadExpression;
|
|
65892
66169
|
}
|
|
@@ -65983,12 +66260,35 @@ iconsOnCellRegistry.add("data_validation_checkbox", (getters, position) => {
|
|
|
65983
66260
|
}
|
|
65984
66261
|
return undefined;
|
|
65985
66262
|
});
|
|
66263
|
+
iconsOnCellRegistry.add("data_validation_chip_icon", (getters, position) => {
|
|
66264
|
+
const chipStyle = getters.getDataValidationChipStyle(position);
|
|
66265
|
+
if (chipStyle) {
|
|
66266
|
+
const cellStyle = getters.getCellComputedStyle(position);
|
|
66267
|
+
return {
|
|
66268
|
+
svg: getChipSvg(chipStyle),
|
|
66269
|
+
hoverSvg: getHoveredChipSvg(chipStyle),
|
|
66270
|
+
priority: 10,
|
|
66271
|
+
horizontalAlign: "right",
|
|
66272
|
+
size: computeTextFontSizeInPixels(cellStyle),
|
|
66273
|
+
margin: 4,
|
|
66274
|
+
position,
|
|
66275
|
+
onClick: (position, env) => {
|
|
66276
|
+
const { col, row } = position;
|
|
66277
|
+
env.model.selection.selectCell(col, row);
|
|
66278
|
+
env.startCellEdition();
|
|
66279
|
+
},
|
|
66280
|
+
type: "data_validation_chip_icon",
|
|
66281
|
+
};
|
|
66282
|
+
}
|
|
66283
|
+
return undefined;
|
|
66284
|
+
});
|
|
65986
66285
|
iconsOnCellRegistry.add("data_validation_list_icon", (getters, position) => {
|
|
65987
66286
|
const hasIcon = !getters.isReadonly() && getters.cellHasListDataValidationIcon(position);
|
|
65988
66287
|
if (hasIcon) {
|
|
66288
|
+
const cellStyle = getters.getCellComputedStyle(position);
|
|
65989
66289
|
return {
|
|
65990
|
-
svg:
|
|
65991
|
-
hoverSvg:
|
|
66290
|
+
svg: getCaretDownSvg(cellStyle),
|
|
66291
|
+
hoverSvg: getHoveredCaretDownSvg(cellStyle),
|
|
65992
66292
|
priority: 2,
|
|
65993
66293
|
horizontalAlign: "right",
|
|
65994
66294
|
size: GRID_ICON_EDGE_LENGTH,
|
|
@@ -67256,10 +67556,15 @@ class PivotUIPlugin extends CoreViewPlugin {
|
|
|
67256
67556
|
const includeTotal = toScalar(args[2]);
|
|
67257
67557
|
const shouldIncludeTotal = includeTotal === undefined ? true : toBoolean(includeTotal);
|
|
67258
67558
|
const includeColumnHeaders = toScalar(args[3]);
|
|
67559
|
+
const includeMeasures = toScalar(args[5]);
|
|
67560
|
+
const shouldIncludeMeasures = includeMeasures === undefined ? true : toBoolean(includeMeasures);
|
|
67259
67561
|
const shouldIncludeColumnHeaders = includeColumnHeaders === undefined ? true : toBoolean(includeColumnHeaders);
|
|
67260
|
-
const
|
|
67261
|
-
|
|
67262
|
-
|
|
67562
|
+
const visibilityOptions = {
|
|
67563
|
+
displayColumnHeaders: shouldIncludeColumnHeaders,
|
|
67564
|
+
displayTotals: shouldIncludeTotal,
|
|
67565
|
+
displayMeasuresRow: shouldIncludeMeasures,
|
|
67566
|
+
};
|
|
67567
|
+
const pivotCells = pivot.getCollapsedTableStructure().getPivotCells(visibilityOptions);
|
|
67263
67568
|
const pivotCol = position.col - mainPosition.col;
|
|
67264
67569
|
const pivotRow = position.row - mainPosition.row;
|
|
67265
67570
|
return pivotCells[pivotCol][pivotRow];
|
|
@@ -68525,11 +68830,11 @@ class OTRegistry extends Registry {
|
|
|
68525
68830
|
* transformation function given
|
|
68526
68831
|
*/
|
|
68527
68832
|
addTransformation(executed, toTransforms, fn) {
|
|
68833
|
+
if (!this.content[executed]) {
|
|
68834
|
+
this.content[executed] = new Map();
|
|
68835
|
+
}
|
|
68528
68836
|
for (const toTransform of toTransforms) {
|
|
68529
|
-
|
|
68530
|
-
this.content[toTransform] = new Map();
|
|
68531
|
-
}
|
|
68532
|
-
this.content[toTransform].set(executed, fn);
|
|
68837
|
+
this.content[executed].set(toTransform, fn);
|
|
68533
68838
|
}
|
|
68534
68839
|
return this;
|
|
68535
68840
|
}
|
|
@@ -68538,7 +68843,7 @@ class OTRegistry extends Registry {
|
|
|
68538
68843
|
* that the executed command happened.
|
|
68539
68844
|
*/
|
|
68540
68845
|
getTransformation(toTransform, executed) {
|
|
68541
|
-
return this.content[
|
|
68846
|
+
return this.content[executed] && this.content[executed].get(toTransform);
|
|
68542
68847
|
}
|
|
68543
68848
|
}
|
|
68544
68849
|
const otRegistry = new OTRegistry();
|
|
@@ -68868,10 +69173,20 @@ function adaptTransform(toTransform, executed) {
|
|
|
68868
69173
|
*/
|
|
68869
69174
|
function transformAll(toTransform, executed) {
|
|
68870
69175
|
let transformedCommands = [...toTransform];
|
|
69176
|
+
const possibleTransformations = new Set(otRegistry.getKeys());
|
|
68871
69177
|
for (const executedCommand of executed) {
|
|
68872
|
-
|
|
68873
|
-
|
|
68874
|
-
|
|
69178
|
+
// If the executed command is not in the registry, we skip it
|
|
69179
|
+
// because we know there won't be any transformation impacting the
|
|
69180
|
+
// commands to transform.
|
|
69181
|
+
if (possibleTransformations.has(executedCommand.type)) {
|
|
69182
|
+
transformedCommands = transformedCommands.reduce((acc, cmd) => {
|
|
69183
|
+
const transformed = transform(cmd, executedCommand);
|
|
69184
|
+
if (transformed) {
|
|
69185
|
+
acc.push(transformed);
|
|
69186
|
+
}
|
|
69187
|
+
return acc;
|
|
69188
|
+
}, []);
|
|
69189
|
+
}
|
|
68875
69190
|
}
|
|
68876
69191
|
return transformedCommands;
|
|
68877
69192
|
}
|
|
@@ -69481,8 +69796,7 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
69481
69796
|
"isFullySynchronized",
|
|
69482
69797
|
];
|
|
69483
69798
|
static layers = ["Selection"];
|
|
69484
|
-
|
|
69485
|
-
colors = {};
|
|
69799
|
+
colors = new AlternatingColorMap(12);
|
|
69486
69800
|
session;
|
|
69487
69801
|
constructor(config) {
|
|
69488
69802
|
super(config);
|
|
@@ -69500,7 +69814,7 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
69500
69814
|
}
|
|
69501
69815
|
getConnectedClients() {
|
|
69502
69816
|
return [...this.session.getConnectedClients()].map((client) => {
|
|
69503
|
-
return { ...client, color: this.colors
|
|
69817
|
+
return { ...client, color: this.colors.get(client.id) };
|
|
69504
69818
|
});
|
|
69505
69819
|
}
|
|
69506
69820
|
isFullySynchronized() {
|
|
@@ -69529,10 +69843,7 @@ class CollaborativePlugin extends UIPlugin {
|
|
|
69529
69843
|
client.position &&
|
|
69530
69844
|
client.position.sheetId === sheetId &&
|
|
69531
69845
|
this.isPositionValid(client.position)) {
|
|
69532
|
-
|
|
69533
|
-
this.colors[client.id] = this.availableColors.next();
|
|
69534
|
-
}
|
|
69535
|
-
clients.push({ ...client, color: this.colors[client.id], position: client.position });
|
|
69846
|
+
clients.push({ ...client, position: client.position });
|
|
69536
69847
|
}
|
|
69537
69848
|
}
|
|
69538
69849
|
return clients;
|
|
@@ -70432,6 +70743,9 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
70432
70743
|
for (const icon of this.getters.getCellIcons(position)) {
|
|
70433
70744
|
contentWidth += icon.margin + icon.size;
|
|
70434
70745
|
}
|
|
70746
|
+
if (this.getters.getDataValidationChipStyle(position)) {
|
|
70747
|
+
contentWidth += DATA_VALIDATION_CHIP_MARGIN * 2;
|
|
70748
|
+
}
|
|
70435
70749
|
if (contentWidth === 0) {
|
|
70436
70750
|
return 0;
|
|
70437
70751
|
}
|
|
@@ -70589,7 +70903,7 @@ class SheetUIPlugin extends UIPlugin {
|
|
|
70589
70903
|
}
|
|
70590
70904
|
const position = this.getters.getCellPosition(cell.id);
|
|
70591
70905
|
const colSize = this.getters.getColSize(sheetId, position.col);
|
|
70592
|
-
if (cell.isFormula) {
|
|
70906
|
+
if (cell.isFormula || this.getters.getArrayFormulaSpreadingOn(position)) {
|
|
70593
70907
|
const content = this.getters.getEvaluatedCell(position).formattedValue;
|
|
70594
70908
|
const evaluatedSize = getCellContentHeight(this.ctx, content, cell?.style, colSize);
|
|
70595
70909
|
if (evaluatedSize > evaluatedRowSize && evaluatedSize > DEFAULT_CELL_HEIGHT) {
|
|
@@ -70792,6 +71106,8 @@ class CellComputedStylePlugin extends UIPlugin {
|
|
|
70792
71106
|
if (invalidateEvaluationCommands.has(cmd.type) ||
|
|
70793
71107
|
cmd.type === "UPDATE_CELL" ||
|
|
70794
71108
|
cmd.type === "SET_FORMATTING" ||
|
|
71109
|
+
cmd.type === "ADD_DATA_VALIDATION_RULE" ||
|
|
71110
|
+
cmd.type === "REMOVE_DATA_VALIDATION_RULE" ||
|
|
70795
71111
|
cmd.type === "EVALUATE_CELLS") {
|
|
70796
71112
|
this.styles = {};
|
|
70797
71113
|
this.borders = {};
|
|
@@ -70863,8 +71179,10 @@ class CellComputedStylePlugin extends UIPlugin {
|
|
|
70863
71179
|
const cell = this.getters.getCell(position);
|
|
70864
71180
|
const cfStyle = this.getters.getCellConditionalFormatStyle(position);
|
|
70865
71181
|
const tableStyle = this.getters.getCellTableStyle(position);
|
|
71182
|
+
const dataValidationStyle = this.getters.getDataValidationCellStyle(position);
|
|
70866
71183
|
const computedStyle = {
|
|
70867
71184
|
...removeFalsyAttributes(tableStyle),
|
|
71185
|
+
...removeFalsyAttributes(dataValidationStyle),
|
|
70868
71186
|
...removeFalsyAttributes(cell?.style),
|
|
70869
71187
|
...removeFalsyAttributes(cfStyle),
|
|
70870
71188
|
};
|
|
@@ -72406,9 +72724,10 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
72406
72724
|
const filteredValues = filterValue.hiddenValues?.map(toLowerCase);
|
|
72407
72725
|
if (!filteredValues)
|
|
72408
72726
|
continue;
|
|
72727
|
+
const filteredValuesSet = new Set(filteredValues);
|
|
72409
72728
|
for (let row = filteredZone.top; row <= filteredZone.bottom; row++) {
|
|
72410
72729
|
const value = this.getCellValueAsString(sheetId, filter.col, row);
|
|
72411
|
-
if (
|
|
72730
|
+
if (filteredValuesSet.has(value)) {
|
|
72412
72731
|
hiddenRows.add(row);
|
|
72413
72732
|
}
|
|
72414
72733
|
}
|
|
@@ -74425,19 +74744,29 @@ autoCompleteProviders.add("dataValidation", {
|
|
|
74425
74744
|
(rule.criterion.type !== "isValueInList" && rule.criterion.type !== "isValueInRange")) {
|
|
74426
74745
|
return [];
|
|
74427
74746
|
}
|
|
74428
|
-
|
|
74429
|
-
|
|
74430
|
-
|
|
74431
|
-
|
|
74432
|
-
|
|
74433
|
-
|
|
74434
|
-
values
|
|
74435
|
-
|
|
74436
|
-
|
|
74437
|
-
|
|
74438
|
-
|
|
74439
|
-
|
|
74440
|
-
|
|
74747
|
+
const sheetId = this.composer.currentEditedCell.sheetId;
|
|
74748
|
+
const values = rule.criterion.type === "isValueInRange"
|
|
74749
|
+
? Array.from(new Set(this.getters.getDataValidationRangeValues(sheetId, rule.criterion)))
|
|
74750
|
+
: rule.criterion.values;
|
|
74751
|
+
const isChip = rule.criterion.displayStyle === "chip";
|
|
74752
|
+
if (!isChip) {
|
|
74753
|
+
return values.map((value) => ({ text: value }));
|
|
74754
|
+
}
|
|
74755
|
+
const colors = rule.criterion.colors;
|
|
74756
|
+
return values.map((value) => {
|
|
74757
|
+
const color = colors?.[value];
|
|
74758
|
+
return {
|
|
74759
|
+
text: value,
|
|
74760
|
+
htmlContent: [
|
|
74761
|
+
{
|
|
74762
|
+
value,
|
|
74763
|
+
color: color ? chipTextColor(color) : undefined,
|
|
74764
|
+
backgroundColor: color || GRAY_200,
|
|
74765
|
+
classes: ["badge rounded-pill fs-6 fw-normal w-100 mt-1 text-start"],
|
|
74766
|
+
},
|
|
74767
|
+
],
|
|
74768
|
+
};
|
|
74769
|
+
});
|
|
74441
74770
|
},
|
|
74442
74771
|
selectProposal(tokenAtCursor, value) {
|
|
74443
74772
|
this.composer.setCurrentContent(value);
|
|
@@ -75090,7 +75419,7 @@ topbarMenuRegistry
|
|
|
75090
75419
|
})
|
|
75091
75420
|
.addChild("settings", ["file"], {
|
|
75092
75421
|
name: _t("Settings"),
|
|
75093
|
-
sequence:
|
|
75422
|
+
sequence: 200,
|
|
75094
75423
|
execute: (env) => env.openSidePanel("Settings"),
|
|
75095
75424
|
isEnabled: (env) => !env.isSmall,
|
|
75096
75425
|
icon: "o-spreadsheet-Icon.COG",
|
|
@@ -77999,7 +78328,7 @@ css /* scss */ `
|
|
|
77999
78328
|
|
|
78000
78329
|
.o-spreadsheet-topbar {
|
|
78001
78330
|
line-height: 1.2;
|
|
78002
|
-
font-size:
|
|
78331
|
+
font-size: 14px;
|
|
78003
78332
|
font-weight: 500;
|
|
78004
78333
|
background-color: #fff;
|
|
78005
78334
|
|
|
@@ -83254,9 +83583,9 @@ const constants = {
|
|
|
83254
83583
|
};
|
|
83255
83584
|
const chartHelpers = { ...CHART_HELPERS, ...CHART_RUNTIME_HELPERS };
|
|
83256
83585
|
|
|
83257
|
-
export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, 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 };
|
|
83586
|
+
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 };
|
|
83258
83587
|
|
|
83259
83588
|
|
|
83260
|
-
__info__.version = "18.4.0-alpha.
|
|
83261
|
-
__info__.date = "2025-06-
|
|
83262
|
-
__info__.hash = "
|
|
83589
|
+
__info__.version = "18.4.0-alpha.9";
|
|
83590
|
+
__info__.date = "2025-06-19T18:23:22.025Z";
|
|
83591
|
+
__info__.hash = "6d4d685";
|