@odoo/o-spreadsheet 17.3.0-alpha.3 → 17.3.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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.3.0-alpha.3
7
- * @date 2024-04-10T12:28:23.658Z
8
- * @hash 80b5056
6
+ * @version 17.3.0-alpha.4
7
+ * @date 2024-04-15T11:02:51.551Z
8
+ * @hash a32a1df
9
9
  */
10
10
 
11
11
  'use strict';
@@ -39,6 +39,9 @@ const GRID_BORDER_COLOR = "#E2E3E3";
39
39
  const FROZEN_PANE_HEADER_BORDER_COLOR = "#BCBCBC";
40
40
  const FROZEN_PANE_BORDER_COLOR = "#DADFE8";
41
41
  const COMPOSER_ASSISTANT_COLOR = "#9B359B";
42
+ const CHART_WATERFALL_POSITIVE_COLOR = "#006FBE";
43
+ const CHART_WATERFALL_NEGATIVE_COLOR = "#E40000";
44
+ const CHART_WATERFALL_SUBTOTAL_COLOR = "#AAAAAA";
42
45
  // Color picker defaults as upper case HEX to match `toHex`helper
43
46
  const COLOR_PICKER_DEFAULTS = [
44
47
  "#000000",
@@ -2122,6 +2125,7 @@ exports.CommandResult = void 0;
2122
2125
  CommandResult["NoChanges"] = "NoChanges";
2123
2126
  CommandResult["InvalidInputId"] = "InvalidInputId";
2124
2127
  CommandResult["SheetIsHidden"] = "SheetIsHidden";
2128
+ CommandResult["InvalidTableResize"] = "InvalidTableResize";
2125
2129
  })(exports.CommandResult || (exports.CommandResult = {}));
2126
2130
 
2127
2131
  const PLAIN_TEXT_FORMAT = "@"; // see OpenXML spec §18.8.31
@@ -2519,6 +2523,15 @@ function matrixMap(matrix, fn) {
2519
2523
  }
2520
2524
  return generateMatrix(matrix.length, matrix[0].length, (col, row) => fn(matrix[col][row]));
2521
2525
  }
2526
+ function matrixForEach(matrix, fn) {
2527
+ const numberOfCols = matrix.length;
2528
+ const numberOfRows = matrix[0]?.length ?? 0;
2529
+ for (let col = 0; col < numberOfCols; col++) {
2530
+ for (let row = 0; row < numberOfRows; row++) {
2531
+ fn(matrix[col][row]);
2532
+ }
2533
+ }
2534
+ }
2522
2535
  function transposeMatrix(matrix) {
2523
2536
  if (!matrix.length) {
2524
2537
  return [];
@@ -9153,6 +9166,62 @@ class ComposerFocusStore extends SpreadsheetStore {
9153
9166
  }
9154
9167
  }
9155
9168
 
9169
+ /** This is a chartJS plugin that will draw connector lines between the bars of a Waterfall chart */
9170
+ const waterfallLinesPlugin = {
9171
+ id: "waterfallLinesPlugin",
9172
+ beforeDraw(chart, args, options) {
9173
+ if (!options.showConnectorLines) {
9174
+ return;
9175
+ }
9176
+ // Note: private properties are not in the typing of chartJS (and some of the existing types are missing properties)
9177
+ // so we don't type anything in this file
9178
+ const drawData = chart._metasets?.[0]?.data;
9179
+ if (!drawData) {
9180
+ return;
9181
+ }
9182
+ const ctx = chart.ctx;
9183
+ ctx.save();
9184
+ ctx.setLineDash([3, 2]);
9185
+ for (let i = 0; i < drawData.length; i++) {
9186
+ const bar = drawData[i];
9187
+ if (bar.height === 0) {
9188
+ continue;
9189
+ }
9190
+ const nextBar = getNextNonEmptyBar(drawData, i);
9191
+ if (!nextBar) {
9192
+ break;
9193
+ }
9194
+ const rect = getBarElementRect(bar);
9195
+ const nextBarRect = getBarElementRect(nextBar);
9196
+ const rawBarValues = bar.$context.raw;
9197
+ const value = rawBarValues[1] - rawBarValues[0];
9198
+ const lineY = Math.round(value < 0 ? rect.bottom - 1 : rect.top);
9199
+ const lineStart = Math.round(rect.right);
9200
+ const lineEnd = Math.round(nextBarRect.left);
9201
+ ctx.strokeStyle = "#999";
9202
+ ctx.beginPath();
9203
+ ctx.moveTo(lineStart + 1, lineY + 0.5);
9204
+ ctx.lineTo(lineEnd, lineY + 0.5);
9205
+ ctx.stroke();
9206
+ }
9207
+ ctx.restore();
9208
+ },
9209
+ };
9210
+ function getBarElementRect(bar) {
9211
+ const flipped = bar.base < bar.y; // Bar are flipped for negative values in the dataset
9212
+ return {
9213
+ left: bar.x - bar.width / 2,
9214
+ right: bar.x + bar.width / 2,
9215
+ bottom: flipped ? bar.base + bar.height : bar.y + bar.height,
9216
+ top: flipped ? bar.base : bar.y,
9217
+ };
9218
+ }
9219
+ function getNextNonEmptyBar(bars, startIndex) {
9220
+ return bars.find((bar, i) => i > startIndex && bar.height !== 0);
9221
+ }
9222
+
9223
+ // @ts-ignore
9224
+ window.Chart?.register(waterfallLinesPlugin);
9156
9225
  class ChartJsComponent extends owl.Component {
9157
9226
  static template = "o-spreadsheet-ChartJsComponent";
9158
9227
  static props = {
@@ -9197,10 +9266,7 @@ class ChartJsComponent extends owl.Component {
9197
9266
  else {
9198
9267
  this.chart.data.datasets = [];
9199
9268
  }
9200
- this.chart.config.options.plugins.tooltip = chartData.options.plugins.tooltip;
9201
- this.chart.config.options.plugins.legend = chartData.options.plugins.legend;
9202
- this.chart.config.options.scales = chartData.options?.scales;
9203
- // ?
9269
+ this.chart.config.options = chartData.options;
9204
9270
  this.chart.update("active");
9205
9271
  }
9206
9272
  }
@@ -18825,7 +18891,7 @@ class FunctionRegistry extends Registry {
18825
18891
  }
18826
18892
  const descr = addMetaInfoFromArg(addDescr);
18827
18893
  validateArguments(descr.args);
18828
- this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr)), name);
18894
+ this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr), name), name);
18829
18895
  super.add(name, descr);
18830
18896
  return this;
18831
18897
  }
