@odoo/o-spreadsheet 16.2.0-alpha.3 → 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} +1587 -1514
- package/dist/{o_spreadsheet.d.ts → o-spreadsheet.d.ts} +132 -132
- package/dist/{o_spreadsheet.esm.js → o-spreadsheet.esm.js} +1588 -1515
- package/dist/{o_spreadsheet.iife.js → o-spreadsheet.iife.js} +1587 -1514
- 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 -2398
|
@@ -2029,10 +2029,10 @@ class RangeImpl {
|
|
|
2029
2029
|
if (right !== undefined && bottom !== undefined)
|
|
2030
2030
|
return { left, top, right, bottom };
|
|
2031
2031
|
else if (bottom === undefined && right !== undefined) {
|
|
2032
|
-
return { right, top, left, bottom: this.getSheetSize(this.sheetId).
|
|
2032
|
+
return { right, top, left, bottom: this.getSheetSize(this.sheetId).numberOfRows - 1 };
|
|
2033
2033
|
}
|
|
2034
2034
|
else if (right === undefined && bottom !== undefined) {
|
|
2035
|
-
return { bottom, left, top, right: this.getSheetSize(this.sheetId).
|
|
2035
|
+
return { bottom, left, top, right: this.getSheetSize(this.sheetId).numberOfCols - 1 };
|
|
2036
2036
|
}
|
|
2037
2037
|
throw new Error(_lt("Bad zone format"));
|
|
2038
2038
|
}
|
|
@@ -2655,13 +2655,13 @@ function positionsDifference(positions, toRemove) {
|
|
|
2655
2655
|
}
|
|
2656
2656
|
function zoneToDimension(zone) {
|
|
2657
2657
|
return {
|
|
2658
|
-
|
|
2659
|
-
|
|
2658
|
+
numberOfRows: zone.bottom - zone.top + 1,
|
|
2659
|
+
numberOfCols: zone.right - zone.left + 1,
|
|
2660
2660
|
};
|
|
2661
2661
|
}
|
|
2662
2662
|
function isOneDimensional(zone) {
|
|
2663
|
-
const {
|
|
2664
|
-
return
|
|
2663
|
+
const { numberOfCols, numberOfRows } = zoneToDimension(zone);
|
|
2664
|
+
return numberOfCols === 1 || numberOfRows === 1;
|
|
2665
2665
|
}
|
|
2666
2666
|
/**
|
|
2667
2667
|
* Array of all positions in the zone.
|
|
@@ -3136,7 +3136,7 @@ class ScorecardChart$1 extends owl.Component {
|
|
|
3136
3136
|
return this.runtime.fontColor;
|
|
3137
3137
|
}
|
|
3138
3138
|
get secondaryFontColor() {
|
|
3139
|
-
return relativeLuminance(this.
|
|
3139
|
+
return relativeLuminance(this.backgroundColor) > 0.3 ? "#525252" : "#C8C8C8";
|
|
3140
3140
|
}
|
|
3141
3141
|
get figure() {
|
|
3142
3142
|
return this.props.figure;
|
|
@@ -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,979 +5615,276 @@ 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
|
-
});
|
|
5747
|
-
}
|
|
5748
|
-
}
|
|
5749
|
-
Popover.template = "o-spreadsheet-Popover";
|
|
5750
|
-
Popover.defaultProps = {
|
|
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;
|
|
5849
|
+
}
|
|
5775
5850
|
}
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
this.availableHeightDown >= this.availableHeightUp);
|
|
5851
|
+
const { col, row } = anchor;
|
|
5852
|
+
if (multiColumns) {
|
|
5853
|
+
result = env.model.dispatch("SORT_CELLS", { sheetId, col, row, zone, sortDirection });
|
|
5780
5854
|
}
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
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.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
|
-
}
|
|
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
|
+
}
|
|
6521
5888
|
}
|
|
6522
5889
|
if (result.isCancelledBecause(63 /* CommandResult.InvalidSortZone */)) {
|
|
6523
5890
|
const { col, row } = anchor;
|
|
@@ -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;
|
|
@@ -16341,484 +15754,1000 @@ autofillModifiersRegistry
|
|
|
16341
15754
|
y = 0;
|
|
16342
15755
|
break;
|
|
16343
15756
|
}
|
|
16344
|
-
const cell = data.cell;
|
|
16345
|
-
if (!cell || !cell.isFormula) {
|
|
16346
|
-
return { cellData: {} };
|
|
15757
|
+
const cell = data.cell;
|
|
15758
|
+
if (!cell || !cell.isFormula) {
|
|
15759
|
+
return { cellData: {} };
|
|
15760
|
+
}
|
|
15761
|
+
const sheetId = data.sheetId;
|
|
15762
|
+
const ranges = getters.createAdaptedRanges(cell.dependencies, x, y, sheetId);
|
|
15763
|
+
const content = getters.buildFormulaContent(sheetId, cell, ranges);
|
|
15764
|
+
return {
|
|
15765
|
+
cellData: {
|
|
15766
|
+
border: data.border,
|
|
15767
|
+
style: cell.style,
|
|
15768
|
+
format: cell.format,
|
|
15769
|
+
content,
|
|
15770
|
+
},
|
|
15771
|
+
tooltip: content ? { props: { content } } : undefined,
|
|
15772
|
+
};
|
|
15773
|
+
},
|
|
15774
|
+
});
|
|
15775
|
+
|
|
15776
|
+
const autofillRulesRegistry = new Registry();
|
|
15777
|
+
const numberPostfixRegExp = /(\d+)$/;
|
|
15778
|
+
const stringPrefixRegExp = /^(.*\D+)/;
|
|
15779
|
+
const alphaNumericValueRegExp = /^(.*\D+)(\d+)$/;
|
|
15780
|
+
/**
|
|
15781
|
+
* Get the consecutive evaluated cells that can pass the filter function (e.g. certain type filter).
|
|
15782
|
+
* Return the one which contains the given cell
|
|
15783
|
+
*/
|
|
15784
|
+
function getGroup(cell, cells, filter) {
|
|
15785
|
+
let group = [];
|
|
15786
|
+
let found = false;
|
|
15787
|
+
for (let x of cells) {
|
|
15788
|
+
if (x === cell) {
|
|
15789
|
+
found = true;
|
|
15790
|
+
}
|
|
15791
|
+
const cellValue = evaluateLiteral(x === null || x === void 0 ? void 0 : x.content);
|
|
15792
|
+
if (filter(cellValue)) {
|
|
15793
|
+
group.push(cellValue);
|
|
15794
|
+
}
|
|
15795
|
+
else {
|
|
15796
|
+
if (found) {
|
|
15797
|
+
return group;
|
|
15798
|
+
}
|
|
15799
|
+
group = [];
|
|
15800
|
+
}
|
|
15801
|
+
}
|
|
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";
|
|
16347
16197
|
}
|
|
16348
|
-
|
|
16349
|
-
|
|
16350
|
-
|
|
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) => {
|
|
16351
16228
|
return {
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
content,
|
|
16357
|
-
},
|
|
16358
|
-
tooltip: content ? { props: { content } } : undefined,
|
|
16229
|
+
isOpen: true,
|
|
16230
|
+
props: { filterPosition: position },
|
|
16231
|
+
Component: FilterMenu,
|
|
16232
|
+
cellCorner: "BottomLeft",
|
|
16359
16233
|
};
|
|
16360
16234
|
},
|
|
16361
|
-
}
|
|
16235
|
+
};
|
|
16362
16236
|
|
|
16363
|
-
const autofillRulesRegistry = new Registry();
|
|
16364
|
-
const numberPostfixRegExp = /(\d+)$/;
|
|
16365
|
-
const stringPrefixRegExp = /^(.*\D+)/;
|
|
16366
|
-
const alphaNumericValueRegExp = /^(.*\D+)(\d+)$/;
|
|
16367
16237
|
/**
|
|
16368
|
-
*
|
|
16369
|
-
*
|
|
16238
|
+
* Return the o-spreadsheet element position relative
|
|
16239
|
+
* to the browser viewport.
|
|
16370
16240
|
*/
|
|
16371
|
-
function
|
|
16372
|
-
|
|
16373
|
-
let
|
|
16374
|
-
|
|
16375
|
-
|
|
16376
|
-
|
|
16377
|
-
|
|
16378
|
-
const cellValue = evaluateLiteral(x === null || x === void 0 ? void 0 : x.content);
|
|
16379
|
-
if (filter(cellValue)) {
|
|
16380
|
-
group.push(cellValue);
|
|
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");
|
|
16381
16248
|
}
|
|
16382
|
-
|
|
16383
|
-
|
|
16384
|
-
|
|
16385
|
-
|
|
16386
|
-
|
|
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;
|
|
16387
16255
|
}
|
|
16388
16256
|
}
|
|
16389
|
-
|
|
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
|
-
|
|
16595
|
-
.o-filter-menu-actions {
|
|
16596
|
-
display: flex;
|
|
16597
|
-
flex-direction: row;
|
|
16598
|
-
margin-bottom: 4px;
|
|
16599
16562
|
|
|
16600
|
-
|
|
16601
|
-
|
|
16602
|
-
|
|
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.getImageSize(this.figureId);
|
|
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
|
// ---------------------------------------------------------------------------
|
|
@@ -17240,20 +17092,105 @@ class ImageFigure extends owl.Component {
|
|
|
17240
17092
|
}
|
|
17241
17093
|
}
|
|
17242
17094
|
ImageFigure.template = "o-spreadsheet-ImageFigure";
|
|
17243
|
-
ImageFigure.components = {
|
|
17095
|
+
ImageFigure.components = {};
|
|
17244
17096
|
ImageFigure.props = {
|
|
17245
17097
|
figure: Object,
|
|
17246
17098
|
onFigureDeleted: Function,
|
|
17247
17099
|
};
|
|
17248
17100
|
|
|
17249
17101
|
const figureRegistry = new Registry();
|
|
17250
|
-
figureRegistry.add("chart", {
|
|
17102
|
+
figureRegistry.add("chart", {
|
|
17103
|
+
Component: ChartFigure,
|
|
17104
|
+
SidePanelComponent: "ChartPanel",
|
|
17105
|
+
menuBuilder: getChartMenu,
|
|
17106
|
+
});
|
|
17251
17107
|
figureRegistry.add("image", {
|
|
17252
17108
|
Component: ImageFigure,
|
|
17253
17109
|
keepRatio: true,
|
|
17254
17110
|
minFigSize: 20,
|
|
17255
17111
|
borderWidth: 0,
|
|
17256
|
-
|
|
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
|
+
}
|
|
17257
17194
|
|
|
17258
17195
|
const inverseCommandRegistry = new Registry()
|
|
17259
17196
|
.add("ADD_COLUMNS_ROWS", inverseAddColumnsRows)
|
|
@@ -18428,7 +18365,7 @@ class ColorPicker extends owl.Component {
|
|
|
18428
18365
|
}
|
|
18429
18366
|
onColorClick(color) {
|
|
18430
18367
|
if (color) {
|
|
18431
|
-
this.props.onColorPicked(color);
|
|
18368
|
+
this.props.onColorPicked(toHex(color));
|
|
18432
18369
|
}
|
|
18433
18370
|
}
|
|
18434
18371
|
getCheckMarkColor() {
|
|
@@ -18443,8 +18380,10 @@ class ColorPicker extends owl.Component {
|
|
|
18443
18380
|
this.state.isCurrentColorInvalid = true;
|
|
18444
18381
|
return;
|
|
18445
18382
|
}
|
|
18383
|
+
const color = toHex(this.state.currentColor);
|
|
18446
18384
|
this.state.isCurrentColorInvalid = false;
|
|
18447
|
-
this.props.onColorPicked(
|
|
18385
|
+
this.props.onColorPicked(color);
|
|
18386
|
+
this.state.currentColor = color;
|
|
18448
18387
|
}
|
|
18449
18388
|
toggleColorPicker() {
|
|
18450
18389
|
this.state.showGradient = !this.state.showGradient;
|
|
@@ -20181,9 +20120,8 @@ css /*SCSS*/ `
|
|
|
20181
20120
|
|
|
20182
20121
|
.o-figure-menu {
|
|
20183
20122
|
right: 0px;
|
|
20123
|
+
top: 0px;
|
|
20184
20124
|
display: none;
|
|
20185
|
-
position: absolute;
|
|
20186
|
-
padding: 5px;
|
|
20187
20125
|
}
|
|
20188
20126
|
|
|
20189
20127
|
.o-figure-menu-item {
|
|
@@ -20201,7 +20139,10 @@ css /*SCSS*/ `
|
|
|
20201
20139
|
class FigureComponent extends owl.Component {
|
|
20202
20140
|
constructor() {
|
|
20203
20141
|
super(...arguments);
|
|
20142
|
+
this.menuState = owl.useState({ isOpen: false, position: null, menuItems: [] });
|
|
20204
20143
|
this.figureRef = owl.useRef("figure");
|
|
20144
|
+
this.menuButtonRef = owl.useRef("menuButton");
|
|
20145
|
+
this.menuButtonRect = useAbsoluteBoundingRect(this.menuButtonRef);
|
|
20205
20146
|
}
|
|
20206
20147
|
get isSelected() {
|
|
20207
20148
|
return this.env.model.getters.getSelectedFigureId() === this.props.figure.id;
|
|
@@ -20309,9 +20250,33 @@ class FigureComponent extends owl.Component {
|
|
|
20309
20250
|
break;
|
|
20310
20251
|
}
|
|
20311
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
|
+
}
|
|
20312
20277
|
}
|
|
20313
20278
|
FigureComponent.template = "o-spreadsheet-FigureComponent";
|
|
20314
|
-
FigureComponent.components = {};
|
|
20279
|
+
FigureComponent.components = { Menu };
|
|
20315
20280
|
FigureComponent.defaultProps = {
|
|
20316
20281
|
onFigureDeleted: () => { },
|
|
20317
20282
|
onMouseDown: () => { },
|
|
@@ -22396,6 +22361,7 @@ const XLSX_RELATION_TYPE = {
|
|
|
22396
22361
|
theme: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme",
|
|
22397
22362
|
table: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table",
|
|
22398
22363
|
hyperlink: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
|
22364
|
+
image: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
|
22399
22365
|
};
|
|
22400
22366
|
const RELATIONSHIP_NSR = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
22401
22367
|
const HEIGHT_FACTOR = 0.75; // 100px => 75 u
|
|
@@ -23419,12 +23385,8 @@ function getCellType(value) {
|
|
|
23419
23385
|
return "n";
|
|
23420
23386
|
}
|
|
23421
23387
|
}
|
|
23422
|
-
/**
|
|
23423
|
-
* For some reason, Excel will only take the devicePixelRatio (i.e. interface scale on Windows desktop)
|
|
23424
|
-
* into account for the height.
|
|
23425
|
-
*/
|
|
23426
23388
|
function convertHeightToExcel(height) {
|
|
23427
|
-
return Math.round(HEIGHT_FACTOR * height *
|
|
23389
|
+
return Math.round(HEIGHT_FACTOR * height * 100) / 100;
|
|
23428
23390
|
}
|
|
23429
23391
|
function convertWidthToExcel(width) {
|
|
23430
23392
|
return Math.round(WIDTH_FACTOR * width * 100) / 100;
|
|
@@ -23566,6 +23528,19 @@ function convertChartId(chartId) {
|
|
|
23566
23528
|
}
|
|
23567
23529
|
return xlsxId + 1;
|
|
23568
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
|
+
}
|
|
23569
23544
|
/**
|
|
23570
23545
|
* Convert a value expressed in dot to EMU.
|
|
23571
23546
|
* EMU = English Metrical Unit
|
|
@@ -26327,6 +26302,7 @@ function createEmptyExcelSheet(sheetId, name) {
|
|
|
26327
26302
|
return {
|
|
26328
26303
|
...createEmptySheet(sheetId, name),
|
|
26329
26304
|
charts: [],
|
|
26305
|
+
images: [],
|
|
26330
26306
|
};
|
|
26331
26307
|
}
|
|
26332
26308
|
function createEmptyExcelWorkbookData() {
|
|
@@ -28962,7 +28938,7 @@ class FiltersPlugin extends CorePlugin {
|
|
|
28962
28938
|
filters.push(new Filter(filter.id, filterZone));
|
|
28963
28939
|
}
|
|
28964
28940
|
// Add filters for new columns
|
|
28965
|
-
if (filters.length < zoneToDimension(zone).
|
|
28941
|
+
if (filters.length < zoneToDimension(zone).numberOfCols) {
|
|
28966
28942
|
for (let col = zone.left; col <= zone.right; col++) {
|
|
28967
28943
|
if (!filters.find((filter) => filter.col === col)) {
|
|
28968
28944
|
filters.push(new Filter(this.uuidGenerator.uuidv4(), { ...zone, left: col, right: col }));
|
|
@@ -29613,6 +29589,24 @@ class ImagePlugin extends CorePlugin {
|
|
|
29613
29589
|
}
|
|
29614
29590
|
}
|
|
29615
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
|
+
}
|
|
29616
29610
|
getAllImages() {
|
|
29617
29611
|
const images = [];
|
|
29618
29612
|
for (const sheetId in this.images) {
|
|
@@ -29810,8 +29804,8 @@ class MergePlugin extends CorePlugin {
|
|
|
29810
29804
|
if (merge) {
|
|
29811
29805
|
return isEqual(zone, merge);
|
|
29812
29806
|
}
|
|
29813
|
-
const {
|
|
29814
|
-
return
|
|
29807
|
+
const { numberOfCols, numberOfRows } = zoneToDimension(zone);
|
|
29808
|
+
return numberOfCols === 1 && numberOfRows === 1;
|
|
29815
29809
|
}
|
|
29816
29810
|
// ---------------------------------------------------------------------------
|
|
29817
29811
|
// Merges
|
|
@@ -29979,8 +29973,8 @@ class MergePlugin extends CorePlugin {
|
|
|
29979
29973
|
this.removeMerge(sheetId, currentZone);
|
|
29980
29974
|
break;
|
|
29981
29975
|
default:
|
|
29982
|
-
const {
|
|
29983
|
-
if (
|
|
29976
|
+
const { numberOfCols, numberOfRows } = zoneToDimension(result.range.zone);
|
|
29977
|
+
if (numberOfCols === 1 && numberOfRows === 1) {
|
|
29984
29978
|
this.removeMerge(sheetId, currentZone);
|
|
29985
29979
|
}
|
|
29986
29980
|
else {
|
|
@@ -30739,8 +30733,8 @@ class SheetPlugin extends CorePlugin {
|
|
|
30739
30733
|
}
|
|
30740
30734
|
getSheetSize(sheetId) {
|
|
30741
30735
|
return {
|
|
30742
|
-
|
|
30743
|
-
|
|
30736
|
+
numberOfRows: this.getNumberRows(sheetId),
|
|
30737
|
+
numberOfCols: this.getNumberCols(sheetId),
|
|
30744
30738
|
};
|
|
30745
30739
|
}
|
|
30746
30740
|
getSheetZone(sheetId) {
|
|
@@ -32256,6 +32250,9 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
32256
32250
|
case "CREATE_SHEET":
|
|
32257
32251
|
this.filterValues[cmd.sheetId] = {};
|
|
32258
32252
|
break;
|
|
32253
|
+
case "HIDE_COLUMNS_ROWS":
|
|
32254
|
+
this.updateHiddenRows();
|
|
32255
|
+
break;
|
|
32259
32256
|
case "UPDATE_FILTER":
|
|
32260
32257
|
this.updateFilter(cmd);
|
|
32261
32258
|
this.updateHiddenRows();
|
|
@@ -32364,9 +32361,16 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
32364
32361
|
updateHiddenRows() {
|
|
32365
32362
|
var _a, _b;
|
|
32366
32363
|
const sheetId = this.getters.getActiveSheetId();
|
|
32367
|
-
const filters = this.getters
|
|
32364
|
+
const filters = this.getters
|
|
32365
|
+
.getFilters(sheetId)
|
|
32366
|
+
.sort((filter1, filter2) => filter1.zoneWithHeaders.top - filter2.zoneWithHeaders.top);
|
|
32368
32367
|
const hiddenRows = new Set();
|
|
32369
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;
|
|
32370
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);
|
|
32371
32375
|
if (!filteredValues || !filter.filteredZone)
|
|
32372
32376
|
continue;
|
|
@@ -32389,7 +32393,7 @@ class FilterEvaluationPlugin extends UIPlugin {
|
|
|
32389
32393
|
const tableZone = toZone(tableData.range);
|
|
32390
32394
|
const filters = [];
|
|
32391
32395
|
const headerNames = [];
|
|
32392
|
-
for (const i of range(0, zoneToDimension(tableZone).
|
|
32396
|
+
for (const i of range(0, zoneToDimension(tableZone).numberOfCols)) {
|
|
32393
32397
|
const position = {
|
|
32394
32398
|
sheetId: sheetData.id,
|
|
32395
32399
|
col: tableZone.left + i,
|
|
@@ -33900,7 +33904,7 @@ class AutomaticSumPlugin extends UIPlugin {
|
|
|
33900
33904
|
dimensionsToSum(sheetId, zone) {
|
|
33901
33905
|
const dimensions = new Set();
|
|
33902
33906
|
if (isOneDimensional(zone)) {
|
|
33903
|
-
dimensions.add(zoneToDimension(zone).
|
|
33907
|
+
dimensions.add(zoneToDimension(zone).numberOfCols === 1 ? "COL" : "ROW");
|
|
33904
33908
|
return dimensions;
|
|
33905
33909
|
}
|
|
33906
33910
|
if (this.lastColIsEmpty(sheetId, zone)) {
|
|
@@ -34488,8 +34492,8 @@ class HighlightPlugin extends UIPlugin {
|
|
|
34488
34492
|
x.zone.bottom < this.getters.getNumberRows(x.sheetId) &&
|
|
34489
34493
|
x.zone.right < this.getters.getNumberCols(x.sheetId))
|
|
34490
34494
|
.map((highlight) => {
|
|
34491
|
-
const {
|
|
34492
|
-
const zone =
|
|
34495
|
+
const { numberOfRows, numberOfCols } = zoneToDimension(highlight.zone);
|
|
34496
|
+
const zone = numberOfRows * numberOfCols === 1
|
|
34493
34497
|
? this.getters.expandZone(highlight.sheetId, highlight.zone)
|
|
34494
34498
|
: highlight.zone;
|
|
34495
34499
|
return {
|
|
@@ -36396,7 +36400,7 @@ class SortPlugin extends UIPlugin {
|
|
|
36396
36400
|
const merges = this.getters.getMerges(sheetId).filter((merge) => overlap(merge, zone));
|
|
36397
36401
|
/*Test the presence of merges of different sizes*/
|
|
36398
36402
|
const mergeDimension = zoneToDimension(merges[0]);
|
|
36399
|
-
let [widthFirst, heightFirst] = [mergeDimension.
|
|
36403
|
+
let [widthFirst, heightFirst] = [mergeDimension.numberOfCols, mergeDimension.numberOfRows];
|
|
36400
36404
|
if (!merges.every((merge) => {
|
|
36401
36405
|
let [widthCurrent, heightCurrent] = [
|
|
36402
36406
|
merge.right - merge.left + 1,
|
|
@@ -37529,9 +37533,9 @@ class ClipboardOsState extends ClipboardCellsAbstractState {
|
|
|
37529
37533
|
const values = this.values;
|
|
37530
37534
|
const pasteZone = this.getPasteZone(target);
|
|
37531
37535
|
const { left: activeCol, top: activeRow } = pasteZone;
|
|
37532
|
-
const {
|
|
37536
|
+
const { numberOfCols, numberOfRows } = zoneToDimension(pasteZone);
|
|
37533
37537
|
const sheetId = this.getters.getActiveSheetId();
|
|
37534
|
-
this.addMissingDimensions(
|
|
37538
|
+
this.addMissingDimensions(numberOfCols, numberOfRows, activeCol, activeRow);
|
|
37535
37539
|
for (let i = 0; i < values.length; i++) {
|
|
37536
37540
|
for (let j = 0; j < values[i].length; j++) {
|
|
37537
37541
|
this.dispatch("UPDATE_CELL", {
|
|
@@ -37545,8 +37549,8 @@ class ClipboardOsState extends ClipboardCellsAbstractState {
|
|
|
37545
37549
|
const zone = {
|
|
37546
37550
|
left: activeCol,
|
|
37547
37551
|
top: activeRow,
|
|
37548
|
-
right: activeCol +
|
|
37549
|
-
bottom: activeRow +
|
|
37552
|
+
right: activeCol + numberOfCols - 1,
|
|
37553
|
+
bottom: activeRow + numberOfRows - 1,
|
|
37550
37554
|
};
|
|
37551
37555
|
this.selection.selectZone({ cell: { col: activeCol, row: activeRow }, zone });
|
|
37552
37556
|
}
|
|
@@ -43645,7 +43649,7 @@ function getExcelThresholdType(type, position) {
|
|
|
43645
43649
|
}
|
|
43646
43650
|
}
|
|
43647
43651
|
|
|
43648
|
-
function createDrawing(
|
|
43652
|
+
function createDrawing(drawingRelIds, sheet, figures) {
|
|
43649
43653
|
const namespaces = [
|
|
43650
43654
|
["xmlns:xdr", NAMESPACE.drawing],
|
|
43651
43655
|
["xmlns:r", RELATIONSHIP_NSR],
|
|
@@ -43654,46 +43658,14 @@ function createDrawing(chartRelIds, sheet, figures) {
|
|
|
43654
43658
|
];
|
|
43655
43659
|
const figuresNodes = [];
|
|
43656
43660
|
for (const [figureIndex, figure] of Object.entries(figures)) {
|
|
43657
|
-
|
|
43658
|
-
|
|
43659
|
-
|
|
43660
|
-
|
|
43661
|
-
|
|
43662
|
-
|
|
43663
|
-
|
|
43664
|
-
|
|
43665
|
-
figuresNodes.push(escapeXml /*xml*/ `
|
|
43666
|
-
<xdr:twoCellAnchor>
|
|
43667
|
-
<xdr:from>
|
|
43668
|
-
<xdr:col>${from.col}</xdr:col>
|
|
43669
|
-
<xdr:colOff>${from.colOff}</xdr:colOff>
|
|
43670
|
-
<xdr:row>${from.row}</xdr:row>
|
|
43671
|
-
<xdr:rowOff>${from.rowOff}</xdr:rowOff>
|
|
43672
|
-
</xdr:from>
|
|
43673
|
-
<xdr:to>
|
|
43674
|
-
<xdr:col>${to.col}</xdr:col>
|
|
43675
|
-
<xdr:colOff>${to.colOff}</xdr:colOff>
|
|
43676
|
-
<xdr:row>${to.row}</xdr:row>
|
|
43677
|
-
<xdr:rowOff>${to.rowOff}</xdr:rowOff>
|
|
43678
|
-
</xdr:to>
|
|
43679
|
-
<xdr:graphicFrame>
|
|
43680
|
-
<xdr:nvGraphicFramePr>
|
|
43681
|
-
<xdr:cNvPr ${formatAttributes(cNvPrAttrs)} />
|
|
43682
|
-
<xdr:cNvGraphicFramePr />
|
|
43683
|
-
</xdr:nvGraphicFramePr>
|
|
43684
|
-
<xdr:xfrm>
|
|
43685
|
-
<a:off x="0" y="0"/>
|
|
43686
|
-
<a:ext cx="0" cy="0"/>
|
|
43687
|
-
</xdr:xfrm>
|
|
43688
|
-
<a:graphic>
|
|
43689
|
-
<a:graphicData uri="${DRAWING_NS_C}">
|
|
43690
|
-
<c:chart r:id="${chartRelIds[figureIndex]}" />
|
|
43691
|
-
</a:graphicData>
|
|
43692
|
-
</a:graphic>
|
|
43693
|
-
</xdr:graphicFrame>
|
|
43694
|
-
<xdr:clientData fLocksWithSheet="0"/>
|
|
43695
|
-
</xdr:twoCellAnchor>
|
|
43696
|
-
`);
|
|
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
|
+
}
|
|
43697
43669
|
}
|
|
43698
43670
|
const xml = escapeXml /*xml*/ `
|
|
43699
43671
|
<xdr:wsDr ${formatAttributes(namespaces)}>
|
|
@@ -43748,6 +43720,93 @@ function figureCoordinates(headers, position) {
|
|
|
43748
43720
|
index: headers.length - 1,
|
|
43749
43721
|
offset: convertDotValueToEMU(position - currentPosition + FIGURE_BORDER_SIZE),
|
|
43750
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
|
+
`;
|
|
43751
43810
|
}
|
|
43752
43811
|
|
|
43753
43812
|
function addNumberFormats(numFmts) {
|
|
@@ -43943,7 +44002,7 @@ function addAutoFilter(table) {
|
|
|
43943
44002
|
function addFilterColumns(table) {
|
|
43944
44003
|
const tableZone = toZone(table.range);
|
|
43945
44004
|
const columns = [];
|
|
43946
|
-
for (const i of range(0, zoneToDimension(tableZone).
|
|
44005
|
+
for (const i of range(0, zoneToDimension(tableZone).numberOfCols)) {
|
|
43947
44006
|
const filter = table.filters[i];
|
|
43948
44007
|
if (!filter || !filter.filteredValues.length) {
|
|
43949
44008
|
continue;
|
|
@@ -43969,7 +44028,7 @@ function addTableColumns(table, sheetData) {
|
|
|
43969
44028
|
var _a;
|
|
43970
44029
|
const tableZone = toZone(table.range);
|
|
43971
44030
|
const columns = [];
|
|
43972
|
-
for (const i of range(0, zoneToDimension(tableZone).
|
|
44031
|
+
for (const i of range(0, zoneToDimension(tableZone).numberOfCols)) {
|
|
43973
44032
|
const colHeaderXc = toXC(tableZone.left + i, tableZone.top);
|
|
43974
44033
|
const colName = ((_a = sheetData.cells[colHeaderXc]) === null || _a === void 0 ? void 0 : _a.content) || `col${i}`;
|
|
43975
44034
|
const colAttributes = [
|
|
@@ -44217,23 +44276,37 @@ function createWorksheets(data, construct) {
|
|
|
44217
44276
|
currentTableIndex += sheet.filterTables.length;
|
|
44218
44277
|
// Figures and Charts
|
|
44219
44278
|
let drawingNode = escapeXml ``;
|
|
44279
|
+
const drawingRelIds = [];
|
|
44220
44280
|
const charts = sheet.charts;
|
|
44221
|
-
|
|
44222
|
-
const
|
|
44223
|
-
|
|
44224
|
-
|
|
44225
|
-
|
|
44226
|
-
|
|
44227
|
-
|
|
44228
|
-
|
|
44229
|
-
|
|
44230
|
-
|
|
44231
|
-
|
|
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) {
|
|
44232
44305
|
const drawingRelId = addRelsToFile(construct.relsFiles, `xl/worksheets/_rels/sheet${sheetIndex}.xml.rels`, {
|
|
44233
44306
|
target: `../drawings/drawing${sheetIndex}.xml`,
|
|
44234
44307
|
type: XLSX_RELATION_TYPE.drawing,
|
|
44235
44308
|
});
|
|
44236
|
-
files.push(createXMLFile(createDrawing(
|
|
44309
|
+
files.push(createXMLFile(createDrawing(drawingRelIds, sheet, drawings), `xl/drawings/drawing${sheetIndex}.xml`, "drawing"));
|
|
44237
44310
|
drawingNode = escapeXml /*xml*/ `<drawing r:id="${drawingRelId}" />`;
|
|
44238
44311
|
}
|
|
44239
44312
|
const sheetXml = escapeXml /*xml*/ `
|
|
@@ -44347,7 +44420,7 @@ function createRelsFiles(relsFiles) {
|
|
|
44347
44420
|
function createContentTypes(files) {
|
|
44348
44421
|
const overrideNodes = [];
|
|
44349
44422
|
for (const file of files) {
|
|
44350
|
-
if (file.contentType) {
|
|
44423
|
+
if ("contentType" in file && file.contentType) {
|
|
44351
44424
|
overrideNodes.push(createOverride("/" + file.path, CONTENT_TYPES[file.contentType]));
|
|
44352
44425
|
}
|
|
44353
44426
|
}
|
|
@@ -44903,6 +44976,6 @@ exports.setTranslationMethod = setTranslationMethod;
|
|
|
44903
44976
|
exports.tokenize = tokenize;
|
|
44904
44977
|
|
|
44905
44978
|
|
|
44906
|
-
__info__.version = '16.2.0-alpha.
|
|
44907
|
-
__info__.date = '2023-03-
|
|
44908
|
-
__info__.hash = '
|
|
44979
|
+
__info__.version = '16.2.0-alpha.4';
|
|
44980
|
+
__info__.date = '2023-03-14T09:24:38.219Z';
|
|
44981
|
+
__info__.hash = '2c6fe88';
|