@odoo/o-spreadsheet 17.1.11 → 17.1.13

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.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 17.1.11
6
- * @date 2024-04-10T12:27:11.107Z
7
- * @hash 246caf7
5
+ * @version 17.1.13
6
+ * @date 2024-04-26T07:39:54.100Z
7
+ * @hash f2ea8c7
8
8
  */
9
9
 
10
10
  import { Component, useRef, onMounted, useEffect, onWillPatch, useState, onWillUpdateProps, onPatched, useComponent, useExternalListener, onWillUnmount, onWillStart, xml, useChildSubEnv, useSubEnv, markRaw } from '@odoo/owl';
@@ -2475,6 +2475,15 @@ function matrixMap(matrix, fn) {
2475
2475
  }
2476
2476
  return generateMatrix(matrix.length, matrix[0].length, (col, row) => fn(matrix[col][row]));
2477
2477
  }
2478
+ function matrixForEach(matrix, fn) {
2479
+ const numberOfCols = matrix.length;
2480
+ const numberOfRows = matrix[0]?.length ?? 0;
2481
+ for (let col = 0; col < numberOfCols; col++) {
2482
+ for (let row = 0; row < numberOfRows; row++) {
2483
+ fn(matrix[col][row]);
2484
+ }
2485
+ }
2486
+ }
2478
2487
  function transposeMatrix(matrix) {
2479
2488
  if (!matrix.length) {
2480
2489
  return [];
@@ -8178,6 +8187,7 @@ class LinkEditor extends Component {
8178
8187
  this.save();
8179
8188
  }
8180
8189
  ev.stopPropagation();
8190
+ ev.preventDefault();
8181
8191
  break;
8182
8192
  case "Escape":
8183
8193
  this.cancel();
@@ -19760,7 +19770,7 @@ class FunctionRegistry extends Registry {
19760
19770
  }
19761
19771
  const descr = addMetaInfoFromArg(addDescr);
19762
19772
  validateArguments(descr.args);
19763
- this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr)), name);
19773
+ this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr), name), name);
19764
19774
  super.add(name, descr);
19765
19775
  return this;
19766
19776
  }
