@odoo/o-spreadsheet 16.2.0-alpha.2 → 16.2.0-alpha.4
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 → o-spreadsheet.cjs.js} +1736 -1606
- package/dist/{o_spreadsheet.d.ts → o-spreadsheet.d.ts} +209 -201
- package/dist/{o_spreadsheet.esm.js → o-spreadsheet.esm.js} +1737 -1607
- package/dist/{o_spreadsheet.iife.js → o-spreadsheet.iife.js} +1736 -1606
- package/dist/o-spreadsheet.iife.min.js +2431 -0
- package/package.json +10 -9
- package/readme.md +3 -1
- package/dist/o_spreadsheet.iife.min.js +0 -2410
|
@@ -277,7 +277,6 @@ var ComponentsImportance;
|
|
|
277
277
|
ComponentsImportance[ComponentsImportance["Dropdown"] = 16] = "Dropdown";
|
|
278
278
|
ComponentsImportance[ComponentsImportance["GridComposer"] = 20] = "GridComposer";
|
|
279
279
|
ComponentsImportance[ComponentsImportance["TopBarComposer"] = 21] = "TopBarComposer";
|
|
280
|
-
ComponentsImportance[ComponentsImportance["ColorPicker"] = 25] = "ColorPicker";
|
|
281
280
|
ComponentsImportance[ComponentsImportance["IconPicker"] = 25] = "IconPicker";
|
|
282
281
|
ComponentsImportance[ComponentsImportance["Popover"] = 30] = "Popover";
|
|
283
282
|
ComponentsImportance[ComponentsImportance["ChartAnchor"] = 1000] = "ChartAnchor";
|
|
@@ -2030,10 +2029,10 @@ class RangeImpl {
|
|
|
2030
2029
|
if (right !== undefined && bottom !== undefined)
|
|
2031
2030
|
return { left, top, right, bottom };
|
|
2032
2031
|
else if (bottom === undefined && right !== undefined) {
|
|
2033
|
-
return { right, top, left, bottom: this.getSheetSize(this.sheetId).
|
|
2032
|
+
return { right, top, left, bottom: this.getSheetSize(this.sheetId).numberOfRows - 1 };
|
|
2034
2033
|
}
|
|
2035
2034
|
else if (right === undefined && bottom !== undefined) {
|
|
2036
|
-
return { bottom, left, top, right: this.getSheetSize(this.sheetId).
|
|
2035
|
+
return { bottom, left, top, right: this.getSheetSize(this.sheetId).numberOfCols - 1 };
|
|
2037
2036
|
}
|
|
2038
2037
|
throw new Error(_lt("Bad zone format"));
|
|
2039
2038
|
}
|
|
@@ -2656,13 +2655,13 @@ function positionsDifference(positions, toRemove) {
|
|
|
2656
2655
|
}
|
|
2657
2656
|
function zoneToDimension(zone) {
|
|
2658
2657
|
return {
|
|
2659
|
-
|
|
2660
|
-
|
|
2658
|
+
numberOfRows: zone.bottom - zone.top + 1,
|
|
2659
|
+
numberOfCols: zone.right - zone.left + 1,
|
|
2661
2660
|
};
|
|
2662
2661
|
}
|
|
2663
2662
|
function isOneDimensional(zone) {
|
|
2664
|
-
const {
|
|
2665
|
-
return
|
|
2663
|
+
const { numberOfCols, numberOfRows } = zoneToDimension(zone);
|
|
2664
|
+
return numberOfCols === 1 || numberOfRows === 1;
|
|
2666
2665
|
}
|
|
2667
2666
|
/**
|
|
2668
2667
|
* Array of all positions in the zone.
|
|
@@ -2847,7 +2846,7 @@ class ChartJsComponent extends owl.Component {
|
|
|
2847
2846
|
return `background-color: ${this.background}`;
|
|
2848
2847
|
}
|
|
2849
2848
|
get chartRuntime() {
|
|
2850
|
-
const runtime = this.env.model.getters.getChartRuntime(this.
|
|
2849
|
+
const runtime = this.env.model.getters.getChartRuntime(this.props.figure.id);
|
|
2851
2850
|
if (!("chartJsConfig" in runtime)) {
|
|
2852
2851
|
throw new Error("Unsupported chart runtime");
|
|
2853
2852
|
}
|
|
@@ -3112,7 +3111,7 @@ class ScorecardChart$1 extends owl.Component {
|
|
|
3112
3111
|
this.ctx = document.createElement("canvas").getContext("2d");
|
|
3113
3112
|
}
|
|
3114
3113
|
get runtime() {
|
|
3115
|
-
return this.env.model.getters.getChartRuntime(this.
|
|
3114
|
+
return this.env.model.getters.getChartRuntime(this.props.figure.id);
|
|
3116
3115
|
}
|
|
3117
3116
|
get title() {
|
|
3118
3117
|
return this.runtime.title;
|
|
@@ -3137,7 +3136,7 @@ class ScorecardChart$1 extends owl.Component {
|
|
|
3137
3136
|
return this.runtime.fontColor;
|
|
3138
3137
|
}
|
|
3139
3138
|
get secondaryFontColor() {
|
|
3140
|
-
return relativeLuminance(this.
|
|
3139
|
+
return relativeLuminance(this.backgroundColor) > 0.3 ? "#525252" : "#C8C8C8";
|
|
3141
3140
|
}
|
|
3142
3141
|
get figure() {
|
|
3143
3142
|
return this.props.figure;
|
|
@@ -3501,7 +3500,7 @@ exports.CommandResult = void 0;
|
|
|
3501
3500
|
CommandResult[CommandResult["InvalidRange"] = 25] = "InvalidRange";
|
|
3502
3501
|
CommandResult[CommandResult["InvalidZones"] = 26] = "InvalidZones";
|
|
3503
3502
|
CommandResult[CommandResult["InvalidSheetId"] = 27] = "InvalidSheetId";
|
|
3504
|
-
CommandResult[CommandResult["
|
|
3503
|
+
CommandResult[CommandResult["InvalidFigureId"] = 28] = "InvalidFigureId";
|
|
3505
3504
|
CommandResult[CommandResult["InputAlreadyFocused"] = 29] = "InputAlreadyFocused";
|
|
3506
3505
|
CommandResult[CommandResult["MaximumRangesReached"] = 30] = "MaximumRangesReached";
|
|
3507
3506
|
CommandResult[CommandResult["InvalidChartDefinition"] = 31] = "InvalidChartDefinition";
|
|
@@ -3558,6 +3557,7 @@ exports.CommandResult = void 0;
|
|
|
3558
3557
|
CommandResult[CommandResult["DuplicatedFigureId"] = 82] = "DuplicatedFigureId";
|
|
3559
3558
|
CommandResult[CommandResult["InvalidSelectionStep"] = 83] = "InvalidSelectionStep";
|
|
3560
3559
|
CommandResult[CommandResult["DuplicatedChartId"] = 84] = "DuplicatedChartId";
|
|
3560
|
+
CommandResult[CommandResult["ChartDoesNotExist"] = 85] = "ChartDoesNotExist";
|
|
3561
3561
|
})(exports.CommandResult || (exports.CommandResult = {}));
|
|
3562
3562
|
|
|
3563
3563
|
var DIRECTION;
|
|
@@ -3748,11 +3748,11 @@ function toExcelDataset(getters, ds) {
|
|
|
3748
3748
|
const labelZone = (_a = ds.labelCell) === null || _a === void 0 ? void 0 : _a.zone;
|
|
3749
3749
|
let dataZone = ds.dataRange.zone;
|
|
3750
3750
|
if (labelZone) {
|
|
3751
|
-
const {
|
|
3752
|
-
if (
|
|
3751
|
+
const { numberOfRows, numberOfCols } = zoneToDimension(dataZone);
|
|
3752
|
+
if (numberOfRows === 1) {
|
|
3753
3753
|
dataZone = { ...dataZone, left: dataZone.left + 1 };
|
|
3754
3754
|
}
|
|
3755
|
-
else if (
|
|
3755
|
+
else if (numberOfCols === 1) {
|
|
3756
3756
|
dataZone = { ...dataZone, top: dataZone.top + 1 };
|
|
3757
3757
|
}
|
|
3758
3758
|
}
|
|
@@ -5465,6 +5465,76 @@ chartComponentRegistry.add("pie", ChartJsComponent);
|
|
|
5465
5465
|
chartComponentRegistry.add("gauge", ChartJsComponent);
|
|
5466
5466
|
chartComponentRegistry.add("scorecard", ScorecardChart$1);
|
|
5467
5467
|
|
|
5468
|
+
// -----------------------------------------------------------------------------
|
|
5469
|
+
// STYLE
|
|
5470
|
+
// -----------------------------------------------------------------------------
|
|
5471
|
+
css /* scss */ `
|
|
5472
|
+
.o-chart-container {
|
|
5473
|
+
width: 100%;
|
|
5474
|
+
height: 100%;
|
|
5475
|
+
position: relative;
|
|
5476
|
+
}
|
|
5477
|
+
`;
|
|
5478
|
+
class ChartFigure extends owl.Component {
|
|
5479
|
+
get chartType() {
|
|
5480
|
+
return this.env.model.getters.getChartType(this.props.figure.id);
|
|
5481
|
+
}
|
|
5482
|
+
get chartComponent() {
|
|
5483
|
+
const type = this.chartType;
|
|
5484
|
+
const component = chartComponentRegistry.get(type);
|
|
5485
|
+
if (!component) {
|
|
5486
|
+
throw new Error(`Component is not defined for type ${type}`);
|
|
5487
|
+
}
|
|
5488
|
+
return component;
|
|
5489
|
+
}
|
|
5490
|
+
}
|
|
5491
|
+
ChartFigure.template = "o-spreadsheet-ChartFigure";
|
|
5492
|
+
ChartFigure.components = {};
|
|
5493
|
+
ChartFigure.props = {
|
|
5494
|
+
figure: Object,
|
|
5495
|
+
onFigureDeleted: Function,
|
|
5496
|
+
};
|
|
5497
|
+
|
|
5498
|
+
function interactiveCut(env) {
|
|
5499
|
+
const result = env.model.dispatch("CUT");
|
|
5500
|
+
if (!result.isSuccessful) {
|
|
5501
|
+
if (result.isCancelledBecause(19 /* CommandResult.WrongCutSelection */)) {
|
|
5502
|
+
env.raiseError(_lt("This operation is not allowed with multiple selections."));
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
|
|
5507
|
+
const PasteInteractiveContent = {
|
|
5508
|
+
wrongPasteSelection: _lt("This operation is not allowed with multiple selections."),
|
|
5509
|
+
willRemoveExistingMerge: _lt("This operation is not possible due to a merge. Please remove the merges first than try again."),
|
|
5510
|
+
wrongFigurePasteOption: _lt("Cannot do a special paste of a figure."),
|
|
5511
|
+
frozenPaneOverlap: _lt("Cannot paste merged cells over a frozen pane."),
|
|
5512
|
+
};
|
|
5513
|
+
function handlePasteResult(env, result) {
|
|
5514
|
+
if (!result.isSuccessful) {
|
|
5515
|
+
if (result.reasons.includes(20 /* CommandResult.WrongPasteSelection */)) {
|
|
5516
|
+
env.raiseError(PasteInteractiveContent.wrongPasteSelection);
|
|
5517
|
+
}
|
|
5518
|
+
else if (result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
|
|
5519
|
+
env.raiseError(PasteInteractiveContent.willRemoveExistingMerge);
|
|
5520
|
+
}
|
|
5521
|
+
else if (result.reasons.includes(22 /* CommandResult.WrongFigurePasteOption */)) {
|
|
5522
|
+
env.raiseError(PasteInteractiveContent.wrongFigurePasteOption);
|
|
5523
|
+
}
|
|
5524
|
+
else if (result.reasons.includes(75 /* CommandResult.FrozenPaneOverlap */)) {
|
|
5525
|
+
env.raiseError(PasteInteractiveContent.frozenPaneOverlap);
|
|
5526
|
+
}
|
|
5527
|
+
}
|
|
5528
|
+
}
|
|
5529
|
+
function interactivePaste(env, target, pasteOption) {
|
|
5530
|
+
const result = env.model.dispatch("PASTE", { target, pasteOption });
|
|
5531
|
+
handlePasteResult(env, result);
|
|
5532
|
+
}
|
|
5533
|
+
function interactivePasteFromOS(env, target, text) {
|
|
5534
|
+
const result = env.model.dispatch("PASTE_FROM_OS_CLIPBOARD", { target, text });
|
|
5535
|
+
handlePasteResult(env, result);
|
|
5536
|
+
}
|
|
5537
|
+
|
|
5468
5538
|
function createMenu(menuItems) {
|
|
5469
5539
|
return menuItems.map(createMenuItem).sort((a, b) => a.sequence - b.sequence);
|
|
5470
5540
|
}
|
|
@@ -5545,984 +5615,281 @@ class MenuItemRegistry extends Registry {
|
|
|
5545
5615
|
}
|
|
5546
5616
|
|
|
5547
5617
|
/**
|
|
5548
|
-
*
|
|
5549
|
-
* to the browser viewport.
|
|
5618
|
+
* Create a function used to create a Chart based on the definition
|
|
5550
5619
|
*/
|
|
5551
|
-
function
|
|
5552
|
-
const
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
spreadsheetElement = document.querySelector(".o-spreadsheet");
|
|
5558
|
-
}
|
|
5559
|
-
if (spreadsheetElement) {
|
|
5560
|
-
const { top, left, width, height } = spreadsheetElement.getBoundingClientRect();
|
|
5561
|
-
position.x = left;
|
|
5562
|
-
position.y = top;
|
|
5563
|
-
position.width = width;
|
|
5564
|
-
position.height = height;
|
|
5620
|
+
function chartFactory(getters) {
|
|
5621
|
+
const builders = chartRegistry.getAll().sort((a, b) => a.sequence - b.sequence);
|
|
5622
|
+
function createChart(id, definition, sheetId) {
|
|
5623
|
+
const builder = builders.find((builder) => builder.match(definition.type));
|
|
5624
|
+
if (!builder) {
|
|
5625
|
+
throw new Error(`No builder for this chart: ${definition.type}`);
|
|
5565
5626
|
}
|
|
5627
|
+
return builder.createChart(definition, sheetId, getters);
|
|
5566
5628
|
}
|
|
5567
|
-
|
|
5568
|
-
owl.onPatched(updatePosition);
|
|
5569
|
-
return position;
|
|
5629
|
+
return createChart;
|
|
5570
5630
|
}
|
|
5571
5631
|
/**
|
|
5572
|
-
*
|
|
5573
|
-
*
|
|
5574
|
-
*
|
|
5575
|
-
* Note: when used with a <Portal/> component, it will
|
|
5576
|
-
* return the portal position, not the teleported position.
|
|
5632
|
+
* Create a function used to create a Chart Runtime based on the chart class
|
|
5633
|
+
* instance
|
|
5577
5634
|
*/
|
|
5578
|
-
function
|
|
5579
|
-
const
|
|
5580
|
-
function
|
|
5581
|
-
const
|
|
5582
|
-
if (
|
|
5583
|
-
|
|
5635
|
+
function chartRuntimeFactory(getters) {
|
|
5636
|
+
const builders = chartRegistry.getAll().sort((a, b) => a.sequence - b.sequence);
|
|
5637
|
+
function createRuntimeChart(chart) {
|
|
5638
|
+
const builder = builders.find((builder) => builder.match(chart.type));
|
|
5639
|
+
if (!builder) {
|
|
5640
|
+
throw new Error("No runtime builder for this chart.");
|
|
5584
5641
|
}
|
|
5585
|
-
|
|
5586
|
-
rect.x = left;
|
|
5587
|
-
rect.y = top;
|
|
5588
|
-
rect.width = width;
|
|
5589
|
-
rect.height = height;
|
|
5642
|
+
return builder.getChartRuntime(chart, getters);
|
|
5590
5643
|
}
|
|
5591
|
-
|
|
5592
|
-
owl.onPatched(updateElRect);
|
|
5593
|
-
return rect;
|
|
5644
|
+
return createRuntimeChart;
|
|
5594
5645
|
}
|
|
5595
5646
|
/**
|
|
5596
|
-
*
|
|
5597
|
-
* It's the value defined in `env.getPopoverContainerRect`, or the Rect of the "o-spreadsheet"
|
|
5598
|
-
* element by default.
|
|
5599
|
-
*
|
|
5600
|
-
* Coordinates are expressed expressed as absolute DOM position.
|
|
5647
|
+
* Validate the chart definition given in arguments
|
|
5601
5648
|
*/
|
|
5602
|
-
function
|
|
5603
|
-
const
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
function updateRect() {
|
|
5607
|
-
const env = component.env;
|
|
5608
|
-
const newRect = "getPopoverContainerRect" in env ? env.getPopoverContainerRect() : spreadsheetRect;
|
|
5609
|
-
container.x = newRect.x;
|
|
5610
|
-
container.y = newRect.y;
|
|
5611
|
-
container.width = newRect.width;
|
|
5612
|
-
container.height = newRect.height;
|
|
5649
|
+
function validateChartDefinition(validator, definition) {
|
|
5650
|
+
const validators = chartRegistry.getAll().find((validator) => validator.match(definition.type));
|
|
5651
|
+
if (!validators) {
|
|
5652
|
+
throw new Error("Unknown chart type.");
|
|
5613
5653
|
}
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
owl.onPatched(updateRect);
|
|
5617
|
-
return container;
|
|
5618
|
-
}
|
|
5619
|
-
|
|
5654
|
+
return validators.validateChartDefinition(validator, definition);
|
|
5655
|
+
}
|
|
5620
5656
|
/**
|
|
5621
|
-
*
|
|
5622
|
-
*
|
|
5657
|
+
* Get a new chart definition transformed with the executed command. This
|
|
5658
|
+
* functions will be called during operational transform process
|
|
5623
5659
|
*/
|
|
5624
|
-
function
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
const spreadsheetElement = document.querySelector(".o-grid-overlay");
|
|
5629
|
-
if (spreadsheetElement) {
|
|
5630
|
-
const { top, left } = spreadsheetElement === null || spreadsheetElement === void 0 ? void 0 : spreadsheetElement.getBoundingClientRect();
|
|
5631
|
-
return { top, left };
|
|
5660
|
+
function transformDefinition(definition, executed) {
|
|
5661
|
+
const transformation = chartRegistry.getAll().find((factory) => factory.match(definition.type));
|
|
5662
|
+
if (!transformation) {
|
|
5663
|
+
throw new Error("Unknown chart type.");
|
|
5632
5664
|
}
|
|
5633
|
-
|
|
5634
|
-
}
|
|
5635
|
-
function getBoundingRectAsPOJO(el) {
|
|
5636
|
-
const rect = el.getBoundingClientRect();
|
|
5637
|
-
return {
|
|
5638
|
-
x: rect.x,
|
|
5639
|
-
y: rect.y,
|
|
5640
|
-
width: rect.width,
|
|
5641
|
-
height: rect.height,
|
|
5642
|
-
};
|
|
5665
|
+
return transformation.transformDefinition(definition, executed);
|
|
5643
5666
|
}
|
|
5644
5667
|
/**
|
|
5645
|
-
*
|
|
5668
|
+
* Get an empty definition based on the given context and the given type
|
|
5646
5669
|
*/
|
|
5647
|
-
function
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
for (let child of el.childNodes) {
|
|
5651
|
-
yield* iterateChildren(child);
|
|
5652
|
-
}
|
|
5653
|
-
}
|
|
5654
|
-
}
|
|
5655
|
-
function getElementScrollTop(el) {
|
|
5656
|
-
return (el === null || el === void 0 ? void 0 : el.scrollTop) || 0;
|
|
5670
|
+
function getChartDefinitionFromContextCreation(context, type) {
|
|
5671
|
+
const chartClass = chartRegistry.get(type);
|
|
5672
|
+
return chartClass.getChartDefinitionFromContextCreation(context);
|
|
5657
5673
|
}
|
|
5658
|
-
function
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5674
|
+
function getChartTypes() {
|
|
5675
|
+
const result = {};
|
|
5676
|
+
for (const key of chartRegistry.getKeys()) {
|
|
5677
|
+
result[key] = chartRegistry.get(key).name;
|
|
5678
|
+
}
|
|
5679
|
+
return result;
|
|
5662
5680
|
}
|
|
5663
|
-
function getOpenedMenus() {
|
|
5664
|
-
return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
|
|
5665
|
-
}
|
|
5666
|
-
|
|
5667
5681
|
/**
|
|
5668
|
-
*
|
|
5682
|
+
* Return a "smart" chart definition in the given zone. The definition is "smart" because it will
|
|
5683
|
+
* use the best type of chart to display the data of the zone.
|
|
5684
|
+
*
|
|
5685
|
+
* It will also try to find labels and datasets in the range, and try to find title for the datasets.
|
|
5686
|
+
*
|
|
5687
|
+
* The type of chart will be :
|
|
5688
|
+
* - If the zone is a single non-empty cell, returns a scorecard
|
|
5689
|
+
* - If the all the labels are numbers/date, returns a line chart
|
|
5690
|
+
* - Else returns a bar chart
|
|
5669
5691
|
*/
|
|
5670
|
-
function
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5692
|
+
function getSmartChartDefinition(zone, getters) {
|
|
5693
|
+
var _a;
|
|
5694
|
+
let dataSetZone = zone;
|
|
5695
|
+
if (zone.left !== zone.right) {
|
|
5696
|
+
dataSetZone = { ...zone, left: zone.left + 1 };
|
|
5697
|
+
}
|
|
5698
|
+
const dataSets = [zoneToXc(dataSetZone)];
|
|
5699
|
+
const sheetId = getters.getActiveSheetId();
|
|
5700
|
+
const topLeftCell = getters.getCell({ sheetId, col: zone.left, row: zone.top });
|
|
5701
|
+
if (getZoneArea(zone) === 1 && (topLeftCell === null || topLeftCell === void 0 ? void 0 : topLeftCell.content)) {
|
|
5702
|
+
return {
|
|
5703
|
+
type: "scorecard",
|
|
5704
|
+
title: "",
|
|
5705
|
+
background: ((_a = topLeftCell.style) === null || _a === void 0 ? void 0 : _a.fillColor) || undefined,
|
|
5706
|
+
keyValue: zoneToXc(zone),
|
|
5707
|
+
baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
|
|
5708
|
+
baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP,
|
|
5709
|
+
baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
|
|
5710
|
+
};
|
|
5711
|
+
}
|
|
5712
|
+
let title = "";
|
|
5713
|
+
const cellsInFirstRow = getters.getEvaluatedCellsInZone(sheetId, {
|
|
5714
|
+
...dataSetZone,
|
|
5715
|
+
bottom: dataSetZone.top,
|
|
5716
|
+
});
|
|
5717
|
+
const dataSetsHaveTitle = !!cellsInFirstRow.find((cell) => cell.type !== CellValueType.empty && cell.type !== CellValueType.number);
|
|
5718
|
+
if (dataSetsHaveTitle) {
|
|
5719
|
+
const texts = cellsInFirstRow
|
|
5720
|
+
.filter((cell) => cell.type !== CellValueType.error && cell.type !== CellValueType.empty)
|
|
5721
|
+
.map((cell) => cell.formattedValue);
|
|
5722
|
+
const lastElement = texts.splice(-1)[0];
|
|
5723
|
+
title = texts.join(", ");
|
|
5724
|
+
if (lastElement) {
|
|
5725
|
+
title += (title ? " " + _t("and") + " " : "") + lastElement;
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
let labelRangeXc;
|
|
5729
|
+
if (zone.left !== zone.right) {
|
|
5730
|
+
labelRangeXc = zoneToXc({
|
|
5731
|
+
...zone,
|
|
5732
|
+
right: zone.left,
|
|
5733
|
+
top: dataSetsHaveTitle ? zone.top + 1 : zone.top,
|
|
5734
|
+
});
|
|
5735
|
+
}
|
|
5736
|
+
// Only display legend for several datasets.
|
|
5737
|
+
const newLegendPos = dataSetZone.right === dataSetZone.left ? "none" : "top";
|
|
5738
|
+
const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
|
|
5739
|
+
if (canChartParseLabels(labelRange, getters)) {
|
|
5740
|
+
return {
|
|
5741
|
+
title,
|
|
5742
|
+
dataSets,
|
|
5743
|
+
labelsAsText: false,
|
|
5744
|
+
stacked: false,
|
|
5745
|
+
aggregated: false,
|
|
5746
|
+
labelRange: labelRangeXc,
|
|
5747
|
+
type: "line",
|
|
5748
|
+
dataSetsHaveTitle,
|
|
5749
|
+
verticalAxisPosition: "left",
|
|
5750
|
+
legendPosition: newLegendPos,
|
|
5751
|
+
};
|
|
5752
|
+
}
|
|
5674
5753
|
return {
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5754
|
+
title,
|
|
5755
|
+
dataSets,
|
|
5756
|
+
labelRange: labelRangeXc,
|
|
5757
|
+
type: "bar",
|
|
5758
|
+
stacked: false,
|
|
5759
|
+
aggregated: false,
|
|
5760
|
+
dataSetsHaveTitle,
|
|
5761
|
+
verticalAxisPosition: "left",
|
|
5762
|
+
legendPosition: newLegendPos,
|
|
5679
5763
|
};
|
|
5764
|
+
}
|
|
5765
|
+
|
|
5766
|
+
function centerFigurePosition(getters, size) {
|
|
5767
|
+
const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates();
|
|
5768
|
+
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
5769
|
+
const dim = getters.getSheetViewDimension();
|
|
5770
|
+
const rect = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
5771
|
+
const scrollableViewportWidth = Math.min(rect.width, dim.width - offsetCorrectionX);
|
|
5772
|
+
const scrollableViewportHeight = Math.min(rect.height, dim.height - offsetCorrectionY);
|
|
5773
|
+
const position = {
|
|
5774
|
+
x: offsetCorrectionX + scrollX + Math.max(0, (scrollableViewportWidth - size.width) / 2),
|
|
5775
|
+
y: offsetCorrectionY + scrollY + Math.max(0, (scrollableViewportHeight - size.height) / 2),
|
|
5776
|
+
}; // Position at the center of the scrollable viewport
|
|
5777
|
+
return position;
|
|
5680
5778
|
}
|
|
5681
|
-
function
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5779
|
+
function getMaxFigureSize(getters, figureSize) {
|
|
5780
|
+
const size = deepCopy(figureSize);
|
|
5781
|
+
const dim = getters.getSheetViewDimension();
|
|
5782
|
+
const maxWidth = dim.width;
|
|
5783
|
+
const maxHeight = dim.height;
|
|
5784
|
+
if (size.width > maxWidth) {
|
|
5785
|
+
const ratio = maxWidth / size.width;
|
|
5786
|
+
size.width = maxWidth;
|
|
5787
|
+
size.height = size.height * ratio;
|
|
5788
|
+
}
|
|
5789
|
+
if (size.height > maxHeight) {
|
|
5790
|
+
const ratio = maxHeight / size.height;
|
|
5791
|
+
size.height = maxHeight;
|
|
5792
|
+
size.width = size.width * ratio;
|
|
5793
|
+
}
|
|
5794
|
+
return size;
|
|
5690
5795
|
}
|
|
5691
5796
|
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5797
|
+
const SORT_TYPES = [
|
|
5798
|
+
CellValueType.number,
|
|
5799
|
+
CellValueType.error,
|
|
5800
|
+
CellValueType.text,
|
|
5801
|
+
CellValueType.boolean,
|
|
5802
|
+
];
|
|
5803
|
+
function sortCells(cells, sortDirection, emptyCellAsZero) {
|
|
5804
|
+
const cellsWithIndex = cells.map((cell, index) => ({
|
|
5805
|
+
index,
|
|
5806
|
+
type: cell.type,
|
|
5807
|
+
value: cell.value,
|
|
5808
|
+
}));
|
|
5809
|
+
let emptyCells = cellsWithIndex.filter((x) => x.type === CellValueType.empty);
|
|
5810
|
+
let nonEmptyCells = cellsWithIndex.filter((x) => x.type !== CellValueType.empty);
|
|
5811
|
+
if (emptyCellAsZero) {
|
|
5812
|
+
nonEmptyCells.push(...emptyCells.map((emptyCell) => ({ ...emptyCell, type: CellValueType.number, value: 0 })));
|
|
5813
|
+
emptyCells = [];
|
|
5709
5814
|
}
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
throw new Error("Popover container is not defined");
|
|
5718
|
-
const el = this.popoverRef.el;
|
|
5719
|
-
const anchor = rectIntersection(this.props.anchorRect, this.containerRect);
|
|
5720
|
-
const newDisplay = anchor ? "block" : "none";
|
|
5721
|
-
if (this.currentDisplayValue !== "none" && newDisplay === "none") {
|
|
5722
|
-
(_b = (_a = this.props).onPopoverHidden) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
5815
|
+
const inverse = sortDirection === "descending" ? -1 : 1;
|
|
5816
|
+
return nonEmptyCells
|
|
5817
|
+
.sort((left, right) => {
|
|
5818
|
+
let typeOrder = SORT_TYPES.indexOf(left.type) - SORT_TYPES.indexOf(right.type);
|
|
5819
|
+
if (typeOrder === 0) {
|
|
5820
|
+
if (left.type === CellValueType.text || left.type === CellValueType.error) {
|
|
5821
|
+
typeOrder = left.value.localeCompare(right.value);
|
|
5723
5822
|
}
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5823
|
+
else
|
|
5824
|
+
typeOrder = left.value - right.value;
|
|
5825
|
+
}
|
|
5826
|
+
return inverse * typeOrder;
|
|
5827
|
+
})
|
|
5828
|
+
.concat(emptyCells);
|
|
5829
|
+
}
|
|
5830
|
+
function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
|
|
5831
|
+
let result = DispatchResult.Success;
|
|
5832
|
+
//several columns => bypass the contiguity check
|
|
5833
|
+
let multiColumns = zone.right > zone.left;
|
|
5834
|
+
if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
|
|
5835
|
+
multiColumns = false;
|
|
5836
|
+
let table;
|
|
5837
|
+
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
5838
|
+
table = [];
|
|
5839
|
+
for (let col = zone.left; col <= zone.right; col++) {
|
|
5840
|
+
let merge = env.model.getters.getMerge({ sheetId, col, row });
|
|
5841
|
+
if (merge && !table.includes(merge.id.toString())) {
|
|
5842
|
+
table.push(merge.id.toString());
|
|
5843
|
+
}
|
|
5740
5844
|
}
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5845
|
+
if (table.length >= 2) {
|
|
5846
|
+
multiColumns = true;
|
|
5847
|
+
break;
|
|
5744
5848
|
}
|
|
5745
|
-
|
|
5746
|
-
});
|
|
5849
|
+
}
|
|
5747
5850
|
}
|
|
5748
|
-
}
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
positioning: "BottomLeft",
|
|
5752
|
-
verticalOffset: 0,
|
|
5753
|
-
onMouseWheel: () => { },
|
|
5754
|
-
onPopoverMoved: () => { },
|
|
5755
|
-
onPopoverHidden: () => { },
|
|
5756
|
-
};
|
|
5757
|
-
Popover.props = {
|
|
5758
|
-
anchorRect: Object,
|
|
5759
|
-
containerRect: { type: Object, optional: true },
|
|
5760
|
-
positioning: { type: String, optional: true },
|
|
5761
|
-
maxWidth: { type: Number, optional: true },
|
|
5762
|
-
maxHeight: { type: Number, optional: true },
|
|
5763
|
-
verticalOffset: { type: Number, optional: true },
|
|
5764
|
-
onMouseWheel: { type: Function, optional: true },
|
|
5765
|
-
onPopoverHidden: { type: Function, optional: true },
|
|
5766
|
-
onPopoverMoved: { type: Function, optional: true },
|
|
5767
|
-
slots: Object,
|
|
5768
|
-
};
|
|
5769
|
-
class PopoverPositionContext {
|
|
5770
|
-
constructor(anchorRect, containerRect, propsMaxSize, spreadsheetOffset) {
|
|
5771
|
-
this.anchorRect = anchorRect;
|
|
5772
|
-
this.containerRect = containerRect;
|
|
5773
|
-
this.propsMaxSize = propsMaxSize;
|
|
5774
|
-
this.spreadsheetOffset = spreadsheetOffset;
|
|
5851
|
+
const { col, row } = anchor;
|
|
5852
|
+
if (multiColumns) {
|
|
5853
|
+
result = env.model.dispatch("SORT_CELLS", { sheetId, col, row, zone, sortDirection });
|
|
5775
5854
|
}
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5855
|
+
else {
|
|
5856
|
+
// check contiguity
|
|
5857
|
+
const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
|
|
5858
|
+
if (isEqual(contiguousZone, zone)) {
|
|
5859
|
+
// merge as it is
|
|
5860
|
+
result = env.model.dispatch("SORT_CELLS", {
|
|
5861
|
+
sheetId,
|
|
5862
|
+
col,
|
|
5863
|
+
row,
|
|
5864
|
+
zone,
|
|
5865
|
+
sortDirection,
|
|
5866
|
+
});
|
|
5867
|
+
}
|
|
5868
|
+
else {
|
|
5869
|
+
env.askConfirmation(_lt("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => {
|
|
5870
|
+
zone = contiguousZone;
|
|
5871
|
+
result = env.model.dispatch("SORT_CELLS", {
|
|
5872
|
+
sheetId,
|
|
5873
|
+
col,
|
|
5874
|
+
row,
|
|
5875
|
+
zone,
|
|
5876
|
+
sortDirection,
|
|
5877
|
+
});
|
|
5878
|
+
}, () => {
|
|
5879
|
+
result = env.model.dispatch("SORT_CELLS", {
|
|
5880
|
+
sheetId,
|
|
5881
|
+
col,
|
|
5882
|
+
row,
|
|
5883
|
+
zone,
|
|
5884
|
+
sortDirection,
|
|
5885
|
+
});
|
|
5886
|
+
});
|
|
5887
|
+
}
|
|
5780
5888
|
}
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
}
|
|
5786
|
-
getMaxHeight(elementHeight) {
|
|
5787
|
-
const shouldRenderAtBottom = this.shouldRenderAtBottom(elementHeight);
|
|
5788
|
-
const availableHeight = shouldRenderAtBottom
|
|
5789
|
-
? this.availableHeightDown
|
|
5790
|
-
: this.availableHeightUp;
|
|
5791
|
-
return this.propsMaxSize.height
|
|
5792
|
-
? Math.min(availableHeight, this.propsMaxSize.height)
|
|
5793
|
-
: availableHeight;
|
|
5794
|
-
}
|
|
5795
|
-
getMaxWidth(elementWidth) {
|
|
5796
|
-
const shouldRenderAtRight = this.shouldRenderAtRight(elementWidth);
|
|
5797
|
-
const availableWidth = shouldRenderAtRight ? this.availableWidthRight : this.availableWidthLeft;
|
|
5798
|
-
return this.propsMaxSize.width
|
|
5799
|
-
? Math.min(availableWidth, this.propsMaxSize.width)
|
|
5800
|
-
: availableWidth;
|
|
5801
|
-
}
|
|
5802
|
-
getCss(elDims, verticalOffset) {
|
|
5803
|
-
const maxHeight = this.getMaxHeight(elDims.height);
|
|
5804
|
-
const maxWidth = this.getMaxWidth(elDims.width);
|
|
5805
|
-
const actualHeight = Math.min(maxHeight, elDims.height);
|
|
5806
|
-
const actualWidth = Math.min(maxWidth, elDims.width);
|
|
5807
|
-
const shouldRenderAtBottom = this.shouldRenderAtBottom(elDims.height);
|
|
5808
|
-
const shouldRenderAtRight = this.shouldRenderAtRight(elDims.width);
|
|
5809
|
-
verticalOffset = shouldRenderAtBottom ? verticalOffset : -verticalOffset;
|
|
5810
|
-
const cssProperties = {
|
|
5811
|
-
"max-height": maxHeight + "px",
|
|
5812
|
-
"max-width": maxWidth + "px",
|
|
5813
|
-
top: this.getTopCoordinate(actualHeight, shouldRenderAtBottom) -
|
|
5814
|
-
this.spreadsheetOffset.y -
|
|
5815
|
-
verticalOffset +
|
|
5816
|
-
"px",
|
|
5817
|
-
left: this.getLeftCoordinate(actualWidth, shouldRenderAtRight) - this.spreadsheetOffset.x + "px",
|
|
5818
|
-
};
|
|
5819
|
-
return cssProperties;
|
|
5820
|
-
}
|
|
5821
|
-
getCurrentPosition(elDims) {
|
|
5822
|
-
const shouldRenderAtBottom = this.shouldRenderAtBottom(elDims.height);
|
|
5823
|
-
const shouldRenderAtRight = this.shouldRenderAtRight(elDims.width);
|
|
5824
|
-
if (shouldRenderAtBottom && shouldRenderAtRight)
|
|
5825
|
-
return "BottomRight";
|
|
5826
|
-
if (shouldRenderAtBottom && !shouldRenderAtRight)
|
|
5827
|
-
return "BottomLeft";
|
|
5828
|
-
if (!shouldRenderAtBottom && shouldRenderAtRight)
|
|
5829
|
-
return "TopRight";
|
|
5830
|
-
return "TopLeft";
|
|
5831
|
-
}
|
|
5832
|
-
}
|
|
5833
|
-
class BottomLeftPopoverContext extends PopoverPositionContext {
|
|
5834
|
-
get availableHeightUp() {
|
|
5835
|
-
return this.anchorRect.y - this.containerRect.y;
|
|
5836
|
-
}
|
|
5837
|
-
get availableHeightDown() {
|
|
5838
|
-
return this.containerRect.height - this.availableHeightUp - this.anchorRect.height;
|
|
5839
|
-
}
|
|
5840
|
-
get availableWidthRight() {
|
|
5841
|
-
return this.containerRect.x + this.containerRect.width - this.anchorRect.x;
|
|
5842
|
-
}
|
|
5843
|
-
get availableWidthLeft() {
|
|
5844
|
-
return this.anchorRect.x + this.anchorRect.width - this.containerRect.x;
|
|
5845
|
-
}
|
|
5846
|
-
getTopCoordinate(elementHeight, shouldRenderAtBottom) {
|
|
5847
|
-
if (shouldRenderAtBottom) {
|
|
5848
|
-
return this.anchorRect.y + this.anchorRect.height;
|
|
5849
|
-
}
|
|
5850
|
-
else {
|
|
5851
|
-
return this.anchorRect.y - elementHeight;
|
|
5852
|
-
}
|
|
5853
|
-
}
|
|
5854
|
-
getLeftCoordinate(elementWidth, shouldRenderAtRight) {
|
|
5855
|
-
if (shouldRenderAtRight) {
|
|
5856
|
-
return this.anchorRect.x;
|
|
5857
|
-
}
|
|
5858
|
-
else {
|
|
5859
|
-
return this.anchorRect.x + this.anchorRect.width - elementWidth;
|
|
5860
|
-
}
|
|
5861
|
-
}
|
|
5862
|
-
}
|
|
5863
|
-
class TopRightPopoverContext extends PopoverPositionContext {
|
|
5864
|
-
get availableHeightUp() {
|
|
5865
|
-
return this.anchorRect.y + this.anchorRect.height - this.containerRect.y;
|
|
5866
|
-
}
|
|
5867
|
-
get availableHeightDown() {
|
|
5868
|
-
return this.containerRect.y + this.containerRect.height - this.anchorRect.y;
|
|
5869
|
-
}
|
|
5870
|
-
get availableWidthRight() {
|
|
5871
|
-
return this.containerRect.width - this.anchorRect.width - this.availableWidthLeft;
|
|
5872
|
-
}
|
|
5873
|
-
get availableWidthLeft() {
|
|
5874
|
-
return this.anchorRect.x - this.containerRect.x;
|
|
5875
|
-
}
|
|
5876
|
-
getTopCoordinate(elementHeight, shouldRenderAtBottom) {
|
|
5877
|
-
if (shouldRenderAtBottom) {
|
|
5878
|
-
return this.anchorRect.y;
|
|
5879
|
-
}
|
|
5880
|
-
else {
|
|
5881
|
-
return this.anchorRect.y + this.anchorRect.height - elementHeight;
|
|
5882
|
-
}
|
|
5883
|
-
}
|
|
5884
|
-
getLeftCoordinate(elementWidth, shouldRenderAtRight) {
|
|
5885
|
-
if (shouldRenderAtRight) {
|
|
5886
|
-
return this.anchorRect.x + this.anchorRect.width;
|
|
5887
|
-
}
|
|
5888
|
-
else {
|
|
5889
|
-
return this.anchorRect.x - elementWidth;
|
|
5890
|
-
}
|
|
5891
|
-
}
|
|
5892
|
-
}
|
|
5893
|
-
|
|
5894
|
-
//------------------------------------------------------------------------------
|
|
5895
|
-
// Context Menu Component
|
|
5896
|
-
//------------------------------------------------------------------------------
|
|
5897
|
-
css /* scss */ `
|
|
5898
|
-
.o-menu {
|
|
5899
|
-
background-color: white;
|
|
5900
|
-
padding: ${MENU_VERTICAL_PADDING}px 0px;
|
|
5901
|
-
width: ${MENU_WIDTH}px;
|
|
5902
|
-
box-sizing: border-box !important;
|
|
5903
|
-
|
|
5904
|
-
.o-menu-item {
|
|
5905
|
-
display: flex;
|
|
5906
|
-
justify-content: space-between;
|
|
5907
|
-
align-items: center;
|
|
5908
|
-
box-sizing: border-box;
|
|
5909
|
-
height: ${MENU_ITEM_HEIGHT}px;
|
|
5910
|
-
padding: 4px 16px;
|
|
5911
|
-
cursor: pointer;
|
|
5912
|
-
user-select: none;
|
|
5913
|
-
|
|
5914
|
-
.o-menu-item-name {
|
|
5915
|
-
overflow: hidden;
|
|
5916
|
-
white-space: nowrap;
|
|
5917
|
-
text-overflow: ellipsis;
|
|
5918
|
-
}
|
|
5919
|
-
|
|
5920
|
-
&.o-menu-root {
|
|
5921
|
-
display: flex;
|
|
5922
|
-
justify-content: space-between;
|
|
5923
|
-
}
|
|
5924
|
-
.o-menu-item-icon {
|
|
5925
|
-
margin-top: auto;
|
|
5926
|
-
margin-bottom: auto;
|
|
5927
|
-
}
|
|
5928
|
-
.o-icon {
|
|
5929
|
-
width: 10px;
|
|
5930
|
-
}
|
|
5931
|
-
|
|
5932
|
-
&:not(.disabled) {
|
|
5933
|
-
&:hover,
|
|
5934
|
-
&.o-menu-item-active {
|
|
5935
|
-
background-color: #ebebeb;
|
|
5936
|
-
}
|
|
5937
|
-
.o-menu-item-description {
|
|
5938
|
-
color: grey;
|
|
5939
|
-
}
|
|
5940
|
-
}
|
|
5941
|
-
&.disabled {
|
|
5942
|
-
color: ${MENU_ITEM_DISABLED_COLOR};
|
|
5943
|
-
cursor: not-allowed;
|
|
5944
|
-
}
|
|
5945
|
-
}
|
|
5946
|
-
}
|
|
5947
|
-
`;
|
|
5948
|
-
class Menu extends owl.Component {
|
|
5949
|
-
constructor() {
|
|
5950
|
-
super(...arguments);
|
|
5951
|
-
this.subMenu = owl.useState({
|
|
5952
|
-
isOpen: false,
|
|
5953
|
-
position: null,
|
|
5954
|
-
scrollOffset: 0,
|
|
5955
|
-
menuItems: [],
|
|
5956
|
-
});
|
|
5957
|
-
this.menuRef = owl.useRef("menu");
|
|
5958
|
-
this.position = useAbsoluteBoundingRect(this.menuRef);
|
|
5959
|
-
}
|
|
5960
|
-
setup() {
|
|
5961
|
-
owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
|
|
5962
|
-
owl.useExternalListener(window, "contextmenu", this.onExternalClick, { capture: true });
|
|
5963
|
-
owl.onWillUpdateProps((nextProps) => {
|
|
5964
|
-
if (nextProps.menuItems !== this.props.menuItems) {
|
|
5965
|
-
this.closeSubMenu();
|
|
5966
|
-
}
|
|
5967
|
-
});
|
|
5968
|
-
}
|
|
5969
|
-
get visibleMenuItems() {
|
|
5970
|
-
return this.props.menuItems.filter((x) => x.isVisible(this.env));
|
|
5971
|
-
}
|
|
5972
|
-
get subMenuPosition() {
|
|
5973
|
-
const position = Object.assign({}, this.subMenu.position);
|
|
5974
|
-
position.y -= this.subMenu.scrollOffset || 0;
|
|
5975
|
-
return position;
|
|
5976
|
-
}
|
|
5977
|
-
get menuHeight() {
|
|
5978
|
-
const menuItems = this.visibleMenuItems;
|
|
5979
|
-
let menuItemsHeight = this.getMenuItemsHeight(menuItems);
|
|
5980
|
-
// We don't display separator at the end of a menu
|
|
5981
|
-
if (menuItems[menuItems.length - 1].separator) {
|
|
5982
|
-
menuItemsHeight -= MENU_SEPARATOR_HEIGHT;
|
|
5983
|
-
}
|
|
5984
|
-
const menuHeight = 2 * MENU_VERTICAL_PADDING + menuItemsHeight;
|
|
5985
|
-
return this.props.maxHeight ? Math.min(menuHeight, this.props.maxHeight) : menuHeight;
|
|
5986
|
-
}
|
|
5987
|
-
get popoverProps() {
|
|
5988
|
-
const isRoot = this.props.depth === 1;
|
|
5989
|
-
return {
|
|
5990
|
-
anchorRect: {
|
|
5991
|
-
x: this.props.position.x - MENU_WIDTH * (this.props.depth - 1),
|
|
5992
|
-
y: this.props.position.y,
|
|
5993
|
-
width: isRoot ? 0 : MENU_WIDTH,
|
|
5994
|
-
height: isRoot ? 0 : MENU_ITEM_HEIGHT,
|
|
5995
|
-
},
|
|
5996
|
-
positioning: "TopRight",
|
|
5997
|
-
verticalOffset: isRoot ? 0 : MENU_VERTICAL_PADDING,
|
|
5998
|
-
onPopoverHidden: () => this.closeSubMenu(),
|
|
5999
|
-
onPopoverMoved: () => this.closeSubMenu(),
|
|
6000
|
-
maxHeight: this.menuHeight,
|
|
6001
|
-
};
|
|
6002
|
-
}
|
|
6003
|
-
getColor(menu) {
|
|
6004
|
-
return menu.textColor ? `color: ${menu.textColor}` : undefined;
|
|
6005
|
-
}
|
|
6006
|
-
async activateMenu(menu) {
|
|
6007
|
-
var _a, _b, _c;
|
|
6008
|
-
const result = await ((_a = menu.action) === null || _a === void 0 ? void 0 : _a.call(menu, this.env));
|
|
6009
|
-
this.close();
|
|
6010
|
-
(_c = (_b = this.props).onMenuClicked) === null || _c === void 0 ? void 0 : _c.call(_b, { detail: result });
|
|
6011
|
-
}
|
|
6012
|
-
close() {
|
|
6013
|
-
this.closeSubMenu();
|
|
6014
|
-
this.props.onClose();
|
|
6015
|
-
}
|
|
6016
|
-
/**
|
|
6017
|
-
* Return the number of pixels between the top of the menu
|
|
6018
|
-
* and the menu item at a given index.
|
|
6019
|
-
*/
|
|
6020
|
-
subMenuVerticalPosition(menuIndex) {
|
|
6021
|
-
const menusAbove = this.visibleMenuItems.slice(0, menuIndex);
|
|
6022
|
-
return this.position.y + this.getMenuItemsHeight(menusAbove) + MENU_VERTICAL_PADDING;
|
|
6023
|
-
}
|
|
6024
|
-
onExternalClick(ev) {
|
|
6025
|
-
// Don't close a root menu when clicked to open the submenus.
|
|
6026
|
-
const el = this.menuRef.el;
|
|
6027
|
-
if (el && getOpenedMenus().some((el) => isChildEvent(el, ev))) {
|
|
6028
|
-
return;
|
|
6029
|
-
}
|
|
6030
|
-
ev.closedMenuId = this.props.menuId;
|
|
6031
|
-
this.close();
|
|
6032
|
-
}
|
|
6033
|
-
getMenuItemsHeight(menuItems) {
|
|
6034
|
-
const numberOfSeparators = menuItems.filter((m) => m.separator).length;
|
|
6035
|
-
return MENU_ITEM_HEIGHT * menuItems.length + MENU_SEPARATOR_HEIGHT * numberOfSeparators;
|
|
6036
|
-
}
|
|
6037
|
-
getName(menu) {
|
|
6038
|
-
return menu.name(this.env);
|
|
6039
|
-
}
|
|
6040
|
-
isRoot(menu) {
|
|
6041
|
-
return !menu.action;
|
|
6042
|
-
}
|
|
6043
|
-
isEnabled(menu) {
|
|
6044
|
-
if (menu.isEnabled(this.env)) {
|
|
6045
|
-
return this.env.model.getters.isReadonly() ? menu.isReadonlyAllowed : true;
|
|
6046
|
-
}
|
|
6047
|
-
return false;
|
|
6048
|
-
}
|
|
6049
|
-
onScroll(ev) {
|
|
6050
|
-
this.subMenu.scrollOffset = ev.target.scrollTop;
|
|
6051
|
-
}
|
|
6052
|
-
/**
|
|
6053
|
-
* If the given menu is not disabled, open it's submenu at the
|
|
6054
|
-
* correct position according to available surrounding space.
|
|
6055
|
-
*/
|
|
6056
|
-
openSubMenu(menu, menuIndex) {
|
|
6057
|
-
const y = this.subMenuVerticalPosition(menuIndex);
|
|
6058
|
-
this.subMenu.position = {
|
|
6059
|
-
x: this.position.x + this.props.depth * MENU_WIDTH,
|
|
6060
|
-
y: y - (this.subMenu.scrollOffset || 0),
|
|
6061
|
-
};
|
|
6062
|
-
this.subMenu.menuItems = menu.children(this.env);
|
|
6063
|
-
this.subMenu.isOpen = true;
|
|
6064
|
-
this.subMenu.parentMenu = menu;
|
|
6065
|
-
}
|
|
6066
|
-
isParentMenu(subMenu, menuItem) {
|
|
6067
|
-
var _a;
|
|
6068
|
-
return ((_a = subMenu.parentMenu) === null || _a === void 0 ? void 0 : _a.id) === menuItem.id;
|
|
6069
|
-
}
|
|
6070
|
-
closeSubMenu() {
|
|
6071
|
-
this.subMenu.isOpen = false;
|
|
6072
|
-
this.subMenu.parentMenu = undefined;
|
|
6073
|
-
}
|
|
6074
|
-
onClickMenu(menu, menuIndex) {
|
|
6075
|
-
if (this.isEnabled(menu)) {
|
|
6076
|
-
if (this.isRoot(menu)) {
|
|
6077
|
-
this.openSubMenu(menu, menuIndex);
|
|
6078
|
-
}
|
|
6079
|
-
else {
|
|
6080
|
-
this.activateMenu(menu);
|
|
6081
|
-
}
|
|
6082
|
-
}
|
|
6083
|
-
}
|
|
6084
|
-
onMouseOver(menu, position) {
|
|
6085
|
-
if (menu.isEnabled(this.env)) {
|
|
6086
|
-
if (this.isRoot(menu)) {
|
|
6087
|
-
this.openSubMenu(menu, position);
|
|
6088
|
-
}
|
|
6089
|
-
else {
|
|
6090
|
-
this.closeSubMenu();
|
|
6091
|
-
}
|
|
6092
|
-
}
|
|
6093
|
-
}
|
|
6094
|
-
}
|
|
6095
|
-
Menu.template = "o-spreadsheet-Menu";
|
|
6096
|
-
Menu.components = { Menu, Popover };
|
|
6097
|
-
Menu.defaultProps = {
|
|
6098
|
-
depth: 1,
|
|
6099
|
-
};
|
|
6100
|
-
Menu.props = {
|
|
6101
|
-
position: Object,
|
|
6102
|
-
menuItems: Array,
|
|
6103
|
-
depth: { type: Number, optional: true },
|
|
6104
|
-
maxHeight: { type: Number, optional: true },
|
|
6105
|
-
onClose: Function,
|
|
6106
|
-
onMenuClicked: { type: Function, optional: true },
|
|
6107
|
-
menuId: { type: String, optional: true },
|
|
6108
|
-
};
|
|
6109
|
-
|
|
6110
|
-
// -----------------------------------------------------------------------------
|
|
6111
|
-
// STYLE
|
|
6112
|
-
// -----------------------------------------------------------------------------
|
|
6113
|
-
css /* scss */ `
|
|
6114
|
-
.o-chart-container {
|
|
6115
|
-
width: 100%;
|
|
6116
|
-
height: 100%;
|
|
6117
|
-
position: relative;
|
|
6118
|
-
}
|
|
6119
|
-
`;
|
|
6120
|
-
class ChartFigure extends owl.Component {
|
|
6121
|
-
constructor() {
|
|
6122
|
-
super(...arguments);
|
|
6123
|
-
this.menuState = owl.useState({ isOpen: false, position: null, menuItems: [] });
|
|
6124
|
-
this.chartContainerRef = owl.useRef("chartContainer");
|
|
6125
|
-
this.menuButtonRef = owl.useRef("menuButton");
|
|
6126
|
-
this.menuButtonRect = useAbsoluteBoundingRect(this.menuButtonRef);
|
|
6127
|
-
this.position = useAbsoluteBoundingRect(this.chartContainerRef);
|
|
6128
|
-
}
|
|
6129
|
-
getMenuItemRegistry() {
|
|
6130
|
-
const registry = new MenuItemRegistry();
|
|
6131
|
-
registry.add("edit", {
|
|
6132
|
-
name: _lt("Edit"),
|
|
6133
|
-
sequence: 1,
|
|
6134
|
-
action: () => {
|
|
6135
|
-
this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figure.id });
|
|
6136
|
-
this.env.openSidePanel("ChartPanel");
|
|
6137
|
-
},
|
|
6138
|
-
});
|
|
6139
|
-
registry.add("copy", {
|
|
6140
|
-
name: _lt("Copy"),
|
|
6141
|
-
sequence: 2,
|
|
6142
|
-
action: async () => {
|
|
6143
|
-
this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figure.id });
|
|
6144
|
-
this.env.model.dispatch("COPY");
|
|
6145
|
-
await this.env.clipboard.write(this.env.model.getters.getClipboardContent());
|
|
6146
|
-
},
|
|
6147
|
-
});
|
|
6148
|
-
registry.add("cut", {
|
|
6149
|
-
name: _lt("Cut"),
|
|
6150
|
-
sequence: 3,
|
|
6151
|
-
action: async () => {
|
|
6152
|
-
this.env.model.dispatch("SELECT_FIGURE", { id: this.props.figure.id });
|
|
6153
|
-
this.env.model.dispatch("CUT");
|
|
6154
|
-
await this.env.clipboard.write(this.env.model.getters.getClipboardContent());
|
|
6155
|
-
},
|
|
6156
|
-
});
|
|
6157
|
-
registry.add("delete", {
|
|
6158
|
-
name: _lt("Delete"),
|
|
6159
|
-
sequence: 10,
|
|
6160
|
-
action: () => {
|
|
6161
|
-
this.env.model.dispatch("DELETE_FIGURE", {
|
|
6162
|
-
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
6163
|
-
id: this.props.figure.id,
|
|
6164
|
-
});
|
|
6165
|
-
this.props.onFigureDeleted();
|
|
6166
|
-
},
|
|
6167
|
-
});
|
|
6168
|
-
return registry;
|
|
6169
|
-
}
|
|
6170
|
-
get chartType() {
|
|
6171
|
-
return this.env.model.getters.getChartType(this.env.model.getters.getActiveSheetId(), this.props.figure.id);
|
|
6172
|
-
}
|
|
6173
|
-
onContextMenu(ev) {
|
|
6174
|
-
const position = {
|
|
6175
|
-
x: this.position.x + ev.offsetX,
|
|
6176
|
-
y: this.position.y + ev.offsetY,
|
|
6177
|
-
};
|
|
6178
|
-
this.openContextMenu(position);
|
|
6179
|
-
}
|
|
6180
|
-
showMenu() {
|
|
6181
|
-
const { x, y, width } = this.menuButtonRect;
|
|
6182
|
-
const menuPosition = {
|
|
6183
|
-
x: x >= MENU_WIDTH ? x - MENU_WIDTH : x + width,
|
|
6184
|
-
y: y,
|
|
6185
|
-
};
|
|
6186
|
-
this.openContextMenu(menuPosition);
|
|
6187
|
-
}
|
|
6188
|
-
openContextMenu(position) {
|
|
6189
|
-
const registry = this.getMenuItemRegistry();
|
|
6190
|
-
this.menuState.isOpen = true;
|
|
6191
|
-
this.menuState.menuItems = registry.getMenuItems();
|
|
6192
|
-
this.menuState.position = position;
|
|
6193
|
-
}
|
|
6194
|
-
get chartComponent() {
|
|
6195
|
-
const type = this.chartType;
|
|
6196
|
-
const component = chartComponentRegistry.get(type);
|
|
6197
|
-
if (!component) {
|
|
6198
|
-
throw new Error(`Component is not defined for type ${type}`);
|
|
6199
|
-
}
|
|
6200
|
-
return component;
|
|
6201
|
-
}
|
|
6202
|
-
}
|
|
6203
|
-
ChartFigure.template = "o-spreadsheet-ChartFigure";
|
|
6204
|
-
ChartFigure.components = { Menu };
|
|
6205
|
-
ChartFigure.props = {
|
|
6206
|
-
figure: Object,
|
|
6207
|
-
onFigureDeleted: Function,
|
|
6208
|
-
};
|
|
6209
|
-
|
|
6210
|
-
function interactiveCut(env) {
|
|
6211
|
-
const result = env.model.dispatch("CUT");
|
|
6212
|
-
if (!result.isSuccessful) {
|
|
6213
|
-
if (result.isCancelledBecause(19 /* CommandResult.WrongCutSelection */)) {
|
|
6214
|
-
env.raiseError(_lt("This operation is not allowed with multiple selections."));
|
|
6215
|
-
}
|
|
6216
|
-
}
|
|
6217
|
-
}
|
|
6218
|
-
|
|
6219
|
-
const PasteInteractiveContent = {
|
|
6220
|
-
wrongPasteSelection: _lt("This operation is not allowed with multiple selections."),
|
|
6221
|
-
willRemoveExistingMerge: _lt("This operation is not possible due to a merge. Please remove the merges first than try again."),
|
|
6222
|
-
wrongFigurePasteOption: _lt("Cannot do a special paste of a figure."),
|
|
6223
|
-
frozenPaneOverlap: _lt("Cannot paste merged cells over a frozen pane."),
|
|
6224
|
-
};
|
|
6225
|
-
function handlePasteResult(env, result) {
|
|
6226
|
-
if (!result.isSuccessful) {
|
|
6227
|
-
if (result.reasons.includes(20 /* CommandResult.WrongPasteSelection */)) {
|
|
6228
|
-
env.raiseError(PasteInteractiveContent.wrongPasteSelection);
|
|
6229
|
-
}
|
|
6230
|
-
else if (result.reasons.includes(2 /* CommandResult.WillRemoveExistingMerge */)) {
|
|
6231
|
-
env.raiseError(PasteInteractiveContent.willRemoveExistingMerge);
|
|
6232
|
-
}
|
|
6233
|
-
else if (result.reasons.includes(22 /* CommandResult.WrongFigurePasteOption */)) {
|
|
6234
|
-
env.raiseError(PasteInteractiveContent.wrongFigurePasteOption);
|
|
6235
|
-
}
|
|
6236
|
-
else if (result.reasons.includes(75 /* CommandResult.FrozenPaneOverlap */)) {
|
|
6237
|
-
env.raiseError(PasteInteractiveContent.frozenPaneOverlap);
|
|
6238
|
-
}
|
|
6239
|
-
}
|
|
6240
|
-
}
|
|
6241
|
-
function interactivePaste(env, target, pasteOption) {
|
|
6242
|
-
const result = env.model.dispatch("PASTE", { target, pasteOption });
|
|
6243
|
-
handlePasteResult(env, result);
|
|
6244
|
-
}
|
|
6245
|
-
function interactivePasteFromOS(env, target, text) {
|
|
6246
|
-
const result = env.model.dispatch("PASTE_FROM_OS_CLIPBOARD", { target, text });
|
|
6247
|
-
handlePasteResult(env, result);
|
|
6248
|
-
}
|
|
6249
|
-
|
|
6250
|
-
/**
|
|
6251
|
-
* Create a function used to create a Chart based on the definition
|
|
6252
|
-
*/
|
|
6253
|
-
function chartFactory(getters) {
|
|
6254
|
-
const builders = chartRegistry.getAll().sort((a, b) => a.sequence - b.sequence);
|
|
6255
|
-
function createChart(id, definition, sheetId) {
|
|
6256
|
-
const builder = builders.find((builder) => builder.match(definition.type));
|
|
6257
|
-
if (!builder) {
|
|
6258
|
-
throw new Error(`No builder for this chart: ${definition.type}`);
|
|
6259
|
-
}
|
|
6260
|
-
return builder.createChart(definition, sheetId, getters);
|
|
6261
|
-
}
|
|
6262
|
-
return createChart;
|
|
6263
|
-
}
|
|
6264
|
-
/**
|
|
6265
|
-
* Create a function used to create a Chart Runtime based on the chart class
|
|
6266
|
-
* instance
|
|
6267
|
-
*/
|
|
6268
|
-
function chartRuntimeFactory(getters) {
|
|
6269
|
-
const builders = chartRegistry.getAll().sort((a, b) => a.sequence - b.sequence);
|
|
6270
|
-
function createRuntimeChart(chart) {
|
|
6271
|
-
const builder = builders.find((builder) => builder.match(chart.type));
|
|
6272
|
-
if (!builder) {
|
|
6273
|
-
throw new Error("No runtime builder for this chart.");
|
|
6274
|
-
}
|
|
6275
|
-
return builder.getChartRuntime(chart, getters);
|
|
6276
|
-
}
|
|
6277
|
-
return createRuntimeChart;
|
|
6278
|
-
}
|
|
6279
|
-
/**
|
|
6280
|
-
* Validate the chart definition given in arguments
|
|
6281
|
-
*/
|
|
6282
|
-
function validateChartDefinition(validator, definition) {
|
|
6283
|
-
const validators = chartRegistry.getAll().find((validator) => validator.match(definition.type));
|
|
6284
|
-
if (!validators) {
|
|
6285
|
-
throw new Error("Unknown chart type.");
|
|
6286
|
-
}
|
|
6287
|
-
return validators.validateChartDefinition(validator, definition);
|
|
6288
|
-
}
|
|
6289
|
-
/**
|
|
6290
|
-
* Get a new chart definition transformed with the executed command. This
|
|
6291
|
-
* functions will be called during operational transform process
|
|
6292
|
-
*/
|
|
6293
|
-
function transformDefinition(definition, executed) {
|
|
6294
|
-
const transformation = chartRegistry.getAll().find((factory) => factory.match(definition.type));
|
|
6295
|
-
if (!transformation) {
|
|
6296
|
-
throw new Error("Unknown chart type.");
|
|
6297
|
-
}
|
|
6298
|
-
return transformation.transformDefinition(definition, executed);
|
|
6299
|
-
}
|
|
6300
|
-
/**
|
|
6301
|
-
* Get an empty definition based on the given context and the given type
|
|
6302
|
-
*/
|
|
6303
|
-
function getChartDefinitionFromContextCreation(context, type) {
|
|
6304
|
-
const chartClass = chartRegistry.get(type);
|
|
6305
|
-
return chartClass.getChartDefinitionFromContextCreation(context);
|
|
6306
|
-
}
|
|
6307
|
-
function getChartTypes() {
|
|
6308
|
-
const result = {};
|
|
6309
|
-
for (const key of chartRegistry.getKeys()) {
|
|
6310
|
-
result[key] = chartRegistry.get(key).name;
|
|
6311
|
-
}
|
|
6312
|
-
return result;
|
|
6313
|
-
}
|
|
6314
|
-
/**
|
|
6315
|
-
* Return a "smart" chart definition in the given zone. The definition is "smart" because it will
|
|
6316
|
-
* use the best type of chart to display the data of the zone.
|
|
6317
|
-
*
|
|
6318
|
-
* It will also try to find labels and datasets in the range, and try to find title for the datasets.
|
|
6319
|
-
*
|
|
6320
|
-
* The type of chart will be :
|
|
6321
|
-
* - If the zone is a single non-empty cell, returns a scorecard
|
|
6322
|
-
* - If the all the labels are numbers/date, returns a line chart
|
|
6323
|
-
* - Else returns a bar chart
|
|
6324
|
-
*/
|
|
6325
|
-
function getSmartChartDefinition(zone, getters) {
|
|
6326
|
-
var _a;
|
|
6327
|
-
let dataSetZone = zone;
|
|
6328
|
-
if (zone.left !== zone.right) {
|
|
6329
|
-
dataSetZone = { ...zone, left: zone.left + 1 };
|
|
6330
|
-
}
|
|
6331
|
-
const dataSets = [zoneToXc(dataSetZone)];
|
|
6332
|
-
const sheetId = getters.getActiveSheetId();
|
|
6333
|
-
const topLeftCell = getters.getCell({ sheetId, col: zone.left, row: zone.top });
|
|
6334
|
-
if (getZoneArea(zone) === 1 && (topLeftCell === null || topLeftCell === void 0 ? void 0 : topLeftCell.content)) {
|
|
6335
|
-
return {
|
|
6336
|
-
type: "scorecard",
|
|
6337
|
-
title: "",
|
|
6338
|
-
background: ((_a = topLeftCell.style) === null || _a === void 0 ? void 0 : _a.fillColor) || undefined,
|
|
6339
|
-
keyValue: zoneToXc(zone),
|
|
6340
|
-
baselineMode: DEFAULT_SCORECARD_BASELINE_MODE,
|
|
6341
|
-
baselineColorUp: DEFAULT_SCORECARD_BASELINE_COLOR_UP,
|
|
6342
|
-
baselineColorDown: DEFAULT_SCORECARD_BASELINE_COLOR_DOWN,
|
|
6343
|
-
};
|
|
6344
|
-
}
|
|
6345
|
-
let title = "";
|
|
6346
|
-
const cellsInFirstRow = getters.getEvaluatedCellsInZone(sheetId, {
|
|
6347
|
-
...dataSetZone,
|
|
6348
|
-
bottom: dataSetZone.top,
|
|
6349
|
-
});
|
|
6350
|
-
const dataSetsHaveTitle = !!cellsInFirstRow.find((cell) => cell.type !== CellValueType.empty && cell.type !== CellValueType.number);
|
|
6351
|
-
if (dataSetsHaveTitle) {
|
|
6352
|
-
const texts = cellsInFirstRow
|
|
6353
|
-
.filter((cell) => cell.type !== CellValueType.error && cell.type !== CellValueType.empty)
|
|
6354
|
-
.map((cell) => cell.formattedValue);
|
|
6355
|
-
const lastElement = texts.splice(-1)[0];
|
|
6356
|
-
title = texts.join(", ");
|
|
6357
|
-
if (lastElement) {
|
|
6358
|
-
title += (title ? " " + _t("and") + " " : "") + lastElement;
|
|
6359
|
-
}
|
|
6360
|
-
}
|
|
6361
|
-
let labelRangeXc;
|
|
6362
|
-
if (zone.left !== zone.right) {
|
|
6363
|
-
labelRangeXc = zoneToXc({
|
|
6364
|
-
...zone,
|
|
6365
|
-
right: zone.left,
|
|
6366
|
-
top: dataSetsHaveTitle ? zone.top + 1 : zone.top,
|
|
6367
|
-
});
|
|
6368
|
-
}
|
|
6369
|
-
// Only display legend for several datasets.
|
|
6370
|
-
const newLegendPos = dataSetZone.right === dataSetZone.left ? "none" : "top";
|
|
6371
|
-
const labelRange = labelRangeXc ? getters.getRangeFromSheetXC(sheetId, labelRangeXc) : undefined;
|
|
6372
|
-
if (canChartParseLabels(labelRange, getters)) {
|
|
6373
|
-
return {
|
|
6374
|
-
title,
|
|
6375
|
-
dataSets,
|
|
6376
|
-
labelsAsText: false,
|
|
6377
|
-
stacked: false,
|
|
6378
|
-
aggregated: false,
|
|
6379
|
-
labelRange: labelRangeXc,
|
|
6380
|
-
type: "line",
|
|
6381
|
-
dataSetsHaveTitle,
|
|
6382
|
-
verticalAxisPosition: "left",
|
|
6383
|
-
legendPosition: newLegendPos,
|
|
6384
|
-
};
|
|
6385
|
-
}
|
|
6386
|
-
return {
|
|
6387
|
-
title,
|
|
6388
|
-
dataSets,
|
|
6389
|
-
labelRange: labelRangeXc,
|
|
6390
|
-
type: "bar",
|
|
6391
|
-
stacked: false,
|
|
6392
|
-
aggregated: false,
|
|
6393
|
-
dataSetsHaveTitle,
|
|
6394
|
-
verticalAxisPosition: "left",
|
|
6395
|
-
legendPosition: newLegendPos,
|
|
6396
|
-
};
|
|
6397
|
-
}
|
|
6398
|
-
|
|
6399
|
-
function centerFigurePosition(getters, size) {
|
|
6400
|
-
const { x: offsetCorrectionX, y: offsetCorrectionY } = getters.getMainViewportCoordinates();
|
|
6401
|
-
const { scrollX, scrollY } = getters.getActiveSheetScrollInfo();
|
|
6402
|
-
const dim = getters.getSheetViewDimension();
|
|
6403
|
-
const rect = getters.getVisibleRect(getters.getActiveMainViewport());
|
|
6404
|
-
const scrollableViewportWidth = Math.min(rect.width, dim.width - offsetCorrectionX);
|
|
6405
|
-
const scrollableViewportHeight = Math.min(rect.height, dim.height - offsetCorrectionY);
|
|
6406
|
-
const position = {
|
|
6407
|
-
x: offsetCorrectionX + scrollX + Math.max(0, (scrollableViewportWidth - size.width) / 2),
|
|
6408
|
-
y: offsetCorrectionY + scrollY + Math.max(0, (scrollableViewportHeight - size.height) / 2),
|
|
6409
|
-
}; // Position at the center of the scrollable viewport
|
|
6410
|
-
return position;
|
|
6411
|
-
}
|
|
6412
|
-
function getMaxFigureSize(getters, figureSize) {
|
|
6413
|
-
const size = deepCopy(figureSize);
|
|
6414
|
-
const dim = getters.getSheetViewDimension();
|
|
6415
|
-
const maxWidth = dim.width;
|
|
6416
|
-
const maxHeight = dim.height;
|
|
6417
|
-
if (size.width > maxWidth) {
|
|
6418
|
-
const ratio = maxWidth / size.width;
|
|
6419
|
-
size.width = maxWidth;
|
|
6420
|
-
size.height = size.height * ratio;
|
|
6421
|
-
}
|
|
6422
|
-
if (size.height > maxHeight) {
|
|
6423
|
-
const ratio = maxHeight / size.height;
|
|
6424
|
-
size.height = maxHeight;
|
|
6425
|
-
size.width = size.width * ratio;
|
|
6426
|
-
}
|
|
6427
|
-
return size;
|
|
6428
|
-
}
|
|
6429
|
-
|
|
6430
|
-
const SORT_TYPES = [
|
|
6431
|
-
CellValueType.number,
|
|
6432
|
-
CellValueType.error,
|
|
6433
|
-
CellValueType.text,
|
|
6434
|
-
CellValueType.boolean,
|
|
6435
|
-
];
|
|
6436
|
-
function sortCells(cells, sortDirection, emptyCellAsZero) {
|
|
6437
|
-
const cellsWithIndex = cells.map((cell, index) => ({
|
|
6438
|
-
index,
|
|
6439
|
-
type: cell.type,
|
|
6440
|
-
value: cell.value,
|
|
6441
|
-
}));
|
|
6442
|
-
let emptyCells = cellsWithIndex.filter((x) => x.type === CellValueType.empty);
|
|
6443
|
-
let nonEmptyCells = cellsWithIndex.filter((x) => x.type !== CellValueType.empty);
|
|
6444
|
-
if (emptyCellAsZero) {
|
|
6445
|
-
nonEmptyCells.push(...emptyCells.map((emptyCell) => ({ ...emptyCell, type: CellValueType.number, value: 0 })));
|
|
6446
|
-
emptyCells = [];
|
|
6447
|
-
}
|
|
6448
|
-
const inverse = sortDirection === "descending" ? -1 : 1;
|
|
6449
|
-
return nonEmptyCells
|
|
6450
|
-
.sort((left, right) => {
|
|
6451
|
-
let typeOrder = SORT_TYPES.indexOf(left.type) - SORT_TYPES.indexOf(right.type);
|
|
6452
|
-
if (typeOrder === 0) {
|
|
6453
|
-
if (left.type === CellValueType.text || left.type === CellValueType.error) {
|
|
6454
|
-
typeOrder = left.value.localeCompare(right.value);
|
|
6455
|
-
}
|
|
6456
|
-
else
|
|
6457
|
-
typeOrder = left.value - right.value;
|
|
6458
|
-
}
|
|
6459
|
-
return inverse * typeOrder;
|
|
6460
|
-
})
|
|
6461
|
-
.concat(emptyCells);
|
|
6462
|
-
}
|
|
6463
|
-
function interactiveSortSelection(env, sheetId, anchor, zone, sortDirection) {
|
|
6464
|
-
let result = DispatchResult.Success;
|
|
6465
|
-
//several columns => bypass the contiguity check
|
|
6466
|
-
let multiColumns = zone.right > zone.left;
|
|
6467
|
-
if (env.model.getters.doesIntersectMerge(sheetId, zone)) {
|
|
6468
|
-
multiColumns = false;
|
|
6469
|
-
let table;
|
|
6470
|
-
for (let row = zone.top; row <= zone.bottom; row++) {
|
|
6471
|
-
table = [];
|
|
6472
|
-
for (let col = zone.left; col <= zone.right; col++) {
|
|
6473
|
-
let merge = env.model.getters.getMerge({ sheetId, col, row });
|
|
6474
|
-
if (merge && !table.includes(merge.id.toString())) {
|
|
6475
|
-
table.push(merge.id.toString());
|
|
6476
|
-
}
|
|
6477
|
-
}
|
|
6478
|
-
if (table.length >= 2) {
|
|
6479
|
-
multiColumns = true;
|
|
6480
|
-
break;
|
|
6481
|
-
}
|
|
6482
|
-
}
|
|
6483
|
-
}
|
|
6484
|
-
const { col, row } = anchor;
|
|
6485
|
-
if (multiColumns) {
|
|
6486
|
-
result = env.model.dispatch("SORT_CELLS", { sheetId, col, row, zone, sortDirection });
|
|
6487
|
-
}
|
|
6488
|
-
else {
|
|
6489
|
-
// check contiguity
|
|
6490
|
-
const contiguousZone = env.model.getters.getContiguousZone(sheetId, zone);
|
|
6491
|
-
if (isEqual(contiguousZone, zone)) {
|
|
6492
|
-
// merge as it is
|
|
6493
|
-
result = env.model.dispatch("SORT_CELLS", {
|
|
6494
|
-
sheetId,
|
|
6495
|
-
col,
|
|
6496
|
-
row,
|
|
6497
|
-
zone,
|
|
6498
|
-
sortDirection,
|
|
6499
|
-
});
|
|
6500
|
-
}
|
|
6501
|
-
else {
|
|
6502
|
-
env.askConfirmation(_lt("We found data next to your selection. Since this data was not selected, it will not be sorted. Do you want to extend your selection?"), () => {
|
|
6503
|
-
zone = contiguousZone;
|
|
6504
|
-
result = env.model.dispatch("SORT_CELLS", {
|
|
6505
|
-
sheetId,
|
|
6506
|
-
col,
|
|
6507
|
-
row,
|
|
6508
|
-
zone,
|
|
6509
|
-
sortDirection,
|
|
6510
|
-
});
|
|
6511
|
-
}, () => {
|
|
6512
|
-
result = env.model.dispatch("SORT_CELLS", {
|
|
6513
|
-
sheetId,
|
|
6514
|
-
col,
|
|
6515
|
-
row,
|
|
6516
|
-
zone,
|
|
6517
|
-
sortDirection,
|
|
6518
|
-
});
|
|
6519
|
-
});
|
|
6520
|
-
}
|
|
6521
|
-
}
|
|
6522
|
-
if (result.isCancelledBecause(63 /* CommandResult.InvalidSortZone */)) {
|
|
6523
|
-
const { col, row } = anchor;
|
|
6524
|
-
env.model.selection.selectZone({ cell: { col, row }, zone });
|
|
6525
|
-
env.raiseError(_lt("Cannot sort. To sort, select only cells or only merges that have the same size."));
|
|
5889
|
+
if (result.isCancelledBecause(63 /* CommandResult.InvalidSortZone */)) {
|
|
5890
|
+
const { col, row } = anchor;
|
|
5891
|
+
env.model.selection.selectZone({ cell: { col, row }, zone });
|
|
5892
|
+
env.raiseError(_lt("Cannot sort. To sort, select only cells or only merges that have the same size."));
|
|
6526
5893
|
}
|
|
6527
5894
|
}
|
|
6528
5895
|
|
|
@@ -7444,6 +6811,53 @@ rowMenuRegistry
|
|
|
7444
6811
|
action: OPEN_CF_SIDEPANEL_ACTION,
|
|
7445
6812
|
});
|
|
7446
6813
|
|
|
6814
|
+
/**
|
|
6815
|
+
* Return true if the event was triggered from
|
|
6816
|
+
* a child element.
|
|
6817
|
+
*/
|
|
6818
|
+
function isChildEvent(parent, ev) {
|
|
6819
|
+
return !!ev.target && parent.contains(ev.target);
|
|
6820
|
+
}
|
|
6821
|
+
function gridOverlayPosition() {
|
|
6822
|
+
const spreadsheetElement = document.querySelector(".o-grid-overlay");
|
|
6823
|
+
if (spreadsheetElement) {
|
|
6824
|
+
const { top, left } = spreadsheetElement === null || spreadsheetElement === void 0 ? void 0 : spreadsheetElement.getBoundingClientRect();
|
|
6825
|
+
return { top, left };
|
|
6826
|
+
}
|
|
6827
|
+
throw new Error("Can't find spreadsheet position");
|
|
6828
|
+
}
|
|
6829
|
+
function getBoundingRectAsPOJO(el) {
|
|
6830
|
+
const rect = el.getBoundingClientRect();
|
|
6831
|
+
return {
|
|
6832
|
+
x: rect.x,
|
|
6833
|
+
y: rect.y,
|
|
6834
|
+
width: rect.width,
|
|
6835
|
+
height: rect.height,
|
|
6836
|
+
};
|
|
6837
|
+
}
|
|
6838
|
+
/**
|
|
6839
|
+
* Iterate over all the children of `el` in the dom tree starting at `el`, depth first.
|
|
6840
|
+
*/
|
|
6841
|
+
function* iterateChildren(el) {
|
|
6842
|
+
yield el;
|
|
6843
|
+
if (el.hasChildNodes()) {
|
|
6844
|
+
for (let child of el.childNodes) {
|
|
6845
|
+
yield* iterateChildren(child);
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
}
|
|
6849
|
+
function getElementScrollTop(el) {
|
|
6850
|
+
return (el === null || el === void 0 ? void 0 : el.scrollTop) || 0;
|
|
6851
|
+
}
|
|
6852
|
+
function setElementScrollTop(el, scroll) {
|
|
6853
|
+
if (!el)
|
|
6854
|
+
return;
|
|
6855
|
+
el.scrollTop = scroll;
|
|
6856
|
+
}
|
|
6857
|
+
function getOpenedMenus() {
|
|
6858
|
+
return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
|
|
6859
|
+
}
|
|
6860
|
+
|
|
7447
6861
|
function startDnd(onMouseMove, onMouseUp, onMouseDown = () => { }) {
|
|
7448
6862
|
const _onMouseDown = (ev) => {
|
|
7449
6863
|
ev.preventDefault();
|
|
@@ -15723,8 +15137,7 @@ class Composer extends owl.Component {
|
|
|
15723
15137
|
return false;
|
|
15724
15138
|
const range = this.env.model.getters.getRangeFromSheetXC(refSheet, xc);
|
|
15725
15139
|
let zone = range.zone;
|
|
15726
|
-
|
|
15727
|
-
zone = height * width === 1 ? this.env.model.getters.expandZone(refSheet, zone) : zone;
|
|
15140
|
+
zone = getZoneArea(zone) === 1 ? this.env.model.getters.expandZone(refSheet, zone) : zone;
|
|
15728
15141
|
return isEqual(zone, highlight.zone);
|
|
15729
15142
|
});
|
|
15730
15143
|
return highlight && highlight.color ? highlight.color : undefined;
|
|
@@ -16386,439 +15799,955 @@ function getGroup(cell, cells, filter) {
|
|
|
16386
15799
|
group = [];
|
|
16387
15800
|
}
|
|
16388
15801
|
}
|
|
16389
|
-
return group;
|
|
15802
|
+
return group;
|
|
15803
|
+
}
|
|
15804
|
+
/**
|
|
15805
|
+
* Get the average steps between numbers
|
|
15806
|
+
*/
|
|
15807
|
+
function getAverageIncrement(group) {
|
|
15808
|
+
const averages = [];
|
|
15809
|
+
let last = group[0];
|
|
15810
|
+
for (let i = 1; i < group.length; i++) {
|
|
15811
|
+
const current = group[i];
|
|
15812
|
+
averages.push(current - last);
|
|
15813
|
+
last = current;
|
|
15814
|
+
}
|
|
15815
|
+
return averages.reduce((a, b) => a + b, 0) / averages.length;
|
|
15816
|
+
}
|
|
15817
|
+
/**
|
|
15818
|
+
* Get the step for a group
|
|
15819
|
+
*/
|
|
15820
|
+
function calculateIncrementBasedOnGroup(group) {
|
|
15821
|
+
let increment = 1;
|
|
15822
|
+
if (group.length >= 2) {
|
|
15823
|
+
increment = getAverageIncrement(group) * group.length;
|
|
15824
|
+
}
|
|
15825
|
+
return increment;
|
|
15826
|
+
}
|
|
15827
|
+
autofillRulesRegistry
|
|
15828
|
+
.add("simple_value_copy", {
|
|
15829
|
+
condition: (cell, cells) => {
|
|
15830
|
+
var _a;
|
|
15831
|
+
return cells.length === 1 && !cell.isFormula && !((_a = cell.format) === null || _a === void 0 ? void 0 : _a.match(DATETIME_FORMAT));
|
|
15832
|
+
},
|
|
15833
|
+
generateRule: () => {
|
|
15834
|
+
return { type: "COPY_MODIFIER" };
|
|
15835
|
+
},
|
|
15836
|
+
sequence: 10,
|
|
15837
|
+
})
|
|
15838
|
+
.add("increment_alphanumeric_value", {
|
|
15839
|
+
condition: (cell) => !cell.isFormula &&
|
|
15840
|
+
evaluateLiteral(cell.content).type === CellValueType.text &&
|
|
15841
|
+
alphaNumericValueRegExp.test(cell.content),
|
|
15842
|
+
generateRule: (cell, cells) => {
|
|
15843
|
+
const numberPostfix = parseInt(cell.content.match(numberPostfixRegExp)[0]);
|
|
15844
|
+
const prefix = cell.content.match(stringPrefixRegExp)[0];
|
|
15845
|
+
const numberPostfixLength = cell.content.length - prefix.length;
|
|
15846
|
+
const group = getGroup(cell, cells, (evaluatedCell) => evaluatedCell.type === CellValueType.text &&
|
|
15847
|
+
alphaNumericValueRegExp.test(evaluatedCell.value)) // get consecutive alphanumeric cells, no matter what the prefix is
|
|
15848
|
+
.filter((cell) => prefix === cell.value.toString().match(stringPrefixRegExp)[0])
|
|
15849
|
+
.map((cell) => parseInt(cell.value.toString().match(numberPostfixRegExp)[0]));
|
|
15850
|
+
const increment = calculateIncrementBasedOnGroup(group);
|
|
15851
|
+
return {
|
|
15852
|
+
type: "ALPHANUMERIC_INCREMENT_MODIFIER",
|
|
15853
|
+
prefix,
|
|
15854
|
+
current: numberPostfix,
|
|
15855
|
+
increment,
|
|
15856
|
+
numberPostfixLength,
|
|
15857
|
+
};
|
|
15858
|
+
},
|
|
15859
|
+
sequence: 15,
|
|
15860
|
+
})
|
|
15861
|
+
.add("copy_text", {
|
|
15862
|
+
condition: (cell) => !cell.isFormula && evaluateLiteral(cell.content).type === CellValueType.text,
|
|
15863
|
+
generateRule: () => {
|
|
15864
|
+
return { type: "COPY_MODIFIER" };
|
|
15865
|
+
},
|
|
15866
|
+
sequence: 20,
|
|
15867
|
+
})
|
|
15868
|
+
.add("update_formula", {
|
|
15869
|
+
condition: (cell) => cell.isFormula,
|
|
15870
|
+
generateRule: (_, cells) => {
|
|
15871
|
+
return { type: "FORMULA_MODIFIER", increment: cells.length, current: 0 };
|
|
15872
|
+
},
|
|
15873
|
+
sequence: 30,
|
|
15874
|
+
})
|
|
15875
|
+
.add("increment_number", {
|
|
15876
|
+
condition: (cell) => !cell.isFormula && evaluateLiteral(cell.content).type === CellValueType.number,
|
|
15877
|
+
generateRule: (cell, cells) => {
|
|
15878
|
+
const group = getGroup(cell, cells, (evaluatedCell) => evaluatedCell.type === CellValueType.number).map((cell) => Number(cell.value));
|
|
15879
|
+
const increment = calculateIncrementBasedOnGroup(group);
|
|
15880
|
+
const evaluation = evaluateLiteral(cell.content);
|
|
15881
|
+
return {
|
|
15882
|
+
type: "INCREMENT_MODIFIER",
|
|
15883
|
+
increment,
|
|
15884
|
+
current: evaluation.type === CellValueType.number ? evaluation.value : 0,
|
|
15885
|
+
};
|
|
15886
|
+
},
|
|
15887
|
+
sequence: 40,
|
|
15888
|
+
});
|
|
15889
|
+
|
|
15890
|
+
const ERROR_TOOLTIP_MAX_HEIGHT = 80;
|
|
15891
|
+
const ERROR_TOOLTIP_WIDTH = 180;
|
|
15892
|
+
css /* scss */ `
|
|
15893
|
+
.o-error-tooltip {
|
|
15894
|
+
font-size: 13px;
|
|
15895
|
+
background-color: white;
|
|
15896
|
+
border-left: 3px solid red;
|
|
15897
|
+
padding: 10px;
|
|
15898
|
+
width: ${ERROR_TOOLTIP_WIDTH}px;
|
|
15899
|
+
box-sizing: border-box !important;
|
|
15900
|
+
}
|
|
15901
|
+
`;
|
|
15902
|
+
class ErrorToolTip extends owl.Component {
|
|
15903
|
+
}
|
|
15904
|
+
ErrorToolTip.maxSize = { maxHeight: ERROR_TOOLTIP_MAX_HEIGHT };
|
|
15905
|
+
ErrorToolTip.template = "o-spreadsheet-ErrorToolTip";
|
|
15906
|
+
ErrorToolTip.components = {};
|
|
15907
|
+
ErrorToolTip.props = {
|
|
15908
|
+
text: String,
|
|
15909
|
+
onClosed: { type: Function, optional: true },
|
|
15910
|
+
};
|
|
15911
|
+
const ErrorToolTipPopoverBuilder = {
|
|
15912
|
+
onHover: (position, getters) => {
|
|
15913
|
+
const cell = getters.getEvaluatedCell(position);
|
|
15914
|
+
if (cell.type === CellValueType.error && cell.error.logLevel > CellErrorLevel.silent) {
|
|
15915
|
+
return {
|
|
15916
|
+
isOpen: true,
|
|
15917
|
+
props: { text: cell.error.message },
|
|
15918
|
+
Component: ErrorToolTip,
|
|
15919
|
+
cellCorner: "TopRight",
|
|
15920
|
+
};
|
|
15921
|
+
}
|
|
15922
|
+
return { isOpen: false };
|
|
15923
|
+
},
|
|
15924
|
+
};
|
|
15925
|
+
|
|
15926
|
+
css /*SCSS*/ `
|
|
15927
|
+
.o-filter-menu-value {
|
|
15928
|
+
padding: 4px;
|
|
15929
|
+
line-height: 20px;
|
|
15930
|
+
height: 28px;
|
|
15931
|
+
.o-filter-menu-value-checked {
|
|
15932
|
+
width: 20px;
|
|
15933
|
+
}
|
|
15934
|
+
}
|
|
15935
|
+
`;
|
|
15936
|
+
class FilterMenuValueItem extends owl.Component {
|
|
15937
|
+
constructor() {
|
|
15938
|
+
super(...arguments);
|
|
15939
|
+
this.itemRef = owl.useRef("menuValueItem");
|
|
15940
|
+
}
|
|
15941
|
+
setup() {
|
|
15942
|
+
owl.onWillPatch(() => {
|
|
15943
|
+
if (this.props.scrolledTo) {
|
|
15944
|
+
this.scrollListToSelectedValue();
|
|
15945
|
+
}
|
|
15946
|
+
});
|
|
15947
|
+
}
|
|
15948
|
+
scrollListToSelectedValue() {
|
|
15949
|
+
var _a, _b;
|
|
15950
|
+
if (!this.itemRef.el) {
|
|
15951
|
+
return;
|
|
15952
|
+
}
|
|
15953
|
+
(_b = (_a = this.itemRef.el).scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
15954
|
+
block: this.props.scrolledTo === "bottom" ? "end" : "start",
|
|
15955
|
+
});
|
|
15956
|
+
}
|
|
15957
|
+
}
|
|
15958
|
+
FilterMenuValueItem.template = "o-spreadsheet-FilterMenuValueItem";
|
|
15959
|
+
FilterMenuValueItem.props = {
|
|
15960
|
+
value: String,
|
|
15961
|
+
isChecked: Boolean,
|
|
15962
|
+
isSelected: Boolean,
|
|
15963
|
+
onMouseMove: Function,
|
|
15964
|
+
onClick: Function,
|
|
15965
|
+
scrolledTo: { type: String, optional: true },
|
|
15966
|
+
};
|
|
15967
|
+
|
|
15968
|
+
const FILTER_MENU_HEIGHT = 295;
|
|
15969
|
+
const CSS = css /* scss */ `
|
|
15970
|
+
.o-filter-menu {
|
|
15971
|
+
box-sizing: border-box;
|
|
15972
|
+
padding: 8px 16px;
|
|
15973
|
+
height: ${FILTER_MENU_HEIGHT}px;
|
|
15974
|
+
line-height: 1;
|
|
15975
|
+
|
|
15976
|
+
.o-filter-menu-item {
|
|
15977
|
+
display: flex;
|
|
15978
|
+
box-sizing: border-box;
|
|
15979
|
+
height: ${MENU_ITEM_HEIGHT}px;
|
|
15980
|
+
padding: 4px 4px 4px 0px;
|
|
15981
|
+
cursor: pointer;
|
|
15982
|
+
user-select: none;
|
|
15983
|
+
|
|
15984
|
+
&.selected {
|
|
15985
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
15986
|
+
}
|
|
15987
|
+
}
|
|
15988
|
+
|
|
15989
|
+
input {
|
|
15990
|
+
box-sizing: border-box;
|
|
15991
|
+
margin-bottom: 5px;
|
|
15992
|
+
border: 1px solid #949494;
|
|
15993
|
+
height: 24px;
|
|
15994
|
+
padding-right: 28px;
|
|
15995
|
+
}
|
|
15996
|
+
|
|
15997
|
+
.o-search-icon {
|
|
15998
|
+
right: 5px;
|
|
15999
|
+
top: 4px;
|
|
16000
|
+
|
|
16001
|
+
svg {
|
|
16002
|
+
height: 16px;
|
|
16003
|
+
width: 16px;
|
|
16004
|
+
vertical-align: middle;
|
|
16005
|
+
}
|
|
16006
|
+
}
|
|
16007
|
+
|
|
16008
|
+
.o-filter-menu-actions {
|
|
16009
|
+
display: flex;
|
|
16010
|
+
flex-direction: row;
|
|
16011
|
+
margin-bottom: 4px;
|
|
16012
|
+
|
|
16013
|
+
.o-filter-menu-action-text {
|
|
16014
|
+
cursor: pointer;
|
|
16015
|
+
margin-right: 10px;
|
|
16016
|
+
color: blue;
|
|
16017
|
+
text-decoration: underline;
|
|
16018
|
+
}
|
|
16019
|
+
}
|
|
16020
|
+
|
|
16021
|
+
.o-filter-menu-list {
|
|
16022
|
+
flex: auto;
|
|
16023
|
+
overflow-y: auto;
|
|
16024
|
+
border: 1px solid #949494;
|
|
16025
|
+
|
|
16026
|
+
.o-filter-menu-no-values {
|
|
16027
|
+
color: #949494;
|
|
16028
|
+
font-style: italic;
|
|
16029
|
+
}
|
|
16030
|
+
}
|
|
16031
|
+
|
|
16032
|
+
.o-filter-menu-buttons {
|
|
16033
|
+
margin-top: 9px;
|
|
16034
|
+
|
|
16035
|
+
.o-filter-menu-button {
|
|
16036
|
+
border: 1px solid lightgrey;
|
|
16037
|
+
padding: 6px 10px;
|
|
16038
|
+
cursor: pointer;
|
|
16039
|
+
border-radius: 4px;
|
|
16040
|
+
font-weight: 500;
|
|
16041
|
+
line-height: 16px;
|
|
16042
|
+
}
|
|
16043
|
+
|
|
16044
|
+
.o-filter-menu-button-cancel {
|
|
16045
|
+
background: white;
|
|
16046
|
+
&:hover {
|
|
16047
|
+
background-color: rgba(0, 0, 0, 0.08);
|
|
16048
|
+
}
|
|
16049
|
+
}
|
|
16050
|
+
|
|
16051
|
+
.o-filter-menu-button-primary {
|
|
16052
|
+
background-color: #188038;
|
|
16053
|
+
&:hover {
|
|
16054
|
+
background-color: #1d9641;
|
|
16055
|
+
}
|
|
16056
|
+
color: white;
|
|
16057
|
+
font-weight: bold;
|
|
16058
|
+
margin-left: 10px;
|
|
16059
|
+
}
|
|
16060
|
+
}
|
|
16061
|
+
}
|
|
16062
|
+
`;
|
|
16063
|
+
class FilterMenu extends owl.Component {
|
|
16064
|
+
constructor() {
|
|
16065
|
+
super(...arguments);
|
|
16066
|
+
this.state = owl.useState({
|
|
16067
|
+
values: [],
|
|
16068
|
+
textFilter: "",
|
|
16069
|
+
selectedValue: undefined,
|
|
16070
|
+
});
|
|
16071
|
+
this.searchBar = owl.useRef("filterMenuSearchBar");
|
|
16072
|
+
}
|
|
16073
|
+
setup() {
|
|
16074
|
+
owl.onWillUpdateProps((nextProps) => {
|
|
16075
|
+
if (!deepEquals(nextProps.filterPosition, this.props.filterPosition)) {
|
|
16076
|
+
this.state.values = this.getFilterValues(nextProps.filterPosition);
|
|
16077
|
+
}
|
|
16078
|
+
});
|
|
16079
|
+
this.state.values = this.getFilterValues(this.props.filterPosition);
|
|
16080
|
+
}
|
|
16081
|
+
getFilterValues(position) {
|
|
16082
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
16083
|
+
const filter = this.env.model.getters.getFilter({ sheetId, ...position });
|
|
16084
|
+
if (!filter) {
|
|
16085
|
+
return [];
|
|
16086
|
+
}
|
|
16087
|
+
const cellValues = (filter.filteredZone ? positions(filter.filteredZone) : [])
|
|
16088
|
+
.filter(({ row }) => !this.env.model.getters.isRowHidden(sheetId, row))
|
|
16089
|
+
.map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
|
|
16090
|
+
const filterValues = this.env.model.getters.getFilterValues({ sheetId, ...position });
|
|
16091
|
+
const strValues = [...cellValues, ...filterValues];
|
|
16092
|
+
const normalizedFilteredValues = filterValues.map(toLowerCase);
|
|
16093
|
+
// Set with lowercase values to avoid duplicates
|
|
16094
|
+
const normalizedValues = [...new Set(strValues.map(toLowerCase))];
|
|
16095
|
+
const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
|
|
16096
|
+
return sortedValues.map((normalizedValue) => {
|
|
16097
|
+
const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
|
|
16098
|
+
-1;
|
|
16099
|
+
return {
|
|
16100
|
+
checked,
|
|
16101
|
+
string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
|
|
16102
|
+
};
|
|
16103
|
+
});
|
|
16104
|
+
}
|
|
16105
|
+
checkValue(value) {
|
|
16106
|
+
var _a;
|
|
16107
|
+
this.state.selectedValue = value.string;
|
|
16108
|
+
value.checked = !value.checked;
|
|
16109
|
+
(_a = this.searchBar.el) === null || _a === void 0 ? void 0 : _a.focus();
|
|
16110
|
+
}
|
|
16111
|
+
onMouseMove(value) {
|
|
16112
|
+
this.state.selectedValue = value.string;
|
|
16113
|
+
}
|
|
16114
|
+
selectAll() {
|
|
16115
|
+
this.state.values.forEach((value) => (value.checked = true));
|
|
16116
|
+
}
|
|
16117
|
+
clearAll() {
|
|
16118
|
+
this.state.values.forEach((value) => (value.checked = false));
|
|
16119
|
+
}
|
|
16120
|
+
get filterTable() {
|
|
16121
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
16122
|
+
const position = this.props.filterPosition;
|
|
16123
|
+
return this.env.model.getters.getFilterTable({ sheetId, ...position });
|
|
16124
|
+
}
|
|
16125
|
+
get displayedValues() {
|
|
16126
|
+
if (!this.state.textFilter) {
|
|
16127
|
+
return this.state.values;
|
|
16128
|
+
}
|
|
16129
|
+
return fuzzyLookup(this.state.textFilter, this.state.values, (val) => val.string);
|
|
16130
|
+
}
|
|
16131
|
+
confirm() {
|
|
16132
|
+
var _a, _b;
|
|
16133
|
+
const position = this.props.filterPosition;
|
|
16134
|
+
this.env.model.dispatch("UPDATE_FILTER", {
|
|
16135
|
+
...position,
|
|
16136
|
+
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
16137
|
+
values: this.state.values.filter((val) => !val.checked).map((val) => val.string),
|
|
16138
|
+
});
|
|
16139
|
+
(_b = (_a = this.props).onClosed) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
16140
|
+
}
|
|
16141
|
+
cancel() {
|
|
16142
|
+
var _a, _b;
|
|
16143
|
+
(_b = (_a = this.props).onClosed) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
16144
|
+
}
|
|
16145
|
+
onKeyDown(ev) {
|
|
16146
|
+
const displayedValues = this.displayedValues;
|
|
16147
|
+
if (displayedValues.length === 0)
|
|
16148
|
+
return;
|
|
16149
|
+
let selectedIndex = undefined;
|
|
16150
|
+
if (this.state.selectedValue !== undefined) {
|
|
16151
|
+
const index = displayedValues.findIndex((val) => val.string === this.state.selectedValue);
|
|
16152
|
+
selectedIndex = index === -1 ? undefined : index;
|
|
16153
|
+
}
|
|
16154
|
+
switch (ev.key) {
|
|
16155
|
+
case "ArrowDown":
|
|
16156
|
+
if (selectedIndex === undefined) {
|
|
16157
|
+
selectedIndex = 0;
|
|
16158
|
+
}
|
|
16159
|
+
else {
|
|
16160
|
+
selectedIndex = Math.min(selectedIndex + 1, displayedValues.length - 1);
|
|
16161
|
+
}
|
|
16162
|
+
ev.preventDefault();
|
|
16163
|
+
ev.stopPropagation();
|
|
16164
|
+
break;
|
|
16165
|
+
case "ArrowUp":
|
|
16166
|
+
if (selectedIndex === undefined) {
|
|
16167
|
+
selectedIndex = displayedValues.length - 1;
|
|
16168
|
+
}
|
|
16169
|
+
else {
|
|
16170
|
+
selectedIndex = Math.max(selectedIndex - 1, 0);
|
|
16171
|
+
}
|
|
16172
|
+
ev.preventDefault();
|
|
16173
|
+
ev.stopPropagation();
|
|
16174
|
+
break;
|
|
16175
|
+
case "Enter":
|
|
16176
|
+
if (selectedIndex !== undefined) {
|
|
16177
|
+
this.checkValue(displayedValues[selectedIndex]);
|
|
16178
|
+
}
|
|
16179
|
+
ev.stopPropagation();
|
|
16180
|
+
ev.preventDefault();
|
|
16181
|
+
break;
|
|
16182
|
+
}
|
|
16183
|
+
this.state.selectedValue =
|
|
16184
|
+
selectedIndex !== undefined ? displayedValues[selectedIndex].string : undefined;
|
|
16185
|
+
if (ev.key === "ArrowUp" || ev.key === "ArrowDown") {
|
|
16186
|
+
this.scrollListToSelectedValue(ev.key);
|
|
16187
|
+
}
|
|
16188
|
+
}
|
|
16189
|
+
clearScrolledToValue() {
|
|
16190
|
+
this.state.values.forEach((val) => (val.scrolledTo = undefined));
|
|
16191
|
+
}
|
|
16192
|
+
scrollListToSelectedValue(arrow) {
|
|
16193
|
+
this.clearScrolledToValue();
|
|
16194
|
+
const selectedValue = this.state.values.find((val) => val.string === this.state.selectedValue);
|
|
16195
|
+
if (selectedValue) {
|
|
16196
|
+
selectedValue.scrolledTo = arrow === "ArrowUp" ? "top" : "bottom";
|
|
16197
|
+
}
|
|
16198
|
+
}
|
|
16199
|
+
sortFilterZone(sortDirection) {
|
|
16200
|
+
var _a, _b;
|
|
16201
|
+
const filterPosition = this.props.filterPosition;
|
|
16202
|
+
const filterTable = this.filterTable;
|
|
16203
|
+
if (!filterPosition || !filterTable || !filterTable.contentZone) {
|
|
16204
|
+
return;
|
|
16205
|
+
}
|
|
16206
|
+
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
16207
|
+
this.env.model.dispatch("SORT_CELLS", {
|
|
16208
|
+
sheetId,
|
|
16209
|
+
col: filterPosition.col,
|
|
16210
|
+
row: filterTable.contentZone.top,
|
|
16211
|
+
zone: filterTable.contentZone,
|
|
16212
|
+
sortDirection,
|
|
16213
|
+
sortOptions: { emptyCellAsZero: true, sortHeaders: true },
|
|
16214
|
+
});
|
|
16215
|
+
(_b = (_a = this.props).onClosed) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
16216
|
+
}
|
|
16217
|
+
}
|
|
16218
|
+
FilterMenu.size = { width: MENU_WIDTH, height: FILTER_MENU_HEIGHT };
|
|
16219
|
+
FilterMenu.template = "o-spreadsheet-FilterMenu";
|
|
16220
|
+
FilterMenu.style = CSS;
|
|
16221
|
+
FilterMenu.components = { FilterMenuValueItem };
|
|
16222
|
+
FilterMenu.props = {
|
|
16223
|
+
filterPosition: Object,
|
|
16224
|
+
onClosed: { type: Function, optional: true },
|
|
16225
|
+
};
|
|
16226
|
+
const FilterMenuPopoverBuilder = {
|
|
16227
|
+
onOpen: (position, getters) => {
|
|
16228
|
+
return {
|
|
16229
|
+
isOpen: true,
|
|
16230
|
+
props: { filterPosition: position },
|
|
16231
|
+
Component: FilterMenu,
|
|
16232
|
+
cellCorner: "BottomLeft",
|
|
16233
|
+
};
|
|
16234
|
+
},
|
|
16235
|
+
};
|
|
16236
|
+
|
|
16237
|
+
/**
|
|
16238
|
+
* Return the o-spreadsheet element position relative
|
|
16239
|
+
* to the browser viewport.
|
|
16240
|
+
*/
|
|
16241
|
+
function useSpreadsheetRect() {
|
|
16242
|
+
const position = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
|
|
16243
|
+
let spreadsheetElement = document.querySelector(".o-spreadsheet");
|
|
16244
|
+
updatePosition();
|
|
16245
|
+
function updatePosition() {
|
|
16246
|
+
if (!spreadsheetElement) {
|
|
16247
|
+
spreadsheetElement = document.querySelector(".o-spreadsheet");
|
|
16248
|
+
}
|
|
16249
|
+
if (spreadsheetElement) {
|
|
16250
|
+
const { top, left, width, height } = spreadsheetElement.getBoundingClientRect();
|
|
16251
|
+
position.x = left;
|
|
16252
|
+
position.y = top;
|
|
16253
|
+
position.width = width;
|
|
16254
|
+
position.height = height;
|
|
16255
|
+
}
|
|
16256
|
+
}
|
|
16257
|
+
owl.onMounted(updatePosition);
|
|
16258
|
+
owl.onPatched(updatePosition);
|
|
16259
|
+
return position;
|
|
16390
16260
|
}
|
|
16391
16261
|
/**
|
|
16392
|
-
*
|
|
16262
|
+
* Return the component (or ref's component) BoundingRect, relative
|
|
16263
|
+
* to the upper left corner of the screen (<body> element).
|
|
16264
|
+
*
|
|
16265
|
+
* Note: when used with a <Portal/> component, it will
|
|
16266
|
+
* return the portal position, not the teleported position.
|
|
16393
16267
|
*/
|
|
16394
|
-
function
|
|
16395
|
-
const
|
|
16396
|
-
|
|
16397
|
-
|
|
16398
|
-
|
|
16399
|
-
|
|
16400
|
-
|
|
16268
|
+
function useAbsoluteBoundingRect(ref) {
|
|
16269
|
+
const rect = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
|
|
16270
|
+
function updateElRect() {
|
|
16271
|
+
const el = ref.el;
|
|
16272
|
+
if (el === null) {
|
|
16273
|
+
return;
|
|
16274
|
+
}
|
|
16275
|
+
const { top, left, width, height } = el.getBoundingClientRect();
|
|
16276
|
+
rect.x = left;
|
|
16277
|
+
rect.y = top;
|
|
16278
|
+
rect.width = width;
|
|
16279
|
+
rect.height = height;
|
|
16401
16280
|
}
|
|
16402
|
-
|
|
16281
|
+
owl.onMounted(updateElRect);
|
|
16282
|
+
owl.onPatched(updateElRect);
|
|
16283
|
+
return rect;
|
|
16403
16284
|
}
|
|
16404
16285
|
/**
|
|
16405
|
-
* Get the
|
|
16286
|
+
* Get the rectangle inside which a popover should stay when being displayed.
|
|
16287
|
+
* It's the value defined in `env.getPopoverContainerRect`, or the Rect of the "o-spreadsheet"
|
|
16288
|
+
* element by default.
|
|
16289
|
+
*
|
|
16290
|
+
* Coordinates are expressed expressed as absolute DOM position.
|
|
16406
16291
|
*/
|
|
16407
|
-
function
|
|
16408
|
-
|
|
16409
|
-
|
|
16410
|
-
|
|
16292
|
+
function usePopoverContainer() {
|
|
16293
|
+
const container = owl.useState({ x: 0, y: 0, width: 0, height: 0 });
|
|
16294
|
+
const component = owl.useComponent();
|
|
16295
|
+
const spreadsheetRect = useSpreadsheetRect();
|
|
16296
|
+
function updateRect() {
|
|
16297
|
+
const env = component.env;
|
|
16298
|
+
const newRect = "getPopoverContainerRect" in env ? env.getPopoverContainerRect() : spreadsheetRect;
|
|
16299
|
+
container.x = newRect.x;
|
|
16300
|
+
container.y = newRect.y;
|
|
16301
|
+
container.width = newRect.width;
|
|
16302
|
+
container.height = newRect.height;
|
|
16411
16303
|
}
|
|
16412
|
-
|
|
16413
|
-
|
|
16414
|
-
|
|
16415
|
-
|
|
16416
|
-
|
|
16417
|
-
var _a;
|
|
16418
|
-
return cells.length === 1 && !cell.isFormula && !((_a = cell.format) === null || _a === void 0 ? void 0 : _a.match(DATETIME_FORMAT));
|
|
16419
|
-
},
|
|
16420
|
-
generateRule: () => {
|
|
16421
|
-
return { type: "COPY_MODIFIER" };
|
|
16422
|
-
},
|
|
16423
|
-
sequence: 10,
|
|
16424
|
-
})
|
|
16425
|
-
.add("increment_alphanumeric_value", {
|
|
16426
|
-
condition: (cell) => !cell.isFormula &&
|
|
16427
|
-
evaluateLiteral(cell.content).type === CellValueType.text &&
|
|
16428
|
-
alphaNumericValueRegExp.test(cell.content),
|
|
16429
|
-
generateRule: (cell, cells) => {
|
|
16430
|
-
const numberPostfix = parseInt(cell.content.match(numberPostfixRegExp)[0]);
|
|
16431
|
-
const prefix = cell.content.match(stringPrefixRegExp)[0];
|
|
16432
|
-
const numberPostfixLength = cell.content.length - prefix.length;
|
|
16433
|
-
const group = getGroup(cell, cells, (evaluatedCell) => evaluatedCell.type === CellValueType.text &&
|
|
16434
|
-
alphaNumericValueRegExp.test(evaluatedCell.value)) // get consecutive alphanumeric cells, no matter what the prefix is
|
|
16435
|
-
.filter((cell) => prefix === cell.value.toString().match(stringPrefixRegExp)[0])
|
|
16436
|
-
.map((cell) => parseInt(cell.value.toString().match(numberPostfixRegExp)[0]));
|
|
16437
|
-
const increment = calculateIncrementBasedOnGroup(group);
|
|
16438
|
-
return {
|
|
16439
|
-
type: "ALPHANUMERIC_INCREMENT_MODIFIER",
|
|
16440
|
-
prefix,
|
|
16441
|
-
current: numberPostfix,
|
|
16442
|
-
increment,
|
|
16443
|
-
numberPostfixLength,
|
|
16444
|
-
};
|
|
16445
|
-
},
|
|
16446
|
-
sequence: 15,
|
|
16447
|
-
})
|
|
16448
|
-
.add("copy_text", {
|
|
16449
|
-
condition: (cell) => !cell.isFormula && evaluateLiteral(cell.content).type === CellValueType.text,
|
|
16450
|
-
generateRule: () => {
|
|
16451
|
-
return { type: "COPY_MODIFIER" };
|
|
16452
|
-
},
|
|
16453
|
-
sequence: 20,
|
|
16454
|
-
})
|
|
16455
|
-
.add("update_formula", {
|
|
16456
|
-
condition: (cell) => cell.isFormula,
|
|
16457
|
-
generateRule: (_, cells) => {
|
|
16458
|
-
return { type: "FORMULA_MODIFIER", increment: cells.length, current: 0 };
|
|
16459
|
-
},
|
|
16460
|
-
sequence: 30,
|
|
16461
|
-
})
|
|
16462
|
-
.add("increment_number", {
|
|
16463
|
-
condition: (cell) => !cell.isFormula && evaluateLiteral(cell.content).type === CellValueType.number,
|
|
16464
|
-
generateRule: (cell, cells) => {
|
|
16465
|
-
const group = getGroup(cell, cells, (evaluatedCell) => evaluatedCell.type === CellValueType.number).map((cell) => Number(cell.value));
|
|
16466
|
-
const increment = calculateIncrementBasedOnGroup(group);
|
|
16467
|
-
const evaluation = evaluateLiteral(cell.content);
|
|
16468
|
-
return {
|
|
16469
|
-
type: "INCREMENT_MODIFIER",
|
|
16470
|
-
increment,
|
|
16471
|
-
current: evaluation.type === CellValueType.number ? evaluation.value : 0,
|
|
16472
|
-
};
|
|
16473
|
-
},
|
|
16474
|
-
sequence: 40,
|
|
16475
|
-
});
|
|
16304
|
+
updateRect();
|
|
16305
|
+
owl.onMounted(updateRect);
|
|
16306
|
+
owl.onPatched(updateRect);
|
|
16307
|
+
return container;
|
|
16308
|
+
}
|
|
16476
16309
|
|
|
16477
|
-
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
|
|
16481
|
-
|
|
16482
|
-
background-color: white;
|
|
16483
|
-
border-left: 3px solid red;
|
|
16484
|
-
padding: 10px;
|
|
16485
|
-
width: ${ERROR_TOOLTIP_WIDTH}px;
|
|
16486
|
-
box-sizing: border-box !important;
|
|
16487
|
-
}
|
|
16488
|
-
`;
|
|
16489
|
-
class ErrorToolTip extends owl.Component {
|
|
16310
|
+
/**
|
|
16311
|
+
* Compute the intersection of two rectangles. Returns nothing if the two rectangles don't overlap
|
|
16312
|
+
*/
|
|
16313
|
+
function rectIntersection(rect1, rect2) {
|
|
16314
|
+
return zoneToRect(intersection(rectToZone(rect1), rectToZone(rect2)));
|
|
16490
16315
|
}
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
|
-
|
|
16494
|
-
|
|
16495
|
-
|
|
16496
|
-
|
|
16497
|
-
};
|
|
16498
|
-
|
|
16499
|
-
|
|
16500
|
-
|
|
16501
|
-
|
|
16502
|
-
|
|
16503
|
-
|
|
16504
|
-
|
|
16505
|
-
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
|
|
16509
|
-
return { isOpen: false };
|
|
16510
|
-
},
|
|
16511
|
-
};
|
|
16316
|
+
function rectToZone(rect) {
|
|
16317
|
+
return {
|
|
16318
|
+
left: rect.x,
|
|
16319
|
+
top: rect.y,
|
|
16320
|
+
right: rect.x + rect.width,
|
|
16321
|
+
bottom: rect.y + rect.height,
|
|
16322
|
+
};
|
|
16323
|
+
}
|
|
16324
|
+
function zoneToRect(zone) {
|
|
16325
|
+
if (!zone)
|
|
16326
|
+
return undefined;
|
|
16327
|
+
return {
|
|
16328
|
+
x: zone.left,
|
|
16329
|
+
y: zone.top,
|
|
16330
|
+
width: zone.right - zone.left,
|
|
16331
|
+
height: zone.bottom - zone.top,
|
|
16332
|
+
};
|
|
16333
|
+
}
|
|
16512
16334
|
|
|
16513
|
-
css /*
|
|
16514
|
-
.o-
|
|
16515
|
-
|
|
16516
|
-
|
|
16517
|
-
|
|
16518
|
-
|
|
16519
|
-
|
|
16520
|
-
|
|
16335
|
+
css /* scss */ `
|
|
16336
|
+
.o-popover {
|
|
16337
|
+
position: absolute;
|
|
16338
|
+
z-index: ${ComponentsImportance.Popover};
|
|
16339
|
+
overflow: auto;
|
|
16340
|
+
box-shadow: 1px 2px 5px 2px rgb(51 51 51 / 15%);
|
|
16341
|
+
width: fit-content;
|
|
16342
|
+
height: fit-content;
|
|
16521
16343
|
}
|
|
16522
16344
|
`;
|
|
16523
|
-
class
|
|
16345
|
+
class Popover extends owl.Component {
|
|
16524
16346
|
constructor() {
|
|
16525
16347
|
super(...arguments);
|
|
16526
|
-
this.
|
|
16348
|
+
this.popoverRef = owl.useRef("popover");
|
|
16349
|
+
this.currentPosition = undefined;
|
|
16350
|
+
this.currentDisplayValue = undefined;
|
|
16351
|
+
this.spreadsheetRect = useSpreadsheetRect();
|
|
16527
16352
|
}
|
|
16528
16353
|
setup() {
|
|
16529
|
-
|
|
16530
|
-
|
|
16531
|
-
|
|
16354
|
+
this.containerRect = usePopoverContainer();
|
|
16355
|
+
// useEffect occurs after the DOM is created and the element width/height are computed, but before
|
|
16356
|
+
// the element in rendered, so we can still set its position
|
|
16357
|
+
owl.useEffect(() => {
|
|
16358
|
+
var _a, _b, _c, _d;
|
|
16359
|
+
if (!this.containerRect)
|
|
16360
|
+
throw new Error("Popover container is not defined");
|
|
16361
|
+
const el = this.popoverRef.el;
|
|
16362
|
+
const anchor = rectIntersection(this.props.anchorRect, this.containerRect);
|
|
16363
|
+
const newDisplay = anchor ? "block" : "none";
|
|
16364
|
+
if (this.currentDisplayValue !== "none" && newDisplay === "none") {
|
|
16365
|
+
(_b = (_a = this.props).onPopoverHidden) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
16366
|
+
}
|
|
16367
|
+
el.style.display = newDisplay;
|
|
16368
|
+
this.currentDisplayValue = newDisplay;
|
|
16369
|
+
if (!anchor)
|
|
16370
|
+
return;
|
|
16371
|
+
const propsMaxSize = { width: this.props.maxWidth, height: this.props.maxHeight };
|
|
16372
|
+
const elDims = {
|
|
16373
|
+
width: el.getBoundingClientRect().width,
|
|
16374
|
+
height: el.getBoundingClientRect().height,
|
|
16375
|
+
};
|
|
16376
|
+
const spreadsheetRect = this.spreadsheetRect;
|
|
16377
|
+
const popoverPositionHelper = this.props.positioning === "BottomLeft"
|
|
16378
|
+
? new BottomLeftPopoverContext(anchor, this.containerRect, propsMaxSize, spreadsheetRect)
|
|
16379
|
+
: new TopRightPopoverContext(anchor, this.containerRect, propsMaxSize, spreadsheetRect);
|
|
16380
|
+
const style = popoverPositionHelper.getCss(elDims, this.props.verticalOffset);
|
|
16381
|
+
for (const property of Object.keys(style)) {
|
|
16382
|
+
el.style[property] = style[property];
|
|
16383
|
+
}
|
|
16384
|
+
const newPosition = popoverPositionHelper.getCurrentPosition(elDims);
|
|
16385
|
+
if (this.currentPosition && newPosition !== this.currentPosition) {
|
|
16386
|
+
(_d = (_c = this.props).onPopoverMoved) === null || _d === void 0 ? void 0 : _d.call(_c);
|
|
16532
16387
|
}
|
|
16388
|
+
this.currentPosition = newPosition;
|
|
16533
16389
|
});
|
|
16534
16390
|
}
|
|
16535
|
-
|
|
16536
|
-
|
|
16537
|
-
|
|
16538
|
-
|
|
16391
|
+
}
|
|
16392
|
+
Popover.template = "o-spreadsheet-Popover";
|
|
16393
|
+
Popover.defaultProps = {
|
|
16394
|
+
positioning: "BottomLeft",
|
|
16395
|
+
verticalOffset: 0,
|
|
16396
|
+
onMouseWheel: () => { },
|
|
16397
|
+
onPopoverMoved: () => { },
|
|
16398
|
+
onPopoverHidden: () => { },
|
|
16399
|
+
};
|
|
16400
|
+
Popover.props = {
|
|
16401
|
+
anchorRect: Object,
|
|
16402
|
+
containerRect: { type: Object, optional: true },
|
|
16403
|
+
positioning: { type: String, optional: true },
|
|
16404
|
+
maxWidth: { type: Number, optional: true },
|
|
16405
|
+
maxHeight: { type: Number, optional: true },
|
|
16406
|
+
verticalOffset: { type: Number, optional: true },
|
|
16407
|
+
onMouseWheel: { type: Function, optional: true },
|
|
16408
|
+
onPopoverHidden: { type: Function, optional: true },
|
|
16409
|
+
onPopoverMoved: { type: Function, optional: true },
|
|
16410
|
+
slots: Object,
|
|
16411
|
+
};
|
|
16412
|
+
class PopoverPositionContext {
|
|
16413
|
+
constructor(anchorRect, containerRect, propsMaxSize, spreadsheetOffset) {
|
|
16414
|
+
this.anchorRect = anchorRect;
|
|
16415
|
+
this.containerRect = containerRect;
|
|
16416
|
+
this.propsMaxSize = propsMaxSize;
|
|
16417
|
+
this.spreadsheetOffset = spreadsheetOffset;
|
|
16418
|
+
}
|
|
16419
|
+
/** Check if there is enough space for the popover to be rendered at the bottom of the anchorRect */
|
|
16420
|
+
shouldRenderAtBottom(elementHeight) {
|
|
16421
|
+
return (elementHeight <= this.availableHeightDown ||
|
|
16422
|
+
this.availableHeightDown >= this.availableHeightUp);
|
|
16423
|
+
}
|
|
16424
|
+
/** Check if there is enough space for the popover to be rendered at the right of the anchorRect */
|
|
16425
|
+
shouldRenderAtRight(elementWidth) {
|
|
16426
|
+
return (elementWidth <= this.availableWidthRight ||
|
|
16427
|
+
this.availableWidthRight >= this.availableWidthLeft);
|
|
16428
|
+
}
|
|
16429
|
+
getMaxHeight(elementHeight) {
|
|
16430
|
+
const shouldRenderAtBottom = this.shouldRenderAtBottom(elementHeight);
|
|
16431
|
+
const availableHeight = shouldRenderAtBottom
|
|
16432
|
+
? this.availableHeightDown
|
|
16433
|
+
: this.availableHeightUp;
|
|
16434
|
+
return this.propsMaxSize.height
|
|
16435
|
+
? Math.min(availableHeight, this.propsMaxSize.height)
|
|
16436
|
+
: availableHeight;
|
|
16437
|
+
}
|
|
16438
|
+
getMaxWidth(elementWidth) {
|
|
16439
|
+
const shouldRenderAtRight = this.shouldRenderAtRight(elementWidth);
|
|
16440
|
+
const availableWidth = shouldRenderAtRight ? this.availableWidthRight : this.availableWidthLeft;
|
|
16441
|
+
return this.propsMaxSize.width
|
|
16442
|
+
? Math.min(availableWidth, this.propsMaxSize.width)
|
|
16443
|
+
: availableWidth;
|
|
16444
|
+
}
|
|
16445
|
+
getCss(elDims, verticalOffset) {
|
|
16446
|
+
const maxHeight = this.getMaxHeight(elDims.height);
|
|
16447
|
+
const maxWidth = this.getMaxWidth(elDims.width);
|
|
16448
|
+
const actualHeight = Math.min(maxHeight, elDims.height);
|
|
16449
|
+
const actualWidth = Math.min(maxWidth, elDims.width);
|
|
16450
|
+
const shouldRenderAtBottom = this.shouldRenderAtBottom(elDims.height);
|
|
16451
|
+
const shouldRenderAtRight = this.shouldRenderAtRight(elDims.width);
|
|
16452
|
+
verticalOffset = shouldRenderAtBottom ? verticalOffset : -verticalOffset;
|
|
16453
|
+
const cssProperties = {
|
|
16454
|
+
"max-height": maxHeight + "px",
|
|
16455
|
+
"max-width": maxWidth + "px",
|
|
16456
|
+
top: this.getTopCoordinate(actualHeight, shouldRenderAtBottom) -
|
|
16457
|
+
this.spreadsheetOffset.y -
|
|
16458
|
+
verticalOffset +
|
|
16459
|
+
"px",
|
|
16460
|
+
left: this.getLeftCoordinate(actualWidth, shouldRenderAtRight) - this.spreadsheetOffset.x + "px",
|
|
16461
|
+
};
|
|
16462
|
+
return cssProperties;
|
|
16463
|
+
}
|
|
16464
|
+
getCurrentPosition(elDims) {
|
|
16465
|
+
const shouldRenderAtBottom = this.shouldRenderAtBottom(elDims.height);
|
|
16466
|
+
const shouldRenderAtRight = this.shouldRenderAtRight(elDims.width);
|
|
16467
|
+
if (shouldRenderAtBottom && shouldRenderAtRight)
|
|
16468
|
+
return "BottomRight";
|
|
16469
|
+
if (shouldRenderAtBottom && !shouldRenderAtRight)
|
|
16470
|
+
return "BottomLeft";
|
|
16471
|
+
if (!shouldRenderAtBottom && shouldRenderAtRight)
|
|
16472
|
+
return "TopRight";
|
|
16473
|
+
return "TopLeft";
|
|
16474
|
+
}
|
|
16475
|
+
}
|
|
16476
|
+
class BottomLeftPopoverContext extends PopoverPositionContext {
|
|
16477
|
+
get availableHeightUp() {
|
|
16478
|
+
return this.anchorRect.y - this.containerRect.y;
|
|
16479
|
+
}
|
|
16480
|
+
get availableHeightDown() {
|
|
16481
|
+
return this.containerRect.height - this.availableHeightUp - this.anchorRect.height;
|
|
16482
|
+
}
|
|
16483
|
+
get availableWidthRight() {
|
|
16484
|
+
return this.containerRect.x + this.containerRect.width - this.anchorRect.x;
|
|
16485
|
+
}
|
|
16486
|
+
get availableWidthLeft() {
|
|
16487
|
+
return this.anchorRect.x + this.anchorRect.width - this.containerRect.x;
|
|
16488
|
+
}
|
|
16489
|
+
getTopCoordinate(elementHeight, shouldRenderAtBottom) {
|
|
16490
|
+
if (shouldRenderAtBottom) {
|
|
16491
|
+
return this.anchorRect.y + this.anchorRect.height;
|
|
16492
|
+
}
|
|
16493
|
+
else {
|
|
16494
|
+
return this.anchorRect.y - elementHeight;
|
|
16495
|
+
}
|
|
16496
|
+
}
|
|
16497
|
+
getLeftCoordinate(elementWidth, shouldRenderAtRight) {
|
|
16498
|
+
if (shouldRenderAtRight) {
|
|
16499
|
+
return this.anchorRect.x;
|
|
16500
|
+
}
|
|
16501
|
+
else {
|
|
16502
|
+
return this.anchorRect.x + this.anchorRect.width - elementWidth;
|
|
16539
16503
|
}
|
|
16540
|
-
(_b = (_a = this.itemRef.el).scrollIntoView) === null || _b === void 0 ? void 0 : _b.call(_a, {
|
|
16541
|
-
block: this.props.scrolledTo === "bottom" ? "end" : "start",
|
|
16542
|
-
});
|
|
16543
16504
|
}
|
|
16544
16505
|
}
|
|
16545
|
-
|
|
16546
|
-
|
|
16547
|
-
|
|
16548
|
-
|
|
16549
|
-
|
|
16550
|
-
|
|
16551
|
-
|
|
16552
|
-
|
|
16553
|
-
|
|
16506
|
+
class TopRightPopoverContext extends PopoverPositionContext {
|
|
16507
|
+
get availableHeightUp() {
|
|
16508
|
+
return this.anchorRect.y + this.anchorRect.height - this.containerRect.y;
|
|
16509
|
+
}
|
|
16510
|
+
get availableHeightDown() {
|
|
16511
|
+
return this.containerRect.y + this.containerRect.height - this.anchorRect.y;
|
|
16512
|
+
}
|
|
16513
|
+
get availableWidthRight() {
|
|
16514
|
+
return this.containerRect.width - this.anchorRect.width - this.availableWidthLeft;
|
|
16515
|
+
}
|
|
16516
|
+
get availableWidthLeft() {
|
|
16517
|
+
return this.anchorRect.x - this.containerRect.x;
|
|
16518
|
+
}
|
|
16519
|
+
getTopCoordinate(elementHeight, shouldRenderAtBottom) {
|
|
16520
|
+
if (shouldRenderAtBottom) {
|
|
16521
|
+
return this.anchorRect.y;
|
|
16522
|
+
}
|
|
16523
|
+
else {
|
|
16524
|
+
return this.anchorRect.y + this.anchorRect.height - elementHeight;
|
|
16525
|
+
}
|
|
16526
|
+
}
|
|
16527
|
+
getLeftCoordinate(elementWidth, shouldRenderAtRight) {
|
|
16528
|
+
if (shouldRenderAtRight) {
|
|
16529
|
+
return this.anchorRect.x + this.anchorRect.width;
|
|
16530
|
+
}
|
|
16531
|
+
else {
|
|
16532
|
+
return this.anchorRect.x - elementWidth;
|
|
16533
|
+
}
|
|
16534
|
+
}
|
|
16535
|
+
}
|
|
16554
16536
|
|
|
16555
|
-
|
|
16556
|
-
|
|
16557
|
-
|
|
16558
|
-
|
|
16559
|
-
|
|
16560
|
-
|
|
16561
|
-
|
|
16537
|
+
//------------------------------------------------------------------------------
|
|
16538
|
+
// Context Menu Component
|
|
16539
|
+
//------------------------------------------------------------------------------
|
|
16540
|
+
css /* scss */ `
|
|
16541
|
+
.o-menu {
|
|
16542
|
+
background-color: white;
|
|
16543
|
+
padding: ${MENU_VERTICAL_PADDING}px 0px;
|
|
16544
|
+
width: ${MENU_WIDTH}px;
|
|
16545
|
+
box-sizing: border-box !important;
|
|
16562
16546
|
|
|
16563
|
-
.o-
|
|
16547
|
+
.o-menu-item {
|
|
16564
16548
|
display: flex;
|
|
16549
|
+
justify-content: space-between;
|
|
16550
|
+
align-items: center;
|
|
16565
16551
|
box-sizing: border-box;
|
|
16566
16552
|
height: ${MENU_ITEM_HEIGHT}px;
|
|
16567
|
-
padding: 4px
|
|
16553
|
+
padding: 4px 16px;
|
|
16568
16554
|
cursor: pointer;
|
|
16569
16555
|
user-select: none;
|
|
16570
16556
|
|
|
16571
|
-
|
|
16572
|
-
|
|
16573
|
-
|
|
16574
|
-
|
|
16575
|
-
|
|
16576
|
-
input {
|
|
16577
|
-
box-sizing: border-box;
|
|
16578
|
-
margin-bottom: 5px;
|
|
16579
|
-
border: 1px solid #949494;
|
|
16580
|
-
height: 24px;
|
|
16581
|
-
padding-right: 28px;
|
|
16582
|
-
}
|
|
16583
|
-
|
|
16584
|
-
.o-search-icon {
|
|
16585
|
-
right: 5px;
|
|
16586
|
-
top: 4px;
|
|
16587
|
-
|
|
16588
|
-
svg {
|
|
16589
|
-
height: 16px;
|
|
16590
|
-
width: 16px;
|
|
16591
|
-
vertical-align: middle;
|
|
16557
|
+
.o-menu-item-name {
|
|
16558
|
+
overflow: hidden;
|
|
16559
|
+
white-space: nowrap;
|
|
16560
|
+
text-overflow: ellipsis;
|
|
16592
16561
|
}
|
|
16593
|
-
}
|
|
16594
16562
|
|
|
16595
|
-
|
|
16596
|
-
|
|
16597
|
-
|
|
16598
|
-
margin-bottom: 4px;
|
|
16599
|
-
|
|
16600
|
-
.o-filter-menu-action-text {
|
|
16601
|
-
cursor: pointer;
|
|
16602
|
-
margin-right: 10px;
|
|
16603
|
-
color: blue;
|
|
16604
|
-
text-decoration: underline;
|
|
16563
|
+
&.o-menu-root {
|
|
16564
|
+
display: flex;
|
|
16565
|
+
justify-content: space-between;
|
|
16605
16566
|
}
|
|
16606
|
-
|
|
16607
|
-
|
|
16608
|
-
|
|
16609
|
-
flex: auto;
|
|
16610
|
-
overflow-y: auto;
|
|
16611
|
-
border: 1px solid #949494;
|
|
16612
|
-
|
|
16613
|
-
.o-filter-menu-no-values {
|
|
16614
|
-
color: #949494;
|
|
16615
|
-
font-style: italic;
|
|
16567
|
+
.o-menu-item-icon {
|
|
16568
|
+
margin-top: auto;
|
|
16569
|
+
margin-bottom: auto;
|
|
16616
16570
|
}
|
|
16617
|
-
|
|
16618
|
-
|
|
16619
|
-
.o-filter-menu-buttons {
|
|
16620
|
-
margin-top: 9px;
|
|
16621
|
-
|
|
16622
|
-
.o-filter-menu-button {
|
|
16623
|
-
border: 1px solid lightgrey;
|
|
16624
|
-
padding: 6px 10px;
|
|
16625
|
-
cursor: pointer;
|
|
16626
|
-
border-radius: 4px;
|
|
16627
|
-
font-weight: 500;
|
|
16628
|
-
line-height: 16px;
|
|
16571
|
+
.o-icon {
|
|
16572
|
+
width: 10px;
|
|
16629
16573
|
}
|
|
16630
16574
|
|
|
16631
|
-
.
|
|
16632
|
-
|
|
16633
|
-
|
|
16634
|
-
background-color:
|
|
16575
|
+
&:not(.disabled) {
|
|
16576
|
+
&:hover,
|
|
16577
|
+
&.o-menu-item-active {
|
|
16578
|
+
background-color: #ebebeb;
|
|
16635
16579
|
}
|
|
16636
|
-
|
|
16637
|
-
|
|
16638
|
-
.o-filter-menu-button-primary {
|
|
16639
|
-
background-color: #188038;
|
|
16640
|
-
&:hover {
|
|
16641
|
-
background-color: #1d9641;
|
|
16580
|
+
.o-menu-item-description {
|
|
16581
|
+
color: grey;
|
|
16642
16582
|
}
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16583
|
+
}
|
|
16584
|
+
&.disabled {
|
|
16585
|
+
color: ${MENU_ITEM_DISABLED_COLOR};
|
|
16586
|
+
cursor: not-allowed;
|
|
16646
16587
|
}
|
|
16647
16588
|
}
|
|
16648
16589
|
}
|
|
16649
16590
|
`;
|
|
16650
|
-
class
|
|
16591
|
+
class Menu extends owl.Component {
|
|
16651
16592
|
constructor() {
|
|
16652
16593
|
super(...arguments);
|
|
16653
|
-
this.
|
|
16654
|
-
|
|
16655
|
-
|
|
16656
|
-
|
|
16594
|
+
this.subMenu = owl.useState({
|
|
16595
|
+
isOpen: false,
|
|
16596
|
+
position: null,
|
|
16597
|
+
scrollOffset: 0,
|
|
16598
|
+
menuItems: [],
|
|
16657
16599
|
});
|
|
16658
|
-
this.
|
|
16600
|
+
this.menuRef = owl.useRef("menu");
|
|
16601
|
+
this.position = useAbsoluteBoundingRect(this.menuRef);
|
|
16659
16602
|
}
|
|
16660
16603
|
setup() {
|
|
16604
|
+
owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
|
|
16605
|
+
owl.useExternalListener(window, "contextmenu", this.onExternalClick, { capture: true });
|
|
16661
16606
|
owl.onWillUpdateProps((nextProps) => {
|
|
16662
|
-
if (
|
|
16663
|
-
this.
|
|
16607
|
+
if (nextProps.menuItems !== this.props.menuItems) {
|
|
16608
|
+
this.closeSubMenu();
|
|
16664
16609
|
}
|
|
16665
16610
|
});
|
|
16666
|
-
this.state.values = this.getFilterValues(this.props.filterPosition);
|
|
16667
16611
|
}
|
|
16668
|
-
|
|
16669
|
-
|
|
16670
|
-
|
|
16671
|
-
|
|
16672
|
-
|
|
16612
|
+
get visibleMenuItems() {
|
|
16613
|
+
return this.props.menuItems.filter((x) => x.isVisible(this.env));
|
|
16614
|
+
}
|
|
16615
|
+
get subMenuPosition() {
|
|
16616
|
+
const position = Object.assign({}, this.subMenu.position);
|
|
16617
|
+
position.y -= this.subMenu.scrollOffset || 0;
|
|
16618
|
+
return position;
|
|
16619
|
+
}
|
|
16620
|
+
get menuHeight() {
|
|
16621
|
+
const menuItems = this.visibleMenuItems;
|
|
16622
|
+
let menuItemsHeight = this.getMenuItemsHeight(menuItems);
|
|
16623
|
+
// We don't display separator at the end of a menu
|
|
16624
|
+
if (menuItems[menuItems.length - 1].separator) {
|
|
16625
|
+
menuItemsHeight -= MENU_SEPARATOR_HEIGHT;
|
|
16673
16626
|
}
|
|
16674
|
-
const
|
|
16675
|
-
|
|
16676
|
-
.map(({ col, row }) => this.env.model.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue);
|
|
16677
|
-
const filterValues = this.env.model.getters.getFilterValues({ sheetId, ...position });
|
|
16678
|
-
const strValues = [...cellValues, ...filterValues];
|
|
16679
|
-
const normalizedFilteredValues = filterValues.map(toLowerCase);
|
|
16680
|
-
// Set with lowercase values to avoid duplicates
|
|
16681
|
-
const normalizedValues = [...new Set(strValues.map(toLowerCase))];
|
|
16682
|
-
const sortedValues = normalizedValues.sort((val1, val2) => val1.localeCompare(val2, undefined, { numeric: true, sensitivity: "base" }));
|
|
16683
|
-
return sortedValues.map((normalizedValue) => {
|
|
16684
|
-
const checked = normalizedFilteredValues.findIndex((filteredValue) => filteredValue === normalizedValue) ===
|
|
16685
|
-
-1;
|
|
16686
|
-
return {
|
|
16687
|
-
checked,
|
|
16688
|
-
string: strValues.find((val) => toLowerCase(val) === normalizedValue) || "",
|
|
16689
|
-
};
|
|
16690
|
-
});
|
|
16627
|
+
const menuHeight = 2 * MENU_VERTICAL_PADDING + menuItemsHeight;
|
|
16628
|
+
return this.props.maxHeight ? Math.min(menuHeight, this.props.maxHeight) : menuHeight;
|
|
16691
16629
|
}
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
|
|
16630
|
+
get popoverProps() {
|
|
16631
|
+
const isRoot = this.props.depth === 1;
|
|
16632
|
+
return {
|
|
16633
|
+
anchorRect: {
|
|
16634
|
+
x: this.props.position.x - MENU_WIDTH * (this.props.depth - 1),
|
|
16635
|
+
y: this.props.position.y,
|
|
16636
|
+
width: isRoot ? 0 : MENU_WIDTH,
|
|
16637
|
+
height: isRoot ? 0 : MENU_ITEM_HEIGHT,
|
|
16638
|
+
},
|
|
16639
|
+
positioning: "TopRight",
|
|
16640
|
+
verticalOffset: isRoot ? 0 : MENU_VERTICAL_PADDING,
|
|
16641
|
+
onPopoverHidden: () => this.closeSubMenu(),
|
|
16642
|
+
onPopoverMoved: () => this.closeSubMenu(),
|
|
16643
|
+
maxHeight: this.menuHeight,
|
|
16644
|
+
};
|
|
16697
16645
|
}
|
|
16698
|
-
|
|
16699
|
-
|
|
16646
|
+
getColor(menu) {
|
|
16647
|
+
return menu.textColor ? `color: ${menu.textColor}` : undefined;
|
|
16700
16648
|
}
|
|
16701
|
-
|
|
16702
|
-
|
|
16649
|
+
async activateMenu(menu) {
|
|
16650
|
+
var _a, _b, _c;
|
|
16651
|
+
const result = await ((_a = menu.action) === null || _a === void 0 ? void 0 : _a.call(menu, this.env));
|
|
16652
|
+
this.close();
|
|
16653
|
+
(_c = (_b = this.props).onMenuClicked) === null || _c === void 0 ? void 0 : _c.call(_b, { detail: result });
|
|
16703
16654
|
}
|
|
16704
|
-
|
|
16705
|
-
this.
|
|
16655
|
+
close() {
|
|
16656
|
+
this.closeSubMenu();
|
|
16657
|
+
this.props.onClose();
|
|
16706
16658
|
}
|
|
16707
|
-
|
|
16708
|
-
|
|
16709
|
-
|
|
16710
|
-
|
|
16659
|
+
/**
|
|
16660
|
+
* Return the number of pixels between the top of the menu
|
|
16661
|
+
* and the menu item at a given index.
|
|
16662
|
+
*/
|
|
16663
|
+
subMenuVerticalPosition(menuIndex) {
|
|
16664
|
+
const menusAbove = this.visibleMenuItems.slice(0, menuIndex);
|
|
16665
|
+
return this.position.y + this.getMenuItemsHeight(menusAbove) + MENU_VERTICAL_PADDING;
|
|
16711
16666
|
}
|
|
16712
|
-
|
|
16713
|
-
|
|
16714
|
-
|
|
16667
|
+
onExternalClick(ev) {
|
|
16668
|
+
// Don't close a root menu when clicked to open the submenus.
|
|
16669
|
+
const el = this.menuRef.el;
|
|
16670
|
+
if (el && getOpenedMenus().some((el) => isChildEvent(el, ev))) {
|
|
16671
|
+
return;
|
|
16715
16672
|
}
|
|
16716
|
-
|
|
16673
|
+
ev.closedMenuId = this.props.menuId;
|
|
16674
|
+
this.close();
|
|
16717
16675
|
}
|
|
16718
|
-
|
|
16719
|
-
|
|
16720
|
-
|
|
16721
|
-
this.env.model.dispatch("UPDATE_FILTER", {
|
|
16722
|
-
...position,
|
|
16723
|
-
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
16724
|
-
values: this.state.values.filter((val) => !val.checked).map((val) => val.string),
|
|
16725
|
-
});
|
|
16726
|
-
(_b = (_a = this.props).onClosed) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
16676
|
+
getMenuItemsHeight(menuItems) {
|
|
16677
|
+
const numberOfSeparators = menuItems.filter((m) => m.separator).length;
|
|
16678
|
+
return MENU_ITEM_HEIGHT * menuItems.length + MENU_SEPARATOR_HEIGHT * numberOfSeparators;
|
|
16727
16679
|
}
|
|
16728
|
-
|
|
16729
|
-
|
|
16730
|
-
(_b = (_a = this.props).onClosed) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
16680
|
+
getName(menu) {
|
|
16681
|
+
return menu.name(this.env);
|
|
16731
16682
|
}
|
|
16732
|
-
|
|
16733
|
-
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
const index = displayedValues.findIndex((val) => val.string === this.state.selectedValue);
|
|
16739
|
-
selectedIndex = index === -1 ? undefined : index;
|
|
16740
|
-
}
|
|
16741
|
-
switch (ev.key) {
|
|
16742
|
-
case "ArrowDown":
|
|
16743
|
-
if (selectedIndex === undefined) {
|
|
16744
|
-
selectedIndex = 0;
|
|
16745
|
-
}
|
|
16746
|
-
else {
|
|
16747
|
-
selectedIndex = Math.min(selectedIndex + 1, displayedValues.length - 1);
|
|
16748
|
-
}
|
|
16749
|
-
ev.preventDefault();
|
|
16750
|
-
ev.stopPropagation();
|
|
16751
|
-
break;
|
|
16752
|
-
case "ArrowUp":
|
|
16753
|
-
if (selectedIndex === undefined) {
|
|
16754
|
-
selectedIndex = displayedValues.length - 1;
|
|
16755
|
-
}
|
|
16756
|
-
else {
|
|
16757
|
-
selectedIndex = Math.max(selectedIndex - 1, 0);
|
|
16758
|
-
}
|
|
16759
|
-
ev.preventDefault();
|
|
16760
|
-
ev.stopPropagation();
|
|
16761
|
-
break;
|
|
16762
|
-
case "Enter":
|
|
16763
|
-
if (selectedIndex !== undefined) {
|
|
16764
|
-
this.checkValue(displayedValues[selectedIndex]);
|
|
16765
|
-
}
|
|
16766
|
-
ev.stopPropagation();
|
|
16767
|
-
ev.preventDefault();
|
|
16768
|
-
break;
|
|
16769
|
-
}
|
|
16770
|
-
this.state.selectedValue =
|
|
16771
|
-
selectedIndex !== undefined ? displayedValues[selectedIndex].string : undefined;
|
|
16772
|
-
if (ev.key === "ArrowUp" || ev.key === "ArrowDown") {
|
|
16773
|
-
this.scrollListToSelectedValue(ev.key);
|
|
16683
|
+
isRoot(menu) {
|
|
16684
|
+
return !menu.action;
|
|
16685
|
+
}
|
|
16686
|
+
isEnabled(menu) {
|
|
16687
|
+
if (menu.isEnabled(this.env)) {
|
|
16688
|
+
return this.env.model.getters.isReadonly() ? menu.isReadonlyAllowed : true;
|
|
16774
16689
|
}
|
|
16690
|
+
return false;
|
|
16775
16691
|
}
|
|
16776
|
-
|
|
16777
|
-
this.
|
|
16692
|
+
onScroll(ev) {
|
|
16693
|
+
this.subMenu.scrollOffset = ev.target.scrollTop;
|
|
16778
16694
|
}
|
|
16779
|
-
|
|
16780
|
-
|
|
16781
|
-
|
|
16782
|
-
|
|
16783
|
-
|
|
16695
|
+
/**
|
|
16696
|
+
* If the given menu is not disabled, open it's submenu at the
|
|
16697
|
+
* correct position according to available surrounding space.
|
|
16698
|
+
*/
|
|
16699
|
+
openSubMenu(menu, menuIndex) {
|
|
16700
|
+
const y = this.subMenuVerticalPosition(menuIndex);
|
|
16701
|
+
this.subMenu.position = {
|
|
16702
|
+
x: this.position.x + this.props.depth * MENU_WIDTH,
|
|
16703
|
+
y: y - (this.subMenu.scrollOffset || 0),
|
|
16704
|
+
};
|
|
16705
|
+
this.subMenu.menuItems = menu.children(this.env);
|
|
16706
|
+
this.subMenu.isOpen = true;
|
|
16707
|
+
this.subMenu.parentMenu = menu;
|
|
16708
|
+
}
|
|
16709
|
+
isParentMenu(subMenu, menuItem) {
|
|
16710
|
+
var _a;
|
|
16711
|
+
return ((_a = subMenu.parentMenu) === null || _a === void 0 ? void 0 : _a.id) === menuItem.id;
|
|
16712
|
+
}
|
|
16713
|
+
closeSubMenu() {
|
|
16714
|
+
this.subMenu.isOpen = false;
|
|
16715
|
+
this.subMenu.parentMenu = undefined;
|
|
16716
|
+
}
|
|
16717
|
+
onClickMenu(menu, menuIndex) {
|
|
16718
|
+
if (this.isEnabled(menu)) {
|
|
16719
|
+
if (this.isRoot(menu)) {
|
|
16720
|
+
this.openSubMenu(menu, menuIndex);
|
|
16721
|
+
}
|
|
16722
|
+
else {
|
|
16723
|
+
this.activateMenu(menu);
|
|
16724
|
+
}
|
|
16784
16725
|
}
|
|
16785
16726
|
}
|
|
16786
|
-
|
|
16787
|
-
|
|
16788
|
-
|
|
16789
|
-
|
|
16790
|
-
|
|
16791
|
-
|
|
16727
|
+
onMouseOver(menu, position) {
|
|
16728
|
+
if (menu.isEnabled(this.env)) {
|
|
16729
|
+
if (this.isRoot(menu)) {
|
|
16730
|
+
this.openSubMenu(menu, position);
|
|
16731
|
+
}
|
|
16732
|
+
else {
|
|
16733
|
+
this.closeSubMenu();
|
|
16734
|
+
}
|
|
16792
16735
|
}
|
|
16793
|
-
const sheetId = this.env.model.getters.getActiveSheetId();
|
|
16794
|
-
this.env.model.dispatch("SORT_CELLS", {
|
|
16795
|
-
sheetId,
|
|
16796
|
-
col: filterPosition.col,
|
|
16797
|
-
row: filterTable.contentZone.top,
|
|
16798
|
-
zone: filterTable.contentZone,
|
|
16799
|
-
sortDirection,
|
|
16800
|
-
sortOptions: { emptyCellAsZero: true, sortHeaders: true },
|
|
16801
|
-
});
|
|
16802
|
-
(_b = (_a = this.props).onClosed) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
16803
16736
|
}
|
|
16804
16737
|
}
|
|
16805
|
-
|
|
16806
|
-
|
|
16807
|
-
|
|
16808
|
-
|
|
16809
|
-
FilterMenu.props = {
|
|
16810
|
-
filterPosition: Object,
|
|
16811
|
-
onClosed: { type: Function, optional: true },
|
|
16738
|
+
Menu.template = "o-spreadsheet-Menu";
|
|
16739
|
+
Menu.components = { Menu, Popover };
|
|
16740
|
+
Menu.defaultProps = {
|
|
16741
|
+
depth: 1,
|
|
16812
16742
|
};
|
|
16813
|
-
|
|
16814
|
-
|
|
16815
|
-
|
|
16816
|
-
|
|
16817
|
-
|
|
16818
|
-
|
|
16819
|
-
|
|
16820
|
-
|
|
16821
|
-
},
|
|
16743
|
+
Menu.props = {
|
|
16744
|
+
position: Object,
|
|
16745
|
+
menuItems: Array,
|
|
16746
|
+
depth: { type: Number, optional: true },
|
|
16747
|
+
maxHeight: { type: Number, optional: true },
|
|
16748
|
+
onClose: Function,
|
|
16749
|
+
onMenuClicked: { type: Function, optional: true },
|
|
16750
|
+
menuId: { type: String, optional: true },
|
|
16822
16751
|
};
|
|
16823
16752
|
|
|
16824
16753
|
const LINK_TOOLTIP_HEIGHT = 32;
|
|
@@ -17152,83 +17081,6 @@ cellPopoverRegistry
|
|
|
17152
17081
|
const currenciesRegistry = new Registry();
|
|
17153
17082
|
|
|
17154
17083
|
class ImageFigure extends owl.Component {
|
|
17155
|
-
constructor() {
|
|
17156
|
-
super(...arguments);
|
|
17157
|
-
this.menuState = owl.useState({
|
|
17158
|
-
isOpen: false,
|
|
17159
|
-
position: null,
|
|
17160
|
-
});
|
|
17161
|
-
this.imageContainerRef = owl.useRef("o-image");
|
|
17162
|
-
this.menuButtonRef = owl.useRef("menuButton");
|
|
17163
|
-
this.menuButtonRect = useAbsoluteBoundingRect(this.menuButtonRef);
|
|
17164
|
-
this.position = useAbsoluteBoundingRect(this.imageContainerRef);
|
|
17165
|
-
this.menuItems = createMenu([
|
|
17166
|
-
{
|
|
17167
|
-
id: "copy",
|
|
17168
|
-
name: _lt("Copy"),
|
|
17169
|
-
description: "Ctrl+C",
|
|
17170
|
-
action: async () => {
|
|
17171
|
-
this.env.model.dispatch("SELECT_FIGURE", { id: this.figureId });
|
|
17172
|
-
this.env.model.dispatch("COPY");
|
|
17173
|
-
await this.env.clipboard.write(this.env.model.getters.getClipboardContent());
|
|
17174
|
-
},
|
|
17175
|
-
},
|
|
17176
|
-
{
|
|
17177
|
-
id: "cut",
|
|
17178
|
-
name: _lt("Cut"),
|
|
17179
|
-
description: "Ctrl+X",
|
|
17180
|
-
action: async () => {
|
|
17181
|
-
this.env.model.dispatch("SELECT_FIGURE", { id: this.figureId });
|
|
17182
|
-
this.env.model.dispatch("CUT");
|
|
17183
|
-
await this.env.clipboard.write(this.env.model.getters.getClipboardContent());
|
|
17184
|
-
},
|
|
17185
|
-
},
|
|
17186
|
-
{
|
|
17187
|
-
id: "reset_size",
|
|
17188
|
-
name: _lt("Reset size"),
|
|
17189
|
-
action: () => {
|
|
17190
|
-
const size = this.env.model.getters.getImage(this.env.model.getters.getActiveSheetId(), this.figureId).size;
|
|
17191
|
-
const { height, width } = getMaxFigureSize(this.env.model.getters, size);
|
|
17192
|
-
this.env.model.dispatch("UPDATE_FIGURE", {
|
|
17193
|
-
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
17194
|
-
id: this.figureId,
|
|
17195
|
-
height,
|
|
17196
|
-
width,
|
|
17197
|
-
});
|
|
17198
|
-
},
|
|
17199
|
-
},
|
|
17200
|
-
{
|
|
17201
|
-
id: "delete",
|
|
17202
|
-
name: _lt("Delete image"),
|
|
17203
|
-
description: "delete",
|
|
17204
|
-
action: () => {
|
|
17205
|
-
this.env.model.dispatch("DELETE_FIGURE", {
|
|
17206
|
-
sheetId: this.env.model.getters.getActiveSheetId(),
|
|
17207
|
-
id: this.figureId,
|
|
17208
|
-
});
|
|
17209
|
-
},
|
|
17210
|
-
},
|
|
17211
|
-
]);
|
|
17212
|
-
}
|
|
17213
|
-
onContextMenu(ev) {
|
|
17214
|
-
const position = {
|
|
17215
|
-
x: this.position.x + ev.offsetX,
|
|
17216
|
-
y: this.position.y + ev.offsetY,
|
|
17217
|
-
};
|
|
17218
|
-
this.openContextMenu(position);
|
|
17219
|
-
}
|
|
17220
|
-
showMenu() {
|
|
17221
|
-
const { x, y, width } = this.menuButtonRect;
|
|
17222
|
-
const menuPosition = {
|
|
17223
|
-
x: x >= MENU_WIDTH ? x - MENU_WIDTH : x + width,
|
|
17224
|
-
y: y,
|
|
17225
|
-
};
|
|
17226
|
-
this.openContextMenu(menuPosition);
|
|
17227
|
-
}
|
|
17228
|
-
openContextMenu(position) {
|
|
17229
|
-
this.menuState.isOpen = true;
|
|
17230
|
-
this.menuState.position = position;
|
|
17231
|
-
}
|
|
17232
17084
|
// ---------------------------------------------------------------------------
|
|
17233
17085
|
// Getters
|
|
17234
17086
|
// ---------------------------------------------------------------------------
|
|
@@ -17236,25 +17088,109 @@ class ImageFigure extends owl.Component {
|
|
|
17236
17088
|
return this.props.figure.id;
|
|
17237
17089
|
}
|
|
17238
17090
|
get getImagePath() {
|
|
17239
|
-
return this.env.model.getters.
|
|
17240
|
-
.path;
|
|
17091
|
+
return this.env.model.getters.getImagePath(this.figureId);
|
|
17241
17092
|
}
|
|
17242
17093
|
}
|
|
17243
17094
|
ImageFigure.template = "o-spreadsheet-ImageFigure";
|
|
17244
|
-
ImageFigure.components = {
|
|
17095
|
+
ImageFigure.components = {};
|
|
17245
17096
|
ImageFigure.props = {
|
|
17246
17097
|
figure: Object,
|
|
17247
17098
|
onFigureDeleted: Function,
|
|
17248
17099
|
};
|
|
17249
17100
|
|
|
17250
17101
|
const figureRegistry = new Registry();
|
|
17251
|
-
figureRegistry.add("chart", {
|
|
17102
|
+
figureRegistry.add("chart", {
|
|
17103
|
+
Component: ChartFigure,
|
|
17104
|
+
SidePanelComponent: "ChartPanel",
|
|
17105
|
+
menuBuilder: getChartMenu,
|
|
17106
|
+
});
|
|
17252
17107
|
figureRegistry.add("image", {
|
|
17253
17108
|
Component: ImageFigure,
|
|
17254
17109
|
keepRatio: true,
|
|
17255
17110
|
minFigSize: 20,
|
|
17256
17111
|
borderWidth: 0,
|
|
17257
|
-
|
|
17112
|
+
menuBuilder: getImageMenuRegistry,
|
|
17113
|
+
});
|
|
17114
|
+
function getChartMenu(figureId, onFigureDeleted, env) {
|
|
17115
|
+
const menuItemSpecs = [
|
|
17116
|
+
{
|
|
17117
|
+
id: "edit",
|
|
17118
|
+
name: _lt("Edit"),
|
|
17119
|
+
sequence: 1,
|
|
17120
|
+
action: () => {
|
|
17121
|
+
env.model.dispatch("SELECT_FIGURE", { id: figureId });
|
|
17122
|
+
env.openSidePanel("ChartPanel");
|
|
17123
|
+
},
|
|
17124
|
+
},
|
|
17125
|
+
getCopyMenuItem(figureId, env),
|
|
17126
|
+
getCutMenuItem(figureId, env),
|
|
17127
|
+
getDeleteMenuItem(figureId, onFigureDeleted, env),
|
|
17128
|
+
];
|
|
17129
|
+
return createMenu(menuItemSpecs);
|
|
17130
|
+
}
|
|
17131
|
+
function getImageMenuRegistry(figureId, onFigureDeleted, env) {
|
|
17132
|
+
const menuItemSpecs = [
|
|
17133
|
+
getCopyMenuItem(figureId, env),
|
|
17134
|
+
getCutMenuItem(figureId, env),
|
|
17135
|
+
{
|
|
17136
|
+
id: "reset_size",
|
|
17137
|
+
name: _lt("Reset size"),
|
|
17138
|
+
sequence: 4,
|
|
17139
|
+
action: () => {
|
|
17140
|
+
const size = env.model.getters.getImageSize(figureId);
|
|
17141
|
+
const { height, width } = getMaxFigureSize(env.model.getters, size);
|
|
17142
|
+
env.model.dispatch("UPDATE_FIGURE", {
|
|
17143
|
+
sheetId: env.model.getters.getActiveSheetId(),
|
|
17144
|
+
id: figureId,
|
|
17145
|
+
height,
|
|
17146
|
+
width,
|
|
17147
|
+
});
|
|
17148
|
+
},
|
|
17149
|
+
},
|
|
17150
|
+
getDeleteMenuItem(figureId, onFigureDeleted, env),
|
|
17151
|
+
];
|
|
17152
|
+
return createMenu(menuItemSpecs);
|
|
17153
|
+
}
|
|
17154
|
+
function getCopyMenuItem(figureId, env) {
|
|
17155
|
+
return {
|
|
17156
|
+
id: "copy",
|
|
17157
|
+
name: _lt("Copy"),
|
|
17158
|
+
sequence: 2,
|
|
17159
|
+
description: "Ctrl+C",
|
|
17160
|
+
action: async () => {
|
|
17161
|
+
env.model.dispatch("SELECT_FIGURE", { id: figureId });
|
|
17162
|
+
env.model.dispatch("COPY");
|
|
17163
|
+
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17164
|
+
},
|
|
17165
|
+
};
|
|
17166
|
+
}
|
|
17167
|
+
function getCutMenuItem(figureId, env) {
|
|
17168
|
+
return {
|
|
17169
|
+
id: "cut",
|
|
17170
|
+
name: _lt("Cut"),
|
|
17171
|
+
sequence: 3,
|
|
17172
|
+
description: "Ctrl+X",
|
|
17173
|
+
action: async () => {
|
|
17174
|
+
env.model.dispatch("SELECT_FIGURE", { id: figureId });
|
|
17175
|
+
env.model.dispatch("CUT");
|
|
17176
|
+
await env.clipboard.write(env.model.getters.getClipboardContent());
|
|
17177
|
+
},
|
|
17178
|
+
};
|
|
17179
|
+
}
|
|
17180
|
+
function getDeleteMenuItem(figureId, onFigureDeleted, env) {
|
|
17181
|
+
return {
|
|
17182
|
+
id: "delete",
|
|
17183
|
+
name: _lt("Delete"),
|
|
17184
|
+
sequence: 10,
|
|
17185
|
+
action: () => {
|
|
17186
|
+
env.model.dispatch("DELETE_FIGURE", {
|
|
17187
|
+
sheetId: env.model.getters.getActiveSheetId(),
|
|
17188
|
+
id: figureId,
|
|
17189
|
+
});
|
|
17190
|
+
onFigureDeleted();
|
|
17191
|
+
},
|
|
17192
|
+
};
|
|
17193
|
+
}
|
|
17258
17194
|
|
|
17259
17195
|
const inverseCommandRegistry = new Registry()
|
|
17260
17196
|
.add("ADD_COLUMNS_ROWS", inverseAddColumnsRows)
|
|
@@ -18042,9 +17978,9 @@ class SelectionInput extends owl.Component {
|
|
|
18042
17978
|
const ranges = existingSelectionRange.length
|
|
18043
17979
|
? existingSelectionRange
|
|
18044
17980
|
: this.props.ranges
|
|
18045
|
-
? this.props.ranges.map((xc,
|
|
17981
|
+
? this.props.ranges.map((xc, id) => ({
|
|
18046
17982
|
xc,
|
|
18047
|
-
id
|
|
17983
|
+
id,
|
|
18048
17984
|
isFocused: false,
|
|
18049
17985
|
}))
|
|
18050
17986
|
: [];
|
|
@@ -18110,6 +18046,7 @@ class SelectionInput extends owl.Component {
|
|
|
18110
18046
|
else if (ev.key === "Enter") {
|
|
18111
18047
|
const target = ev.target;
|
|
18112
18048
|
target.blur();
|
|
18049
|
+
this.confirm();
|
|
18113
18050
|
}
|
|
18114
18051
|
}
|
|
18115
18052
|
focus(rangeId) {
|
|
@@ -18138,7 +18075,7 @@ class SelectionInput extends owl.Component {
|
|
|
18138
18075
|
});
|
|
18139
18076
|
this.triggerChange();
|
|
18140
18077
|
}
|
|
18141
|
-
|
|
18078
|
+
confirm() {
|
|
18142
18079
|
var _a, _b;
|
|
18143
18080
|
this.env.model.dispatch("UNFOCUS_SELECTION_INPUT");
|
|
18144
18081
|
const ranges = this.env.model.getters.getSelectionInputValue(this.id);
|
|
@@ -18233,7 +18170,6 @@ class LineBarPieConfigPanel extends owl.Component {
|
|
|
18233
18170
|
LineBarPieConfigPanel.template = "o-spreadsheet-LineBarPieConfigPanel";
|
|
18234
18171
|
LineBarPieConfigPanel.components = { SelectionInput };
|
|
18235
18172
|
LineBarPieConfigPanel.props = {
|
|
18236
|
-
sheetId: String,
|
|
18237
18173
|
figureId: String,
|
|
18238
18174
|
definition: Object,
|
|
18239
18175
|
updateChart: Function,
|
|
@@ -18266,9 +18202,6 @@ const GRADIENT_WIDTH = PICKER_WIDTH - 2 * LINE_HORIZONTAL_PADDING - 2 * ITEM_BOR
|
|
|
18266
18202
|
const GRADIENT_HEIGHT = PICKER_WIDTH - 50;
|
|
18267
18203
|
css /* scss */ `
|
|
18268
18204
|
.o-color-picker {
|
|
18269
|
-
position: absolute;
|
|
18270
|
-
top: calc(100% + 5px);
|
|
18271
|
-
z-index: ${ComponentsImportance.ColorPicker};
|
|
18272
18205
|
padding: ${PICKER_PADDING}px 0px;
|
|
18273
18206
|
box-shadow: 1px 2px 5px 2px rgba(51, 51, 51, 0.15);
|
|
18274
18207
|
background-color: white;
|
|
@@ -18383,15 +18316,6 @@ css /* scss */ `
|
|
|
18383
18316
|
width: 100%;
|
|
18384
18317
|
}
|
|
18385
18318
|
}
|
|
18386
|
-
&.right {
|
|
18387
|
-
left: 0;
|
|
18388
|
-
}
|
|
18389
|
-
&.left {
|
|
18390
|
-
right: 0;
|
|
18391
|
-
}
|
|
18392
|
-
&.center {
|
|
18393
|
-
left: calc(50% - ${PICKER_WIDTH / 2}px);
|
|
18394
|
-
}
|
|
18395
18319
|
}
|
|
18396
18320
|
.o-magnifier-glass {
|
|
18397
18321
|
position: absolute;
|
|
@@ -18426,18 +18350,22 @@ class ColorPicker extends owl.Component {
|
|
|
18426
18350
|
});
|
|
18427
18351
|
}
|
|
18428
18352
|
get colorPickerStyle() {
|
|
18429
|
-
if (this.props.maxHeight
|
|
18430
|
-
return "";
|
|
18431
|
-
if (this.props.maxHeight <= 0) {
|
|
18353
|
+
if (this.props.maxHeight !== undefined && this.props.maxHeight <= 0) {
|
|
18432
18354
|
return cssPropertiesToCss({ display: "none" });
|
|
18433
18355
|
}
|
|
18434
|
-
return
|
|
18435
|
-
|
|
18436
|
-
|
|
18356
|
+
return "";
|
|
18357
|
+
}
|
|
18358
|
+
get popoverProps() {
|
|
18359
|
+
return {
|
|
18360
|
+
anchorRect: this.props.anchorRect,
|
|
18361
|
+
maxHeight: this.props.maxHeight,
|
|
18362
|
+
positioning: "BottomLeft",
|
|
18363
|
+
verticalOffset: 0,
|
|
18364
|
+
};
|
|
18437
18365
|
}
|
|
18438
18366
|
onColorClick(color) {
|
|
18439
18367
|
if (color) {
|
|
18440
|
-
this.props.onColorPicked(color);
|
|
18368
|
+
this.props.onColorPicked(toHex(color));
|
|
18441
18369
|
}
|
|
18442
18370
|
}
|
|
18443
18371
|
getCheckMarkColor() {
|
|
@@ -18452,8 +18380,10 @@ class ColorPicker extends owl.Component {
|
|
|
18452
18380
|
this.state.isCurrentColorInvalid = true;
|
|
18453
18381
|
return;
|
|
18454
18382
|
}
|
|
18383
|
+
const color = toHex(this.state.currentColor);
|
|
18455
18384
|
this.state.isCurrentColorInvalid = false;
|
|
18456
|
-
this.props.onColorPicked(
|
|
18385
|
+
this.props.onColorPicked(color);
|
|
18386
|
+
this.state.currentColor = color;
|
|
18457
18387
|
}
|
|
18458
18388
|
toggleColorPicker() {
|
|
18459
18389
|
this.state.showGradient = !this.state.showGradient;
|
|
@@ -18488,11 +18418,12 @@ ColorPicker.template = "o-spreadsheet-ColorPicker";
|
|
|
18488
18418
|
ColorPicker.defaultProps = {
|
|
18489
18419
|
currentColor: "", //TODO Change it to false instead of empty string
|
|
18490
18420
|
};
|
|
18421
|
+
ColorPicker.components = { Popover };
|
|
18491
18422
|
ColorPicker.props = {
|
|
18492
|
-
dropdownDirection: { type: String, optional: true },
|
|
18493
18423
|
onColorPicked: Function,
|
|
18494
18424
|
currentColor: { type: String, optional: true },
|
|
18495
18425
|
maxHeight: { type: Number, optional: true },
|
|
18426
|
+
anchorRect: Object,
|
|
18496
18427
|
};
|
|
18497
18428
|
|
|
18498
18429
|
css /* scss */ `
|
|
@@ -18528,11 +18459,25 @@ css /* scss */ `
|
|
|
18528
18459
|
}
|
|
18529
18460
|
`;
|
|
18530
18461
|
class ColorPickerWidget extends owl.Component {
|
|
18462
|
+
constructor() {
|
|
18463
|
+
super(...arguments);
|
|
18464
|
+
this.colorPickerButtonRef = owl.useRef("colorPickerButton");
|
|
18465
|
+
}
|
|
18531
18466
|
get iconStyle() {
|
|
18532
18467
|
return this.props.currentColor
|
|
18533
18468
|
? `border-color: ${this.props.currentColor}`
|
|
18534
18469
|
: "border-bottom-style: hidden";
|
|
18535
18470
|
}
|
|
18471
|
+
get colorPickerAnchorRect() {
|
|
18472
|
+
const button = this.colorPickerButtonRef.el;
|
|
18473
|
+
const buttonRect = button.getBoundingClientRect();
|
|
18474
|
+
return {
|
|
18475
|
+
x: buttonRect.x,
|
|
18476
|
+
y: buttonRect.y,
|
|
18477
|
+
width: buttonRect.width,
|
|
18478
|
+
height: buttonRect.height,
|
|
18479
|
+
};
|
|
18480
|
+
}
|
|
18536
18481
|
}
|
|
18537
18482
|
ColorPickerWidget.template = "o-spreadsheet-ColorPickerWidget";
|
|
18538
18483
|
ColorPickerWidget.components = { ColorPicker };
|
|
@@ -18542,7 +18487,6 @@ ColorPickerWidget.props = {
|
|
|
18542
18487
|
showColorPicker: Boolean,
|
|
18543
18488
|
onColorPicked: Function,
|
|
18544
18489
|
icon: String,
|
|
18545
|
-
dropdownDirection: { type: String, optional: true },
|
|
18546
18490
|
title: { type: String, optional: true },
|
|
18547
18491
|
disabled: { type: Boolean, optional: true },
|
|
18548
18492
|
dropdownMaxHeight: { type: Number, optional: true },
|
|
@@ -18622,7 +18566,6 @@ class GaugeChartConfigPanel extends owl.Component {
|
|
|
18622
18566
|
GaugeChartConfigPanel.template = "o-spreadsheet-GaugeChartConfigPanel";
|
|
18623
18567
|
GaugeChartConfigPanel.components = { SelectionInput };
|
|
18624
18568
|
GaugeChartConfigPanel.props = {
|
|
18625
|
-
sheetId: String,
|
|
18626
18569
|
figureId: String,
|
|
18627
18570
|
definition: Object,
|
|
18628
18571
|
updateChart: Function,
|
|
@@ -18769,7 +18712,7 @@ GaugeChartDesignPanel.props = {
|
|
|
18769
18712
|
|
|
18770
18713
|
class LineConfigPanel extends LineBarPieConfigPanel {
|
|
18771
18714
|
get canTreatLabelsAsText() {
|
|
18772
|
-
const chart = this.env.model.getters.getChart(this.props.
|
|
18715
|
+
const chart = this.env.model.getters.getChart(this.props.figureId);
|
|
18773
18716
|
if (chart && chart instanceof LineChart) {
|
|
18774
18717
|
return canChartParseLabels(chart.labelRange, this.env.model.getters);
|
|
18775
18718
|
}
|
|
@@ -18846,7 +18789,6 @@ class ScorecardChartConfigPanel extends owl.Component {
|
|
|
18846
18789
|
ScorecardChartConfigPanel.template = "o-spreadsheet-ScorecardChartConfigPanel";
|
|
18847
18790
|
ScorecardChartConfigPanel.components = { SelectionInput };
|
|
18848
18791
|
ScorecardChartConfigPanel.props = {
|
|
18849
|
-
sheetId: String,
|
|
18850
18792
|
figureId: String,
|
|
18851
18793
|
definition: Object,
|
|
18852
18794
|
updateChart: Function,
|
|
@@ -18982,7 +18924,7 @@ class ChartPanel extends owl.Component {
|
|
|
18982
18924
|
else {
|
|
18983
18925
|
this.shouldUpdateChart = true;
|
|
18984
18926
|
}
|
|
18985
|
-
if (!this.env.model.getters.isChartDefined(this.
|
|
18927
|
+
if (!this.env.model.getters.isChartDefined(this.figureId)) {
|
|
18986
18928
|
this.props.onCloseSidePanel();
|
|
18987
18929
|
return;
|
|
18988
18930
|
}
|
|
@@ -19003,7 +18945,7 @@ class ChartPanel extends owl.Component {
|
|
|
19003
18945
|
});
|
|
19004
18946
|
}
|
|
19005
18947
|
onTypeChange(type) {
|
|
19006
|
-
const context = this.env.model.getters.getContextCreationChart(this.
|
|
18948
|
+
const context = this.env.model.getters.getContextCreationChart(this.figureId);
|
|
19007
18949
|
if (!context) {
|
|
19008
18950
|
throw new Error("Chart not defined.");
|
|
19009
18951
|
}
|
|
@@ -19015,7 +18957,7 @@ class ChartPanel extends owl.Component {
|
|
|
19015
18957
|
});
|
|
19016
18958
|
}
|
|
19017
18959
|
get chartPanel() {
|
|
19018
|
-
const type = this.env.model.getters.getChartType(this.
|
|
18960
|
+
const type = this.env.model.getters.getChartType(this.figureId);
|
|
19019
18961
|
if (!type) {
|
|
19020
18962
|
throw new Error("Chart not defined.");
|
|
19021
18963
|
}
|
|
@@ -19025,8 +18967,8 @@ class ChartPanel extends owl.Component {
|
|
|
19025
18967
|
}
|
|
19026
18968
|
return chartPanel;
|
|
19027
18969
|
}
|
|
19028
|
-
getChartDefinition() {
|
|
19029
|
-
return this.env.model.getters.getChartDefinition(
|
|
18970
|
+
getChartDefinition(figureId = this.figureId) {
|
|
18971
|
+
return this.env.model.getters.getChartDefinition(figureId);
|
|
19030
18972
|
}
|
|
19031
18973
|
get chartTypes() {
|
|
19032
18974
|
return getChartTypes();
|
|
@@ -20178,9 +20120,8 @@ css /*SCSS*/ `
|
|
|
20178
20120
|
|
|
20179
20121
|
.o-figure-menu {
|
|
20180
20122
|
right: 0px;
|
|
20123
|
+
top: 0px;
|
|
20181
20124
|
display: none;
|
|
20182
|
-
position: absolute;
|
|
20183
|
-
padding: 5px;
|
|
20184
20125
|
}
|
|
20185
20126
|
|
|
20186
20127
|
.o-figure-menu-item {
|
|
@@ -20198,7 +20139,10 @@ css /*SCSS*/ `
|
|
|
20198
20139
|
class FigureComponent extends owl.Component {
|
|
20199
20140
|
constructor() {
|
|
20200
20141
|
super(...arguments);
|
|
20142
|
+
this.menuState = owl.useState({ isOpen: false, position: null, menuItems: [] });
|
|
20201
20143
|
this.figureRef = owl.useRef("figure");
|
|
20144
|
+
this.menuButtonRef = owl.useRef("menuButton");
|
|
20145
|
+
this.menuButtonRect = useAbsoluteBoundingRect(this.menuButtonRef);
|
|
20202
20146
|
}
|
|
20203
20147
|
get isSelected() {
|
|
20204
20148
|
return this.env.model.getters.getSelectedFigureId() === this.props.figure.id;
|
|
@@ -20306,9 +20250,33 @@ class FigureComponent extends owl.Component {
|
|
|
20306
20250
|
break;
|
|
20307
20251
|
}
|
|
20308
20252
|
}
|
|
20253
|
+
onContextMenu(ev) {
|
|
20254
|
+
if (this.env.isDashboard())
|
|
20255
|
+
return;
|
|
20256
|
+
const position = {
|
|
20257
|
+
x: ev.clientX,
|
|
20258
|
+
y: ev.clientY,
|
|
20259
|
+
};
|
|
20260
|
+
this.openContextMenu(position);
|
|
20261
|
+
}
|
|
20262
|
+
showMenu() {
|
|
20263
|
+
const { x, y, width } = this.menuButtonRect;
|
|
20264
|
+
const menuPosition = {
|
|
20265
|
+
x: x >= MENU_WIDTH ? x - MENU_WIDTH : x + width,
|
|
20266
|
+
y: y,
|
|
20267
|
+
};
|
|
20268
|
+
this.openContextMenu(menuPosition);
|
|
20269
|
+
}
|
|
20270
|
+
openContextMenu(position) {
|
|
20271
|
+
this.menuState.isOpen = true;
|
|
20272
|
+
this.menuState.position = position;
|
|
20273
|
+
this.menuState.menuItems = figureRegistry
|
|
20274
|
+
.get(this.props.figure.tag)
|
|
20275
|
+
.menuBuilder(this.props.figure.id, this.props.onFigureDeleted, this.env);
|
|
20276
|
+
}
|
|
20309
20277
|
}
|
|
20310
20278
|
FigureComponent.template = "o-spreadsheet-FigureComponent";
|
|
20311
|
-
FigureComponent.components = {};
|
|
20279
|
+
FigureComponent.components = { Menu };
|
|
20312
20280
|
FigureComponent.defaultProps = {
|
|
20313
20281
|
onFigureDeleted: () => { },
|
|
20314
20282
|
onMouseDown: () => { },
|
|
@@ -22393,6 +22361,7 @@ const XLSX_RELATION_TYPE = {
|
|
|
22393
22361
|
theme: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme",
|
|
22394
22362
|
table: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table",
|
|
22395
22363
|
hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
|
22364
|
+
image: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
|
22396
22365
|
};
|
|
22397
22366
|
const RELATIONSHIP_NSR = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
22398
22367
|
const HEIGHT_FACTOR = 0.75; // 100px => 75 u
|
|
@@ -23416,12 +23385,8 @@ function getCellType(value) {
|
|
|
23416
23385
|
return "n";
|
|
23417
23386
|
}
|
|
23418
23387
|
}
|
|
23419
|
-
/**
|
|
23420
|
-
* For some reason, Excel will only take the devicePixelRatio (i.e. interface scale on Windows desktop)
|
|
23421
|
-
* into account for the height.
|
|
23422
|
-
*/
|
|
23423
23388
|
function convertHeightToExcel(height) {
|
|
23424
|
-
return Math.round(HEIGHT_FACTOR * height *
|
|
23389
|
+
return Math.round(HEIGHT_FACTOR * height * 100) / 100;
|
|
23425
23390
|
}
|
|
23426
23391
|
function convertWidthToExcel(width) {
|
|
23427
23392
|
return Math.round(WIDTH_FACTOR * width * 100) / 100;
|
|
@@ -23563,6 +23528,19 @@ function convertChartId(chartId) {
|
|
|
23563
23528
|
}
|
|
23564
23529
|
return xlsxId + 1;
|
|
23565
23530
|
}
|
|
23531
|
+
const imageIds = [];
|
|
23532
|
+
/**
|
|
23533
|
+
* Convert a image o-spreadsheet id to a xlsx id which
|
|
23534
|
+
* are unsigned integers (starting from 1).
|
|
23535
|
+
*/
|
|
23536
|
+
function convertImageId(imageId) {
|
|
23537
|
+
const xlsxId = imageIds.findIndex((id) => id === imageId);
|
|
23538
|
+
if (xlsxId === -1) {
|
|
23539
|
+
imageIds.push(imageId);
|
|
23540
|
+
return imageIds.length;
|
|
23541
|
+
}
|
|
23542
|
+
return xlsxId + 1;
|
|
23543
|
+
}
|
|
23566
23544
|
/**
|
|
23567
23545
|
* Convert a value expressed in dot to EMU.
|
|
23568
23546
|
* EMU = English Metrical Unit
|
|
@@ -25898,13 +25876,7 @@ function load(data, verboseImport) {
|
|
|
25898
25876
|
data = migrate(data);
|
|
25899
25877
|
}
|
|
25900
25878
|
}
|
|
25901
|
-
|
|
25902
|
-
// sensible default values
|
|
25903
|
-
data = Object.assign(createEmptyWorkbookData(), data, { version: CURRENT_VERSION });
|
|
25904
|
-
data.sheets = data.sheets.map((s, i) => Object.assign(createEmptySheet(`Sheet${i + 1}`, `Sheet${i + 1}`), s));
|
|
25905
|
-
if (data.sheets.length === 0) {
|
|
25906
|
-
data.sheets.push(createEmptySheet(INITIAL_SHEET_ID, "Sheet1"));
|
|
25907
|
-
}
|
|
25879
|
+
data = repairData(data);
|
|
25908
25880
|
return data;
|
|
25909
25881
|
}
|
|
25910
25882
|
function migrate(data) {
|
|
@@ -26142,6 +26114,48 @@ const MIGRATIONS = [
|
|
|
26142
26114
|
},
|
|
26143
26115
|
},
|
|
26144
26116
|
];
|
|
26117
|
+
/**
|
|
26118
|
+
* This function is used to repair faulty data independently of the migration.
|
|
26119
|
+
*/
|
|
26120
|
+
function repairData(data) {
|
|
26121
|
+
data = forceUnicityOfFigure(data);
|
|
26122
|
+
data = setDefaults(data);
|
|
26123
|
+
return data;
|
|
26124
|
+
}
|
|
26125
|
+
/**
|
|
26126
|
+
* Force the unicity of figure ids accross sheets
|
|
26127
|
+
*/
|
|
26128
|
+
function forceUnicityOfFigure(data) {
|
|
26129
|
+
if (data.uniqueFigureIds) {
|
|
26130
|
+
return data;
|
|
26131
|
+
}
|
|
26132
|
+
const figureIds = new Set();
|
|
26133
|
+
const uuidGenerator = new UuidGenerator();
|
|
26134
|
+
for (const sheet of data.sheets || []) {
|
|
26135
|
+
for (const figure of sheet.figures || []) {
|
|
26136
|
+
if (figureIds.has(figure.id)) {
|
|
26137
|
+
figure.id += uuidGenerator.uuidv4();
|
|
26138
|
+
}
|
|
26139
|
+
figureIds.add(figure.id);
|
|
26140
|
+
}
|
|
26141
|
+
}
|
|
26142
|
+
data.uniqueFigureIds = true;
|
|
26143
|
+
return data;
|
|
26144
|
+
}
|
|
26145
|
+
/**
|
|
26146
|
+
* sanity check: try to fix missing fields/corrupted state by providing
|
|
26147
|
+
* sensible default values
|
|
26148
|
+
*/
|
|
26149
|
+
function setDefaults(data) {
|
|
26150
|
+
data = Object.assign(createEmptyWorkbookData(), data, { version: CURRENT_VERSION });
|
|
26151
|
+
data.sheets = data.sheets
|
|
26152
|
+
? data.sheets.map((s, i) => Object.assign(createEmptySheet(`Sheet${i + 1}`, `Sheet${i + 1}`), s))
|
|
26153
|
+
: [];
|
|
26154
|
+
if (data.sheets.length === 0) {
|
|
26155
|
+
data.sheets.push(createEmptySheet(INITIAL_SHEET_ID, "Sheet1"));
|
|
26156
|
+
}
|
|
26157
|
+
return data;
|
|
26158
|
+
}
|
|
26145
26159
|
/**
|
|
26146
26160
|
* The goal of this function is to repair corrupted/wrong initial messages caused by
|
|
26147
26161
|
* a bug.
|
|
@@ -26280,6 +26294,7 @@ function createEmptyWorkbookData(sheetName = "Sheet1") {
|
|
|
26280
26294
|
formats: {},
|
|
26281
26295
|
borders: {},
|
|
26282
26296
|
revisionId: DEFAULT_REVISION_ID,
|
|
26297
|
+
uniqueFigureIds: true,
|
|
26283
26298
|
};
|
|
26284
26299
|
return data;
|
|
26285
26300
|
}
|
|
@@ -26287,6 +26302,7 @@ function createEmptyExcelSheet(sheetId, name) {
|
|
|
26287
26302
|
return {
|
|
26288
26303
|
...createEmptySheet(sheetId, name),
|
|
26289
26304
|
charts: [],
|
|
26305
|
+
images: [],
|
|
26290
26306
|
};
|
|
26291
26307
|
}
|
|
26292
26308
|
function createEmptyExcelWorkbookData() {
|
|
@@ -28059,10 +28075,8 @@ class ChartPlugin extends CorePlugin {
|
|
|
28059
28075
|
this.validateChartDefinition = (cmd) => validateChartDefinition(this, cmd.definition);
|
|
28060
28076
|
}
|
|
28061
28077
|
adaptRanges(applyChange) {
|
|
28062
|
-
for (const
|
|
28063
|
-
|
|
28064
|
-
this.history.update("charts", sheetId, chartId, chart === null || chart === void 0 ? void 0 : chart.updateRanges(applyChange));
|
|
28065
|
-
}
|
|
28078
|
+
for (const [chartId, chart] of Object.entries(this.charts)) {
|
|
28079
|
+
this.history.update("charts", chartId, chart === null || chart === void 0 ? void 0 : chart.updateRanges(applyChange));
|
|
28066
28080
|
}
|
|
28067
28081
|
}
|
|
28068
28082
|
// ---------------------------------------------------------------------------
|
|
@@ -28073,20 +28087,20 @@ class ChartPlugin extends CorePlugin {
|
|
|
28073
28087
|
case "CREATE_CHART":
|
|
28074
28088
|
return this.checkValidations(cmd, this.chainValidations(this.validateChartDefinition, this.checkChartDuplicate));
|
|
28075
28089
|
case "UPDATE_CHART":
|
|
28076
|
-
return this.
|
|
28090
|
+
return this.checkValidations(cmd, this.chainValidations(this.validateChartDefinition, this.checkChartExists));
|
|
28077
28091
|
default:
|
|
28078
28092
|
return 0 /* CommandResult.Success */;
|
|
28079
28093
|
}
|
|
28080
28094
|
}
|
|
28081
28095
|
handle(cmd) {
|
|
28082
|
-
var _a
|
|
28096
|
+
var _a;
|
|
28083
28097
|
switch (cmd.type) {
|
|
28084
28098
|
case "CREATE_CHART":
|
|
28085
28099
|
this.addFigure(cmd.id, cmd.sheetId, cmd.position, cmd.size);
|
|
28086
|
-
this.addChart(cmd.id, cmd.
|
|
28100
|
+
this.addChart(cmd.id, cmd.definition);
|
|
28087
28101
|
break;
|
|
28088
28102
|
case "UPDATE_CHART": {
|
|
28089
|
-
this.addChart(cmd.id, cmd.
|
|
28103
|
+
this.addChart(cmd.id, cmd.definition);
|
|
28090
28104
|
break;
|
|
28091
28105
|
}
|
|
28092
28106
|
case "DUPLICATE_SHEET": {
|
|
@@ -28095,7 +28109,7 @@ class ChartPlugin extends CorePlugin {
|
|
|
28095
28109
|
if (fig.tag === "chart") {
|
|
28096
28110
|
const figureIdBase = fig.id.split(FIGURE_ID_SPLITTER).pop();
|
|
28097
28111
|
const duplicatedFigureId = `${cmd.sheetIdTo}${FIGURE_ID_SPLITTER}${figureIdBase}`;
|
|
28098
|
-
const chart = (
|
|
28112
|
+
const chart = (_a = this.charts[fig.id]) === null || _a === void 0 ? void 0 : _a.copyForSheetId(cmd.sheetIdTo);
|
|
28099
28113
|
if (chart) {
|
|
28100
28114
|
this.dispatch("CREATE_CHART", {
|
|
28101
28115
|
id: duplicatedFigureId,
|
|
@@ -28110,41 +28124,44 @@ class ChartPlugin extends CorePlugin {
|
|
|
28110
28124
|
break;
|
|
28111
28125
|
}
|
|
28112
28126
|
case "DELETE_FIGURE":
|
|
28113
|
-
this.history.update("charts", cmd.
|
|
28127
|
+
this.history.update("charts", cmd.id, undefined);
|
|
28114
28128
|
break;
|
|
28115
28129
|
case "DELETE_SHEET":
|
|
28116
|
-
this.
|
|
28130
|
+
for (let id of this.getChartIds(cmd.sheetId)) {
|
|
28131
|
+
this.history.update("charts", id, undefined);
|
|
28132
|
+
}
|
|
28117
28133
|
break;
|
|
28118
28134
|
}
|
|
28119
28135
|
}
|
|
28120
28136
|
// ---------------------------------------------------------------------------
|
|
28121
28137
|
// Getters
|
|
28122
28138
|
// ---------------------------------------------------------------------------
|
|
28123
|
-
getContextCreationChart(
|
|
28124
|
-
var _a, _b;
|
|
28125
|
-
return (_b = (_a = this.charts[sheetId]) === null || _a === void 0 ? void 0 : _a[figureId]) === null || _b === void 0 ? void 0 : _b.getContextCreation();
|
|
28126
|
-
}
|
|
28127
|
-
getChart(sheetId, figureId) {
|
|
28139
|
+
getContextCreationChart(figureId) {
|
|
28128
28140
|
var _a;
|
|
28129
|
-
return (_a = this.charts[
|
|
28141
|
+
return (_a = this.charts[figureId]) === null || _a === void 0 ? void 0 : _a.getContextCreation();
|
|
28130
28142
|
}
|
|
28131
|
-
|
|
28132
|
-
|
|
28133
|
-
|
|
28143
|
+
getChart(figureId) {
|
|
28144
|
+
return this.charts[figureId];
|
|
28145
|
+
}
|
|
28146
|
+
getChartType(figureId) {
|
|
28147
|
+
var _a;
|
|
28148
|
+
const type = (_a = this.charts[figureId]) === null || _a === void 0 ? void 0 : _a.type;
|
|
28134
28149
|
if (!type) {
|
|
28135
28150
|
throw new Error("Chart not defined.");
|
|
28136
28151
|
}
|
|
28137
28152
|
return type;
|
|
28138
28153
|
}
|
|
28139
|
-
isChartDefined(
|
|
28140
|
-
return figureId in
|
|
28154
|
+
isChartDefined(figureId) {
|
|
28155
|
+
return figureId in this.charts && this.charts !== undefined;
|
|
28141
28156
|
}
|
|
28142
28157
|
getChartIds(sheetId) {
|
|
28143
|
-
return Object.
|
|
28158
|
+
return Object.entries(this.charts)
|
|
28159
|
+
.filter(([, chart]) => (chart === null || chart === void 0 ? void 0 : chart.sheetId) === sheetId)
|
|
28160
|
+
.map(([id]) => id);
|
|
28144
28161
|
}
|
|
28145
|
-
getChartDefinition(
|
|
28146
|
-
var _a
|
|
28147
|
-
const definition = (
|
|
28162
|
+
getChartDefinition(figureId) {
|
|
28163
|
+
var _a;
|
|
28164
|
+
const definition = (_a = this.charts[figureId]) === null || _a === void 0 ? void 0 : _a.getDefinition();
|
|
28148
28165
|
if (!definition) {
|
|
28149
28166
|
throw new Error(`There is no chart with the given figureId: ${figureId}`);
|
|
28150
28167
|
}
|
|
@@ -28156,28 +28173,35 @@ class ChartPlugin extends CorePlugin {
|
|
|
28156
28173
|
import(data) {
|
|
28157
28174
|
for (let sheet of data.sheets) {
|
|
28158
28175
|
if (sheet.figures) {
|
|
28159
|
-
const charts = {};
|
|
28160
28176
|
for (let figure of sheet.figures) {
|
|
28161
28177
|
// TODO:
|
|
28162
28178
|
// figure data should be external IMO => chart should be in sheet.chart
|
|
28163
28179
|
// instead of in figure.data
|
|
28164
28180
|
if (figure.tag === "chart") {
|
|
28165
|
-
charts[figure.id] = this.createChart(figure.id, figure.data, sheet.id);
|
|
28181
|
+
this.charts[figure.id] = this.createChart(figure.id, figure.data, sheet.id);
|
|
28166
28182
|
}
|
|
28167
28183
|
}
|
|
28168
|
-
this.charts[sheet.id] = charts;
|
|
28169
28184
|
}
|
|
28170
28185
|
}
|
|
28171
28186
|
}
|
|
28172
28187
|
export(data) {
|
|
28188
|
+
var _a;
|
|
28173
28189
|
if (data.sheets) {
|
|
28174
28190
|
for (let sheet of data.sheets) {
|
|
28175
28191
|
// TODO This code is false, if two plugins want ot insert figures on the sheet, it will crash !
|
|
28176
28192
|
const sheetFigures = this.getters.getFigures(sheet.id);
|
|
28177
|
-
const figures =
|
|
28178
|
-
for (let
|
|
28193
|
+
const figures = [];
|
|
28194
|
+
for (let sheetFigure of sheetFigures) {
|
|
28195
|
+
const figure = sheetFigure;
|
|
28179
28196
|
if (figure && figure.tag === "chart") {
|
|
28180
|
-
|
|
28197
|
+
const data = (_a = this.charts[figure.id]) === null || _a === void 0 ? void 0 : _a.getDefinition();
|
|
28198
|
+
if (data) {
|
|
28199
|
+
figure.data = data;
|
|
28200
|
+
figures.push(figure);
|
|
28201
|
+
}
|
|
28202
|
+
}
|
|
28203
|
+
else {
|
|
28204
|
+
figures.push(figure);
|
|
28181
28205
|
}
|
|
28182
28206
|
}
|
|
28183
28207
|
sheet.figures = figures;
|
|
@@ -28185,13 +28209,13 @@ class ChartPlugin extends CorePlugin {
|
|
|
28185
28209
|
}
|
|
28186
28210
|
}
|
|
28187
28211
|
exportForExcel(data) {
|
|
28188
|
-
var _a
|
|
28212
|
+
var _a;
|
|
28189
28213
|
for (let sheet of data.sheets) {
|
|
28190
28214
|
const sheetFigures = this.getters.getFigures(sheet.id);
|
|
28191
28215
|
const figures = [];
|
|
28192
28216
|
for (let figure of sheetFigures) {
|
|
28193
28217
|
if (figure && figure.tag === "chart") {
|
|
28194
|
-
const figureData = (
|
|
28218
|
+
const figureData = (_a = this.charts[figure.id]) === null || _a === void 0 ? void 0 : _a.getDefinitionForExcel();
|
|
28195
28219
|
if (figureData) {
|
|
28196
28220
|
figures.push({
|
|
28197
28221
|
...figure,
|
|
@@ -28230,15 +28254,21 @@ class ChartPlugin extends CorePlugin {
|
|
|
28230
28254
|
* Add a chart in the local state. If a chart already exists, this chart is
|
|
28231
28255
|
* replaced
|
|
28232
28256
|
*/
|
|
28233
|
-
addChart(id,
|
|
28234
|
-
|
|
28257
|
+
addChart(id, definition) {
|
|
28258
|
+
const sheetId = this.getters.getFigureSheetId(id);
|
|
28259
|
+
if (sheetId) {
|
|
28260
|
+
this.history.update("charts", id, this.createChart(id, definition, sheetId));
|
|
28261
|
+
}
|
|
28235
28262
|
}
|
|
28236
28263
|
checkChartDuplicate(cmd) {
|
|
28237
|
-
|
|
28238
|
-
|
|
28239
|
-
|
|
28240
|
-
|
|
28241
|
-
|
|
28264
|
+
return this.getters.getFigureSheetId(cmd.id)
|
|
28265
|
+
? 84 /* CommandResult.DuplicatedChartId */
|
|
28266
|
+
: 0 /* CommandResult.Success */;
|
|
28267
|
+
}
|
|
28268
|
+
checkChartExists(cmd) {
|
|
28269
|
+
return this.getters.getFigureSheetId(cmd.id)
|
|
28270
|
+
? 0 /* CommandResult.Success */
|
|
28271
|
+
: 85 /* CommandResult.ChartDoesNotExist */;
|
|
28242
28272
|
}
|
|
28243
28273
|
}
|
|
28244
28274
|
ChartPlugin.getters = [
|
|
@@ -28619,7 +28649,7 @@ class FigurePlugin extends CorePlugin {
|
|
|
28619
28649
|
allowDispatch(cmd) {
|
|
28620
28650
|
switch (cmd.type) {
|
|
28621
28651
|
case "CREATE_FIGURE":
|
|
28622
|
-
return this.checkFigureDuplicate(cmd.
|
|
28652
|
+
return this.checkFigureDuplicate(cmd.figure.id);
|
|
28623
28653
|
case "UPDATE_FIGURE":
|
|
28624
28654
|
case "DELETE_FIGURE":
|
|
28625
28655
|
return this.checkFigureExists(cmd.sheetId, cmd.id);
|
|
@@ -28685,9 +28715,8 @@ class FigurePlugin extends CorePlugin {
|
|
|
28685
28715
|
}
|
|
28686
28716
|
return 0 /* CommandResult.Success */;
|
|
28687
28717
|
}
|
|
28688
|
-
checkFigureDuplicate(
|
|
28689
|
-
|
|
28690
|
-
if ((_a = this.figures[sheetId]) === null || _a === void 0 ? void 0 : _a[figureId]) {
|
|
28718
|
+
checkFigureDuplicate(figureId) {
|
|
28719
|
+
if (Object.values(this.figures).find((sheet) => sheet === null || sheet === void 0 ? void 0 : sheet[figureId])) {
|
|
28691
28720
|
return 82 /* CommandResult.DuplicatedFigureId */;
|
|
28692
28721
|
}
|
|
28693
28722
|
return 0 /* CommandResult.Success */;
|
|
@@ -28702,6 +28731,9 @@ class FigurePlugin extends CorePlugin {
|
|
|
28702
28731
|
var _a;
|
|
28703
28732
|
return (_a = this.figures[sheetId]) === null || _a === void 0 ? void 0 : _a[figureId];
|
|
28704
28733
|
}
|
|
28734
|
+
getFigureSheetId(figureId) {
|
|
28735
|
+
return Object.keys(this.figures).find((sheetId) => { var _a; return ((_a = this.figures[sheetId]) === null || _a === void 0 ? void 0 : _a[figureId]) !== undefined; });
|
|
28736
|
+
}
|
|
28705
28737
|
// ---------------------------------------------------------------------------
|
|
28706
28738
|
// Import/Export
|
|
28707
28739
|
// ---------------------------------------------------------------------------
|
|
@@ -28726,7 +28758,7 @@ class FigurePlugin extends CorePlugin {
|
|
|
28726
28758
|
this.export(data);
|
|
28727
28759
|
}
|
|
28728
28760
|
}
|
|
28729
|
-
FigurePlugin.getters = ["getFigures", "getFigure"];
|
|
28761
|
+
FigurePlugin.getters = ["getFigures", "getFigure", "getFigureSheetId"];
|
|
28730
28762
|
|
|
28731
28763
|
class FilterTable {
|
|
28732
28764
|
constructor(zone) {
|
|
@@ -28906,7 +28938,7 @@ class FiltersPlugin extends CorePlugin {
|
|
|
28906
28938
|
filters.push(new Filter(filter.id, filterZone));
|
|
28907
28939
|
}
|
|
28908
28940
|
// Add filters for new columns
|
|
28909
|
-
if (filters.length < zoneToDimension(zone).
|
|
28941
|
+
if (filters.length < zoneToDimension(zone).numberOfCols) {
|
|
28910
28942
|
for (let col = zone.left; col <= zone.right; col++) {
|
|
28911
28943
|
if (!filters.find((filter) => filter.col === col)) {
|
|
28912
28944
|
filters.push(new Filter(this.uuidGenerator.uuidv4(), { ...zone, left: col, right: col }));
|
|
@@ -29453,7 +29485,7 @@ class ImagePlugin extends CorePlugin {
|
|
|
29453
29485
|
switch (cmd.type) {
|
|
29454
29486
|
case "CREATE_IMAGE":
|
|
29455
29487
|
if (this.getters.getFigure(cmd.sheetId, cmd.figureId)) {
|
|
29456
|
-
return 28 /* CommandResult.
|
|
29488
|
+
return 28 /* CommandResult.InvalidFigureId */;
|
|
29457
29489
|
}
|
|
29458
29490
|
return 0 /* CommandResult.Success */;
|
|
29459
29491
|
default:
|
|
@@ -29473,7 +29505,7 @@ class ImagePlugin extends CorePlugin {
|
|
|
29473
29505
|
if (fig.tag === "image") {
|
|
29474
29506
|
const figureIdBase = fig.id.split(FIGURE_ID_SPLITTER).pop();
|
|
29475
29507
|
const duplicatedFigureId = `${cmd.sheetIdTo}${FIGURE_ID_SPLITTER}${figureIdBase}`;
|
|
29476
|
-
const image = this.getImage(
|
|
29508
|
+
const image = this.getImage(fig.id);
|
|
29477
29509
|
if (image) {
|
|
29478
29510
|
const size = { width: fig.width, height: fig.height };
|
|
29479
29511
|
this.dispatch("CREATE_IMAGE", {
|
|
@@ -29511,13 +29543,19 @@ class ImagePlugin extends CorePlugin {
|
|
|
29511
29543
|
// ---------------------------------------------------------------------------
|
|
29512
29544
|
// Getters
|
|
29513
29545
|
// ---------------------------------------------------------------------------
|
|
29514
|
-
getImage(
|
|
29515
|
-
|
|
29516
|
-
|
|
29517
|
-
|
|
29518
|
-
|
|
29546
|
+
getImage(figureId) {
|
|
29547
|
+
for (const sheet of Object.values(this.images)) {
|
|
29548
|
+
if (sheet && sheet[figureId]) {
|
|
29549
|
+
return sheet[figureId];
|
|
29550
|
+
}
|
|
29519
29551
|
}
|
|
29520
|
-
|
|
29552
|
+
throw new Error(`There is no image with the given figureId: ${figureId}`);
|
|
29553
|
+
}
|
|
29554
|
+
getImagePath(figureId) {
|
|
29555
|
+
return this.getImage(figureId).path;
|
|
29556
|
+
}
|
|
29557
|
+
getImageSize(figureId) {
|
|
29558
|
+
return this.getImage(figureId).size;
|
|
29521
29559
|
}
|
|
29522
29560
|
// ---------------------------------------------------------------------------
|
|
29523
29561
|
// Private
|
|
@@ -29551,6 +29589,24 @@ class ImagePlugin extends CorePlugin {
|
|
|
29551
29589
|
}
|
|
29552
29590
|
}
|
|
29553
29591
|
}
|
|
29592
|
+
exportForExcel(data) {
|
|
29593
|
+
for (const sheet of data.sheets) {
|
|
29594
|
+
const figures = this.getters.getFigures(sheet.id);
|
|
29595
|
+
const images = [];
|
|
29596
|
+
for (const figure of figures) {
|
|
29597
|
+
if ((figure === null || figure === void 0 ? void 0 : figure.tag) === "image") {
|
|
29598
|
+
const image = this.getImage(figure.id);
|
|
29599
|
+
if (image) {
|
|
29600
|
+
images.push({
|
|
29601
|
+
...figure,
|
|
29602
|
+
data: deepCopy(image),
|
|
29603
|
+
});
|
|
29604
|
+
}
|
|
29605
|
+
}
|
|
29606
|
+
}
|
|
29607
|
+
sheet.images = images;
|
|
29608
|
+
}
|
|
29609
|
+
}
|
|
29554
29610
|
getAllImages() {
|
|
29555
29611
|
const images = [];
|
|
29556
29612
|
for (const sheetId in this.images) {
|
|
@@ -29559,7 +29615,7 @@ class ImagePlugin extends CorePlugin {
|
|
|
29559
29615
|
return images;
|
|
29560
29616
|
}
|
|
29561
29617
|
}
|
|
29562
|
-
ImagePlugin.getters = ["getImage"];
|
|
29618
|
+
ImagePlugin.getters = ["getImage", "getImagePath", "getImageSize"];
|
|
29563
29619
|
|
|
29564
29620
|
class MergePlugin extends CorePlugin {
|
|
29565
29621
|
constructor() {
|
|
@@ -29748,8 +29804,8 @@ class MergePlugin extends CorePlugin {
|
|
|
29748
29804
|
if (merge) {
|
|
29749
29805
|
return isEqual(zone, merge);
|
|
29750
29806
|
}
|
|
29751
|
-
const {
|
|
29752
|
-
return
|
|
29807
|
+
const { numberOfCols, numberOfRows } = zoneToDimension(zone);
|
|
29808
|
+
return numberOfCols === 1 && numberOfRows === 1;
|
|
29753
29809
|
}
|
|
29754
29810
|
// ---------------------------------------------------------------------------
|
|
29755
29811
|
// Merges
|
|
@@ -29917,8 +29973,8 @@ class MergePlugin extends CorePlugin {
|
|
|
29917
29973
|
this.removeMerge(sheetId, currentZone);
|
|
29918
29974
|
break;
|
|
29919
29975
|
default:
|
|
29920
|
-
const {
|
|
29921
|
-
if (
|
|
29976
|
+
const { numberOfCols, numberOfRows } = zoneToDimension(result.range.zone);
|
|
29977
|
+
if (numberOfCols === 1 && numberOfRows === 1) {
|
|
29922
29978
|
this.removeMerge(sheetId, currentZone);
|
|
29923
29979
|
}
|
|
29924
29980
|
else {
|
|
@@ -30677,8 +30733,8 @@ class SheetPlugin extends CorePlugin {
|
|
|
30677
30733
|
}
|
|
30678
30734
|
getSheetSize(sheetId) {
|
|
30679
30735
|
return {
|
|
30680
|
-
|
|
30681
|
-
|
|
30736
|
+
numberOfRows: this.getNumberRows(sheetId),
|
|
30737
|
+
numberOfCols: this.getNumberCols(sheetId),
|
|
30682
30738
|
};
|
|
30683
30739
|
}
|
|
30684
30740
|
getSheetZone(sheetId) {
|
|
@@ -31365,9 +31421,7 @@ class CustomColorsPlugin extends UIPlugin {
|
|
|
31365
31421
|
return formatColors.filter(isDefined$1);
|
|
31366
31422
|
}
|
|
31367
31423
|
getChartColors(sheetId) {
|
|
31368
|
-
const charts = this.getters
|
|
31369
|
-
.getChartIds(sheetId)
|
|
31370
|
-
.map((cid) => this.getters.getChart(sheetId, cid));
|
|
31424
|
+
const charts = this.getters.getChartIds(sheetId).map((cid) => this.getters.getChart(cid));
|
|
31371
31425
|
let chartsColors = new Set();
|
|
31372
31426
|
for (let chart of charts) {
|
|
31373
31427
|
if (chart === undefined) {
|
|
@@ -31733,37 +31787,34 @@ class EvaluationChartPlugin extends UIPlugin {
|
|
|
31733
31787
|
invalidateCFEvaluationCommands.has(cmd.type) ||
|
|
31734
31788
|
cmd.type === "EVALUATE_CELLS" ||
|
|
31735
31789
|
cmd.type === "UPDATE_CELL") {
|
|
31736
|
-
for (const
|
|
31737
|
-
|
|
31738
|
-
this.charts[sheetId][chartId] = undefined;
|
|
31739
|
-
}
|
|
31790
|
+
for (const chartId in this.charts) {
|
|
31791
|
+
this.charts[chartId] = undefined;
|
|
31740
31792
|
}
|
|
31741
31793
|
}
|
|
31742
31794
|
switch (cmd.type) {
|
|
31743
31795
|
case "UPDATE_CHART":
|
|
31744
31796
|
case "CREATE_CHART":
|
|
31745
31797
|
case "DELETE_FIGURE":
|
|
31746
|
-
|
|
31747
|
-
this.charts[cmd.sheetId][cmd.id] = undefined;
|
|
31748
|
-
}
|
|
31798
|
+
this.charts[cmd.id] = undefined;
|
|
31749
31799
|
break;
|
|
31750
31800
|
case "DELETE_SHEET":
|
|
31751
|
-
|
|
31801
|
+
for (let chartId in this.charts) {
|
|
31802
|
+
if (!this.getters.isChartDefined(chartId)) {
|
|
31803
|
+
this.charts[chartId] = undefined;
|
|
31804
|
+
}
|
|
31805
|
+
}
|
|
31752
31806
|
break;
|
|
31753
31807
|
}
|
|
31754
31808
|
}
|
|
31755
|
-
getChartRuntime(
|
|
31756
|
-
|
|
31757
|
-
|
|
31758
|
-
const chart = this.getters.getChart(sheetId, figureId);
|
|
31809
|
+
getChartRuntime(figureId) {
|
|
31810
|
+
if (!this.charts[figureId]) {
|
|
31811
|
+
const chart = this.getters.getChart(figureId);
|
|
31759
31812
|
if (!chart) {
|
|
31760
31813
|
throw new Error(`No chart for the given id: ${figureId}`);
|
|
31761
31814
|
}
|
|
31762
|
-
|
|
31763
|
-
this.charts[sheetId] = {};
|
|
31764
|
-
this.charts[sheetId][figureId] = this.createRuntimeChart(chart);
|
|
31815
|
+
this.charts[figureId] = this.createRuntimeChart(chart);
|
|
31765
31816
|
}
|
|
31766
|
-
return this.charts[
|
|
31817
|
+
return this.charts[figureId];
|
|
31767
31818
|
}
|
|
31768
31819
|
/**
|
|
31769
31820
|
* Get the background color of a chart based on the color of the first cell of the main range
|
|
@@ -32199,6 +32250,9 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
32199
32250
|
case "CREATE_SHEET":
|
|
32200
32251
|
this.filterValues[cmd.sheetId] = {};
|
|
32201
32252
|
break;
|
|
32253
|
+
case "HIDE_COLUMNS_ROWS":
|
|
32254
|
+
this.updateHiddenRows();
|
|
32255
|
+
break;
|
|
32202
32256
|
case "UPDATE_FILTER":
|
|
32203
32257
|
this.updateFilter(cmd);
|
|
32204
32258
|
this.updateHiddenRows();
|
|
@@ -32307,9 +32361,16 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
32307
32361
|
updateHiddenRows() {
|
|
32308
32362
|
var _a, _b;
|
|
32309
32363
|
const sheetId = this.getters.getActiveSheetId();
|
|
32310
|
-
const filters = this.getters
|
|
32364
|
+
const filters = this.getters
|
|
32365
|
+
.getFilters(sheetId)
|
|
32366
|
+
.sort((filter1, filter2) => filter1.zoneWithHeaders.top - filter2.zoneWithHeaders.top);
|
|
32311
32367
|
const hiddenRows = new Set();
|
|
32312
32368
|
for (let filter of filters) {
|
|
32369
|
+
// Disable filters whose header are hidden
|
|
32370
|
+
if (this.getters.isRowHiddenByUser(sheetId, filter.zoneWithHeaders.top))
|
|
32371
|
+
continue;
|
|
32372
|
+
if (hiddenRows.has(filter.zoneWithHeaders.top))
|
|
32373
|
+
continue;
|
|
32313
32374
|
const filteredValues = (_b = (_a = this.filterValues[sheetId]) === null || _a === void 0 ? void 0 : _a[filter.id]) === null || _b === void 0 ? void 0 : _b.map(toLowerCase);
|
|
32314
32375
|
if (!filteredValues || !filter.filteredZone)
|
|
32315
32376
|
continue;
|
|
@@ -32332,7 +32393,7 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
32332
32393
|
const tableZone = toZone(tableData.range);
|
|
32333
32394
|
const filters = [];
|
|
32334
32395
|
const headerNames = [];
|
|
32335
|
-
for (const i of range(0, zoneToDimension(tableZone).
|
|
32396
|
+
for (const i of range(0, zoneToDimension(tableZone).numberOfCols)) {
|
|
32336
32397
|
const position = {
|
|
32337
32398
|
sheetId: sheetData.id,
|
|
32338
32399
|
col: tableZone.left + i,
|
|
@@ -33843,7 +33904,7 @@ class AutomaticSumPlugin extends UIPlugin {
|
|
|
33843
33904
|
dimensionsToSum(sheetId, zone) {
|
|
33844
33905
|
const dimensions = new Set();
|
|
33845
33906
|
if (isOneDimensional(zone)) {
|
|
33846
|
-
dimensions.add(zoneToDimension(zone).
|
|
33907
|
+
dimensions.add(zoneToDimension(zone).numberOfCols === 1 ? "COL" : "ROW");
|
|
33847
33908
|
return dimensions;
|
|
33848
33909
|
}
|
|
33849
33910
|
if (this.lastColIsEmpty(sheetId, zone)) {
|
|
@@ -34431,8 +34492,8 @@ class HighlightPlugin extends UIPlugin {
|
|
|
34431
34492
|
x.zone.bottom < this.getters.getNumberRows(x.sheetId) &&
|
|
34432
34493
|
x.zone.right < this.getters.getNumberCols(x.sheetId))
|
|
34433
34494
|
.map((highlight) => {
|
|
34434
|
-
const {
|
|
34435
|
-
const zone =
|
|
34495
|
+
const { numberOfRows, numberOfCols } = zoneToDimension(highlight.zone);
|
|
34496
|
+
const zone = numberOfRows * numberOfCols === 1
|
|
34436
34497
|
? this.getters.expandZone(highlight.sheetId, highlight.zone)
|
|
34437
34498
|
: highlight.zone;
|
|
34438
34499
|
return {
|
|
@@ -35248,8 +35309,8 @@ class SelectionInputPlugin extends UIPlugin {
|
|
|
35248
35309
|
const currentMaxId = Math.max(0, ...this.ranges.map((range) => Number(range.id)));
|
|
35249
35310
|
this.ranges.splice(index, 0, ...values.map((xc, i) => ({
|
|
35250
35311
|
xc,
|
|
35251
|
-
id:
|
|
35252
|
-
color: colors$1[(currentMaxId +
|
|
35312
|
+
id: currentMaxId + i + 1,
|
|
35313
|
+
color: colors$1[(currentMaxId + i) % colors$1.length],
|
|
35253
35314
|
})));
|
|
35254
35315
|
}
|
|
35255
35316
|
/**
|
|
@@ -36339,7 +36400,7 @@ class SortPlugin extends UIPlugin {
|
|
|
36339
36400
|
const merges = this.getters.getMerges(sheetId).filter((merge) => overlap(merge, zone));
|
|
36340
36401
|
/*Test the presence of merges of different sizes*/
|
|
36341
36402
|
const mergeDimension = zoneToDimension(merges[0]);
|
|
36342
|
-
let [widthFirst, heightFirst] = [mergeDimension.
|
|
36403
|
+
let [widthFirst, heightFirst] = [mergeDimension.numberOfCols, mergeDimension.numberOfRows];
|
|
36343
36404
|
if (!merges.every((merge) => {
|
|
36344
36405
|
let [widthCurrent, heightCurrent] = [
|
|
36345
36406
|
merge.right - merge.left + 1,
|
|
@@ -37415,7 +37476,7 @@ class ClipboardFigureChart {
|
|
|
37415
37476
|
constructor(dispatch, getters, sheetId, copiedFigureId) {
|
|
37416
37477
|
this.dispatch = dispatch;
|
|
37417
37478
|
this.sheetId = sheetId;
|
|
37418
|
-
const chart = getters.getChart(
|
|
37479
|
+
const chart = getters.getChart(copiedFigureId);
|
|
37419
37480
|
if (!chart) {
|
|
37420
37481
|
throw new Error(`No chart for the given id: ${copiedFigureId}`);
|
|
37421
37482
|
}
|
|
@@ -37436,7 +37497,7 @@ class ClipboardFigureImage {
|
|
|
37436
37497
|
constructor(dispatch, getters, sheetId, copiedFigureId) {
|
|
37437
37498
|
this.dispatch = dispatch;
|
|
37438
37499
|
this.sheetId = sheetId;
|
|
37439
|
-
const image = getters.getImage(
|
|
37500
|
+
const image = getters.getImage(copiedFigureId);
|
|
37440
37501
|
this.copiedImage = deepCopy(image);
|
|
37441
37502
|
}
|
|
37442
37503
|
paste(sheetId, figureId, position, size) {
|
|
@@ -37472,9 +37533,9 @@ class ClipboardOsState extends ClipboardCellsAbstractState {
|
|
|
37472
37533
|
const values = this.values;
|
|
37473
37534
|
const pasteZone = this.getPasteZone(target);
|
|
37474
37535
|
const { left: activeCol, top: activeRow } = pasteZone;
|
|
37475
|
-
const {
|
|
37536
|
+
const { numberOfCols, numberOfRows } = zoneToDimension(pasteZone);
|
|
37476
37537
|
const sheetId = this.getters.getActiveSheetId();
|
|
37477
|
-
this.addMissingDimensions(
|
|
37538
|
+
this.addMissingDimensions(numberOfCols, numberOfRows, activeCol, activeRow);
|
|
37478
37539
|
for (let i = 0; i < values.length; i++) {
|
|
37479
37540
|
for (let j = 0; j < values[i].length; j++) {
|
|
37480
37541
|
this.dispatch("UPDATE_CELL", {
|
|
@@ -37488,8 +37549,8 @@ class ClipboardOsState extends ClipboardCellsAbstractState {
|
|
|
37488
37549
|
const zone = {
|
|
37489
37550
|
left: activeCol,
|
|
37490
37551
|
top: activeRow,
|
|
37491
|
-
right: activeCol +
|
|
37492
|
-
bottom: activeRow +
|
|
37552
|
+
right: activeCol + numberOfCols - 1,
|
|
37553
|
+
bottom: activeRow + numberOfRows - 1,
|
|
37493
37554
|
};
|
|
37494
37555
|
this.selection.selectZone({ cell: { col: activeCol, row: activeRow }, zone });
|
|
37495
37556
|
}
|
|
@@ -43588,7 +43649,7 @@ function getExcelThresholdType(type, position) {
|
|
|
43588
43649
|
}
|
|
43589
43650
|
}
|
|
43590
43651
|
|
|
43591
|
-
function createDrawing(
|
|
43652
|
+
function createDrawing(drawingRelIds, sheet, figures) {
|
|
43592
43653
|
const namespaces = [
|
|
43593
43654
|
["xmlns:xdr", NAMESPACE.drawing],
|
|
43594
43655
|
["xmlns:r", RELATIONSHIP_NSR],
|
|
@@ -43597,46 +43658,14 @@ function createDrawing(chartRelIds, sheet, figures) {
|
|
|
43597
43658
|
];
|
|
43598
43659
|
const figuresNodes = [];
|
|
43599
43660
|
for (const [figureIndex, figure] of Object.entries(figures)) {
|
|
43600
|
-
|
|
43601
|
-
|
|
43602
|
-
|
|
43603
|
-
|
|
43604
|
-
|
|
43605
|
-
|
|
43606
|
-
|
|
43607
|
-
|
|
43608
|
-
figuresNodes.push(escapeXml /*xml*/ `
|
|
43609
|
-
<xdr:twoCellAnchor>
|
|
43610
|
-
<xdr:from>
|
|
43611
|
-
<xdr:col>${from.col}</xdr:col>
|
|
43612
|
-
<xdr:colOff>${from.colOff}</xdr:colOff>
|
|
43613
|
-
<xdr:row>${from.row}</xdr:row>
|
|
43614
|
-
<xdr:rowOff>${from.rowOff}</xdr:rowOff>
|
|
43615
|
-
</xdr:from>
|
|
43616
|
-
<xdr:to>
|
|
43617
|
-
<xdr:col>${to.col}</xdr:col>
|
|
43618
|
-
<xdr:colOff>${to.colOff}</xdr:colOff>
|
|
43619
|
-
<xdr:row>${to.row}</xdr:row>
|
|
43620
|
-
<xdr:rowOff>${to.rowOff}</xdr:rowOff>
|
|
43621
|
-
</xdr:to>
|
|
43622
|
-
<xdr:graphicFrame>
|
|
43623
|
-
<xdr:nvGraphicFramePr>
|
|
43624
|
-
<xdr:cNvPr ${formatAttributes(cNvPrAttrs)} />
|
|
43625
|
-
<xdr:cNvGraphicFramePr />
|
|
43626
|
-
</xdr:nvGraphicFramePr>
|
|
43627
|
-
<xdr:xfrm>
|
|
43628
|
-
<a:off x="0" y="0"/>
|
|
43629
|
-
<a:ext cx="0" cy="0"/>
|
|
43630
|
-
</xdr:xfrm>
|
|
43631
|
-
<a:graphic>
|
|
43632
|
-
<a:graphicData uri="${DRAWING_NS_C}">
|
|
43633
|
-
<c:chart r:id="${chartRelIds[figureIndex]}" />
|
|
43634
|
-
</a:graphicData>
|
|
43635
|
-
</a:graphic>
|
|
43636
|
-
</xdr:graphicFrame>
|
|
43637
|
-
<xdr:clientData fLocksWithSheet="0"/>
|
|
43638
|
-
</xdr:twoCellAnchor>
|
|
43639
|
-
`);
|
|
43661
|
+
switch (figure === null || figure === void 0 ? void 0 : figure.tag) {
|
|
43662
|
+
case "chart":
|
|
43663
|
+
figuresNodes.push(createChartDrawing(figure, sheet, drawingRelIds[figureIndex]));
|
|
43664
|
+
break;
|
|
43665
|
+
case "image":
|
|
43666
|
+
figuresNodes.push(createImageDrawing(figure, sheet, drawingRelIds[figureIndex]));
|
|
43667
|
+
break;
|
|
43668
|
+
}
|
|
43640
43669
|
}
|
|
43641
43670
|
const xml = escapeXml /*xml*/ `
|
|
43642
43671
|
<xdr:wsDr ${formatAttributes(namespaces)}>
|
|
@@ -43691,6 +43720,93 @@ function figureCoordinates(headers, position) {
|
|
|
43691
43720
|
index: headers.length - 1,
|
|
43692
43721
|
offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_SIZE),
|
|
43693
43722
|
};
|
|
43723
|
+
}
|
|
43724
|
+
function createChartDrawing(figure, sheet, chartRelId) {
|
|
43725
|
+
// position
|
|
43726
|
+
const { from, to } = convertFigureData(figure, sheet);
|
|
43727
|
+
const chartId = convertChartId(figure.id);
|
|
43728
|
+
const cNvPrAttrs = [
|
|
43729
|
+
["id", chartId],
|
|
43730
|
+
["name", `Chart ${chartId}`],
|
|
43731
|
+
["title", "Chart"],
|
|
43732
|
+
];
|
|
43733
|
+
return escapeXml /*xml*/ `
|
|
43734
|
+
<xdr:twoCellAnchor>
|
|
43735
|
+
<xdr:from>
|
|
43736
|
+
<xdr:col>${from.col}</xdr:col>
|
|
43737
|
+
<xdr:colOff>${from.colOff}</xdr:colOff>
|
|
43738
|
+
<xdr:row>${from.row}</xdr:row>
|
|
43739
|
+
<xdr:rowOff>${from.rowOff}</xdr:rowOff>
|
|
43740
|
+
</xdr:from>
|
|
43741
|
+
<xdr:to>
|
|
43742
|
+
<xdr:col>${to.col}</xdr:col>
|
|
43743
|
+
<xdr:colOff>${to.colOff}</xdr:colOff>
|
|
43744
|
+
<xdr:row>${to.row}</xdr:row>
|
|
43745
|
+
<xdr:rowOff>${to.rowOff}</xdr:rowOff>
|
|
43746
|
+
</xdr:to>
|
|
43747
|
+
<xdr:graphicFrame>
|
|
43748
|
+
<xdr:nvGraphicFramePr>
|
|
43749
|
+
<xdr:cNvPr ${formatAttributes(cNvPrAttrs)} />
|
|
43750
|
+
<xdr:cNvGraphicFramePr />
|
|
43751
|
+
</xdr:nvGraphicFramePr>
|
|
43752
|
+
<xdr:xfrm>
|
|
43753
|
+
<a:off x="0" y="0"/>
|
|
43754
|
+
<a:ext cx="0" cy="0"/>
|
|
43755
|
+
</xdr:xfrm>
|
|
43756
|
+
<a:graphic>
|
|
43757
|
+
<a:graphicData uri="${DRAWING_NS_C}">
|
|
43758
|
+
<c:chart r:id="${chartRelId}" />
|
|
43759
|
+
</a:graphicData>
|
|
43760
|
+
</a:graphic>
|
|
43761
|
+
</xdr:graphicFrame>
|
|
43762
|
+
<xdr:clientData fLocksWithSheet="0"/>
|
|
43763
|
+
</xdr:twoCellAnchor>
|
|
43764
|
+
`;
|
|
43765
|
+
}
|
|
43766
|
+
function createImageDrawing(figure, sheet, imageRelId) {
|
|
43767
|
+
// position
|
|
43768
|
+
const { from, to } = convertFigureData(figure, sheet);
|
|
43769
|
+
const imageId = convertImageId(figure.id);
|
|
43770
|
+
const cNvPrAttrs = [
|
|
43771
|
+
["id", imageId],
|
|
43772
|
+
["name", `Image ${imageId}`],
|
|
43773
|
+
["title", "Image"],
|
|
43774
|
+
];
|
|
43775
|
+
return escapeXml /*xml*/ `
|
|
43776
|
+
<xdr:twoCellAnchor editAs="oneCell">
|
|
43777
|
+
<xdr:from>
|
|
43778
|
+
<xdr:col>${from.col}</xdr:col>
|
|
43779
|
+
<xdr:colOff>${from.colOff}</xdr:colOff>
|
|
43780
|
+
<xdr:row>${from.row}</xdr:row>
|
|
43781
|
+
<xdr:rowOff>${from.rowOff}</xdr:rowOff>
|
|
43782
|
+
</xdr:from>
|
|
43783
|
+
<xdr:to>
|
|
43784
|
+
<xdr:col>${to.col}</xdr:col>
|
|
43785
|
+
<xdr:colOff>${to.colOff}</xdr:colOff>
|
|
43786
|
+
<xdr:row>${to.row}</xdr:row>
|
|
43787
|
+
<xdr:rowOff>${to.rowOff}</xdr:rowOff>
|
|
43788
|
+
</xdr:to>
|
|
43789
|
+
<xdr:pic>
|
|
43790
|
+
<xdr:nvPicPr>
|
|
43791
|
+
<xdr:cNvPr ${formatAttributes(cNvPrAttrs)}/>
|
|
43792
|
+
<xdr:cNvPicPr preferRelativeResize="0"/>
|
|
43793
|
+
</xdr:nvPicPr>
|
|
43794
|
+
<xdr:blipFill>
|
|
43795
|
+
<a:blip cstate="print" r:embed="${imageRelId}"/>
|
|
43796
|
+
<a:stretch>
|
|
43797
|
+
<a:fillRect/>
|
|
43798
|
+
</a:stretch>
|
|
43799
|
+
</xdr:blipFill>
|
|
43800
|
+
<xdr:spPr>
|
|
43801
|
+
<a:prstGeom prst="rect">
|
|
43802
|
+
<a:avLst/>
|
|
43803
|
+
</a:prstGeom>
|
|
43804
|
+
<a:noFill/>
|
|
43805
|
+
</xdr:spPr>
|
|
43806
|
+
</xdr:pic>
|
|
43807
|
+
<xdr:clientData fLocksWithSheet="0"/>
|
|
43808
|
+
</xdr:twoCellAnchor>
|
|
43809
|
+
`;
|
|
43694
43810
|
}
|
|
43695
43811
|
|
|
43696
43812
|
function addNumberFormats(numFmts) {
|
|
@@ -43886,7 +44002,7 @@ function addAutoFilter(table) {
|
|
|
43886
44002
|
function addFilterColumns(table) {
|
|
43887
44003
|
const tableZone = toZone(table.range);
|
|
43888
44004
|
const columns = [];
|
|
43889
|
-
for (const i of range(0, zoneToDimension(tableZone).
|
|
44005
|
+
for (const i of range(0, zoneToDimension(tableZone).numberOfCols)) {
|
|
43890
44006
|
const filter = table.filters[i];
|
|
43891
44007
|
if (!filter || !filter.filteredValues.length) {
|
|
43892
44008
|
continue;
|
|
@@ -43912,7 +44028,7 @@ function addTableColumns(table, sheetData) {
|
|
|
43912
44028
|
var _a;
|
|
43913
44029
|
const tableZone = toZone(table.range);
|
|
43914
44030
|
const columns = [];
|
|
43915
|
-
for (const i of range(0, zoneToDimension(tableZone).
|
|
44031
|
+
for (const i of range(0, zoneToDimension(tableZone).numberOfCols)) {
|
|
43916
44032
|
const colHeaderXc = toXC(tableZone.left + i, tableZone.top);
|
|
43917
44033
|
const colName = ((_a = sheetData.cells[colHeaderXc]) === null || _a === void 0 ? void 0 : _a.content) || `col${i}`;
|
|
43918
44034
|
const colAttributes = [
|
|
@@ -44160,23 +44276,37 @@ function createWorksheets(data, construct) {
|
|
|
44160
44276
|
currentTableIndex += sheet.filterTables.length;
|
|
44161
44277
|
// Figures and Charts
|
|
44162
44278
|
let drawingNode = escapeXml ``;
|
|
44279
|
+
const drawingRelIds = [];
|
|
44163
44280
|
const charts = sheet.charts;
|
|
44164
|
-
|
|
44165
|
-
const
|
|
44166
|
-
|
|
44167
|
-
|
|
44168
|
-
|
|
44169
|
-
|
|
44170
|
-
|
|
44171
|
-
|
|
44172
|
-
|
|
44173
|
-
|
|
44174
|
-
|
|
44281
|
+
for (const chart of charts) {
|
|
44282
|
+
const xlsxChartId = convertChartId(chart.id);
|
|
44283
|
+
const chartRelId = addRelsToFile(construct.relsFiles, `xl/drawings/_rels/drawing${sheetIndex}.xml.rels`, {
|
|
44284
|
+
target: `../charts/chart${xlsxChartId}.xml`,
|
|
44285
|
+
type: XLSX_RELATION_TYPE.chart,
|
|
44286
|
+
});
|
|
44287
|
+
drawingRelIds.push(chartRelId);
|
|
44288
|
+
files.push(createXMLFile(createChart(chart, sheetIndex, data), `xl/charts/chart${xlsxChartId}.xml`, "chart"));
|
|
44289
|
+
}
|
|
44290
|
+
const images = sheet.images;
|
|
44291
|
+
for (const image of images) {
|
|
44292
|
+
const xlsxImageId = convertImageId(image.id);
|
|
44293
|
+
const imageRelId = addRelsToFile(construct.relsFiles, `xl/drawings/_rels/drawing${sheetIndex}.xml.rels`, {
|
|
44294
|
+
target: `../media/image${xlsxImageId}`,
|
|
44295
|
+
type: XLSX_RELATION_TYPE.image,
|
|
44296
|
+
});
|
|
44297
|
+
drawingRelIds.push(imageRelId);
|
|
44298
|
+
files.push({
|
|
44299
|
+
path: `xl/media/image${xlsxImageId}`,
|
|
44300
|
+
imagePath: image.data.path,
|
|
44301
|
+
});
|
|
44302
|
+
}
|
|
44303
|
+
const drawings = [...charts, ...images];
|
|
44304
|
+
if (drawings.length) {
|
|
44175
44305
|
const drawingRelId = addRelsToFile(construct.relsFiles, `xl/worksheets/_rels/sheet${sheetIndex}.xml.rels`, {
|
|
44176
44306
|
target: `../drawings/drawing${sheetIndex}.xml`,
|
|
44177
44307
|
type: XLSX_RELATION_TYPE.drawing,
|
|
44178
44308
|
});
|
|
44179
|
-
files.push(createXMLFile(createDrawing(
|
|
44309
|
+
files.push(createXMLFile(createDrawing(drawingRelIds, sheet, drawings), `xl/drawings/drawing${sheetIndex}.xml`, "drawing"));
|
|
44180
44310
|
drawingNode = escapeXml /*xml*/ `<drawing r:id="${drawingRelId}" />`;
|
|
44181
44311
|
}
|
|
44182
44312
|
const sheetXml = escapeXml /*xml*/ `
|
|
@@ -44290,7 +44420,7 @@ function createRelsFiles(relsFiles) {
|
|
|
44290
44420
|
function createContentTypes(files) {
|
|
44291
44421
|
const overrideNodes = [];
|
|
44292
44422
|
for (const file of files) {
|
|
44293
|
-
if (file.contentType) {
|
|
44423
|
+
if ("contentType" in file && file.contentType) {
|
|
44294
44424
|
overrideNodes.push(createOverride("/" + file.path, CONTENT_TYPES[file.contentType]));
|
|
44295
44425
|
}
|
|
44296
44426
|
}
|
|
@@ -44846,6 +44976,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
44846
44976
|
exports.tokenize = tokenize;
|
|
44847
44977
|
|
|
44848
44978
|
|
|
44849
|
-
__info__.version = '16.2.0-alpha.
|
|
44850
|
-
__info__.date = '2023-03-
|
|
44851
|
-
__info__.hash = '
|
|
44979
|
+
__info__.version = '16.2.0-alpha.4';
|
|
44980
|
+
__info__.date = '2023-03-14T09:24:38.219Z';
|
|
44981
|
+
__info__.hash = '2c6fe88';
|