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