@@ -19799,9 +19809,7 @@ function handleError(e, functionName) {
19799
19809
  // so we fallback to a generic error
19800
19810
  if (hasStringValue(e) && isEvaluationError(e.value)) {
19801
19811
  if (hasStringMessage(e)) {
19802
- if (e.message?.includes("[[FUNCTION_NAME]]")) {
19803
- e.message = e.message.replace("[[FUNCTION_NAME]]", functionName);
19804
- }
19812
+ replaceFunctionNamePlaceholder(e, functionName);
19805
19813
  }
19806
19814
  return e;
19807
19815
  }
@@ -19816,21 +19824,29 @@ function hasStringMessage(obj) {
19816
19824
  return (obj?.message !== undefined &&
19817
19825
  typeof obj.message === "string");
19818
19826
  }
19819
- function addResultHandling(compute) {
19820
- return function (...args) {
19827
+ function addResultHandling(compute, functionName) {
19828
+ return function computeWithResultHandling(...args) {
19821
19829
  const result = compute.apply(this, args);
19822
19830
  if (!isMatrix(result)) {
19823
19831
  if (typeof result === "object" && result !== null && "value" in result) {
19832
+ replaceFunctionNamePlaceholder(result, functionName);
19824
19833
  return result;
19825
19834
  }
19826
19835
  return { value: result };
19827
19836
  }
19828
19837
  if (typeof result[0][0] === "object" && result[0][0] !== null && "value" in result[0][0]) {
19838
+ matrixForEach(result, (result) => replaceFunctionNamePlaceholder(result, functionName));
19829
19839
  return result;
19830
19840
  }
19831
19841
  return matrixMap(result, (row) => ({ value: row }));
19832
19842
  };
19833
19843
  }
19844
+ function replaceFunctionNamePlaceholder(fPayload, functionName) {
19845
+ // for performance reasons: change in place and only if needed
19846
+ if (fPayload.message?.includes("[[FUNCTION_NAME]]")) {
19847
+ fPayload.message = fPayload.message.replace("[[FUNCTION_NAME]]", functionName);
19848
+ }
19849
+ }
19834
19850
  const functionRegistry = new FunctionRegistry();
19835
19851
  for (let category of categories) {
19836
19852
  const fns = category.functions;
@@ -20202,6 +20218,332 @@ function interactiveAddFilter(env, sheetId, target) {
20202
20218
  }
20203
20219
  }
20204
20220
 
20221
+ function interactiveFreezeColumnsRows(env, dimension, base) {
20222
+ const sheetId = env.model.getters.getActiveSheetId();
20223
+ const cmd = dimension === "COL" ? "FREEZE_COLUMNS" : "FREEZE_ROWS";
20224
+ const result = env.model.dispatch(cmd, { sheetId, quantity: base });
20225
+ if (result.isCancelledBecause("MergeOverlap" /* CommandResult.MergeOverlap */)) {
20226
+ env.raiseError(MergeErrorMessage);
20227
+ }
20228
+ }
20229
+
20230
+ const hideCols = {
20231
+ name: HIDE_COLUMNS_NAME,
20232
+ execute: (env) => {
20233
+ const columns = env.model.getters.getElementsFromSelection("COL");
20234
+ env.model.dispatch("HIDE_COLUMNS_ROWS", {
20235
+ sheetId: env.model.getters.getActiveSheetId(),
20236
+ dimension: "COL",
20237
+ elements: columns,
20238
+ });
20239
+ },
20240
+ isVisible: NOT_ALL_VISIBLE_COLS_SELECTED,
20241
+ icon: "o-spreadsheet-Icon.HIDE_COL",
20242
+ };
20243
+ const unhideCols = {
20244
+ name: _t("Unhide columns"),
20245
+ execute: (env) => {
20246
+ const columns = env.model.getters.getElementsFromSelection("COL");
20247
+ env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
20248
+ sheetId: env.model.getters.getActiveSheetId(),
20249
+ dimension: "COL",
20250
+ elements: columns,
20251
+ });
20252
+ },
20253
+ isVisible: (env) => {
20254
+ const hiddenCols = env.model.getters
20255
+ .getHiddenColsGroups(env.model.getters.getActiveSheetId())
20256
+ .flat();
20257
+ const currentCols = env.model.getters.getElementsFromSelection("COL");
20258
+ return currentCols.some((col) => hiddenCols.includes(col));
20259
+ },
20260
+ };
20261
+ const unhideAllCols = {
20262
+ name: _t("Unhide all columns"),
20263
+ execute: (env) => {
20264
+ const sheetId = env.model.getters.getActiveSheetId();
20265
+ env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
20266
+ sheetId,
20267
+ dimension: "COL",
20268
+ elements: Array.from(Array(env.model.getters.getNumberCols(sheetId)).keys()),
20269
+ });
20270
+ },
20271
+ isVisible: (env) => env.model.getters.getHiddenColsGroups(env.model.getters.getActiveSheetId()).length > 0,
20272
+ };
20273
+ const hideRows = {
20274
+ name: HIDE_ROWS_NAME,
20275
+ execute: (env) => {
20276
+ const rows = env.model.getters.getElementsFromSelection("ROW");
20277
+ env.model.dispatch("HIDE_COLUMNS_ROWS", {
20278
+ sheetId: env.model.getters.getActiveSheetId(),
20279
+ dimension: "ROW",
20280
+ elements: rows,
20281
+ });
20282
+ },
20283
+ isVisible: NOT_ALL_VISIBLE_ROWS_SELECTED,
20284
+ icon: "o-spreadsheet-Icon.HIDE_ROW",
20285
+ };
20286
+ const unhideRows = {
20287
+ name: _t("Unhide rows"),
20288
+ execute: (env) => {
20289
+ const columns = env.model.getters.getElementsFromSelection("ROW");
20290
+ env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
20291
+ sheetId: env.model.getters.getActiveSheetId(),
20292
+ dimension: "ROW",
20293
+ elements: columns,
20294
+ });
20295
+ },
20296
+ isVisible: (env) => {
20297
+ const hiddenRows = env.model.getters
20298
+ .getHiddenRowsGroups(env.model.getters.getActiveSheetId())
20299
+ .flat();
20300
+ const currentRows = env.model.getters.getElementsFromSelection("ROW");
20301
+ return currentRows.some((col) => hiddenRows.includes(col));
20302
+ },
20303
+ };
20304
+ const unhideAllRows = {
20305
+ name: _t("Unhide all rows"),
20306
+ execute: (env) => {
20307
+ const sheetId = env.model.getters.getActiveSheetId();
20308
+ env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
20309
+ sheetId,
20310
+ dimension: "ROW",
20311
+ elements: Array.from(Array(env.model.getters.getNumberRows(sheetId)).keys()),
20312
+ });
20313
+ },
20314
+ isVisible: (env) => env.model.getters.getHiddenRowsGroups(env.model.getters.getActiveSheetId()).length > 0,
20315
+ };
20316
+ const unFreezePane = {
20317
+ name: _t("Unfreeze"),
20318
+ isVisible: (env) => {
20319
+ const { xSplit, ySplit } = env.model.getters.getPaneDivisions(env.model.getters.getActiveSheetId());
20320
+ return xSplit + ySplit > 0;
20321
+ },
20322
+ execute: (env) => env.model.dispatch("UNFREEZE_COLUMNS_ROWS", {
20323
+ sheetId: env.model.getters.getActiveSheetId(),
20324
+ }),
20325
+ icon: "o-spreadsheet-Icon.UNFREEZE",
20326
+ };
20327
+ const freezePane = {
20328
+ name: _t("Freeze"),
20329
+ icon: "o-spreadsheet-Icon.FREEZE",
20330
+ };
20331
+ const unFreezeRows = {
20332
+ name: _t("No rows"),
20333
+ execute: (env) => env.model.dispatch("UNFREEZE_ROWS", {
20334
+ sheetId: env.model.getters.getActiveSheetId(),
20335
+ }),
20336
+ isReadonlyAllowed: true,
20337
+ isVisible: (env) => !!env.model.getters.getPaneDivisions(env.model.getters.getActiveSheetId()).ySplit,
20338
+ };
20339
+ const freezeFirstRow = {
20340
+ name: _t("1 row"),
20341
+ execute: (env) => interactiveFreezeColumnsRows(env, "ROW", 1),
20342
+ isReadonlyAllowed: true,
20343
+ };
20344
+ const freezeSecondRow = {
20345
+ name: _t("2 rows"),
20346
+ execute: (env) => interactiveFreezeColumnsRows(env, "ROW", 2),
20347
+ isReadonlyAllowed: true,
20348
+ };
20349
+ const freezeCurrentRow = {
20350
+ name: _t("Up to current row"),
20351
+ execute: (env) => {
20352
+ const { bottom } = env.model.getters.getSelectedZone();
20353
+ interactiveFreezeColumnsRows(env, "ROW", bottom + 1);
20354
+ },
20355
+ isReadonlyAllowed: true,
20356
+ };
20357
+ const unFreezeCols = {
20358
+ name: _t("No columns"),
20359
+ execute: (env) => env.model.dispatch("UNFREEZE_COLUMNS", {
20360
+ sheetId: env.model.getters.getActiveSheetId(),
20361
+ }),
20362
+ isReadonlyAllowed: true,
20363
+ isVisible: (env) => !!env.model.getters.getPaneDivisions(env.model.getters.getActiveSheetId()).xSplit,
20364
+ };
20365
+ const freezeFirstCol = {
20366
+ name: _t("1 column"),
20367
+ execute: (env) => interactiveFreezeColumnsRows(env, "COL", 1),
20368
+ isReadonlyAllowed: true,
20369
+ };
20370
+ const freezeSecondCol = {
20371
+ name: _t("2 columns"),
20372
+ execute: (env) => interactiveFreezeColumnsRows(env, "COL", 2),
20373
+ isReadonlyAllowed: true,
20374
+ };
20375
+ const freezeCurrentCol = {
20376
+ name: _t("Up to current column"),
20377
+ execute: (env) => {
20378
+ const { right } = env.model.getters.getSelectedZone();
20379
+ interactiveFreezeColumnsRows(env, "COL", right + 1);
20380
+ },
20381
+ isReadonlyAllowed: true,
20382
+ };
20383
+ const viewGridlines = {
20384
+ name: (env) => env.model.getters.getGridLinesVisibility(env.model.getters.getActiveSheetId())
20385
+ ? _t("Hide gridlines")
20386
+ : _t("Show gridlines"),
20387
+ execute: (env) => {
20388
+ const sheetId = env.model.getters.getActiveSheetId();
20389
+ env.model.dispatch("SET_GRID_LINES_VISIBILITY", {
20390
+ sheetId,
20391
+ areGridLinesVisible: !env.model.getters.getGridLinesVisibility(sheetId),
20392
+ });
20393
+ },
20394
+ icon: "o-spreadsheet-Icon.SHOW_HIDE_GRID",
20395
+ };
20396
+ const viewFormulas = {
20397
+ name: (env) => env.model.getters.shouldShowFormulas() ? "Hide formulas" : _t("Show formulas"),
20398
+ execute: (env) => env.model.dispatch("SET_FORMULA_VISIBILITY", { show: !env.model.getters.shouldShowFormulas() }),
20399
+ isReadonlyAllowed: true,
20400
+ icon: "o-spreadsheet-Icon.SHOW_HIDE_FORMULA",
20401
+ };
20402
+ const createRemoveFilter = {
20403
+ name: (env) => selectionContainsFilter(env) ? _t("Remove selected filters") : _t("Create filter"),
20404
+ isActive: (env) => selectionContainsFilter(env),
20405
+ isEnabled: (env) => !cannotCreateFilter(env),
20406
+ execute: (env) => createRemoveFilterAction(env),
20407
+ icon: "o-spreadsheet-Icon.FILTER_ICON_INACTIVE",
20408
+ };
20409
+ const groupColumns = {
20410
+ name: (env) => {
20411
+ const selection = env.model.getters.getSelectedZone();
20412
+ if (selection.left === selection.right) {
20413
+ return _t("Group column %s", numberToLetters(selection.left));
20414
+ }
20415
+ return _t("Group columns %s - %s", numberToLetters(selection.left), numberToLetters(selection.right));
20416
+ },
20417
+ execute: (env) => groupHeadersAction(env, "COL"),
20418
+ isVisible: (env) => {
20419
+ const sheetId = env.model.getters.getActiveSheetId();
20420
+ const selection = env.model.getters.getSelectedZone();
20421
+ const groups = env.model.getters.getHeaderGroupsInZone(sheetId, "COL", selection);
20422
+ return (IS_ONLY_ONE_RANGE(env) &&
20423
+ !groups.some((group) => group.start === selection.left && group.end === selection.right));
20424
+ },
20425
+ icon: "o-spreadsheet-Icon.GROUP_COLUMNS",
20426
+ };
20427
+ const groupRows = {
20428
+ name: (env) => {
20429
+ const selection = env.model.getters.getSelectedZone();
20430
+ if (selection.top === selection.bottom) {
20431
+ return _t("Group row %s", String(selection.top + 1));
20432
+ }
20433
+ return _t("Group rows %s - %s", String(selection.top + 1), String(selection.bottom + 1));
20434
+ },
20435
+ execute: (env) => groupHeadersAction(env, "ROW"),
20436
+ isVisible: (env) => {
20437
+ const sheetId = env.model.getters.getActiveSheetId();
20438
+ const selection = env.model.getters.getSelectedZone();
20439
+ const groups = env.model.getters.getHeaderGroupsInZone(sheetId, "ROW", selection);
20440
+ return (IS_ONLY_ONE_RANGE(env) &&
20441
+ !groups.some((group) => group.start === selection.top && group.end === selection.bottom));
20442
+ },
20443
+ icon: "o-spreadsheet-Icon.GROUP_ROWS",
20444
+ };
20445
+ const ungroupColumns = {
20446
+ name: (env) => {
20447
+ const selection = env.model.getters.getSelectedZone();
20448
+ if (selection.left === selection.right) {
20449
+ return _t("Ungroup column %s", numberToLetters(selection.left));
20450
+ }
20451
+ return _t("Ungroup columns %s - %s", numberToLetters(selection.left), numberToLetters(selection.right));
20452
+ },
20453
+ execute: (env) => ungroupHeaders(env, "COL"),
20454
+ icon: "o-spreadsheet-Icon.UNGROUP_COLUMNS",
20455
+ };
20456
+ const ungroupRows = {
20457
+ name: (env) => {
20458
+ const selection = env.model.getters.getSelectedZone();
20459
+ if (selection.top === selection.bottom) {
20460
+ return _t("Ungroup row %s", String(selection.top + 1));
20461
+ }
20462
+ return _t("Ungroup rows %s - %s", String(selection.top + 1), String(selection.bottom + 1));
20463
+ },
20464
+ execute: (env) => ungroupHeaders(env, "ROW"),
20465
+ icon: "o-spreadsheet-Icon.UNGROUP_ROWS",
20466
+ };
20467
+ function selectionContainsFilter(env) {
20468
+ const sheetId = env.model.getters.getActiveSheetId();
20469
+ const selectedZones = env.model.getters.getSelectedZones();
20470
+ return env.model.getters.doesZonesContainFilter(sheetId, selectedZones);
20471
+ }
20472
+ function cannotCreateFilter(env) {
20473
+ return !areZonesContinuous(...env.model.getters.getSelectedZones());
20474
+ }
20475
+ function createRemoveFilterAction(env) {
20476
+ if (selectionContainsFilter(env)) {
20477
+ env.model.dispatch("REMOVE_FILTER_TABLE", {
20478
+ sheetId: env.model.getters.getActiveSheetId(),
20479
+ target: env.model.getters.getSelectedZones(),
20480
+ });
20481
+ return;
20482
+ }
20483
+ if (cannotCreateFilter(env)) {
20484
+ return;
20485
+ }
20486
+ env.model.selection.selectTableAroundSelection();
20487
+ const sheetId = env.model.getters.getActiveSheetId();
20488
+ const selection = env.model.getters.getSelectedZones();
20489
+ interactiveAddFilter(env, sheetId, selection);
20490
+ }
20491
+ function groupHeadersAction(env, dim) {
20492
+ const selection = env.model.getters.getSelectedZone();
20493
+ const sheetId = env.model.getters.getActiveSheetId();
20494
+ env.model.dispatch("GROUP_HEADERS", {
20495
+ sheetId,
20496
+ dimension: dim,
20497
+ start: dim === "COL" ? selection.left : selection.top,
20498
+ end: dim === "COL" ? selection.right : selection.bottom,
20499
+ });
20500
+ }
20501
+ function ungroupHeaders(env, dim) {
20502
+ const selection = env.model.getters.getSelectedZone();
20503
+ const sheetId = env.model.getters.getActiveSheetId();
20504
+ env.model.dispatch("UNGROUP_HEADERS", {
20505
+ sheetId,
20506
+ dimension: dim,
20507
+ start: dim === "COL" ? selection.left : selection.top,
20508
+ end: dim === "COL" ? selection.right : selection.bottom,
20509
+ });
20510
+ }
20511
+ function canUngroupHeaders(env, dimension) {
20512
+ const sheetId = env.model.getters.getActiveSheetId();
20513
+ const selection = env.model.getters.getSelectedZones();
20514
+ return (selection.length === 1 &&
20515
+ env.model.getters.getHeaderGroupsInZone(sheetId, dimension, selection[0]).length > 0);
20516
+ }
20517
+
20518
+ var ACTION_VIEW = /*#__PURE__*/Object.freeze({
20519
+ __proto__: null,
20520
+ canUngroupHeaders: canUngroupHeaders,
20521
+ createRemoveFilter: createRemoveFilter,
20522
+ createRemoveFilterAction: createRemoveFilterAction,
20523
+ freezeCurrentCol: freezeCurrentCol,
20524
+ freezeCurrentRow: freezeCurrentRow,
20525
+ freezeFirstCol: freezeFirstCol,
20526
+ freezeFirstRow: freezeFirstRow,
20527
+ freezePane: freezePane,
20528
+ freezeSecondCol: freezeSecondCol,
20529
+ freezeSecondRow: freezeSecondRow,
20530
+ groupColumns: groupColumns,
20531
+ groupRows: groupRows,
20532
+ hideCols: hideCols,
20533
+ hideRows: hideRows,
20534
+ unFreezeCols: unFreezeCols,
20535
+ unFreezePane: unFreezePane,
20536
+ unFreezeRows: unFreezeRows,
20537
+ ungroupColumns: ungroupColumns,
20538
+ ungroupRows: ungroupRows,
20539
+ unhideAllCols: unhideAllCols,
20540
+ unhideAllRows: unhideAllRows,
20541
+ unhideCols: unhideCols,
20542
+ unhideRows: unhideRows,
20543
+ viewFormulas: viewFormulas,
20544
+ viewGridlines: viewGridlines
20545
+ });
20546
+
20205
20547
  const sortRange = {
20206
20548
  name: _t("Sort range"),
20207
20549
  isVisible: IS_ONLY_ONE_RANGE,
@@ -20244,32 +20586,15 @@ const sortDescending = {
20244
20586
  },
20245
20587
  icon: "o-spreadsheet-Icon.SORT_DESCENDING",
20246
20588
  };