@@ -18864,9 +18930,7 @@ function handleError(e, functionName) {
18864
18930
  // so we fallback to a generic error
18865
18931
  if (hasStringValue(e) && isEvaluationError(e.value)) {
18866
18932
  if (hasStringMessage(e)) {
18867
- if (e.message?.includes("[[FUNCTION_NAME]]")) {
18868
- e.message = e.message.replace("[[FUNCTION_NAME]]", functionName);
18869
- }
18933
+ replaceFunctionNamePlaceholder(e, functionName);
18870
18934
  }
18871
18935
  return e;
18872
18936
  }
@@ -18881,21 +18945,29 @@ function hasStringMessage(obj) {
18881
18945
  return (obj?.message !== undefined &&
18882
18946
  typeof obj.message === "string");
18883
18947
  }
18884
- function addResultHandling(compute) {
18885
- return function (...args) {
18948
+ function addResultHandling(compute, functionName) {
18949
+ return function computeWithResultHandling(...args) {
18886
18950
  const result = compute.apply(this, args);
18887
18951
  if (!isMatrix(result)) {
18888
18952
  if (typeof result === "object" && result !== null && "value" in result) {
18953
+ replaceFunctionNamePlaceholder(result, functionName);
18889
18954
  return result;
18890
18955
  }
18891
18956
  return { value: result };
18892
18957
  }
18893
18958
  if (typeof result[0][0] === "object" && result[0][0] !== null && "value" in result[0][0]) {
18959
+ matrixForEach(result, (result) => replaceFunctionNamePlaceholder(result, functionName));
18894
18960
  return result;
18895
18961
  }
18896
18962
  return matrixMap(result, (row) => ({ value: row }));
18897
18963
  };
18898
18964
  }
18965
+ function replaceFunctionNamePlaceholder(fPayload, functionName) {
18966
+ // for performance reasons: change in place and only if needed
18967
+ if (fPayload.message?.includes("[[FUNCTION_NAME]]")) {
18968
+ fPayload.message = fPayload.message.replace("[[FUNCTION_NAME]]", functionName);
18969
+ }
18970
+ }
18899
18971
  const functionRegistry = new FunctionRegistry();
18900
18972
  for (let category of categories) {
18901
18973
  const fns = category.functions;
@@ -21238,6 +21310,263 @@ function createScatterChartRuntime(chart, getters) {
21238
21310
  return { chartJsConfig, background };
21239
21311
  }
21240
21312
 
21313
+ class WaterfallChart extends AbstractChart {
21314
+ dataSets;
21315
+ labelRange;
21316
+ background;
21317
+ verticalAxisPosition;
21318
+ legendPosition;
21319
+ aggregated;
21320
+ type = "waterfall";
21321
+ dataSetsHaveTitle;
21322
+ showSubTotals;
21323
+ firstValueAsSubtotal;
21324
+ showConnectorLines;
21325
+ positiveValuesColor;
21326
+ negativeValuesColor;
21327
+ subTotalValuesColor;
21328
+ constructor(definition, sheetId, getters) {
21329
+ super(definition, sheetId, getters);
21330
+ this.dataSets = createDataSets(getters, definition.dataSets, sheetId, definition.dataSetsHaveTitle);
21331
+ this.labelRange = createRange(getters, sheetId, definition.labelRange);
21332
+ this.background = definition.background;
21333
+ this.verticalAxisPosition = definition.verticalAxisPosition;
21334
+ this.legendPosition = definition.legendPosition;
21335
+ this.aggregated = definition.aggregated;
21336
+ this.dataSetsHaveTitle = definition.dataSetsHaveTitle;
21337
+ this.showSubTotals = definition.showSubTotals;
21338
+ this.showConnectorLines = definition.showConnectorLines;
21339
+ this.positiveValuesColor = definition.positiveValuesColor;
21340
+ this.negativeValuesColor = definition.negativeValuesColor;
21341
+ this.subTotalValuesColor = definition.subTotalValuesColor;
21342
+ this.firstValueAsSubtotal = definition.firstValueAsSubtotal;
21343
+ }
21344
+ static transformDefinition(definition, executed) {
21345
+ return transformChartDefinitionWithDataSetsWithZone(definition, executed);
21346
+ }
21347
+ static validateChartDefinition(validator, definition) {
21348
+ return validator.checkValidations(definition, checkDataset, checkLabelRange);
21349
+ }
21350
+ static getDefinitionFromContextCreation(context) {
21351
+ return {
21352
+ background: context.background,
21353
+ dataSets: context.range ? context.range : [],
21354
+ dataSetsHaveTitle: false,
21355
+ aggregated: context.aggregated ?? false,
21356
+ legendPosition: "top",
21357
+ title: context.title || "",
21358
+ type: "waterfall",
21359
+ verticalAxisPosition: "left",
21360
+ labelRange: context.auxiliaryRange || undefined,
21361
+ showSubTotals: true,
21362
+ showConnectorLines: true,
21363
+ firstValueAsSubtotal: false,
21364
+ };
21365
+ }
21366
+ getContextCreation() {
21367
+ return {
21368
+ background: this.background,
21369
+ title: this.title,
21370
+ range: this.dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, this.sheetId)),
21371
+ auxiliaryRange: this.labelRange
21372
+ ? this.getters.getRangeString(this.labelRange, this.sheetId)
21373
+ : undefined,
21374
+ aggregated: this.aggregated,
21375
+ };
21376
+ }
21377
+ copyForSheetId(sheetId) {
21378
+ const dataSets = copyDataSetsWithNewSheetId(this.sheetId, sheetId, this.dataSets);
21379
+ const labelRange = copyLabelRangeWithNewSheetId(this.sheetId, sheetId, this.labelRange);
21380
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange, sheetId);
21381
+ return new WaterfallChart(definition, sheetId, this.getters);
21382
+ }
21383
+ copyInSheetId(sheetId) {
21384
+ const definition = this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange, sheetId);
21385
+ return new WaterfallChart(definition, sheetId, this.getters);
21386
+ }
21387
+ getDefinition() {
21388
+ return this.getDefinitionWithSpecificDataSets(this.dataSets, this.labelRange);
21389
+ }
21390
+ getDefinitionWithSpecificDataSets(dataSets, labelRange, targetSheetId) {
21391
+ return {
21392
+ type: "waterfall",
21393
+ dataSetsHaveTitle: dataSets.length ? Boolean(dataSets[0].labelCell) : false,
21394
+ background: this.background,
21395
+ dataSets: dataSets.map((ds) => this.getters.getRangeString(ds.dataRange, targetSheetId || this.sheetId)),
21396
+ legendPosition: this.legendPosition,
21397
+ verticalAxisPosition: this.verticalAxisPosition,
21398
+ labelRange: labelRange
21399
+ ? this.getters.getRangeString(labelRange, targetSheetId || this.sheetId)
21400
+ : undefined,
21401
+ title: this.title,
21402
+ aggregated: this.aggregated,
21403
+ showSubTotals: this.showSubTotals,
21404
+ showConnectorLines: this.showConnectorLines,
21405
+ positiveValuesColor: this.positiveValuesColor,
21406
+ negativeValuesColor: this.negativeValuesColor,
21407
+ subTotalValuesColor: this.subTotalValuesColor,
21408
+ firstValueAsSubtotal: this.firstValueAsSubtotal,
21409
+ };
21410
+ }
21411
+ getDefinitionForExcel() {
21412
+ // TODO: implement export excel
21413
+ return undefined;
21414
+ }
21415
+ updateRanges(applyChange) {
21416
+ const { dataSets, labelRange, isStale } = updateChartRangesWithDataSets(this.getters, applyChange, this.dataSets, this.labelRange);
21417
+ if (!isStale) {
21418
+ return this;
21419
+ }
21420
+ const definition = this.getDefinitionWithSpecificDataSets(dataSets, labelRange);
21421
+ return new WaterfallChart(definition, this.sheetId, this.getters);
21422
+ }
21423
+ }
21424
+ function getWaterfallConfiguration(chart, labels, dataSeriesLabels, localeFormat) {
21425
+ const { locale, format } = localeFormat;
21426
+ const fontColor = chartFontColor(chart.background);
21427
+ const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat);
21428
+ const negativeColor = chart.negativeValuesColor || CHART_WATERFALL_NEGATIVE_COLOR;
21429
+ const positiveColor = chart.positiveValuesColor || CHART_WATERFALL_POSITIVE_COLOR;
21430
+ const subTotalColor = chart.subTotalValuesColor || CHART_WATERFALL_SUBTOTAL_COLOR;
21431
+ const legend = {
21432
+ labels: {
21433
+ generateLabels: () => {
21434
+ const legendValues = [
21435
+ { text: _t("Positive values"), fontColor, fillStyle: positiveColor },
21436
+ { text: _t("Negative values"), fontColor, fillStyle: negativeColor },
21437
+ ];
21438
+ if (chart.showSubTotals || chart.firstValueAsSubtotal) {
21439
+ legendValues.push({
21440
+ text: _t("Subtotals"),
21441
+ fontColor,
21442
+ fillStyle: subTotalColor,
21443
+ });
21444
+ }
21445
+ return legendValues;
21446
+ },
21447
+ },
21448
+ };
21449
+ if (chart.legendPosition === "none") {
21450
+ legend.display = false;
21451
+ }
21452
+ else {
21453
+ legend.position = chart.legendPosition;
21454
+ }
21455
+ config.options.plugins.legend = { ...config.options.plugins?.legend, ...legend };
21456
+ config.options.layout = {
21457
+ padding: { left: 20, right: 20, top: chart.title ? 10 : 25, bottom: 10 },
21458
+ };
21459
+ config.options.scales = {
21460
+ x: {
21461
+ ticks: {
21462
+ padding: 5,
21463
+ color: fontColor,
21464
+ },
21465
+ grid: {
21466
+ display: false,
21467
+ },
21468
+ },
21469
+ y: {
21470
+ position: chart.verticalAxisPosition,
21471
+ ticks: {
21472
+ color: fontColor,
21473
+ callback: (value) => {
21474
+ value = Number(value);
21475
+ if (isNaN(value))
21476
+ return value;
21477
+ return formatValue(value, {
21478
+ locale,
21479
+ format: !format && Math.abs(value) > 1000 ? "#,##" : format,
21480
+ });
21481
+ },
21482
+ },
21483
+ grid: {
21484
+ lineWidth: (context) => {
21485
+ return context.tick.value === 0 ? 2 : 1;
21486
+ },
21487
+ },
21488
+ },
21489
+ };
21490
+ config.options.plugins.tooltip = {
21491
+ callbacks: {
21492
+ label: function (tooltipItem) {
21493
+ const [lastValue, currentValue] = tooltipItem.raw;
21494
+ const yLabel = currentValue - lastValue;
21495
+ const dataSeriesIndex = Math.floor(tooltipItem.dataIndex / labels.length);
21496
+ const dataSeriesLabel = dataSeriesLabels[dataSeriesIndex];
21497
+ const toolTipFormat = !format && Math.abs(yLabel) > 1000 ? "#,##" : format;
21498
+ const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
21499
+ return dataSeriesLabel ? `${dataSeriesLabel}: ${yLabelStr}` : yLabelStr;
21500
+ },
21501
+ },
21502
+ };
21503
+ config.options.plugins.waterfallLinesPlugin = { showConnectorLines: chart.showConnectorLines };
21504
+ return config;
21505
+ }
21506
+ function createWaterfallChartRuntime(chart, getters) {
21507
+ const labelValues = getChartLabelValues(getters, chart.dataSets, chart.labelRange);
21508
+ let labels = labelValues.formattedValues;
21509
+ let dataSetsValues = getChartDatasetValues(getters, chart.dataSets);
21510
+ if (chart.dataSetsHaveTitle &&
21511
+ dataSetsValues[0] &&
21512
+ labels.length > dataSetsValues[0].data.length) {
21513
+ labels.shift();
21514
+ }
21515
+ ({ labels, dataSetsValues } = filterEmptyDataPoints(labels, dataSetsValues));
21516
+ if (chart.aggregated) {
21517
+ ({ labels, dataSetsValues } = aggregateDataForLabels(labels, dataSetsValues));
21518
+ }
21519
+ if (chart.showSubTotals) {
21520
+ labels.push(_t("Subtotal"));
21521
+ }
21522
+ const dataSetFormat = getChartDatasetFormat(getters, chart.dataSets);
21523
+ const locale = getters.getLocale();
21524
+ const dataSeriesLabels = dataSetsValues.map((dataSet) => dataSet.label);
21525
+ const config = getWaterfallConfiguration(chart, labels, dataSeriesLabels, {
21526
+ format: dataSetFormat,
21527
+ locale,
21528
+ });
21529
+ config.type = "bar";
21530
+ const negativeColor = chart.negativeValuesColor || CHART_WATERFALL_NEGATIVE_COLOR;
21531
+ const positiveColor = chart.positiveValuesColor || CHART_WATERFALL_POSITIVE_COLOR;
21532
+ const subTotalColor = chart.subTotalValuesColor || CHART_WATERFALL_SUBTOTAL_COLOR;
21533
+ const backgroundColor = [];
21534
+ const datasetValues = [];
21535
+ const dataset = {
21536
+ label: "",
21537
+ data: datasetValues,
21538
+ backgroundColor,
21539
+ };
21540
+ const labelsWithSubTotals = [];
21541
+ let lastValue = 0;
21542
+ for (const dataSetsValue of dataSetsValues) {
21543
+ for (let i = 0; i < dataSetsValue.data.length; i++) {
21544
+ const data = dataSetsValue.data[i];
21545
+ labelsWithSubTotals.push(labels[i]);
21546
+ if (isNaN(Number(data))) {
21547
+ datasetValues.push([lastValue, lastValue]);
21548
+ backgroundColor.push("");
21549
+ continue;
21550
+ }
21551
+ datasetValues.push([lastValue, data + lastValue]);
21552
+ let color = data >= 0 ? positiveColor : negativeColor;
21553
+ if (i === 0 && dataSetsValue === dataSetsValues[0] && chart.firstValueAsSubtotal) {
21554
+ color = subTotalColor;
21555
+ }
21556
+ backgroundColor.push(color);
21557
+ lastValue += data;
21558
+ }
21559
+ if (chart.showSubTotals) {
21560
+ labelsWithSubTotals.push(_t("Subtotal"));
21561
+ datasetValues.push([0, lastValue]);
21562
+ backgroundColor.push(subTotalColor);
21563
+ }
21564
+ }
21565
+ config.data.datasets.push(dataset);
21566
+ config.data.labels = labelsWithSubTotals.map(truncateLabel);
21567
+ return { chartJsConfig: config, background: chart.background || BACKGROUND_CHART_COLOR };
21568
+ }
21569
+
21241
21570
  /**
21242
21571
  * This registry is intended to map a cell content (raw string) to
21243
21572
  * an instance of a cell.
@@ -21247,9 +21576,9 @@ chartRegistry.add("bar", {
21247
21576
  match: (type) => type === "bar",
21248
21577
  createChart: (definition, sheetId, getters) => new BarChart(definition, sheetId, getters),
21249
21578
  getChartRuntime: createBarChartRuntime,
21250
- validateChartDefinition: (validator, definition) => BarChart.validateChartDefinition(validator, definition),
21251
- transformDefinition: (definition, executed) => BarChart.transformDefinition(definition, executed),
21252
- getChartDefinitionFromContextCreation: (context) => BarChart.getDefinitionFromContextCreation(context),
21579
+ validateChartDefinition: BarChart.validateChartDefinition,
21580
+ transformDefinition: BarChart.transformDefinition,
21581
+ getChartDefinitionFromContextCreation: BarChart.getDefinitionFromContextCreation,
21253
21582
  name: _t("Bar"),
21254
21583
  sequence: 10,
21255
21584
  });
@@ -21257,9 +21586,9 @@ chartRegistry.add("combo", {
21257
21586
  match: (type) => type === "combo",
21258
21587
  createChart: (definition, sheetId, getters) => new ComboChart(definition, sheetId, getters),
21259
21588
  getChartRuntime: createComboChartRuntime,
21260
- validateChartDefinition: (validator, definition) => ComboChart.validateChartDefinition(validator, definition),
21261
- transformDefinition: (definition, executed) => ComboChart.transformDefinition(definition, executed),
21262
- getChartDefinitionFromContextCreation: (context) => ComboChart.getDefinitionFromContextCreation(context),
21589
+ validateChartDefinition: ComboChart.validateChartDefinition,
21590
+ transformDefinition: ComboChart.transformDefinition,
21591
+ getChartDefinitionFromContextCreation: ComboChart.getDefinitionFromContextCreation,
21263
21592
  name: _t("Combo"),
21264
21593
  sequence: 15,
21265
21594
  });
@@ -21267,9 +21596,9 @@ chartRegistry.add("line", {
21267
21596
  match: (type) => type === "line",
21268
21597
  createChart: (definition, sheetId, getters) => new LineChart(definition, sheetId, getters),
21269
21598
  getChartRuntime: createLineChartRuntime,
21270
- validateChartDefinition: (validator, definition) => LineChart.validateChartDefinition(validator, definition),
21271
- transformDefinition: (definition, executed) => LineChart.transformDefinition(definition, executed),
21272
- getChartDefinitionFromContextCreation: (context) => LineChart.getDefinitionFromContextCreation(context),
21599
+ validateChartDefinition: LineChart.validateChartDefinition,
21600
+ transformDefinition: LineChart.transformDefinition,
21601
+ getChartDefinitionFromContextCreation: LineChart.getDefinitionFromContextCreation,
21273
21602
  name: _t("Line"),
21274
21603
  sequence: 20,
21275
21604
  });
@@ -21277,9 +21606,9 @@ chartRegistry.add("pie", {
21277
21606
  match: (type) => type === "pie",
21278
21607
  createChart: (definition, sheetId, getters) => new PieChart(definition, sheetId, getters),
21279
21608
  getChartRuntime: createPieChartRuntime,
21280
- validateChartDefinition: (validator, definition) => PieChart.validateChartDefinition(validator, definition),
21281
- transformDefinition: (definition, executed) => PieChart.transformDefinition(definition, executed),
21282
- getChartDefinitionFromContextCreation: (context) => PieChart.getDefinitionFromContextCreation(context),
21609
+ validateChartDefinition: PieChart.validateChartDefinition,
21610
+ transformDefinition: PieChart.transformDefinition,
21611
+ getChartDefinitionFromContextCreation: PieChart.getDefinitionFromContextCreation,
21283
21612
  name: _t("Pie"),
21284
21613
  sequence: 30,
21285
21614
  });
@@ -21287,9 +21616,9 @@ chartRegistry.add("scorecard", {
21287
21616
  match: (type) => type === "scorecard",
21288
21617
  createChart: (definition, sheetId, getters) => new ScorecardChart$1(definition, sheetId, getters),
21289
21618
  getChartRuntime: createScorecardChartRuntime,
21290
- validateChartDefinition: (validator, definition) => ScorecardChart$1.validateChartDefinition(validator, definition),
21291
- transformDefinition: (definition, executed) => ScorecardChart$1.transformDefinition(definition, executed),
21292
- getChartDefinitionFromContextCreation: (context) => ScorecardChart$1.getDefinitionFromContextCreation(context),
21619
+ validateChartDefinition: ScorecardChart$1.validateChartDefinition,
21620
+ transformDefinition: ScorecardChart$1.transformDefinition,
21621
+ getChartDefinitionFromContextCreation: ScorecardChart$1.getDefinitionFromContextCreation,
21293
21622
  name: _t("Scorecard"),
21294
21623
  sequence: 40,
21295
21624
  });
@@ -21297,9 +21626,9 @@ chartRegistry.add("gauge", {
21297
21626
  match: (type) => type === "gauge",
21298
21627
  createChart: (definition, sheetId, getters) => new GaugeChart(definition, sheetId, getters),
21299
21628
  getChartRuntime: createGaugeChartRuntime,
21300
- validateChartDefinition: (validator, definition) => GaugeChart.validateChartDefinition(validator, definition),
21301
- transformDefinition: (definition, executed) => GaugeChart.transformDefinition(definition, executed),
21302
- getChartDefinitionFromContextCreation: (context) => GaugeChart.getDefinitionFromContextCreation(context),
21629
+ validateChartDefinition: GaugeChart.validateChartDefinition,
21630
+ transformDefinition: GaugeChart.transformDefinition,
21631
+ getChartDefinitionFromContextCreation: GaugeChart.getDefinitionFromContextCreation,
21303
21632
  name: _t("Gauge"),
21304
21633
  sequence: 50,
21305
21634
  });
@@ -21307,12 +21636,22 @@ chartRegistry.add("scatter", {
21307
21636
  match: (type) => type === "scatter",
21308
21637
  createChart: (definition, sheetId, getters) => new ScatterChart(definition, sheetId, getters),
21309
21638
  getChartRuntime: createScatterChartRuntime,
21310
- validateChartDefinition: (validator, definition) => ScatterChart.validateChartDefinition(validator, definition),
21311
- transformDefinition: (definition, executed) => ScatterChart.transformDefinition(definition, executed),
21312
- getChartDefinitionFromContextCreation: (context) => ScatterChart.getDefinitionFromContextCreation(context),
21639
+ validateChartDefinition: ScatterChart.validateChartDefinition,
21640
+ transformDefinition: ScatterChart.transformDefinition,
21641
+ getChartDefinitionFromContextCreation: ScatterChart.getDefinitionFromContextCreation,
21313
21642
  name: _t("Scatter"),
21314
21643
  sequence: 60,
21315
21644
  });
21645
+ chartRegistry.add("waterfall", {
21646
+ match: (type) => type === "waterfall",
21647
+ createChart: (definition, sheetId, getters) => new WaterfallChart(definition, sheetId, getters),
21648
+ getChartRuntime: createWaterfallChartRuntime,
21649
+ validateChartDefinition: WaterfallChart.validateChartDefinition,
21650
+ transformDefinition: WaterfallChart.transformDefinition,
21651
+ getChartDefinitionFromContextCreation: WaterfallChart.getDefinitionFromContextCreation,
21652
+ name: _t("Waterfall"),
21653
+ sequence: 70,
21654
+ });
21316
21655
  const chartComponentRegistry = new Registry();
21317
21656
  chartComponentRegistry.add("line", ChartJsComponent);
21318
21657
  chartComponentRegistry.add("bar", ChartJsComponent);
@@ -21321,6 +21660,7 @@ chartComponentRegistry.add("pie", ChartJsComponent);
21321
21660
  chartComponentRegistry.add("gauge", GaugeChartComponent);
21322
21661
  chartComponentRegistry.add("scatter", ChartJsComponent);
21323
21662
  chartComponentRegistry.add("scorecard", ScorecardChart);
21663
+ chartComponentRegistry.add("waterfall", ChartJsComponent);
21324
21664
 
21325
21665
  /**
21326
21666
  * Registry intended to support usual currencies. It is mainly used to create
@@ -23300,6 +23640,7 @@ class LinkEditor extends owl.Component {
23300
23640
  this.save();
23301
23641
  }
23302
23642
  ev.stopPropagation();
23643
+ ev.preventDefault();
23303
23644
  break;
23304
23645
  case "Escape":
23305
23646
  this.cancel();
@@ -26953,8 +27294,8 @@ class ChartLabelRange extends owl.Component {
26953
27294
  };
26954
27295
  }
26955
27296
 
26956
- class LineBarPieConfigPanel extends owl.Component {
26957
- static template = "o-spreadsheet-LineBarPieConfigPanel";
27297
+ class GenericChartConfigPanel extends owl.Component {
27298
+ static template = "o-spreadsheet-GenericChartConfigPanel";
26958
27299
  static components = {
26959
27300
  SelectionInput,
26960
27301
  ValidationMessages,
@@ -27071,7 +27412,7 @@ class LineBarPieConfigPanel extends owl.Component {
27071
27412
  }
27072
27413
  }
27073
27414
 
27074
- class BarConfigPanel extends LineBarPieConfigPanel {
27415
+ class BarConfigPanel extends GenericChartConfigPanel {
27075
27416
  static template = "o-spreadsheet-BarConfigPanel";
27076
27417
  get stackedLabel() {
27077
27418
  return _t("Stacked barchart");
@@ -27678,8 +28019,8 @@ class ChartTitle extends owl.Component {
27678
28019
  }
27679
28020
  }
27680
28021
 
27681
- class LineBarPieDesignPanel extends owl.Component {
27682
- static template = "o-spreadsheet-LineBarPieDesignPanel";
28022
+ class GenericChartDesignPanel extends owl.Component {
28023
+ static template = "o-spreadsheet-GenericChartDesignPanel";
27683
28024
  static components = { RoundColorPicker, ChartTitle, Section };
27684
28025
  static props = {
27685
28026
  figureId: String,
@@ -27708,11 +28049,11 @@ class LineBarPieDesignPanel extends owl.Component {
27708
28049
  }
27709
28050
  }
27710
28051
 
27711
- class BarChartDesignPanel extends LineBarPieDesignPanel {
28052
+ class BarChartDesignPanel extends GenericChartDesignPanel {
27712
28053
  static template = "o-spreadsheet-BarChartDesignPanel";
27713
28054
  }
27714
28055
 
27715
- class ComboChartConfigPanel extends LineBarPieConfigPanel {
28056
+ class ComboChartConfigPanel extends GenericChartConfigPanel {
27716
28057
  static template = "o-spreadsheet-ComboChartConfigPanel";
27717
28058
  get shouldUseRightAxis() {
27718
28059
  return _t("Use right axis for line series");
@@ -27724,7 +28065,7 @@ class ComboChartConfigPanel extends LineBarPieConfigPanel {
27724
28065
  }
27725
28066
  }
27726
28067
 
27727
- class ComboChartDesignPanel extends LineBarPieDesignPanel {
28068
+ class ComboChartDesignPanel extends GenericChartDesignPanel {
27728
28069
  static template = "o-spreadsheet-ComboChartDesignPanel";
27729
28070
  }
27730
28071
 
@@ -27877,7 +28218,7 @@ class GaugeChartDesignPanel extends owl.Component {
27877
28218
  }
27878
28219
  }
27879
28220
 
27880
- class LineConfigPanel extends LineBarPieConfigPanel {
28221
+ class LineConfigPanel extends GenericChartConfigPanel {
27881
28222
  static template = "o-spreadsheet-LineConfigPanel";
27882
28223
  get canTreatLabelsAsText() {
27883
28224
  const chart = this.env.model.getters.getChart(this.props.figureId);
@@ -27926,11 +28267,11 @@ class LineConfigPanel extends LineBarPieConfigPanel {
27926
28267
  }
27927
28268
  }
27928
28269
 
27929
- class LineChartDesignPanel extends LineBarPieDesignPanel {
28270
+ class LineChartDesignPanel extends GenericChartDesignPanel {
27930
28271
  static template = "o-spreadsheet-LineChartDesignPanel";
27931
28272
  }
27932
28273
 
27933
- class ScatterConfigPanel extends LineBarPieConfigPanel {
28274
+ class ScatterConfigPanel extends GenericChartConfigPanel {
27934
28275
  static template = "o-spreadsheet-ScatterConfigPanel";
27935
28276
  get canTreatLabelsAsText() {
27936
28277
  const chart = this.env.model.getters.getChart(this.props.figureId);
@@ -28069,6 +28410,46 @@ class ScorecardChartDesignPanel extends owl.Component {
28069
28410
  }
28070
28411
  }
28071
28412
 
28413
+ class WaterfallChartDesignPanel extends GenericChartDesignPanel {
28414
+ static template = "o-spreadsheet-WaterfallChartDesignPanel";
28415
+ static components = { ...GenericChartDesignPanel.components, Checkbox, RoundColorPicker };
28416
+ state = owl.useState({ pickerOpened: false });
28417
+ setup() {
28418
+ super.setup();
28419
+ owl.useExternalListener(window, "click", this.closePicker);
28420
+ }
28421
+ onUpdateShowSubTotals(showSubTotals) {
28422
+ this.props.updateChart(this.props.figureId, { showSubTotals });
28423
+ }
28424
+ onUpdateShowConnectorLines(showConnectorLines) {
28425
+ this.props.updateChart(this.props.figureId, { showConnectorLines });
28426
+ }
28427
+ onUpdateFirstValueAsSubtotal(firstValueAsSubtotal) {
28428
+ this.props.updateChart(this.props.figureId, { firstValueAsSubtotal });
28429
+ }
28430
+ updateColor(colorName, color) {
28431
+ this.props.updateChart(this.props.figureId, { [colorName]: color });
28432
+ }
28433
+ closePicker() {
28434
+ this.state.pickerOpened = false;
28435
+ }
28436
+ togglePicker() {
28437
+ this.state.pickerOpened = !this.state.pickerOpened;
28438
+ }
28439
+ get positiveValuesColor() {
28440
+ return (this.props.definition.positiveValuesColor ||
28441
+ CHART_WATERFALL_POSITIVE_COLOR);
28442
+ }
28443
+ get negativeValuesColor() {
28444
+ return (this.props.definition.negativeValuesColor ||
28445
+ CHART_WATERFALL_NEGATIVE_COLOR);
28446
+ }
28447
+ get subTotalValuesColor() {
28448
+ return (this.props.definition.subTotalValuesColor ||
28449
+ CHART_WATERFALL_SUBTOTAL_COLOR);
28450
+ }
28451
+ }
28452
+
28072
28453
  const chartSidePanelComponentRegistry = new Registry();
28073
28454
  chartSidePanelComponentRegistry
28074
28455
  .add("line", {
@@ -28088,8 +28469,8 @@ chartSidePanelComponentRegistry
28088
28469
  design: ComboChartDesignPanel,
28089
28470
  })
28090
28471
  .add("pie", {
28091
- configuration: LineBarPieConfigPanel,
28092
- design: LineBarPieDesignPanel,
28472
+ configuration: GenericChartConfigPanel,
28473
+ design: GenericChartDesignPanel,
28093
28474
  })
28094
28475
  .add("gauge", {
28095
28476
  configuration: GaugeChartConfigPanel,
@@ -28098,6 +28479,10 @@ chartSidePanelComponentRegistry
28098
28479
  .add("scorecard", {
28099
28480
  configuration: ScorecardChartConfigPanel,
28100
28481
  design: ScorecardChartDesignPanel,
28482
+ })
28483
+ .add("waterfall", {
28484
+ configuration: GenericChartConfigPanel,
28485
+ design: WaterfallChartDesignPanel,
28101
28486
  });
28102
28487
 
28103
28488
  class MainChartPanelStore extends SpreadsheetStore {
@@ -30835,6 +31220,9 @@ function createFilter(id, range, config, createRange) {
30835
31220
  filteredRange: filteredZone.top > filteredZone.bottom ? undefined : filteredRange,
30836
31221
  };
30837
31222
  }
31223
+ function isStaticTable(table) {
31224
+ return table.type === "static" || table.type === "forceStatic";
31225
+ }
30838
31226
  function getComputedTableStyle(tableConfig, numberOfCols, numberOfRows) {
30839
31227
  return {
30840
31228
  borders: getAllTableBorders(tableConfig, numberOfCols, numberOfRows),
@@ -31369,7 +31757,12 @@ class TablePanel extends owl.Component {
31369
31757
  const extendedZone = this.env.model.getters.getContiguousZone(sheetId, newRange.zone);
31370
31758
  newRange = this.env.model.getters.getRangeFromZone(sheetId, extendedZone);
31371
31759
  }
31372
- const result = this.env.model.dispatch("UPDATE_TABLE", {
31760
+ const newTableZone = newRange.zone;
31761
+ const oldTableZone = this.props.table.range.zone;
31762
+ const cmdToCall = newTableZone.top === oldTableZone.top && newTableZone.left === oldTableZone.left
31763
+ ? "RESIZE_TABLE"
31764
+ : "UPDATE_TABLE";
31765
+ const result = this.env.model.dispatch(cmdToCall, {
31373
31766
  sheetId,
31374
31767
  zone: this.props.table.range.zone,
31375
31768
  newTableRange: newRange.rangeData,
@@ -31858,6 +32251,38 @@ class DOMFocusableElementStore {
31858
32251
  }
31859
32252
  }
31860
32253
 
32254
+ class ArrayFormulaHighlight extends SpreadsheetStore {
32255
+ highlightStore = this.get(HighlightStore);
32256
+ constructor(get) {
32257
+ super(get);
32258
+ this.highlightStore.register(this);
32259
+ }
32260
+ get highlights() {
32261
+ const zone = this.getHighlightZone();
32262
+ if (!zone) {
32263
+ return [];
32264
+ }
32265
+ const sheetId = this.model.getters.getActiveSheetId();
32266
+ return [
32267
+ {
32268
+ sheetId,
32269
+ zone,
32270
+ color: "#17A2B8",
32271
+ noFill: true,
32272
+ thinLine: true,
32273
+ },
32274
+ ];
32275
+ }
32276
+ getHighlightZone() {
32277
+ const position = this.model.getters.getActivePosition();
32278
+ const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
32279
+ const spreadZone = spreader
32280
+ ? this.model.getters.getSpreadZone(spreader)
32281
+ : this.model.getters.getSpreadZone(position);
32282
+ return spreadZone;
32283
+ }
32284
+ }
32285
+
31861
32286
  // -----------------------------------------------------------------------------
31862
32287
  // Autofill
31863
32288
  // -----------------------------------------------------------------------------
@@ -33634,6 +34059,10 @@ css /*SCSS*/ `
33634
34059
  height: 0px;
33635
34060
  }
33636
34061
  }
34062
+ .o-figure-container {
34063
+ -webkit-user-select: none; // safari
34064
+ user-select: none;
34065
+ }
33637
34066
  `;
