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