20247
- const addDataFilter = {
20248
- name: _t("Create filter"),
20249
- execute: (env) => {
20250
- const sheetId = env.model.getters.getActiveSheetId();
20251
- const selection = env.model.getters.getSelection().zones;
20252
- interactiveAddFilter(env, sheetId, selection);
20253
- },
20254
- isVisible: (env) => !SELECTION_CONTAINS_FILTER(env),
20589
+ const addRemoveDataFilter = {
20590
+ name: (env) => SELECTION_CONTAINS_FILTER(env) ? _t("Remove filter") : _t("Create filter"),
20591
+ execute: (env) => createRemoveFilterAction(env),
20255
20592
  isEnabled: (env) => {
20256
20593
  const selectedZones = env.model.getters.getSelectedZones();
20257
20594
  return areZonesContinuous(...selectedZones);
20258
20595
  },
20259
20596
  icon: "o-spreadsheet-Icon.MENU_FILTER_ICON",
20260
20597
  };
20261
- const removeDataFilter = {
20262
- name: _t("Remove filter"),
20263
- execute: (env) => {
20264
- const sheetId = env.model.getters.getActiveSheetId();
20265
- env.model.dispatch("REMOVE_FILTER_TABLE", {
20266
- sheetId,
20267
- target: env.model.getters.getSelectedZones(),
20268
- });
20269
- },
20270
- isVisible: SELECTION_CONTAINS_FILTER,
20271
- icon: "o-spreadsheet-Icon.MENU_FILTER_ICON",
20272
- };
20273
20598
  const splitToColumns = {
20274
20599
  name: _t("Split text to columns"),
20275
20600
  sequence: 1,
@@ -20681,331 +21006,6 @@ var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
20681
21006
  textColor: textColor
20682
21007
  });
20683
21008
 
