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