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