20684
- function interactiveFreezeColumnsRows(env, dimension, base) {
20685
- const sheetId = env.model.getters.getActiveSheetId();
20686
- const cmd = dimension === "COL" ? "FREEZE_COLUMNS" : "FREEZE_ROWS";
20687
- const result = env.model.dispatch(cmd, { sheetId, quantity: base });
20688
- if (result.isCancelledBecause("MergeOverlap" /* CommandResult.MergeOverlap */)) {
20689
- env.raiseError(MergeErrorMessage);
20690
- }
20691
- }
20692
-
20693
- const hideCols = {
20694
- name: HIDE_COLUMNS_NAME,
20695
- execute: (env) => {
20696
- const columns = env.model.getters.getElementsFromSelection("COL");
20697
- env.model.dispatch("HIDE_COLUMNS_ROWS", {
20698
- sheetId: env.model.getters.getActiveSheetId(),
20699
- dimension: "COL",
20700
- elements: columns,
20701
- });
20702
- },
20703
- isVisible: NOT_ALL_VISIBLE_COLS_SELECTED,
20704
- icon: "o-spreadsheet-Icon.HIDE_COL",
20705
- };
20706
- const unhideCols = {
20707
- name: _t("Unhide columns"),
20708
- execute: (env) => {
20709
- const columns = env.model.getters.getElementsFromSelection("COL");
20710
- env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
20711
- sheetId: env.model.getters.getActiveSheetId(),
20712
- dimension: "COL",
20713
- elements: columns,
20714
- });
20715
- },
20716
- isVisible: (env) => {
20717
- const hiddenCols = env.model.getters
20718
- .getHiddenColsGroups(env.model.getters.getActiveSheetId())
20719
- .flat();
20720
- const currentCols = env.model.getters.getElementsFromSelection("COL");
20721
- return currentCols.some((col) => hiddenCols.includes(col));
20722
- },
20723
- };
20724
- const unhideAllCols = {
20725
- name: _t("Unhide all columns"),
20726
- execute: (env) => {
20727
- const sheetId = env.model.getters.getActiveSheetId();
20728
- env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
20729
- sheetId,
20730
- dimension: "COL",
20731
- elements: Array.from(Array(env.model.getters.getNumberCols(sheetId)).keys()),
20732
- });
20733
- },
20734
- isVisible: (env) => env.model.getters.getHiddenColsGroups(env.model.getters.getActiveSheetId()).length > 0,
20735
- };
20736
- const hideRows = {
20737
- name: HIDE_ROWS_NAME,
20738
- execute: (env) => {
20739
- const rows = env.model.getters.getElementsFromSelection("ROW");
20740
- env.model.dispatch("HIDE_COLUMNS_ROWS", {
20741
- sheetId: env.model.getters.getActiveSheetId(),
20742
- dimension: "ROW",
20743
- elements: rows,
20744
- });
20745
- },
20746
- isVisible: NOT_ALL_VISIBLE_ROWS_SELECTED,
20747
- icon: "o-spreadsheet-Icon.HIDE_ROW",
20748
- };
20749
- const unhideRows = {
20750
- name: _t("Unhide rows"),
20751
- execute: (env) => {
20752
- const columns = env.model.getters.getElementsFromSelection("ROW");
20753
- env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
20754
- sheetId: env.model.getters.getActiveSheetId(),
20755
- dimension: "ROW",
20756
- elements: columns,
20757
- });
20758
- },
20759
- isVisible: (env) => {
20760
- const hiddenRows = env.model.getters
20761
- .getHiddenRowsGroups(env.model.getters.getActiveSheetId())
20762
- .flat();
20763
- const currentRows = env.model.getters.getElementsFromSelection("ROW");
20764
- return currentRows.some((col) => hiddenRows.includes(col));
20765
- },
20766
- };
20767
- const unhideAllRows = {
20768
- name: _t("Unhide all rows"),
20769
- execute: (env) => {
20770
- const sheetId = env.model.getters.getActiveSheetId();
20771
- env.model.dispatch("UNHIDE_COLUMNS_ROWS", {
20772
- sheetId,
20773
- dimension: "ROW",
20774
- elements: Array.from(Array(env.model.getters.getNumberRows(sheetId)).keys()),
20775
- });
20776
- },
20777
- isVisible: (env) => env.model.getters.getHiddenRowsGroups(env.model.getters.getActiveSheetId()).length > 0,
20778
- };
20779
- const unFreezePane = {
20780
- name: _t("Unfreeze"),
20781
- isVisible: (env) => {
20782
- const { xSplit, ySplit } = env.model.getters.getPaneDivisions(env.model.getters.getActiveSheetId());
20783
- return xSplit + ySplit > 0;
20784
- },
20785
- execute: (env) => env.model.dispatch("UNFREEZE_COLUMNS_ROWS", {
20786
- sheetId: env.model.getters.getActiveSheetId(),
20787
- }),
20788
- icon: "o-spreadsheet-Icon.UNFREEZE",
20789
- };
20790
- const freezePane = {
20791
- name: _t("Freeze"),
20792
- icon: "o-spreadsheet-Icon.FREEZE",
20793
- };
20794
- const unFreezeRows = {
20795
- name: _t("No rows"),
20796
- execute: (env) => env.model.dispatch("UNFREEZE_ROWS", {
20797
- sheetId: env.model.getters.getActiveSheetId(),
20798
- }),
20799
- isReadonlyAllowed: true,
20800
- isVisible: (env) => !!env.model.getters.getPaneDivisions(env.model.getters.getActiveSheetId()).ySplit,
20801
- };
20802
- const freezeFirstRow = {
20803
- name: _t("1 row"),
20804
- execute: (env) => interactiveFreezeColumnsRows(env, "ROW", 1),
20805
- isReadonlyAllowed: true,
20806
- };
20807
- const freezeSecondRow = {
20808
- name: _t("2 rows"),
20809
- execute: (env) => interactiveFreezeColumnsRows(env, "ROW", 2),
20810
- isReadonlyAllowed: true,
20811
- };
20812
- const freezeCurrentRow = {
20813
- name: _t("Up to current row"),
20814
- execute: (env) => {
20815
- const { bottom } = env.model.getters.getSelectedZone();
20816
- interactiveFreezeColumnsRows(env, "ROW", bottom + 1);
20817
- },
20818
- isReadonlyAllowed: true,
20819
- };
20820
- const unFreezeCols = {
20821
- name: _t("No columns"),
20822
- execute: (env) => env.model.dispatch("UNFREEZE_COLUMNS", {
20823
- sheetId: env.model.getters.getActiveSheetId(),
20824
- }),
20825
- isReadonlyAllowed: true,
20826
- isVisible: (env) => !!env.model.getters.getPaneDivisions(env.model.getters.getActiveSheetId()).xSplit,
20827
- };
20828
- const freezeFirstCol = {
20829
- name: _t("1 column"),
20830
- execute: (env) => interactiveFreezeColumnsRows(env, "COL", 1),
20831
- isReadonlyAllowed: true,
20832
- };
20833
- const freezeSecondCol = {
20834
- name: _t("2 columns"),
20835
- execute: (env) => interactiveFreezeColumnsRows(env, "COL", 2),
20836
- isReadonlyAllowed: true,
20837
- };
20838
- const freezeCurrentCol = {
20839
- name: _t("Up to current column"),
20840
- execute: (env) => {
20841
- const { right } = env.model.getters.getSelectedZone();
20842
- interactiveFreezeColumnsRows(env, "COL", right + 1);
20843
- },
20844
- isReadonlyAllowed: true,
20845
- };
20846
- const viewGridlines = {
20847
- name: (env) => env.model.getters.getGridLinesVisibility(env.model.getters.getActiveSheetId())
20848
- ? _t("Hide gridlines")
20849
- : _t("Show gridlines"),
20850
- execute: (env) => {
20851
- const sheetId = env.model.getters.getActiveSheetId();
20852
- env.model.dispatch("SET_GRID_LINES_VISIBILITY", {
20853
- sheetId,
20854
- areGridLinesVisible: !env.model.getters.getGridLinesVisibility(sheetId),
20855
- });
20856
- },
20857
- icon: "o-spreadsheet-Icon.SHOW_HIDE_GRID",
20858
- };
20859
- const viewFormulas = {
20860
- name: (env) => env.model.getters.shouldShowFormulas() ? "Hide formulas" : _t("Show formulas"),
20861
- execute: (env) => env.model.dispatch("SET_FORMULA_VISIBILITY", { show: !env.model.getters.shouldShowFormulas() }),
20862
- isReadonlyAllowed: true,
20863
- icon: "o-spreadsheet-Icon.SHOW_HIDE_FORMULA",
20864
- };
20865
- const createRemoveFilter = {
20866
- name: (env) => selectionContainsFilter(env) ? _t("Remove selected filters") : _t("Create filter"),
20867
- isActive: (env) => selectionContainsFilter(env),
20868
- isEnabled: (env) => !cannotCreateFilter(env),
20869
- execute: (env) => createRemoveFilterAction(env),
20870
- icon: "o-spreadsheet-Icon.FILTER_ICON_INACTIVE",
20871
- };
20872
- const groupColumns = {
20873
- name: (env) => {
20874
- const selection = env.model.getters.getSelectedZone();
20875
- if (selection.left === selection.right) {
20876
- return _t("Group column %s", numberToLetters(selection.left));
20877
- }
20878
- return _t("Group columns %s - %s", numberToLetters(selection.left), numberToLetters(selection.right));
20879
- },
20880
- execute: (env) => groupHeadersAction(env, "COL"),
20881
- isVisible: (env) => {
20882
- const sheetId = env.model.getters.getActiveSheetId();
20883
- const selection = env.model.getters.getSelectedZone();
20884
- const groups = env.model.getters.getHeaderGroupsInZone(sheetId, "COL", selection);
20885
- return (IS_ONLY_ONE_RANGE(env) &&
20886
- !groups.some((group) => group.start === selection.left && group.end === selection.right));
20887
- },
20888
- icon: "o-spreadsheet-Icon.GROUP_COLUMNS",
20889
- };
20890
- const groupRows = {
20891
- name: (env) => {
20892
- const selection = env.model.getters.getSelectedZone();
20893
- if (selection.top === selection.bottom) {
20894
- return _t("Group row %s", String(selection.top + 1));
20895
- }
20896
- return _t("Group rows %s - %s", String(selection.top + 1), String(selection.bottom + 1));
20897
- },
20898
- execute: (env) => groupHeadersAction(env, "ROW"),
20899
- isVisible: (env) => {
20900
- const sheetId = env.model.getters.getActiveSheetId();
20901
- const selection = env.model.getters.getSelectedZone();
20902
- const groups = env.model.getters.getHeaderGroupsInZone(sheetId, "ROW", selection);
20903
- return (IS_ONLY_ONE_RANGE(env) &&
20904
- !groups.some((group) => group.start === selection.top && group.end === selection.bottom));
20905
- },
20906
- icon: "o-spreadsheet-Icon.GROUP_ROWS",
20907
- };
20908
- const ungroupColumns = {
20909
- name: (env) => {
20910
- const selection = env.model.getters.getSelectedZone();
20911
- if (selection.left === selection.right) {
20912
- return _t("Ungroup column %s", numberToLetters(selection.left));
20913
- }
20914
- return _t("Ungroup columns %s - %s", numberToLetters(selection.left), numberToLetters(selection.right));
20915
- },
20916
- execute: (env) => ungroupHeaders(env, "COL"),
20917
- icon: "o-spreadsheet-Icon.UNGROUP_COLUMNS",
20918
- };
20919
- const ungroupRows = {
20920
- name: (env) => {
20921
- const selection = env.model.getters.getSelectedZone();
20922
- if (selection.top === selection.bottom) {
20923
- return _t("Ungroup row %s", String(selection.top + 1));
20924
- }
20925
- return _t("Ungroup rows %s - %s", String(selection.top + 1), String(selection.bottom + 1));
20926
- },
20927
- execute: (env) => ungroupHeaders(env, "ROW"),
20928
- icon: "o-spreadsheet-Icon.UNGROUP_ROWS",
20929
- };
20930
- function selectionContainsFilter(env) {
20931
- const sheetId = env.model.getters.getActiveSheetId();
20932
- const selectedZones = env.model.getters.getSelectedZones();
20933
- return env.model.getters.doesZonesContainFilter(sheetId, selectedZones);
20934
- }
20935
- function cannotCreateFilter(env) {
20936
- return !areZonesContinuous(...env.model.getters.getSelectedZones());
20937
- }
20938
- function createRemoveFilterAction(env) {
20939
- if (selectionContainsFilter(env)) {
20940
- env.model.dispatch("REMOVE_FILTER_TABLE", {
20941
- sheetId: env.model.getters.getActiveSheetId(),
20942
- target: env.model.getters.getSelectedZones(),
20943
- });
20944
- return;
20945
- }
20946
- if (cannotCreateFilter(env)) {
20947
- return;
20948
- }
20949
- env.model.selection.selectTableAroundSelection();
20950
- const sheetId = env.model.getters.getActiveSheetId();
20951
- const selection = env.model.getters.getSelectedZones();
20952
- interactiveAddFilter(env, sheetId, selection);
20953
- }
20954
- function groupHeadersAction(env, dim) {
20955
- const selection = env.model.getters.getSelectedZone();
20956
- const sheetId = env.model.getters.getActiveSheetId();
20957
- env.model.dispatch("GROUP_HEADERS", {
20958
- sheetId,
20959
- dimension: dim,
20960
- start: dim === "COL" ? selection.left : selection.top,
20961
- end: dim === "COL" ? selection.right : selection.bottom,
20962
- });
20963
- }
20964
- function ungroupHeaders(env, dim) {
20965
- const selection = env.model.getters.getSelectedZone();
20966
- const sheetId = env.model.getters.getActiveSheetId();
20967
- env.model.dispatch("UNGROUP_HEADERS", {
20968
- sheetId,
20969
- dimension: dim,
20970
- start: dim === "COL" ? selection.left : selection.top,
20971
- end: dim === "COL" ? selection.right : selection.bottom,
20972
- });
20973
- }
20974
- function canUngroupHeaders(env, dimension) {
20975
- const sheetId = env.model.getters.getActiveSheetId();
20976
- const selection = env.model.getters.getSelectedZones();
20977
- return (selection.length === 1 &&
20978
- env.model.getters.getHeaderGroupsInZone(sheetId, dimension, selection[0]).length > 0);
20979
- }
20980
-
20981
- var ACTION_VIEW = /*#__PURE__*/Object.freeze({
20982
- __proto__: null,
20983
- canUngroupHeaders: canUngroupHeaders,
20984
- createRemoveFilter: createRemoveFilter,
20985
- freezeCurrentCol: freezeCurrentCol,
20986
- freezeCurrentRow: freezeCurrentRow,
20987
- freezeFirstCol: freezeFirstCol,
20988
- freezeFirstRow: freezeFirstRow,
20989
- freezePane: freezePane,
20990
- freezeSecondCol: freezeSecondCol,
20991
- freezeSecondRow: freezeSecondRow,
20992
- groupColumns: groupColumns,
20993
- groupRows: groupRows,
20994
- hideCols: hideCols,
20995
- hideRows: hideRows,
20996
- unFreezeCols: unFreezeCols,
20997
- unFreezePane: unFreezePane,
20998
- unFreezeRows: unFreezeRows,
20999
- ungroupColumns: ungroupColumns,
21000
- ungroupRows: ungroupRows,
21001
- unhideAllCols: unhideAllCols,
21002
- unhideAllRows: unhideAllRows,
21003
- unhideCols: unhideCols,
21004
- unhideRows: unhideRows,
21005
- viewFormulas: viewFormulas,
21006
- viewGridlines: viewGridlines
21007
- });
21008
-
21009
21009
  const colMenuRegistry = new MenuItemRegistry();
