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