@odoo/o-spreadsheet 17.4.8 → 17.4.9

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,13 +2,118 @@
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.4.8
6
- * @date 2024-09-17T08:10:18.777Z
7
- * @hash caad8bd
5
+ * @version 17.4.9
6
+ * @date 2024-10-14T07:53:51.633Z
7
+ * @hash b7015b7
8
8
  */
9
9
 
10
10
  import { useEnv, useSubEnv, onWillUnmount, useComponent, status, Component, useRef, onMounted, useEffect, useState, onPatched, onWillPatch, onWillUpdateProps, useExternalListener, onWillStart, xml, useChildSubEnv, markRaw, toRaw } from '@odoo/owl';
11
11
 
12
+ function createActions(menuItems) {
13
+ return menuItems.map(createAction).sort((a, b) => a.sequence - b.sequence);
14
+ }
15
+ let nextItemId = 1;
16
+ function createAction(item) {
17
+ const name = item.name;
18
+ const children = item.children;
19
+ const description = item.description;
20
+ const icon = item.icon;
21
+ const secondaryIcon = item.secondaryIcon;
22
+ const itemId = item.id || nextItemId++;
23
+ return {
24
+ id: itemId.toString(),
25
+ name: typeof name === "function" ? name : () => name,
26
+ isVisible: item.isVisible ? item.isVisible : () => true,
27
+ isEnabled: item.isEnabled ? item.isEnabled : () => true,
28
+ isActive: item.isActive,
29
+ execute: item.execute,
30
+ children: children
31
+ ? (env) => {
32
+ return children
33
+ .map((child) => (typeof child === "function" ? child(env) : child))
34
+ .flat()
35
+ .map(createAction);
36
+ }
37
+ : () => [],
38
+ isReadonlyAllowed: item.isReadonlyAllowed || false,
39
+ separator: item.separator || false,
40
+ icon: typeof icon === "function" ? icon : () => icon || "",
41
+ secondaryIcon: typeof secondaryIcon === "function" ? secondaryIcon : () => secondaryIcon || "",
42
+ description: typeof description === "function" ? description : () => description || "",
43
+ textColor: item.textColor,
44
+ sequence: item.sequence || 0,
45
+ onStartHover: item.onStartHover,
46
+ onStopHover: item.onStopHover,
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Registry
52
+ *
53
+ * The Registry class is basically just a mapping from a string key to an object.
54
+ * It is really not much more than an object. It is however useful for the
55
+ * following reasons:
56
+ *
57
+ * 1. it let us react and execute code when someone add something to the registry
58
+ * (for example, the FunctionRegistry subclass this for this purpose)
59
+ * 2. it throws an error when the get operation fails
60
+ * 3. it provides a chained API to add items to the registry.
61
+ */
62
+ class Registry {
63
+ content = {};
64
+ /**
65
+ * Add an item to the registry
66
+ *
67
+ * Note that this also returns the registry, so another add method call can
68
+ * be chained
69
+ */
70
+ add(key, value) {
71
+ this.content[key] = value;
72
+ return this;
73
+ }
74
+ /**
75
+ * Get an item from the registry
76
+ */
77
+ get(key) {
78
+ /**
79
+ * Note: key in {} is ~12 times slower than {}[key].
80
+ * So, we check the absence of key only when the direct access returns
81
+ * a falsy value. It's done to ensure that the registry can contains falsy values
82
+ */
83
+ const content = this.content[key];
84
+ if (!content) {
85
+ if (!(key in this.content)) {
86
+ throw new Error(`Cannot find ${key} in this registry!`);
87
+ }
88
+ }
89
+ return content;
90
+ }
91
+ /**
92
+ * Check if the key is already in the registry
93
+ */
94
+ contains(key) {
95
+ return key in this.content;
96
+ }
97
+ /**
98
+ * Get a list of all elements in the registry
99
+ */
100
+ getAll() {
101
+ return Object.values(this.content);
102
+ }
103
+ /**
104
+ * Get a list of all keys in the registry
105
+ */
106
+ getKeys() {
107
+ return Object.keys(this.content);
108
+ }
109
+ /**
110
+ * Remove an item from the registry
111
+ */
112
+ remove(key) {
113
+ delete this.content[key];
114
+ }
115
+ }
116
+
12
117
  const CANVAS_SHIFT = 0.5;
13
118
  // Colors
14
119
  const HIGHLIGHT_COLOR = "#37A850";
@@ -165,7 +270,7 @@ const DEFAULT_STYLE = {
165
270
  underline: false,
166
271
  fontSize: 10,
167
272
  fillColor: "",
168
- textColor: "#000000",
273
+ textColor: "",
169
274
  };
170
275
  const DEFAULT_VERTICAL_ALIGN = DEFAULT_STYLE.verticalAlign;
171
276
  const DEFAULT_WRAPPING_MODE = DEFAULT_STYLE.wrapping;
@@ -5397,110 +5502,6 @@ class UuidGenerator {
5397
5502
  }
5398
5503
  }
5399
5504
 
5400
- function createActions(menuItems) {
5401
- return menuItems.map(createAction).sort((a, b) => a.sequence - b.sequence);
5402
- }
5403
- const uuidGenerator$1 = new UuidGenerator();
5404
- function createAction(item) {
5405
- const name = item.name;
5406
- const children = item.children;
5407
- const description = item.description;
5408
- const icon = item.icon;
5409
- const secondaryIcon = item.secondaryIcon;
5410
- return {
5411
- id: item.id || uuidGenerator$1.uuidv4(),
5412
- name: typeof name === "function" ? name : () => name,
5413
- isVisible: item.isVisible ? item.isVisible : () => true,
5414
- isEnabled: item.isEnabled ? item.isEnabled : () => true,
5415
- isActive: item.isActive,
5416
- execute: item.execute,
5417
- children: children
5418
- ? (env) => {
5419
- return children
5420
- .map((child) => (typeof child === "function" ? child(env) : child))
5421
- .flat()
5422
- .map(createAction);
5423
- }
5424
- : () => [],
5425
- isReadonlyAllowed: item.isReadonlyAllowed || false,
5426
- separator: item.separator || false,
5427
- icon: typeof icon === "function" ? icon : () => icon || "",
5428
- secondaryIcon: typeof secondaryIcon === "function" ? secondaryIcon : () => secondaryIcon || "",
5429
- description: typeof description === "function" ? description : () => description || "",
5430
- textColor: item.textColor,
5431
- sequence: item.sequence || 0,
5432
- onStartHover: item.onStartHover,
5433
- onStopHover: item.onStopHover,
5434
- };
5435
- }
5436
-
5437
- /**
5438
- * Registry
5439
- *
5440
- * The Registry class is basically just a mapping from a string key to an object.
5441
- * It is really not much more than an object. It is however useful for the
5442
- * following reasons:
5443
- *
5444
- * 1. it let us react and execute code when someone add something to the registry
5445
- * (for example, the FunctionRegistry subclass this for this purpose)
5446
- * 2. it throws an error when the get operation fails
5447
- * 3. it provides a chained API to add items to the registry.
5448
- */
5449
- class Registry {
5450
- content = {};
5451
- /**
5452
- * Add an item to the registry
5453
- *
5454
- * Note that this also returns the registry, so another add method call can
5455
- * be chained
5456
- */
5457
- add(key, value) {
5458
- this.content[key] = value;
5459
- return this;
5460
- }
5461
- /**
5462
- * Get an item from the registry
5463
- */
5464
- get(key) {
5465
- /**
5466
- * Note: key in {} is ~12 times slower than {}[key].
5467
- * So, we check the absence of key only when the direct access returns
5468
- * a falsy value. It's done to ensure that the registry can contains falsy values
5469
- */
5470
- const content = this.content[key];
5471
- if (!content) {
5472
- if (!(key in this.content)) {
5473
- throw new Error(`Cannot find ${key} in this registry!`);
5474
- }
5475
- }
5476
- return content;
5477
- }
5478
- /**
5479
- * Check if the key is already in the registry
5480
- */
5481
- contains(key) {
5482
- return key in this.content;
5483
- }
5484
- /**
5485
- * Get a list of all elements in the registry
5486
- */
5487
- getAll() {
5488
- return Object.values(this.content);
5489
- }
5490
- /**
5491
- * Get a list of all keys in the registry
5492
- */
5493
- getKeys() {
5494
- return Object.keys(this.content);
5495
- }
5496
- /**
5497
- * Remove an item from the registry
5498
- */
5499
- remove(key) {
5500
- delete this.content[key];
5501
- }
5502
- }
5503
-
5504
5505
  function getClipboardDataPositions(sheetId, zones) {
5505
5506
  const lefts = new Set(zones.map((z) => z.left));
5506
5507
  const rights = new Set(zones.map((z) => z.right));
@@ -7279,31 +7280,34 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
7279
7280
  for (let col of columnsIndexes) {
7280
7281
  const position = { col, row, sheetId };
7281
7282
  const table = this.getters.getTable(position);
7282
- if (!table || copiedTablesIds.has(table.id)) {
7283
+ if (!table) {
7283
7284
  tableCellsInRow.push({});
7284
7285
  continue;
7285
7286
  }
7286
7287
  const coreTable = this.getters.getCoreTable(position);
7287
7288
  const tableZone = coreTable?.range.zone;
7289
+ let copiedTable = undefined;
7288
7290
  // Copy whole table
7289
- if (coreTable && tableZone && zones.some((z) => isZoneInside(tableZone, z))) {
7290
- copiedTablesIds.add(coreTable.id);
7291
+ if (!copiedTablesIds.has(table.id) &&
7292
+ coreTable &&
7293
+ tableZone &&
7294
+ zones.some((z) => isZoneInside(tableZone, z))) {
7295
+ copiedTablesIds.add(table.id);
7291
7296
  const values = [];
7292
7297
  for (const col of range(tableZone.left, tableZone.right + 1)) {
7293
7298
  values.push(this.getters.getFilterHiddenValues({ sheetId, col, row: tableZone.top }));
7294
7299
  }
7295
- tableCellsInRow.push({
7296
- table: {
7297
- range: coreTable.range,
7298
- config: coreTable.config,
7299
- type: coreTable.type,
7300
- },
7301
- });
7302
- }
7303
- // Copy only style of cell
7304
- else if (table) {
7305
- tableCellsInRow.push({ style: this.getTableStyleToCopy(position) });
7300
+ copiedTable = {
7301
+ range: coreTable.range,
7302
+ config: coreTable.config,
7303
+ type: coreTable.type,
7304
+ };
7306
7305
  }
7306
+ tableCellsInRow.push({
7307
+ table: copiedTable,
7308
+ style: this.getTableStyleToCopy(position),
7309
+ isWholeTableCopied: copiedTablesIds.has(table.id),
7310
+ });
7307
7311
  }
7308
7312
  }
7309
7313
  return {
@@ -7384,11 +7388,14 @@ class TableClipboardHandler extends AbstractCellClipboardHandler {
7384
7388
  tableType: tableCell.table.type,
7385
7389
  });
7386
7390
  }
7387
- // Do not paste table style if we're inside another table
7388
7391
  // We cannot check for dynamic tables, because at this point the paste can have changed the evaluation, and the
7389
7392
  // dynamic tables are not yet computed
7390
- if (!this.getters.getCoreTable(position)) {
7391
- if (tableCell.style?.style && options?.pasteOption !== "asValue") {
7393
+ if (this.getters.getCoreTable(position) || options?.pasteOption === "asValue") {
7394
+ return;
7395
+ }
7396
+ if ((!options?.pasteOption && !tableCell.isWholeTableCopied) ||
7397
+ options?.pasteOption === "onlyFormat") {
7398
+ if (tableCell.style?.style) {
7392
7399
  this.dispatch("UPDATE_CELL", { ...position, style: tableCell.style.style });
7393
7400
  }
7394
7401
  if (tableCell.style?.border) {
@@ -19154,9 +19161,10 @@ const PIVOT_VALUE = {
19154
19161
  return error;
19155
19162
  }
19156
19163
  if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
19164
+ const suggestion = _t("Consider using a dynamic pivot formula: %s. Or re-insert the static pivot from the Data menu.", `=PIVOT(${_pivotFormulaId})`);
19157
19165
  return {
19158
19166
  value: CellErrorType.GenericError,
19159
- message: _t("Dimensions don't match the pivot definition"),
19167
+ message: _t("Dimensions don't match the pivot definition") + ". " + suggestion,
19160
19168
  };
19161
19169
  }
19162
19170
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
@@ -19183,9 +19191,10 @@ const PIVOT_HEADER = {
19183
19191
  return error;
19184
19192
  }
19185
19193
  if (!pivot.areDomainArgsFieldsValid(domainArgs)) {
19194
+ const suggestion = _t("Consider using a dynamic pivot formula: %s. Or re-insert the static pivot from the Data menu.", `=PIVOT(${_pivotFormulaId})`);
19186
19195
  return {
19187
19196
  value: CellErrorType.GenericError,
19188
- message: _t("Dimensions don't match the pivot definition"),
19197
+ message: _t("Dimensions don't match the pivot definition") + ". " + suggestion,
19189
19198
  };
19190
19199
  }
19191
19200
  const domain = pivot.parseArgsToPivotDomain(domainArgs);
@@ -22361,6 +22370,7 @@ function insertTokenAfterArgSeparator(tokenAtCursor, value) {
22361
22370
  // replace the whole token
22362
22371
  start = tokenAtCursor.start;
22363
22372
  }
22373
+ this.composer.stopComposerRangeSelection();
22364
22374
  this.composer.changeComposerCursorSelection(start, end);
22365
22375
  this.composer.replaceComposerCursorSelection(value);
22366
22376
  }
@@ -22378,6 +22388,7 @@ function insertTokenAfterLeftParenthesis(tokenAtCursor, value) {
22378
22388
  // replace the whole token
22379
22389
  start = tokenAtCursor.start;
22380
22390
  }
22391
+ this.composer.stopComposerRangeSelection();
22381
22392
  this.composer.changeComposerCursorSelection(start, end);
22382
22393
  this.composer.replaceComposerCursorSelection(value);
22383
22394
  }
@@ -37193,6 +37204,8 @@ class PivotSidePanelStore extends SpreadsheetStore {
37193
37204
  mutators = ["reset", "deferUpdates", "applyUpdate", "discardPendingUpdate", "update"];
37194
37205
  updatesAreDeferred = false;
37195
37206
  draft = null;
37207
+ notification = this.get(NotificationStore);
37208
+ alreadyNotified = false;
37196
37209
  constructor(get, pivotId) {
37197
37210
  super(get);
37198
37211
  this.pivotId = pivotId;
@@ -37299,6 +37312,16 @@ class PivotSidePanelStore extends SpreadsheetStore {
37299
37312
  pivot: this.draft,
37300
37313
  });
37301
37314
  this.draft = null;
37315
+ if (!this.alreadyNotified && !this.isDynamicPivotInViewport()) {
37316
+ const formulaId = this.getters.getPivotFormulaId(this.pivotId);
37317
+ const pivotExample = `=PIVOT(${formulaId})`;
37318
+ this.alreadyNotified = true;
37319
+ this.notification.notifyUser({
37320
+ type: "info",
37321
+ text: _t("Pivot updates only work with dynamic pivot tables. Use %s or re-insert the static pivot from the Data menu.", pivotExample),
37322
+ sticky: false,
37323
+ });
37324
+ }
37302
37325
  }
37303
37326
  }
37304
37327
  discardPendingUpdate() {
@@ -37329,15 +37352,22 @@ class PivotSidePanelStore extends SpreadsheetStore {
37329
37352
  return;
37330
37353
  }
37331
37354
  const cleanedWithGranularity = this.addDefaultDateTimeGranularity(this.fields, cleanedDefinition);
37332
- if (this.updatesAreDeferred) {
37333
- this.draft = cleanedWithGranularity;
37355
+ this.draft = cleanedWithGranularity;
37356
+ if (!this.updatesAreDeferred) {
37357
+ this.applyUpdate();
37334
37358
  }
37335
- else {
37336
- this.model.dispatch("UPDATE_PIVOT", {
37337
- pivotId: this.pivotId,
37338
- pivot: cleanedWithGranularity,
37339
- });
37359
+ }
37360
+ isDynamicPivotInViewport() {
37361
+ const sheetId = this.getters.getActiveSheetId();
37362
+ for (const col of this.getters.getSheetViewVisibleCols()) {
37363
+ for (const row of this.getters.getSheetViewVisibleRows()) {
37364
+ const isDynamicPivot = this.getters.isSpillPivotFormula({ sheetId, col, row });
37365
+ if (isDynamicPivot) {
37366
+ return true;
37367
+ }
37368
+ }
37340
37369
  }
37370
+ return false;
37341
37371
  }
37342
37372
  addDefaultDateTimeGranularity(fields, definition) {
37343
37373
  const { columns, rows } = definition;
@@ -52428,7 +52458,7 @@ class PivotCorePlugin extends CorePlugin {
52428
52458
  case "DUPLICATE_PIVOT": {
52429
52459
  const { pivotId, newPivotId } = cmd;
52430
52460
  const pivot = deepCopy(this.getPivotCore(pivotId).definition);
52431
- pivot.name = _t("%s (copy)", pivot.name);
52461
+ pivot.name = cmd.duplicatedPivotName ?? pivot.name + " (copy)";
52432
52462
  this.addPivot(newPivotId, pivot);
52433
52463
  break;
52434
52464
  }
@@ -57517,6 +57547,7 @@ class Session extends EventBus {
57517
57547
  case "REMOTE_REVISION":
57518
57548
  case "REVISION_REDONE":
57519
57549
  case "REVISION_UNDONE":
57550
+ case "SNAPSHOT_CREATED":
57520
57551
  return this.processedRevisions.has(message.nextRevisionId);
57521
57552
  default:
57522
57553
  return false;
@@ -58032,12 +58063,13 @@ class InsertPivotPlugin extends UIPlugin {
58032
58063
  this.dispatch("DUPLICATE_PIVOT", {
58033
58064
  pivotId,
58034
58065
  newPivotId,
58066
+ duplicatedPivotName: _t("%s (copy)", this.getters.getPivotCoreDefinition(pivotId).name),
58035
58067
  });
58036
58068
  const activeSheetId = this.getters.getActiveSheetId();
58037
58069
  const position = this.getters.getSheetIds().indexOf(activeSheetId) + 1;
58038
58070
  const formulaId = this.getters.getPivotFormulaId(newPivotId);
58039
58071
  const newPivotName = this.getters.getPivotName(newPivotId);
58040
- this.dispatch("CREATE_SHEET", {
58072
+ const result = this.dispatch("CREATE_SHEET", {
58041
58073
  sheetId: newSheetId,
58042
58074
  name: this.getPivotDuplicateSheetName(_t("%(newPivotName)s (Pivot #%(formulaId)s)", {
58043
58075
  newPivotName,
@@ -58045,20 +58077,23 @@ class InsertPivotPlugin extends UIPlugin {
58045
58077
  })),
58046
58078
  position,
58047
58079
  });
58048
- this.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: newSheetId });
58049
- this.dispatch("UPDATE_CELL", {
58050
- sheetId: newSheetId,
58051
- col: 0,
58052
- row: 0,
58053
- content: `=PIVOT(${formulaId})`,
58054
- });
58080
+ if (result.isSuccessful) {
58081
+ this.dispatch("ACTIVATE_SHEET", { sheetIdFrom: activeSheetId, sheetIdTo: newSheetId });
58082
+ this.dispatch("UPDATE_CELL", {
58083
+ sheetId: newSheetId,
58084
+ col: 0,
58085
+ row: 0,
58086
+ content: `=PIVOT(${formulaId})`,
58087
+ });
58088
+ }
58055
58089
  }
58056
58090
  getPivotDuplicateSheetName(pivotName) {
58057
58091
  let i = 1;
58058
58092
  const names = this.getters.getSheetIds().map((id) => this.getters.getSheetName(id));
58059
- let name = pivotName;
58093
+ const sanitizedName = pivotName.replace(new RegExp(FORBIDDEN_IN_EXCEL_REGEX, "g"), " ");
58094
+ let name = sanitizedName;
58060
58095
  while (names.includes(name)) {
58061
- name = `${pivotName} (${i})`;
58096
+ name = `${sanitizedName} (${i})`;
58062
58097
  i++;
58063
58098
  }
58064
58099
  return name;
@@ -58639,9 +58674,12 @@ const invalidateTableStyleCommands = [
58639
58674
  "HIDE_COLUMNS_ROWS",
58640
58675
  "UNHIDE_COLUMNS_ROWS",
58641
58676
  "UNFOLD_HEADER_GROUP",
58677
+ "UNGROUP_HEADERS",
58642
58678
  "FOLD_HEADER_GROUP",
58643
58679
  "FOLD_ALL_HEADER_GROUPS",
58644
58680
  "UNFOLD_ALL_HEADER_GROUPS",
58681
+ "FOLD_HEADER_GROUPS_IN_ZONE",
58682
+ "UNFOLD_HEADER_GROUPS_IN_ZONE",
58645
58683
  "CREATE_TABLE",
58646
58684
  "UPDATE_TABLE",
58647
58685
  "UPDATE_FILTER",
@@ -59957,6 +59995,8 @@ class FilterEvaluationPlugin extends UIPlugin {
59957
59995
  case "UNFOLD_HEADER_GROUP":
59958
59996
  case "FOLD_ALL_HEADER_GROUPS":
59959
59997
  case "UNFOLD_ALL_HEADER_GROUPS":
59998
+ case "FOLD_HEADER_GROUPS_IN_ZONE":
59999
+ case "UNFOLD_HEADER_GROUPS_IN_ZONE":
59960
60000
  this.updateHiddenRows(cmd.sheetId);
59961
60001
  break;
59962
60002
  case "UPDATE_FILTER":
@@ -65903,7 +65943,9 @@ class StateObserver {
65903
65943
  * The value does not matter, it can be hardcoded.
65904
65944
  */
65905
65945
  const catAxId = 17781237;
65946
+ const secondaryCatAxId = 17781238;
65906
65947
  const valAxId = 88853993;
65948
+ const secondaryValAxId = 88853994;
65907
65949
  function createChart(chart, chartSheetIndex, data) {
65908
65950
  const namespaces = [
65909
65951
  ["xmlns:r", RELATIONSHIP_NSR],
@@ -66219,8 +66261,8 @@ function addComboChart(chart) {
66219
66261
  <!-- each data marker in the series does not have a different color -->
66220
66262
  <c:varyColors val="0"/>
66221
66263
  ${barDataSetNode}
66222
- <c:axId val="${catAxId + (useRightAxisForBarSerie ? 1 : 0)}" />
66223
- <c:axId val="${valAxId + (useRightAxisForBarSerie ? 1 : 0)}" />
66264
+ <c:axId val="${useRightAxisForBarSerie ? secondaryCatAxId : catAxId}" />
66265
+ <c:axId val="${useRightAxisForBarSerie ? secondaryValAxId : valAxId}" />
66224
66266
  </c:barChart>
66225
66267
  ${leftDataSetsNodes.length
66226
66268
  ? escapeXml /*xml*/ `
@@ -66241,21 +66283,21 @@ function addComboChart(chart) {
66241
66283
  <!-- each data marker in the series does not have a different color -->
66242
66284
  <c:varyColors val="0"/>
66243
66285
  ${joinXmlNodes(rightDataSetsNodes)}
66244
- <c:axId val="${catAxId + 1}" />
66245
- <c:axId val="${valAxId + 1}" />
66286
+ <c:axId val="${secondaryCatAxId}" />
66287
+ <c:axId val="${secondaryValAxId}" />
66246
66288
  </c:lineChart>
66247
66289
  `
66248
66290
  : ""}
66249
66291
  ${!useRightAxisForBarSerie || leftDataSetsNodes.length
66250
66292
  ? escapeXml /*xml*/ `
66251
- ${addAx("b", "c:catAx", catAxId + 1, valAxId + 1, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length ? 1 : 0)}
66252
- ${addAx("r", "c:valAx", valAxId + 1, catAxId + 1, chart.axesDesign?.y1?.title, chart.fontColor)}
66293
+ ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length ? 1 : 0)}
66294
+ ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
66253
66295
  `
66254
66296
  : ""}
66255
66297
  ${useRightAxisForBarSerie || rightDataSetsNodes.length
66256
66298
  ? escapeXml /*xml*/ `
66257
- ${addAx("b", "c:catAx", catAxId, valAxId, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length || !useRightAxisForBarSerie ? 1 : 0)}
66258
- ${addAx("l", "c:valAx", valAxId, catAxId, chart.axesDesign?.y?.title, chart.fontColor)}
66299
+ ${addAx("b", "c:catAx", secondaryCatAxId, secondaryValAxId, chart.axesDesign?.x?.title, chart.fontColor, leftDataSetsNodes.length || !useRightAxisForBarSerie ? 1 : 0)}
66300
+ ${addAx("r", "c:valAx", secondaryValAxId, secondaryCatAxId, chart.axesDesign?.y1?.title, chart.fontColor)}
66259
66301
  `
66260
66302
  : ""}
66261
66303
  `;
@@ -67276,6 +67318,15 @@ function addTableColumns(table, sheetData) {
67276
67318
  ["id", i + 1], // id cannot be 0
67277
67319
  ["name", colName],
67278
67320
  ];
67321
+ if (table.config.totalRow) {
67322
+ // Note: To be 100% complete, we could also add a `totalsRowLabel` attribute for total strings, and a tag
67323
+ // `<totalsRowFormula>` for the formula of the total. But those doesn't seem to be mandatory for Excel.
67324
+ const colTotalXc = toXC(tableZone.left + i, tableZone.bottom);
67325
+ const colTotalContent = sheetData.cells[colTotalXc]?.content;
67326
+ if (colTotalContent?.startsWith("=")) {
67327
+ colAttributes.push(["totalsRowFunction", "custom"]);
67328
+ }
67329
+ }
67279
67330
  columns.push(escapeXml /*xml*/ `<tableColumn ${formatAttributes(colAttributes)}/>`);
67280
67331
  }
67281
67332
  return escapeXml /*xml*/ `
@@ -67370,8 +67421,9 @@ function addRows(construct, data, sheet) {
67370
67421
  }
67371
67422
  else if (cell.content && cell.content !== "") {
67372
67423
  const isTableHeader = isCellTableHeader(c, r, sheet);
67424
+ const isTableTotal = isCellTableTotal(c, r, sheet);
67373
67425
  const isPlainText = !!(cell.format && data.formats[cell.format] === PLAIN_TEXT_FORMAT);
67374
- ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isPlainText));
67426
+ ({ attrs: additionalAttrs, node: cellNode } = addContent(cell.content, construct.sharedStrings, isTableHeader || isTableTotal || isPlainText));
67375
67427
  }
67376
67428
  attributes.push(...additionalAttrs);
67377
67429
  // prettier-ignore
@@ -67405,6 +67457,16 @@ function isCellTableHeader(col, row, sheet) {
67405
67457
  return isInside(col, row, headerZone);
67406
67458
  });
67407
67459
  }
67460
+ function isCellTableTotal(col, row, sheet) {
67461
+ return sheet.tables.some((table) => {
67462
+ if (!table.config.totalRow) {
67463
+ return false;
67464
+ }
67465
+ const zone = toZone(table.range);
67466
+ const totalZone = { ...zone, top: zone.bottom };
67467
+ return isInside(col, row, totalZone);
67468
+ });
67469
+ }
67408
67470
  function addHyperlinks(construct, data, sheetIndex) {
67409
67471
  const sheet = data.sheets[sheetIndex];
67410
67472
  const cells = sheet.cells;
@@ -67883,7 +67945,6 @@ class Model extends EventBus {
67883
67945
  isReadonly: () => this.config.mode === "readonly" || this.config.mode === "dashboard",
67884
67946
  isDashboard: () => this.config.mode === "dashboard",
67885
67947
  };
67886
- this.uuidGenerator.setIsFastStrategy(true);
67887
67948
  // Initiate stream processor
67888
67949
  this.selection = new SelectionStreamProcessorImpl(this.getters);
67889
67950
  this.coreHandlers.push(this.range);
@@ -68504,6 +68565,6 @@ const constants = {
68504
68565
  export { AbstractCellClipboardHandler, AbstractChart, AbstractFigureClipboardHandler, CellErrorType, CommandResult, CorePlugin, DispatchResult, EvaluationError, Model, PivotRuntimeDefinition, Registry, Revision, SPREADSHEET_DIMENSIONS, Spreadsheet, SpreadsheetPivotTable, UIPlugin, __info__, addFunction, addRenderingLayer, astToFormula, compile, compileTokens, components, constants, convertAstNodes, coreTypes, findCellInNewZone, functionCache, helpers, hooks, invalidateCFEvaluationCommands, invalidateDependenciesCommands, invalidateEvaluationCommands, iterateAstNodes, links, load, parse, parseTokens, readonlyAllowedCommands, registries, setDefaultSheetViewSize, setTranslationMethod, stores, tokenColors, tokenize };
68505
68566
 
68506
68567
 
68507
- __info__.version = "17.4.8";
68508
- __info__.date = "2024-09-17T08:10:18.777Z";
68509
- __info__.hash = "caad8bd";
68568
+ __info__.version = "17.4.9";
68569
+ __info__.date = "2024-10-14T07:53:51.633Z";
68570
+ __info__.hash = "b7015b7";