21010
21010
  colMenuRegistry
21011
21011
  .add("cut", {
@@ -21652,13 +21652,8 @@ topbarMenuRegistry
21652
21652
  sequence: 30,
21653
21653
  separator: true,
21654
21654
  })
21655
- .addChild("add_data_filter", ["data"], {
21656
- ...addDataFilter,
21657
- sequence: 40,
21658
- separator: true,
21659
- })
21660
- .addChild("remove_data_filter", ["data"], {
21661
- ...removeDataFilter,
21655
+ .addChild("add_remove_data_filter", ["data"], {
21656
+ ...addRemoveDataFilter,
21662
21657
  sequence: 40,
21663
21658
  separator: true,
21664
21659
  });
@@ -27183,17 +27178,13 @@ class Composer extends Component {
27183
27178
  this.env.focusableElement.setFocusableElement(el);
27184
27179
  }
27185
27180
  this.contentHelper.updateEl(el);
27186
- this.processTokenAtCursor();
27187
27181
  });
27188
27182
  useEffect(() => {
27189
27183
  this.processContent();
27190
27184
  });
27191
- onPatched(() => {
27192
- // Required because typing '=SUM' and double-clicking another cell leaves ShowProvider/ShowDescription true
27193
- if (this.env.model.getters.getEditionMode() === "inactive") {
27194
- this.processTokenAtCursor();
27195
- }
27196
- });
27185
+ useEffect(() => {
27186
+ this.processTokenAtCursor();
27187
+ }, () => [this.env.model.getters.getEditionMode() !== "inactive"]);
27197
27188
  }
27198
27189
  // ---------------------------------------------------------------------------
27199
27190
  // Handlers