33638
34067
  /**
33639
34068
  * Each figure ⭐ is positioned inside a container `div` placed and sized
@@ -36035,6 +36464,77 @@ class SidePanelStore extends SpreadsheetStore {
36035
36464
  }
36036
36465
  }
36037
36466
 
36467
+ const SIZE = 3;
36468
+ const COLOR = "#777";
36469
+ css /* scss */ `
36470
+ .o-table-resizer {
36471
+ width: ${SIZE}px;
36472
+ height: ${SIZE}px;
36473
+ border-bottom: ${SIZE}px solid ${COLOR};
36474
+ border-right: ${SIZE}px solid ${COLOR};
36475
+ cursor: nwse-resize;
36476
+ }
36477
+ `;
36478
+ class TableResizer extends owl.Component {
36479
+ static template = "o-spreadsheet-TableResizer";
36480
+ static props = { table: Object };
36481
+ state = owl.useState({ highlightZone: undefined });
36482
+ setup() {
36483
+ useHighlights(this);
36484
+ }
36485
+ get containerStyle() {
36486
+ const tableZone = this.props.table.range.zone;
36487
+ const bottomRight = { ...tableZone, left: tableZone.right, top: tableZone.bottom };
36488
+ const rect = this.env.model.getters.getVisibleRect(bottomRight);
36489
+ if (rect.height === 0 || rect.width === 0) {
36490
+ return cssPropertiesToCss({ display: "none" });
36491
+ }
36492
+ return cssPropertiesToCss({
36493
+ top: `${rect.y + rect.height - SIZE * 2}px`,
36494
+ left: `${rect.x + rect.width - SIZE * 2}px`,
36495
+ });
36496
+ }
36497
+ onMouseDown(ev) {
36498
+ const tableZone = this.props.table.range.zone;
36499
+ const topLeft = { col: tableZone.left, row: tableZone.top };
36500
+ document.body.style.cursor = "nwse-resize";
36501
+ const onMouseUp = () => {
36502
+ document.body.style.cursor = "";
36503
+ const newTableZone = this.state.highlightZone;
36504
+ if (!newTableZone)
36505
+ return;
36506
+ const sheetId = this.props.table.range.sheetId;
36507
+ this.env.model.dispatch("RESIZE_TABLE", {
36508
+ sheetId,
36509
+ zone: this.props.table.range.zone,
36510
+ newTableRange: this.env.model.getters.getRangeDataFromZone(sheetId, newTableZone),
36511
+ });
36512
+ this.state.highlightZone = undefined;
36513
+ };
36514
+ const onMouseMove = (col, row, ev) => {
36515
+ this.state.highlightZone = {
36516
+ left: topLeft.col,
36517
+ top: topLeft.row,
36518
+ right: Math.max(col, topLeft.col),
36519
+ bottom: Math.max(row, topLeft.row),
36520
+ };
36521
+ };
36522
+ dragAndDropBeyondTheViewport(this.env, onMouseMove, onMouseUp);
36523
+ }
36524
+ get highlights() {
36525
+ if (!this.state.highlightZone)
36526
+ return [];
36527
+ return [
36528
+ {
36529
+ zone: this.state.highlightZone,
36530
+ sheetId: this.props.table.range.sheetId,
36531
+ color: COLOR,
36532
+ noFill: true,
36533
+ },
36534
+ ];
36535
+ }
36536
+ }
36537
+
36038
36538
  const registries$1 = {
36039
36539
  ROW: rowMenuRegistry,
36040
36540
  COL: colMenuRegistry,
@@ -36062,6 +36562,7 @@ class Grid extends owl.Component {
36062
36562
  Popover,
36063
36563
  VerticalScrollBar,
36064
36564
  HorizontalScrollBar,
36565
+ TableResizer,
36065
36566
  };
36066
36567
  HEADER_HEIGHT = HEADER_HEIGHT;
36067
36568
  HEADER_WIDTH = HEADER_WIDTH;
@@ -36090,6 +36591,7 @@ class Grid extends owl.Component {
36090
36591
  this.composerFocusStore = useStore(ComposerFocusStore);
36091
36592
  this.DOMFocusableElementStore = useStore(DOMFocusableElementStore);
36092
36593
  this.sidePanel = useStore(SidePanelStore);
36594
+ useStore(ArrayFormulaHighlight);
36093
36595
  owl.useChildSubEnv({ getPopoverContainerRect: () => this.getGridRect() });
36094
36596
  owl.useExternalListener(document.body, "cut", this.copy.bind(this, true));
36095
36597
  owl.useExternalListener(document.body, "copy", this.copy.bind(this, false));
@@ -36658,6 +37160,10 @@ class Grid extends owl.Component {
36658
37160
  onComposerContentFocused() {
36659
37161
  this.composerFocusStore.focusGridComposerContent();
36660
37162
  }
37163
+ get staticTables() {
37164
+ const sheetId = this.env.model.getters.getActiveSheetId();
37165
+ return this.env.model.getters.getCoreTables(sheetId).filter(isStaticTable);
37166
+ }
36661
37167
  }
36662
37168
 
36663
37169
  /**
@@ -45451,7 +45957,7 @@ class SheetPlugin extends CorePlugin {
45451
45957
  }
45452
45958
 
45453
45959
  class TablePlugin extends CorePlugin {
45454
- static getters = ["getCoreTable", "getCoreTables"];
45960
+ static getters = ["getCoreTable", "getCoreTables", "getCoreTableMatchingTopLeft"];
45455
45961
  tables = {};
45456
45962
  adaptRanges(applyChange, sheetId) {
45457
45963
  const sheetIds = sheetId ? [sheetId] : this.getters.getSheetIds();
@@ -45472,7 +45978,7 @@ class TablePlugin extends CorePlugin {
45472
45978
  ? "TableOverlap" /* CommandResult.TableOverlap */
45473
45979
  : "Success" /* CommandResult.Success */, (cmd) => this.checkTableConfigUpdateIsValid(cmd.config));
45474
45980
  case "UPDATE_TABLE":
45475
- const updatedTable = this.getTableFromZone(cmd.sheetId, cmd.zone);
45981
+ const updatedTable = this.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
45476
45982
  if (!updatedTable) {
45477
45983
  return "TableNotFound" /* CommandResult.TableNotFound */;
45478
45984
  }
@@ -45628,10 +46134,9 @@ class TablePlugin extends CorePlugin {
45628
46134
  }
45629
46135
  return direction;
45630
46136
  }
