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