@@ -27631,6 +27622,7 @@ class Composer extends Component {
27631
27622
  const dataValidationAutocompleteValues = this.env.model.getters.getAutoCompleteDataValidationValues();
27632
27623
  if (!content.startsWith("=") && dataValidationAutocompleteValues.length) {
27633
27624
  this.showDataValidationAutocomplete(dataValidationAutocompleteValues);
27625
+ return;
27634
27626
  }
27635
27627
  if (content.startsWith("=")) {
27636
27628
  const token = this.env.model.getters.getTokenAtCursor();
@@ -28343,6 +28335,10 @@ css /*SCSS*/ `
28343
28335
  height: 0px;
28344
28336
  }
28345
28337
  }
28338
+ .o-figure-container {
28339
+ -webkit-user-select: none; // safari
28340
+ user-select: none;
28341
+ }
28346
28342
  `;
28347
28343
  /**
28348
28344
  * Each figure ⭐ is positioned inside a container `div` placed and sized
@@ -34869,7 +34865,7 @@ class BordersPlugin extends CorePlugin {
34869
34865
  this.clearBorders(cmd.sheetId, cmd.target);
34870
34866
  break;
34871
34867
  case "REMOVE_COLUMNS_ROWS":
34872
- for (let el of cmd.elements) {
34868
+ for (let el of [...cmd.elements].sort((a, b) => b - a)) {
34873
34869
  if (cmd.dimension === "COL") {
34874
34870
  this.shiftBordersHorizontally(cmd.sheetId, el + 1, -1);
34875
34871
  }
@@ -40625,6 +40621,353 @@ class CompilationParametersBuilder {
40625
40621
  }
40626
40622
  }
40627
40623
 
40624
+ /**
40625
+ * ####################################################
40626
+ * # INTRODUCTION
40627
+ * ####################################################
40628
+ *
40629
+ * This file contain the function recomputeZones.
40630
+ * This function try to recompute in a performant way
40631
+ * an ensemble of zones possibly overlapping to avoid
40632
+ * overlapping and to reduce the number of zones.
40633
+ *
40634
+ * It also allows to remove some zones from the ensemble.
40635
+ *
40636
+ * In the following example, 2 zones are overlapping.
40637
+ * Applying recomputeZones will return zones without
40638
+ * overlapping:
40639
+ *
40640
+ * ["B3:D4", "D2:E3"] ["B3:C4", "D2:D4", "E2:E3"]
40641
+ *
40642
+ * A B C D E A B C D E
40643
+ * 1 ___ 1 ___
40644
+ * 2 ___|_ | 2 ___| | |
40645
+ * 3 | |_|_| ---> 3 | | |_|
40646
+ * 4 |_____| 4 |___|_|
40647
+ * 6 6
40648
+ * 7 7
40649
+ *
40650
+ *
40651
+ * In the following example, 2 zones are contiguous.
40652
+ * Applying recomputeZones will return only one zone:
40653
+ *
40654
+ * ["B2:B3", "C2:D3"] ["B2:D3"]
40655
+ *
40656
+ * A B C D E A B C D E
40657
+ * 1 _ ___ 1 _____
40658
+ * 2 | | | ---> 2 | |
40659
+ * 3 |_|___| 3 |_____|
40660
+ * 4 4
40661
+ *
40662
+ *
40663
+ * In the following example, we want to remove a zone
40664
+ * from the ensemble. Applying recomputeZones will
40665
+ * return the ensemble without the zone to remove:
40666
+ *
40667
+ * remove ["C3:D3"] ["B2:B4", "C2:D2",
40668
+ * "C4:D4", "E2:E4"]
40669
+ *
40670
+ * A B C D E F A B C D E F
40671
+ * 1 _______ 1 _______
40672
+ * 2 | | ---> 2 | |___| |
40673
+ * 3 | xxx | 3 | |___| |
40674
+ * 4 |_______| 4 |_|___|_|
40675
+ * 5 5
40676
+ *
40677
+ *
40678
+ * The exercise seems simple when we have only 2 zones.
40679
+ * But with n zones and in a performant way, we want to
40680
+ * avoid comparing each zone with all the others.
40681
+ *
40682
+ *
40683
+ * ####################################################
40684
+ * # Methodological approach
40685
+ * ####################################################
40686
+ *
40687
+ * The methodological approach to avoid comparing each
40688
+ * zone with all the others is to use a data structure
40689
+ * that allow to quickly find which zones are
40690
+ * overlapping with any other given zone.
40691
+ *
40692
+ * Here the idea is to profile the zones at the columns level.
40693
+ *
40694
+ * To do that, we propose to use a data structure
40695
+ * composed of 2 parts:
40696
+ * - profilesStartingPosition: a sorted number array
40697
+ * indicating on which columns a new profile begins.
40698
+ * - profiles: a map where the key is a column
40699
+ * position (from profilesStartingPosition) and the
40700
+ * value is a sorted number array representing a
40701
+ * profile.
40702
+ *
40703
+ *
40704
+ * See the following example: here profileStartingPosition
40705
+ * corresponds to [A,C,E,G,K]
40706
+ * A B C D E F G H I J K so with number [0,2,4,6,10]
40707
+ * 1 ' ' ' '
40708
+ * 2 ' ' '_______' here profile correspond
40709
+ * 3 '___' |_______| for A to []
40710
+ * 4 | | for C to [3, 5]
40711
+ * 5 |___| for E to []
40712
+ * 6 for G to [2, 3]
40713
+ * 7 for K to []
40714
+ *
40715
+ *
40716
+ * Now we can easily find which zones are overlapping
40717
+ * with a given zone. Suppose we want to add a new zone
40718
+ * D5:H6 to the ensemble:
40719
+ *
40720
+ * With a binary search of left and right
40721
+ * A B C D E F G H I J K on profilesStartingPosition, we can
40722
+ * 1 ' ' ' ' find the indexes of the profiles on which
40723
+ * 2 ' ' '_______' to apply a modification.
40724
+ * 3 '___' |_______|
40725
+ * 4 | _|_______ Here we will:
40726
+ * 5 |_|_| | - add a new profile in D --> become [3, 6]
40727
+ * 6 |_________| - modify the profile in E --> become [4, 6]
40728
+ * 7 - modify the profile in G --> become [2, 3, 4, 6]
40729
+ * - add a new profile in I --> become [8, 10]
40730
+ *
40731
+ * See below the result:
40732
+ *
40733
+ * Note the particularity of the profile
40734
+ * A B C D E F G H I J K for G: it will correspond to [2, 3, 4, 6]
40735
+ * 1 ' ' ' ' ' '
40736
+ * 2 ' ' ' '___'___' To know how to modify the profile (add a
40737
+ * 3 '_'_' |___|___| zone or remove it) we do a binary
40738
+ * 4 | | |___ ___ search of the top and bottom value on the
40739
+ * 5 |_| | | | profile array. Depending on the result index
40740
+ * 6 |_|___|___| parity (odd or even), because zone boundaries
40741
+ * 7 go by pairs, we know if we are in a zone or
40742
+ * not and how operate.
40743
+ */
40744
+ /**
40745
+ * Recompute the zone without the cells in toRemoveZones and avoid overlapping.
40746
+ * This compute is particularly useful because after this function:
40747
+ * - you will find coordinate of a cell only once among all the zones
40748
+ * - the number of zones will be reduced to the minimum
40749
+ */
40750
+ function futureRecomputeZones(zones, zonesToRemove = []) {
40751
+ const profilesStartingPosition = [0];
40752
+ const profiles = new Map([[0, []]]);
40753
+ modifyProfiles(profilesStartingPosition, profiles, zones, false);
40754
+ modifyProfiles(profilesStartingPosition, profiles, zonesToRemove, true);
40755
+ return constructZonesFromProfiles(profilesStartingPosition, profiles);
40756
+ }
40757
+ function modifyProfiles(// export for testing only
40758
+ profilesStartingPosition, profiles, zones, toRemove = false) {
40759
+ for (const zone of zones) {
40760
+ const leftValue = zone.left;
40761
+ const rightValue = zone.right === undefined ? undefined : zone.right + 1;
40762
+ const leftIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, leftValue, true, 0);
40763
+ const rightIndex = findIndexAndCreateProfile(profilesStartingPosition, profiles, rightValue, false, leftIndex);
40764
+ for (let i = leftIndex; i <= rightIndex; i++) {
40765
+ const profile = profiles.get(profilesStartingPosition[i]);
40766
+ modifyProfile(profile, zone, toRemove);
40767
+ }
40768
+ // maybe this part cost in performance, and maybe it's not necessary (depending on the use case). To be checked
40769
+ removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex);
40770
+ }
40771
+ }
40772
+ function findIndexAndCreateProfile(profilesStartingPosition, profiles, value, searchLeft, startIndex) {
40773
+ if (value === undefined) {
40774
+ // this is only the case when the value correspond to a bottom value that could be undefined
40775
+ return profilesStartingPosition.length - 1;
40776
+ }
40777
+ const predecessorIndex = binaryPredecessorSearch(profilesStartingPosition, value, startIndex);
40778
+ if (value != profilesStartingPosition[predecessorIndex]) {
40779
+ // mean that the value is not ending/starting at the same position as the previous/next profile
40780
+ // --> it's a new profile
40781
+ // --> we need to add it
40782
+ profilesStartingPosition.splice(predecessorIndex + 1, 0, value);
40783
+ // suppose the we want to add the for the left value
40784
+ // following profile following zone: 'C', the predecessor index
40785
+ // for B: [1, 3] "C3:D4" correspond to 'B'.
40786
+ // The next line code will
40787
+ // A B C D A B C D copy the profile of 'B'
40788
+ // 1 '___' 1 '___' to 'C'. In the rest of the
40789
+ // 2 | | ---> 2 | _|_ process the 'modifyProfile'
40790
+ // 3 |___| 3 |_|_| | function will adapt the waiting
40791
+ // 4 4 |___| 'C' profile [1, 3] to the
40792
+ // correct 'C' profile [1, 4]
40793
+ profiles.set(value, [...profiles.get(profilesStartingPosition[predecessorIndex])]);
40794
+ return searchLeft ? predecessorIndex + 1 : predecessorIndex;
40795
+ }
40796
+ return searchLeft ? predecessorIndex : predecessorIndex - 1;
40797
+ }
40798
+ /**
40799
+ * Suppose the following Suppose we want to add We want to have the
40800
+ * profile: the following zone: following profile:
40801
+ *
40802
+ * A B C D E F A B C D E F A B C D E F
40803
+ * 1 '___' 1 ' ' 1 '___'
40804
+ * 2 |___| 2 '___' 2 | |
40805
+ * 3 ' ' 3 | | 3 | |
40806
+ * 4 '___' --> 4 | | --> 4 | |
40807
+ * 6 | | 6 |___| 6 | |
40808
+ * 7 |___| 7 7 |___|
40809
+ * 8 8 8
40810
+ *
40811
+ * the profile for 'C' the top zone correspond Here [2, 3, 5, 8] with [3, 7]
40812
+ * corresponds to: to 3 and the bottom zone would be merged into [2, 8]
40813
+ * ____ ____ correspond to 6
40814
+ * [2, 3, 5, 8] would be the profile: The difficulty of modify profile
40815
+ * ____ is to know what must be deleted
40816
+ * Note that the 'filled [3, 7] and what must be added to the
40817
+ * zone' are always between existing profile.
40818
+ * an even index and its
40819
+ * next index
40820
+ *
40821
+ */
40822
+ function modifyProfile(profile, zone, toRemove = false) {
40823
+ const topValue = zone.top;
40824
+ const bottomValue = zone.bottom === undefined ? undefined : zone.bottom + 1;
40825
+ const newPoints = [];
40826
+ // Case we want to add a zone to the profile:
40827
+ // - If the top predecessor index `topPredIndex` is even, it means the top of the zone is already positioned on a filled zone
40828
+ // so we don't need to add it to the profile. we can keep in reference the index of the predecessor.
40829
+ // - If it is odd, it means the top of the zone must be the beginning of a filled zone.
40830
+ // so we can keep the index of the top position
40831
+ // Case we want to remove a zone from the profile: it's the opposite of the previous case
40832
+ const topPredIndex = binaryPredecessorSearch(profile, topValue, 0, false);
40833
+ if ((topPredIndex % 2 !== 0 && !toRemove) || (topPredIndex % 2 === 0 && toRemove)) {
40834
+ newPoints.push(topValue);
40835
+ }
40836
+ if (bottomValue === undefined) {
40837
+ // The following two code lines will not impact the final result,
40838
+ // but they will impact the intermediate profile.
40839
+ // We keep them for performance reason
40840
+ profile.splice(topPredIndex + 1);
40841
+ profile.push(...newPoints);
40842
+ return;
40843
+ }
40844
+ // Case we want to add a zone to the profile:
40845
+ // - If the bottom successor index `bottomSuccIndex` is even, it means the bottom of the zone must be the ending of a filled zone
40846
+ // so we can keep the index of the bottom position.
40847
+ // - If it is odd, it means the bottom of the zone is already positioned on a filled zone
40848
+ // so we don't need to add it to the profile. we can keep in reference the index of the successor
40849
+ // Case we want to remove a zone from the profile: it's the opposite of the previous case
40850
+ const bottomSuccIndex = binarySuccessorSearch(profile, bottomValue, 0, false);
40851
+ if ((bottomSuccIndex % 2 === 0 && !toRemove) || (bottomSuccIndex % 2 !== 0 && toRemove)) {
40852
+ newPoints.push(bottomValue);
40853
+ }
40854
+ // add the top and bottom value to the profile and
40855
+ // remove all information between the top and bottom index
40856
+ profile.splice(topPredIndex + 1, bottomSuccIndex - topPredIndex - 1, ...newPoints);
40857
+ }
40858
+ function removeContiguousProfiles(profilesStartingPosition, profiles, leftIndex, rightIndex) {
40859
+ const start = leftIndex - 1 === -1 ? 0 : leftIndex - 1;
40860
+ const end = rightIndex === profilesStartingPosition.length - 1 ? rightIndex : rightIndex + 1;
40861
+ for (let i = end; i > start; i--) {
40862
+ if (deepEqualsArray(profiles.get(profilesStartingPosition[i]), profiles.get(profilesStartingPosition[i - 1]))) {
40863
+ profiles.delete(profilesStartingPosition[i]);
40864
+ profilesStartingPosition.splice(i, 1);
40865
+ }
40866
+ }
40867
+ }
40868
+ function constructZonesFromProfiles(profilesStartingPosition, profiles) {
40869
+ const mergedZone = [];
40870
+ let pendingZones = [];
40871
+ for (let colIndex = 0; colIndex < profilesStartingPosition.length; colIndex++) {
40872
+ const left = profilesStartingPosition[colIndex];
40873
+ const profile = profiles.get(left);
40874
+ if (!profile || profile.length === 0) {
40875
+ mergedZone.push(...pendingZones);
40876
+ pendingZones = [];
40877
+ continue;
40878
+ }
40879
+ let right = profilesStartingPosition[colIndex + 1];
40880
+ if (right !== undefined) {
40881
+ right--;
40882
+ }
40883
+ const nextPendingZones = [];
40884
+ for (let i = 0; i < profile.length; i += 2) {
40885
+ const top = profile[i];
40886
+ let bottom = profile[i + 1];
40887
+ if (bottom !== undefined) {
40888
+ bottom--;
40889
+ }
40890
+ const profileZone = {
40891
+ top,
40892
+ left,
40893
+ bottom,
40894
+ right,
40895
+ hasHeader: (bottom === undefined && top !== 0) || (right === undefined && left !== 0),
40896
+ };
40897
+ let findCorrespondingZone = false;
40898
+ for (let j = pendingZones.length - 1; j >= 0; j--) {
40899
+ const pendingZone = pendingZones[j];
40900
+ if (pendingZone.top === profileZone.top && pendingZone.bottom === profileZone.bottom) {
40901
+ pendingZone.right = profileZone.right;
40902
+ pendingZones.splice(j, 1);
40903
+ nextPendingZones.push(pendingZone);
40904
+ findCorrespondingZone = true;
40905
+ break;
40906
+ }
40907
+ }
40908
+ if (!findCorrespondingZone) {
40909
+ nextPendingZones.push(profileZone);
40910
+ }
40911
+ }
40912
+ mergedZone.push(...pendingZones);
40913
+ pendingZones = nextPendingZones;
40914
+ }
40915
+ mergedZone.push(...pendingZones);
40916
+ return mergedZone;
40917
+ }
40918
+ function binaryPredecessorSearch(arr, val, start = 0, matchEqual = true) {
40919
+ let end = arr.length - 1;
40920
+ let result = -1;
40921
+ while (start <= end) {
40922
+ const mid = Math.floor((start + end) / 2);
40923
+ if (arr[mid] === val && matchEqual) {
40924
+ return mid;
40925
+ }
40926
+ else if (arr[mid] < val) {
40927
+ result = mid;
40928
+ start = mid + 1;
40929
+ }
40930
+ else {
40931
+ end = mid - 1;
40932
+ }
40933
+ }
40934
+ return result;
40935
+ }
40936
+ function binarySuccessorSearch(arr, val, start = 0, matchEqual = true) {
40937
+ let end = arr.length - 1;
40938
+ let result = arr.length;
40939
+ while (start <= end) {
40940
+ const mid = Math.floor((start + end) / 2);
40941
+ if (arr[mid] === val && matchEqual) {
40942
+ return mid;
40943
+ }
40944
+ else if (arr[mid] > val) {
40945
+ result = mid;
40946
+ end = mid - 1;
40947
+ }
40948
+ else {
40949
+ start = mid + 1;
40950
+ }
40951
+ }
40952
+ return result;
40953
+ }
40954
+ /**
40955
+ * Compares two arrays.
40956
+ * For performance reasons, this function is to be preferred
40957
+ * to 'deepEquals' in the case we know that the inputs are arrays.
40958
+ */
40959
+ function deepEqualsArray(arr1, arr2) {
40960
+ if (arr1.length !== arr2.length) {
40961
+ return false;
40962
+ }
40963
+ for (let i = 0; i < arr1.length; i++) {
40964
+ if (!deepEquals(arr1[i], arr2[i])) {
40965
+ return false;
40966
+ }
40967
+ }
40968
+ return true;
40969
+ }
40970
+
40628
40971
  function quickselect(arr, k, left, right, compare) {
40629
40972
  quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
40630
40973
  }