45631
- getTableFromZone(sheetId, zone) {
46137
+ getCoreTableMatchingTopLeft(sheetId, zone) {
45632
46138
  for (const table of this.getCoreTables(sheetId)) {
45633
46139
  const tableZone = table.range.zone;
45634
- // Only check top left to match dynamic tables
45635
46140
  if (tableZone.left === zone.left && tableZone.top === zone.top) {
45636
46141
  return table;
45637
46142
  }
@@ -45647,7 +46152,7 @@ class TablePlugin extends CorePlugin {
45647
46152
  if (zoneIsInSheet !== "Success" /* CommandResult.Success */) {
45648
46153
  return zoneIsInSheet;
45649
46154
  }
45650
- const updatedTable = this.getTableFromZone(cmd.sheetId, cmd.zone);
46155
+ const updatedTable = this.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
45651
46156
  if (!updatedTable) {
45652
46157
  return "TableNotFound" /* CommandResult.TableNotFound */;
45653
46158
  }
@@ -45697,7 +46202,7 @@ class TablePlugin extends CorePlugin {
45697
46202
  };
45698
46203
  }
45699
46204
  updateTable(cmd) {
45700
- const table = this.getTableFromZone(cmd.sheetId, cmd.zone);
46205
+ const table = this.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
45701
46206
  if (!table) {
45702
46207
  return;
45703
46208
  }
@@ -51738,6 +52243,7 @@ const invalidateTableStyleCommands = [
51738
52243
  "UPDATE_TABLE",
51739
52244
  "UPDATE_FILTER",
51740
52245
  "REMOVE_TABLE",
52246
+ "RESIZE_TABLE",
51741
52247
  ];
51742
52248
  const invalidateTableStyleCommandsSet = new Set(invalidateTableStyleCommands);
51743
52249
  function doesCommandInvalidatesTableStyle(cmd) {
@@ -52372,7 +52878,10 @@ class TableAutofillPlugin extends UIPlugin {
52372
52878
  const { col, row } = cmd;
52373
52879
  const tableContentZone = getTableContentZone(table.range.zone, table.config);
52374
52880
  if (tableContentZone && isInside(col, row, tableContentZone)) {
52375
- this.autofillTableZone(cmd, tableContentZone);
52881
+ const top = cmd.autofillRowStart ?? tableContentZone.top;
52882
+ const bottom = cmd.autofillRowEnd ?? tableContentZone.bottom;
52883
+ const autofillZone = { ...tableContentZone, top, bottom };
52884
+ this.autofillTableZone(cmd, autofillZone);
52376
52885
  }
52377
52886
  break;
52378
52887
  }
@@ -52406,6 +52915,50 @@ class TableAutofillPlugin extends UIPlugin {
52406
52915
  }
52407
52916
  }
52408
52917
 
52918
+ class TableResizeUI extends UIPlugin {
52919
+ allowDispatch(cmd) {
52920
+ switch (cmd.type) {
52921
+ case "RESIZE_TABLE":
52922
+ const table = this.getters.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
52923
+ if (!table) {
52924
+ return "TableNotFound" /* CommandResult.TableNotFound */;
52925
+ }
52926
+ const oldTableZone = table.range.zone;
52927
+ const newTableZone = this.getters.getRangeFromRangeData(cmd.newTableRange).zone;
52928
+ if (newTableZone.top !== oldTableZone.top || newTableZone.left !== oldTableZone.left) {
52929
+ return "InvalidTableResize" /* CommandResult.InvalidTableResize */;
52930
+ }
52931
+ return this.canDispatch("UPDATE_TABLE", { ...cmd }).reasons;
52932
+ }
52933
+ return "Success" /* CommandResult.Success */;
52934
+ }
52935
+ handle(cmd) {
52936
+ switch (cmd.type) {
52937
+ case "RESIZE_TABLE": {
52938
+ const table = this.getters.getCoreTableMatchingTopLeft(cmd.sheetId, cmd.zone);
52939
+ this.dispatch("UPDATE_TABLE", { ...cmd });
52940
+ if (!table || !table.config.automaticAutofill)
52941
+ return;
52942
+ const oldTableZone = table.range.zone;
52943
+ const newTableZone = this.getters.getRangeFromRangeData(cmd.newTableRange).zone;
52944
+ if (newTableZone.bottom >= oldTableZone.bottom) {
52945
+ for (let col = newTableZone.left; col <= newTableZone.right; col++) {
52946
+ const autofillSource = { col, row: oldTableZone.bottom, sheetId: cmd.sheetId };
52947
+ if (this.getters.getCell(autofillSource)?.content.startsWith("=")) {
52948
+ this.dispatch("AUTOFILL_TABLE_COLUMN", {
52949
+ ...autofillSource,
52950
+ autofillRowStart: oldTableZone.bottom,
52951
+ autofillRowEnd: newTableZone.bottom,
52952
+ });
52953
+ }
52954
+ }
52955
+ break;
52956
+ }
52957
+ }
52958
+ }
52959
+ }
52960
+ }
52961
+
52409
52962
  /**
52410
52963
  * Clipboard Plugin
52411
52964
  *
@@ -54080,13 +54633,14 @@ class SheetViewPlugin extends UIPlugin {
54080
54633
  }
54081
54634
  }
54082
54635
  handleEvent(event) {
54636
+ const sheetId = this.getters.getActiveSheetId();
54083
54637
  if (event.options.scrollIntoView) {
54084
54638
  let { col, row } = findCellInNewZone(event.previousAnchor.zone, event.anchor.zone);
54085
54639
  if (event.mode === "updateAnchor") {
54086
54640
  const oldZone = event.previousAnchor.zone;
54087
54641
  const newZone = event.anchor.zone;
54088
54642
  // altering a zone should not move the viewport in a dimension that wasn't changed
54089
- const { top, bottom, left, right } = this.getters.getActiveMainViewport();
54643
+ const { top, bottom, left, right } = this.getMainInternalViewport(sheetId);
54090
54644
  if (oldZone.left === newZone.left && oldZone.right === newZone.right) {
54091
54645
  col = left > col || col > right ? left : col;
54092
54646
  }
@@ -54094,7 +54648,6 @@ class SheetViewPlugin extends UIPlugin {
54094
54648
  row = top > row || row > bottom ? top : row;
54095
54649
  }
54096
54650
  }
54097
- const sheetId = this.getters.getActiveSheetId();
54098
54651
  col = Math.min(col, this.getters.getNumberCols(sheetId) - 1);
54099
54652
  row = Math.min(row, this.getters.getNumberRows(sheetId) - 1);
54100
54653
  if (!this.sheetsWithDirtyViewports.has(sheetId)) {
@@ -54131,16 +54684,16 @@ class SheetViewPlugin extends UIPlugin {
54131
54684
  this.setSheetViewOffset(cmd.offsetX, cmd.offsetY);
54132
54685
  break;
54133
54686
  case "SHIFT_VIEWPORT_DOWN":
54134
- const { top } = this.getActiveMainViewport();
54135
54687
  const sheetId = this.getters.getActiveSheetId();
54136
- const shiftedOffsetY = this.clipOffsetY(this.getters.getRowDimensions(sheetId, top).start + this.sheetViewHeight);
54137
- this.shiftVertically(shiftedOffsetY);
54688
+ const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId);
54689
+ const topRowDims = this.getters.getRowDimensions(sheetId, top);
54690
+ this.shiftVertically(topRowDims.start + viewportHeight - offsetCorrectionY);
54138
54691
  break;
54139
54692
  case "SHIFT_VIEWPORT_UP": {
54140
- const { top } = this.getActiveMainViewport();
54141
54693
  const sheetId = this.getters.getActiveSheetId();
54142
- const shiftedOffsetY = this.clipOffsetY(this.getters.getRowDimensions(sheetId, top).end - this.sheetViewHeight);
54143
- this.shiftVertically(shiftedOffsetY);
54694
+ const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId);
54695
+ const topRowDims = this.getters.getRowDimensions(sheetId, top);
54696
+ this.shiftVertically(topRowDims.end - offsetCorrectionY - viewportHeight);
54144
54697
  break;
54145
54698
  }
54146
54699
  case "REMOVE_TABLE":
@@ -54563,17 +55116,6 @@ class SheetViewPlugin extends UIPlugin {
54563
55116
  const { maxOffsetX, maxOffsetY } = this.getMaximumSheetOffset();
54564
55117
  Object.values(this.getSubViewports(sheetId)).forEach((viewport) => viewport.setViewportOffset(clip(offsetX, 0, maxOffsetX), clip(offsetY, 0, maxOffsetY)));
54565
55118
  }
54566
- /**
54567
- * Clip the vertical offset within the allowed range.
54568
- * Not above the sheet, nor below the sheet.
54569
- */
54570
- clipOffsetY(offsetY) {
54571
- const { height } = this.getMainViewportRect();
54572
- const maxOffset = height - this.sheetViewHeight;
54573
- offsetY = Math.min(offsetY, maxOffset);
54574
- offsetY = Math.max(offsetY, 0);
54575
- return offsetY;
54576
- }
54577
55119
  getViewportOffset(sheetId) {
54578
55120
  return {
54579
55121
  x: this.viewports[sheetId]?.bottomRight.offsetScrollbarX || 0,
@@ -54629,12 +55171,15 @@ class SheetViewPlugin extends UIPlugin {
54629
55171
  * viewport top.
54630
55172
  */
54631
55173
  shiftVertically(offset) {
54632
- const { top } = this.getActiveMainViewport();
55174
+ const sheetId = this.getters.getActiveSheetId();
55175
+ const { top } = this.getMainInternalViewport(sheetId);
54633
55176
  const { scrollX } = this.getActiveSheetScrollInfo();
54634
55177
  this.setSheetViewOffset(scrollX, offset);
54635
55178
  const { anchor } = this.getters.getSelection();
54636
- const deltaRow = this.getActiveMainViewport().top - top;
54637
- this.selection.selectCell(anchor.cell.col, anchor.cell.row + deltaRow);
55179
+ if (anchor.cell.row >= this.getters.getPaneDivisions(sheetId).ySplit) {
55180
+ const deltaRow = this.getMainInternalViewport(sheetId).top - top;
55181
+ this.selection.selectCell(anchor.cell.col, anchor.cell.row + deltaRow);
55182
+ }
54638
55183
  }
54639
55184
  getVisibleFigures() {
54640
55185
  const sheetId = this.getters.getActiveSheetId();
@@ -54823,7 +55368,8 @@ const featurePluginRegistry = new Registry()
54823
55368
  .add("collaborative", CollaborativePlugin)
54824
55369
  .add("history", HistoryPlugin)
54825
55370
  .add("data_cleanup", DataCleanupPlugin)
54826
- .add("table_autofill", TableAutofillPlugin);
55371
+ .add("table_autofill", TableAutofillPlugin)
55372
+ .add("table_ui_resize", TableResizeUI);
54827
55373
  // Plugins which have a state, but which should not be shared in collaborative
54828
55374
  const statefulUIPluginRegistry = new Registry()
54829
55375
  .add("selection", GridSelectionPlugin)
@@ -54891,38 +55437,6 @@ class ImageProvider {
54891
55437
  }
54892
55438
  }
54893
55439
 
54894
- class ArrayFormulaHighlight extends SpreadsheetStore {
54895
- highlightStore = this.get(HighlightStore);
54896
- constructor(get) {
54897
- super(get);
54898
- this.highlightStore.register(this);
54899
- }
54900
- get highlights() {
54901
- const zone = this.getHighlightZone();
54902
- if (!zone) {
54903
- return [];
54904
- }
54905
- const sheetId = this.model.getters.getActiveSheetId();
54906
- return [
54907
- {
54908
- sheetId,
54909
- zone,
54910
- color: "#17A2B8",
54911
- noFill: true,
54912
- thinLine: true,
54913
- },
54914
- ];
54915
- }
54916
- getHighlightZone() {
54917
- const position = this.model.getters.getActivePosition();
54918
- const spreader = this.model.getters.getArrayFormulaSpreadingOn(position);
54919
- const spreadZone = spreader
54920
- ? this.model.getters.getSpreadZone(spreader)
54921
- : this.model.getters.getSpreadZone(position);
54922
- return spreadZone;
54923
- }
54924
- }
54925
-
54926
55440
  const RIPPLE_KEY_FRAMES = [
54927
55441
  { transform: "scale(0)" },
54928
55442
  { transform: "scale(0.8)", offset: 0.33 },
@@ -57256,7 +57770,6 @@ class Spreadsheet extends owl.Component {
57256
57770
  this.notificationStore = useStore(NotificationStore);
57257
57771
  this.composerFocusStore = useStore(ComposerFocusStore);
57258
57772
  this.sidePanel = useStore(SidePanelStore);
57259
- useStore(ArrayFormulaHighlight);
57260
57773
  this.keyDownMapping = {
57261
57774
  "CTRL+H": () => this.sidePanel.toggle("FindAndReplace", {}),
57262
57775
  "CTRL+F": () => this.sidePanel.toggle("FindAndReplace", {}),
@@ -61198,9 +61711,9 @@ const components = {
61198
61711
  GridOverlay,
61199
61712
  ScorecardChart,
61200
61713
  LineConfigPanel,
61201
- LineBarPieDesignPanel,
61714
+ GenericChartDesignPanel,
61202
61715
  BarConfigPanel,
61203
- LineBarPieConfigPanel,
61716
+ GenericChartConfigPanel,
61204
61717
  GaugeChartConfigPanel,
61205
61718
  GaugeChartDesignPanel,
61206
61719
  ScorecardChartConfigPanel,
@@ -61289,6 +61802,6 @@ exports.tokenColors = tokenColors;
61289
61802
  exports.tokenize = tokenize;
61290
61803
 
61291
61804
 
61292
- __info__.version = "17.3.0-alpha.3";
61293
- __info__.date = "2024-04-10T12:28:23.658Z";
61294
- __info__.hash = "80b5056";
61805
+ __info__.version = "17.3.0-alpha.4";
61806
+ __info__.date = "2024-04-15T11:02:51.551Z";
61807
+ __info__.hash = "a32a1df";