@@ -41388,7 +41731,7 @@ class FormulaDependencyGraph {
41388
41731
  }
41389
41732
  }
41390
41733
  /**
41391
- * Return the cell and all cells that depend on it,
41734
+ * Return all the cells that depend on the provided ranges,
41392
41735
  * in the correct order they should be evaluated.
41393
41736
  * This is called a topological ordering (excluding cycles)
41394
41737
  */
@@ -41399,11 +41742,20 @@ class FormulaDependencyGraph {
41399
41742
  const range = queue.pop();
41400
41743
  visited.addMany(this.encoder.encodeBoundingBox(range));
41401
41744
  const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
41745
+ const nextInQueue = {};
41402
41746
  for (const positionId of impactedPositionIds) {
41403
41747
  if (!visited.has(positionId)) {
41404
- queue.push(this.encoder.decodeToBoundingBox(positionId));
41748
+ const { sheetId, zone } = this.encoder.decodeToBoundingBox(positionId);
41749
+ if (!nextInQueue[sheetId]) {
41750
+ nextInQueue[sheetId] = [];
41751
+ }
41752
+ nextInQueue[sheetId].push(zone);
41405
41753
  }
41406
41754
  }
41755
+ for (const sheetId in nextInQueue) {
41756
+ const zones = futureRecomputeZones(nextInQueue[sheetId]);
41757
+ queue.push(...zones.map((zone) => ({ sheetId, zone })));
41758
+ }
41407
41759
  }
41408
41760
  visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
41409
41761
  return visited;
@@ -41561,7 +41913,7 @@ class Evaluator {
41561
41913
  }
41562
41914
  if (!content) {
41563
41915
  // The previous content could have blocked some array formulas
41564
- impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
41916
+ impactedPositionIds.addMany(this.getArrayFormulasBlockedBy(positionId));
41565
41917
  }
41566
41918
  }
41567
41919
  return impactedPositionIds;
@@ -41609,13 +41961,22 @@ class Evaluator {
41609
41961
  }
41610
41962
  return positionIds;
41611
41963
  }
41612
- getArrayFormulasBlockedByOrSpreadingOn(positionId) {
41964
+ /**
41965
+ * Return the position of formulas blocked by the given position
41966
+ * as well as all their dependencies.
41967
+ */
41968
+ getArrayFormulasBlockedBy(positionId) {
41613
41969
  if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
41614
41970
  return [];
41615
41971
  }
41616
41972
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
41617
41973
  const cells = new JetSet(arrayFormulas);
41618
- cells.addMany(this.getCellsDependingOn(arrayFormulas));
41974
+ const arrayFormulaPositionId = this.getArrayFormulaSpreadingOnId(positionId);
41975
+ if (arrayFormulaPositionId) {
41976
+ // ignore the formula spreading on the position. Keep only the blocked ones
41977
+ cells.delete(arrayFormulaPositionId);
41978
+ }
41979
+ cells.addMany(this.getCellsDependingOn(cells));
41619
41980
  return cells;
41620
41981
  }
41621
41982
  nextPositionsToUpdate = new JetSet();
@@ -41769,7 +42130,7 @@ class Evaluator {
41769
42130
  }
41770
42131
  this.evaluatedCells.delete(child);
41771
42132
  this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
41772
- this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
42133
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedBy(child));
41773
42134
  }
41774
42135
  this.spreadingRelations.removeNode(positionId);
41775
42136
  }
@@ -49305,7 +49666,7 @@ class FilterEvaluationPlugin extends UIPlugin {
49305
49666
  "isFilterActive",
49306
49667
  ];
49307
49668
  filterValues = {};
49308
- hiddenRows = new Set();
49669
+ hiddenRows = {};
49309
49670
  isEvaluationDirty = false;
49310
49671
  allowDispatch(cmd) {
49311
49672
  switch (cmd.type) {
@@ -49348,11 +49709,11 @@ class FilterEvaluationPlugin extends UIPlugin {
49348
49709
  case "UNFOLD_HEADER_GROUP":
49349
49710
  case "FOLD_ALL_HEADER_GROUPS":
49350
49711
  case "UNFOLD_ALL_HEADER_GROUPS":
49351
- this.updateHiddenRows();
49712
+ this.updateHiddenRows(cmd.sheetId);
49352
49713
  break;
49353
49714
  case "UPDATE_FILTER":
49354
49715
  this.updateFilter(cmd);
49355
- this.updateHiddenRows();
49716
+ this.updateHiddenRows(cmd.sheetId);
49356
49717
  break;
49357
49718
  case "DUPLICATE_SHEET":
49358
49719
  const filterValues = {};
@@ -49372,15 +49733,14 @@ class FilterEvaluationPlugin extends UIPlugin {
49372
49733
  }
49373
49734
  finalize() {
49374
49735
  if (this.isEvaluationDirty) {
49375
- this.updateHiddenRows();
49736
+ for (const sheetId of this.getters.getSheetIds()) {
49737
+ this.updateHiddenRows(sheetId);
49738
+ }
49376
49739
  this.isEvaluationDirty = false;
49377
49740
  }
49378
49741
  }
49379
49742
  isRowFiltered(sheetId, row) {
49380
- if (sheetId !== this.getters.getActiveSheetId()) {
49381
- return false;
49382
- }
49383
- return this.hiddenRows.has(row);
49743
+ return !!this.hiddenRows[sheetId]?.has(row);
49384
49744
  }
49385
49745
  getCellBorderWithFilterBorder(position) {
49386
49746
  const { sheetId, col, row } = position;
@@ -49431,8 +49791,7 @@ class FilterEvaluationPlugin extends UIPlugin {
49431
49791
  this.filterValues[sheetId] = {};
49432
49792
  this.filterValues[sheetId][id] = hiddenValues;
49433
49793
  }
49434
- updateHiddenRows() {
49435
- const sheetId = this.getters.getActiveSheetId();
49794
+ updateHiddenRows(sheetId) {
49436
49795
  const filters = this.getters
49437
49796
  .getFilters(sheetId)
49438
49797
  .sort((filter1, filter2) => filter1.zoneWithHeaders.top - filter2.zoneWithHeaders.top);
@@ -49453,7 +49812,7 @@ class FilterEvaluationPlugin extends UIPlugin {
49453
49812
  }
49454
49813
  }
49455
49814
  }
49456
- this.hiddenRows = hiddenRows;
49815
+ this.hiddenRows[sheetId] = hiddenRows;
49457
49816
  }
49458
49817
  getCellValueAsString(sheetId, col, row) {
49459
49818
  const value = this.getters.getEvaluatedCell({ sheetId, col, row }).formattedValue;
@@ -50563,13 +50922,14 @@ class SheetViewPlugin extends UIPlugin {
50563
50922
  }
50564
50923
  }
50565
50924
  handleEvent(event) {
50925
+ const sheetId = this.getters.getActiveSheetId();
50566
50926
  if (event.options.scrollIntoView) {
50567
50927
  let { col, row } = findCellInNewZone(event.previousAnchor.zone, event.anchor.zone);
50568
50928
  if (event.mode === "updateAnchor") {
50569
50929
  const oldZone = event.previousAnchor.zone;
50570
50930
  const newZone = event.anchor.zone;
50571
50931
  // altering a zone should not move the viewport in a dimension that wasn't changed
50572
- const { top, bottom, left, right } = this.getters.getActiveMainViewport();
50932
+ const { top, bottom, left, right } = this.getMainInternalViewport(sheetId);
50573
50933
  if (oldZone.left === newZone.left && oldZone.right === newZone.right) {
50574
50934
  col = left > col || col > right ? left : col;
50575
50935
  }
@@ -50577,7 +50937,6 @@ class SheetViewPlugin extends UIPlugin {
50577
50937
  row = top > row || row > bottom ? top : row;
50578
50938
  }
50579
50939
  }
50580
- const sheetId = this.getters.getActiveSheetId();
50581
50940
  col = Math.min(col, this.getters.getNumberCols(sheetId) - 1);
50582
50941
  row = Math.min(row, this.getters.getNumberRows(sheetId) - 1);
50583
50942
  if (!this.sheetsWithDirtyViewports.has(sheetId)) {
@@ -50614,16 +50973,16 @@ class SheetViewPlugin extends UIPlugin {
50614
50973
  this.setSheetViewOffset(cmd.offsetX, cmd.offsetY);
50615
50974
  break;
50616
50975
  case "SHIFT_VIEWPORT_DOWN":
50617
- const { top } = this.getActiveMainViewport();
50618
50976
  const sheetId = this.getters.getActiveSheetId();
50619
- const shiftedOffsetY = this.clipOffsetY(this.getters.getRowDimensions(sheetId, top).start + this.sheetViewHeight);
50620
- this.shiftVertically(shiftedOffsetY);
50977
+ const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId);
50978
+ const topRowDims = this.getters.getRowDimensions(sheetId, top);
50979
+ this.shiftVertically(topRowDims.start + viewportHeight - offsetCorrectionY);
50621
50980
  break;
50622
50981
  case "SHIFT_VIEWPORT_UP": {
50623
- const { top } = this.getActiveMainViewport();
50624
50982
  const sheetId = this.getters.getActiveSheetId();
50625
- const shiftedOffsetY = this.clipOffsetY(this.getters.getRowDimensions(sheetId, top).end - this.sheetViewHeight);
50626
- this.shiftVertically(shiftedOffsetY);
50983
+ const { top, viewportHeight, offsetCorrectionY } = this.getMainInternalViewport(sheetId);
50984
+ const topRowDims = this.getters.getRowDimensions(sheetId, top);
50985
+ this.shiftVertically(topRowDims.end - offsetCorrectionY - viewportHeight);
50627
50986
  break;
50628
50987
  }
50629
50988
  case "REMOVE_FILTER_TABLE":
@@ -51016,17 +51375,6 @@ class SheetViewPlugin extends UIPlugin {
51016
51375
  const { maxOffsetX, maxOffsetY } = this.getMaximumSheetOffset();
51017
51376
  Object.values(this.getSubViewports(sheetId)).forEach((viewport) => viewport.setViewportOffset(clip(offsetX, 0, maxOffsetX), clip(offsetY, 0, maxOffsetY)));
51018
51377
  }
51019
- /**
51020
- * Clip the vertical offset within the allowed range.
51021
- * Not above the sheet, nor below the sheet.
51022
- */
51023
- clipOffsetY(offsetY) {
51024
- const { height } = this.getMainViewportRect();
51025
- const maxOffset = height - this.sheetViewHeight;
51026
- offsetY = Math.min(offsetY, maxOffset);
51027
- offsetY = Math.max(offsetY, 0);
51028
- return offsetY;
51029
- }
51030
51378
  getViewportOffset(sheetId) {
51031
51379
  return {
51032
51380
  x: this.viewports[sheetId]?.bottomRight.offsetScrollbarX || 0,
@@ -51082,12 +51430,15 @@ class SheetViewPlugin extends UIPlugin {
51082
51430
  * viewport top.
51083
51431
  */
51084
51432
  shiftVertically(offset) {
51085
- const { top } = this.getActiveMainViewport();
51433
+ const sheetId = this.getters.getActiveSheetId();
51434
+ const { top } = this.getMainInternalViewport(sheetId);
51086
51435
  const { scrollX } = this.getActiveSheetScrollInfo();
51087
51436
  this.setSheetViewOffset(scrollX, offset);
51088
51437
  const { anchor } = this.getters.getSelection();
51089
- const deltaRow = this.getActiveMainViewport().top - top;
51090
- this.selection.selectCell(anchor.cell.col, anchor.cell.row + deltaRow);
51438
+ if (anchor.cell.row >= this.getters.getPaneDivisions(sheetId).ySplit) {
51439
+ const deltaRow = this.getMainInternalViewport(sheetId).top - top;
51440
+ this.selection.selectCell(anchor.cell.col, anchor.cell.row + deltaRow);
51441
+ }
51091
51442
  }
51092
51443
  getVisibleFigures() {
51093
51444
  const sheetId = this.getters.getActiveSheetId();
@@ -52018,7 +52369,6 @@ css /* scss */ `
52018
52369
  cursor: pointer;
52019
52370
  }
52020
52371
  `;
52021
- let tKey = 1;
52022
52372
  class SpreadsheetDashboard extends Component {
52023
52373
  static template = "o-spreadsheet-SpreadsheetDashboard";
52024
52374
  static props = {};
@@ -52097,13 +52447,9 @@ class SpreadsheetDashboard extends Component {
52097
52447
  coordinates: rect,
52098
52448
  position: { col, row },
52099
52449
  action,
52100
- // we can't rely on position only because a row or a column could
52101
- // be inserted at any time.
52102
- tKey: `${tKey}-${col}-${row}`,
52103
52450
  });
52104
52451
  }
52105
52452
  }
52106
- tKey++;
52107
52453
  return cells;
52108
52454
  }
52109
52455
  getClickableAction(position) {
@@ -57334,6 +57680,6 @@ const constants = {
57334
57680
  export { AbstractChart, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, UIPlugin, __info__, addFunction, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, tokenize };
57335
57681
 
57336
57682
 
57337
- __info__.version = "17.1.11";
57338
- __info__.date = "2024-04-10T12:27:11.107Z";
57339
- __info__.hash = "246caf7";
57683
+ __info__.version = "17.1.13";
57684
+ __info__.date = "2024-04-26T07:39:54.100Z";
57685
+ __info__.hash = "f2ea8c7";