@odoo/o-spreadsheet 17.3.0-alpha.5 → 17.3.0-alpha.6

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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.3.0-alpha.5
7
- * @date 2024-04-18T14:38:57.166Z
8
- * @hash cff0ef9
6
+ * @version 17.3.0-alpha.6
7
+ * @date 2024-04-26T07:39:53.611Z
8
+ * @hash f58a0d5
9
9
  */
10
10
 
11
11
  (function (exports, owl) {
@@ -1990,6 +1990,8 @@
1990
1990
  "CREATE_TABLE",
1991
1991
  "REMOVE_TABLE",
1992
1992
  "UPDATE_TABLE",
1993
+ "CREATE_TABLE_STYLE",
1994
+ "REMOVE_TABLE_STYLE",
1993
1995
  /** IMAGE */
1994
1996
  "CREATE_IMAGE",
1995
1997
  /** HEADER GROUP */
@@ -2136,6 +2138,7 @@
2136
2138
  CommandResult["TableNotFound"] = "TableNotFound";
2137
2139
  CommandResult["TableOverlap"] = "TableOverlap";
2138
2140
  CommandResult["InvalidTableConfig"] = "InvalidTableConfig";
2141
+ CommandResult["InvalidTableStyle"] = "InvalidTableStyle";
2139
2142
  CommandResult["FilterNotFound"] = "FilterNotFound";
2140
2143
  CommandResult["MergeInTable"] = "MergeInTable";
2141
2144
  CommandResult["NonContinuousTargets"] = "NonContinuousTargets";
@@ -18139,6 +18142,9 @@ stores.inject(MyMetaStore, storeInstance);
18139
18142
  ],
18140
18143
  returns: ["NUMBER"],
18141
18144
  compute: function (cellReference) {
18145
+ if (isEvaluationError(cellReference?.value)) {
18146
+ throw cellReference;
18147
+ }
18142
18148
  const _cellReference = cellReference === undefined ? this.__originCellXC?.() : cellReference.value;
18143
18149
  assert(() => !!_cellReference, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18144
18150
  const zone = toZone(_cellReference);
@@ -18154,6 +18160,9 @@ stores.inject(MyMetaStore, storeInstance);
18154
18160
  args: [arg("range (meta)", _t("The range whose column count will be returned."))],
18155
18161
  returns: ["NUMBER"],
18156
18162
  compute: function (range) {
18163
+ if (isEvaluationError(range?.value)) {
18164
+ throw range;
18165
+ }
18157
18166
  const zone = toZone(range.value);
18158
18167
  return zone.right - zone.left + 1;
18159
18168
  },
@@ -18376,6 +18385,9 @@ stores.inject(MyMetaStore, storeInstance);
18376
18385
  ],
18377
18386
  returns: ["NUMBER"],
18378
18387
  compute: function (cellReference) {
18388
+ if (isEvaluationError(cellReference?.value)) {
18389
+ throw cellReference;
18390
+ }
18379
18391
  const _cellReference = cellReference === undefined ? this.__originCellXC?.() : cellReference.value;
18380
18392
  assert(() => !!_cellReference, "In this context, the function [[FUNCTION_NAME]] needs to have a cell or range in parameter.");
18381
18393
  const zone = toZone(_cellReference);
@@ -18391,6 +18403,9 @@ stores.inject(MyMetaStore, storeInstance);
18391
18403
  args: [arg("range (meta)", _t("The range whose row count will be returned."))],
18392
18404
  returns: ["NUMBER"],
18393
18405
  compute: function (range) {
18406
+ if (isEvaluationError(range?.value)) {
18407
+ throw range;
18408
+ }
18394
18409
  const zone = toZone(range.value);
18395
18410
  return zone.bottom - zone.top + 1;
18396
18411
  },
@@ -20343,17 +20358,13 @@ stores.inject(MyMetaStore, storeInstance);
20343
20358
  this.DOMFocusableElementStore.setFocusableElement(el);
20344
20359
  }
20345
20360
  this.contentHelper.updateEl(el);
20346
- this.processTokenAtCursor();
20347
20361
  });
20348
20362
  owl.useEffect(() => {
20349
20363
  this.processContent();
20350
20364
  });
20351
- owl.onPatched(() => {
20352
- // Required because typing '=SUM' and double-clicking another cell leaves ShowProvider/ShowDescription true
20353
- if (this.composerStore.editionMode === "inactive") {
20354
- this.processTokenAtCursor();
20355
- }
20356
- });
20365
+ owl.useEffect(() => {
20366
+ this.processTokenAtCursor();
20367
+ }, () => [this.composerStore.editionMode !== "inactive"]);
20357
20368
  }
20358
20369
  // ---------------------------------------------------------------------------
20359
20370
  // Handlers
@@ -20955,13 +20966,10 @@ stores.inject(MyMetaStore, storeInstance);
20955
20966
  const isRangeOnly = argTypes.every((t) => isRangeType(t));
20956
20967
  if (isRangeOnly) {
20957
20968
  if (!isRangeInput(currentArg)) {
20958
- throw new BadExpressionError(_t("Function %s expects the parameter %s to be reference to a cell or range, not a %s.", functionName, (i + 1).toString(), currentArg.type.toLowerCase()));
20969
+ throw new BadExpressionError(_t("Function %(function_name)s expects the parameter %(arg_index)s to be a reference to a cell or a range.", { function_name: functionName, arg_index: i + 1 }));
20959
20970
  }
20960
20971
  }
20961
- compiledArgs.push(compileAST(currentArg, isMeta, hasRange, {
20962
- functionName,
20963
- paramIndex: i + 1,
20964
- }));
20972
+ compiledArgs.push(compileAST(currentArg, isMeta, hasRange));
20965
20973
  }
20966
20974
  return compiledArgs;
20967
20975
  }
@@ -20977,7 +20985,7 @@ stores.inject(MyMetaStore, storeInstance);
20977
20985
  * function needs to receive as argument the coordinates of a cell rather
20978
20986
  * than its value. For this we have meta arguments.
20979
20987
  */
20980
- function compileAST(ast, isMeta = false, hasRange = false, referenceVerification = {}) {
20988
+ function compileAST(ast, isMeta = false, hasRange = false) {
20981
20989
  const code = new FunctionCodeBuilder(scope);
20982
20990
  if (ast.type !== "REFERENCE" && !(ast.type === "BIN_OPERATION" && ast.value === ":")) {
20983
20991
  if (isMeta) {
@@ -20996,11 +21004,11 @@ stores.inject(MyMetaStore, storeInstance);
20996
21004
  return code.return(`{ value: this.constantValues.strings[${constantValues.strings.indexOf(ast.value)}] }`);
20997
21005
  case "REFERENCE":
20998
21006
  const referenceIndex = dependencies.indexOf(ast.value);
20999
- if (hasRange) {
21007
+ if ((!isMeta && ast.value.includes(":")) || hasRange) {
21000
21008
  return code.return(`range(deps[${referenceIndex}])`);
21001
21009
  }
21002
21010
  else {
21003
- return code.return(`ref(deps[${referenceIndex}], ${isMeta ? "true" : "false"}, "${referenceVerification.functionName || OPERATOR_MAP["="]}", ${referenceVerification.paramIndex})`);
21011
+ return code.return(`ref(deps[${referenceIndex}], ${isMeta ? "true" : "false"})`);
21004
21012
  }
21005
21013
  case "FUNCALL":
21006
21014
  const args = compileFunctionArgs(ast).map((arg) => arg.assignResultToVariable());
@@ -21009,20 +21017,14 @@ stores.inject(MyMetaStore, storeInstance);
21009
21017
  return code.return(`ctx['${fnName}'](${args.map((arg) => arg.returnExpression)})`);
21010
21018
  case "UNARY_OPERATION": {
21011
21019
  const fnName = UNARY_OPERATOR_MAP[ast.value];
21012
- const operand = compileAST(ast.operand, false, false, {
21013
- functionName: fnName,
21014
- }).assignResultToVariable();
21020
+ const operand = compileAST(ast.operand, false, false).assignResultToVariable();
21015
21021
  code.append(operand);
21016
21022
  return code.return(`ctx['${fnName}'](${operand.returnExpression})`);
21017
21023
  }
21018
21024
  case "BIN_OPERATION": {
21019
21025
  const fnName = OPERATOR_MAP[ast.value];
21020
- const left = compileAST(ast.left, false, false, {
21021
- functionName: fnName,
21022
- }).assignResultToVariable();
21023
- const right = compileAST(ast.right, false, false, {
21024
- functionName: fnName,
21025
- }).assignResultToVariable();
21026
+ const left = compileAST(ast.left, false, false).assignResultToVariable();
21027
+ const right = compileAST(ast.right, false, false).assignResultToVariable();
21026
21028
  code.append(left);
21027
21029
  code.append(right);
21028
21030
  return code.return(`ctx['${fnName}'](${left.returnExpression}, ${right.returnExpression})`);
@@ -21060,7 +21062,10 @@ stores.inject(MyMetaStore, storeInstance);
21060
21062
  return `|N${constantValues.numbers.indexOf(parseNumber(token.value, DEFAULT_LOCALE))}|`;
21061
21063
  case "REFERENCE":
21062
21064
  case "INVALID_REFERENCE":
21063
- return `|${dependencies.indexOf(token.value)}|`;
21065
+ if (token.value.includes(":")) {
21066
+ return `R|${dependencies.indexOf(token.value)}|`;
21067
+ }
21068
+ return `C|${dependencies.indexOf(token.value)}|`;
21064
21069
  case "SPACE":
21065
21070
  return "";
21066
21071
  default:
@@ -21195,6 +21200,7 @@ stores.inject(MyMetaStore, storeInstance);
21195
21200
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
21196
21201
  char: ["count_distinct", "count"],
21197
21202
  many2one: ["count_distinct", "count"],
21203
+ reference: ["count_distinct", "count"],
21198
21204
  };
21199
21205
  const AGGREGATORS = {};
21200
21206
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -24362,7 +24368,7 @@ stores.inject(MyMetaStore, storeInstance);
24362
24368
  });
24363
24369
  onFigureDeleted();
24364
24370
  },
24365
- icon: "o-spreadsheet-Icon.DELETE",
24371
+ icon: "o-spreadsheet-Icon.TRASH",
24366
24372
  };
24367
24373
  }
24368
24374
 
@@ -24377,7 +24383,8 @@ stores.inject(MyMetaStore, storeInstance);
24377
24383
  .add("CREATE_FIGURE", inverseCreateFigure)
24378
24384
  .add("CREATE_CHART", inverseCreateChart)
24379
24385
  .add("HIDE_COLUMNS_ROWS", inverseHideColumnsRows)
24380
- .add("UNHIDE_COLUMNS_ROWS", inverseUnhideColumnsRows);
24386
+ .add("UNHIDE_COLUMNS_ROWS", inverseUnhideColumnsRows)
24387
+ .add("CREATE_TABLE_STYLE", inverseCreateTableStyle);
24381
24388
  for (const cmd of coreTypes.values()) {
24382
24389
  if (!inverseCommandRegistry.contains(cmd)) {
24383
24390
  inverseCommandRegistry.add(cmd, identity);
@@ -24462,6 +24469,9 @@ stores.inject(MyMetaStore, storeInstance);
24462
24469
  },
24463
24470
  ];
24464
24471
  }
24472
+ function inverseCreateTableStyle(cmd) {
24473
+ return [{ type: "REMOVE_TABLE_STYLE", tableStyleId: cmd.tableStyleId }];
24474
+ }
24465
24475
 
24466
24476
  /**
24467
24477
  * The class Registry is extended in order to add the function addChild
@@ -25059,7 +25069,7 @@ stores.inject(MyMetaStore, storeInstance);
25059
25069
 
25060
25070
  .o-search-icon {
25061
25071
  right: 5px;
25062
- top: 4px;
25072
+ top: 3px;
25063
25073
  opacity: 0.4;
25064
25074
 
25065
25075
  svg {
@@ -25146,8 +25156,12 @@ stores.inject(MyMetaStore, storeInstance);
25146
25156
  });
25147
25157
  this.state.values = this.getFilterHiddenValues(this.props.filterPosition);
25148
25158
  }
25149
- get isReadonly() {
25150
- return this.env.model.getters.isReadonly();
25159
+ get isSortable() {
25160
+ if (!this.table) {
25161
+ return false;
25162
+ }
25163
+ const coreTable = this.env.model.getters.getCoreTableMatchingTopLeft(this.table.range.sheetId, this.table.range.zone);
25164
+ return !this.env.model.getters.isReadonly() && coreTable?.type !== "dynamic";
25151
25165
  }
25152
25166
  getFilterHiddenValues(position) {
25153
25167
  const sheetId = this.env.model.getters.getActiveSheetId();
@@ -26117,10 +26131,10 @@ stores.inject(MyMetaStore, storeInstance);
26117
26131
  }
26118
26132
 
26119
26133
  const TABLE_STYLE_CATEGORIES = {
26120
- none: _t("None"),
26121
26134
  light: _t("Light"),
26122
26135
  medium: _t("Medium"),
26123
26136
  dark: _t("Dark"),
26137
+ custom: _t("Custom"),
26124
26138
  };
26125
26139
  const DEFAULT_TABLE_CONFIG = {
26126
26140
  hasFilters: true,
@@ -26133,7 +26147,7 @@ stores.inject(MyMetaStore, storeInstance);
26133
26147
  automaticAutofill: true,
26134
26148
  styleId: "TableStyleMedium2",
26135
26149
  };
26136
- function generateColorSet(name, highlightColor) {
26150
+ function generateTableColorSet(name, highlightColor) {
26137
26151
  return {
26138
26152
  coloredText: darkenColor(highlightColor, 0.3),
26139
26153
  light: lightenColor(highlightColor, 0.8),
@@ -26154,10 +26168,10 @@ stores.inject(MyMetaStore, storeInstance);
26154
26168
  mediumBorder: "#000000",
26155
26169
  highlight: "#000000",
26156
26170
  },
26157
- lightBlue: generateColorSet(_t("Light blue"), "#346B90"),
26158
- red: generateColorSet(_t("Red"), "#C53628"),
26159
- lightGreen: generateColorSet(_t("Light green"), "#748747"),
26160
- purple: generateColorSet(_t("Purple"), "#6C4E65"),
26171
+ lightBlue: generateTableColorSet(_t("Light blue"), "#346B90"),
26172
+ red: generateTableColorSet(_t("Red"), "#C53628"),
26173
+ lightGreen: generateTableColorSet(_t("Light green"), "#748747"),
26174
+ purple: generateTableColorSet(_t("Purple"), "#6C4E65"),
26161
26175
  gray: {
26162
26176
  name: _t("Gray"),
26163
26177
  coloredText: "#666666",
@@ -26167,7 +26181,7 @@ stores.inject(MyMetaStore, storeInstance);
26167
26181
  mediumBorder: "#D0D0D0",
26168
26182
  highlight: "#A9A9A9",
26169
26183
  },
26170
- orange: generateColorSet(_t("Orange"), "#C37034"),
26184
+ orange: generateTableColorSet(_t("Orange"), "#C37034"),
26171
26185
  };
26172
26186
  const DARK_COLOR_SETS = {
26173
26187
  black: COLOR_SETS.black,
@@ -26175,9 +26189,10 @@ stores.inject(MyMetaStore, storeInstance);
26175
26189
  purpleGreen: { ...COLOR_SETS.lightGreen, highlight: COLOR_SETS.purple.highlight },
26176
26190
  redBlue: { ...COLOR_SETS.lightBlue, highlight: COLOR_SETS.red.highlight },
26177
26191
  };
26178
- const lightTemplateColoredText = (colorSet) => ({
26192
+ const lightColoredText = (colorSet) => ({
26179
26193
  category: "light",
26180
- colorName: colorSet.name,
26194
+ templateName: "lightColoredText",
26195
+ primaryColor: colorSet.highlight,
26181
26196
  wholeTable: {
26182
26197
  style: { textColor: colorSet.coloredText },
26183
26198
  border: {
@@ -26189,9 +26204,10 @@ stores.inject(MyMetaStore, storeInstance);
26189
26204
  totalRow: { border: { top: { color: colorSet.highlight, style: "thin" } } },
26190
26205
  firstRowStripe: { style: { fillColor: colorSet.light } },
26191
26206
  });
26192
- const lightTemplateWithHeader = (colorSet) => ({
26207
+ const lightWithHeader = (colorSet) => ({
26193
26208
  category: "light",
26194
- colorName: colorSet.name,
26209
+ templateName: "lightWithHeader",
26210
+ primaryColor: colorSet.highlight,
26195
26211
  wholeTable: {
26196
26212
  border: {
26197
26213
  top: { color: colorSet.highlight, style: "thin" },
@@ -26208,9 +26224,10 @@ stores.inject(MyMetaStore, storeInstance);
26208
26224
  firstRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
26209
26225
  secondRowStripe: { border: { bottom: { color: colorSet.highlight, style: "thin" } } },
26210
26226
  });
26211
- const lightTemplateAllBorders = (colorSet) => ({
26227
+ const lightAllBorders = (colorSet) => ({
26212
26228
  category: "light",
26213
- colorName: colorSet.name,
26229
+ templateName: "lightAllBorders",
26230
+ primaryColor: colorSet.highlight,
26214
26231
  wholeTable: {
26215
26232
  border: {
26216
26233
  top: { color: colorSet.highlight, style: "thin" },
@@ -26226,9 +26243,10 @@ stores.inject(MyMetaStore, storeInstance);
26226
26243
  firstRowStripe: { style: { fillColor: colorSet.light } },
26227
26244
  firstColumnStripe: { style: { fillColor: colorSet.light } },
26228
26245
  });
26229
- const mediumTemplateBandedBorders = (colorSet) => ({
26246
+ const mediumBandedBorders = (colorSet) => ({
26230
26247
  category: "medium",
26231
- colorName: colorSet.name,
26248
+ templateName: "mediumBandedBorders",
26249
+ primaryColor: colorSet.highlight,
26232
26250
  wholeTable: {
26233
26251
  border: {
26234
26252
  top: { color: colorSet.mediumBorder, style: "thin" },
@@ -26245,9 +26263,10 @@ stores.inject(MyMetaStore, storeInstance);
26245
26263
  firstRowStripe: { style: { fillColor: colorSet.light } },
26246
26264
  firstColumnStripe: { style: { fillColor: colorSet.light } },
26247
26265
  });
26248
- const mediumTemplateWhiteBorders = (colorSet) => ({
26266
+ const mediumWhiteBorders = (colorSet) => ({
26249
26267
  category: "medium",
26250
- colorName: colorSet.name,
26268
+ templateName: "mediumWhiteBorders",
26269
+ primaryColor: colorSet.highlight,
26251
26270
  wholeTable: {
26252
26271
  border: {
26253
26272
  horizontal: { color: "#FFFFFF", style: "thin" },
@@ -26268,9 +26287,10 @@ stores.inject(MyMetaStore, storeInstance);
26268
26287
  firstRowStripe: { style: { fillColor: colorSet.medium } },
26269
26288
  firstColumnStripe: { style: { fillColor: colorSet.medium } },
26270
26289
  });
26271
- const mediumTemplateMinimalBorders = (colorSet) => ({
26290
+ const mediumMinimalBorders = (colorSet) => ({
26272
26291
  category: "medium",
26273
- colorName: colorSet.name,
26292
+ templateName: "mediumMinimalBorders",
26293
+ primaryColor: colorSet.highlight,
26274
26294
  wholeTable: {
26275
26295
  border: {
26276
26296
  top: { color: "#000000", style: "medium" },
@@ -26287,9 +26307,10 @@ stores.inject(MyMetaStore, storeInstance);
26287
26307
  firstRowStripe: { style: { fillColor: COLOR_SETS.black.light } },
26288
26308
  firstColumnStripe: { style: { fillColor: COLOR_SETS.black.light } },
26289
26309
  });
26290
- const mediumTemplateAllBorders = (colorSet) => ({
26310
+ const mediumAllBorders = (colorSet) => ({
26291
26311
  category: "medium",
26292
- colorName: colorSet.name,
26312
+ templateName: "mediumAllBorders",
26313
+ primaryColor: colorSet.highlight,
26293
26314
  wholeTable: {
26294
26315
  border: {
26295
26316
  top: { color: colorSet.mediumBorder, style: "thin" },
@@ -26305,9 +26326,10 @@ stores.inject(MyMetaStore, storeInstance);
26305
26326
  firstRowStripe: { style: { fillColor: colorSet.medium } },
26306
26327
  firstColumnStripe: { style: { fillColor: colorSet.medium } },
26307
26328
  });
26308
- const darkTemplate = (colorSet) => ({
26329
+ const dark = (colorSet) => ({
26309
26330
  category: "dark",
26310
- colorName: colorSet.name,
26331
+ templateName: "dark",
26332
+ primaryColor: colorSet.highlight,
26311
26333
  wholeTable: { style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" } },
26312
26334
  totalRow: {
26313
26335
  style: { fillColor: colorSet.dark, textColor: "#FFFFFF" },
@@ -26328,18 +26350,19 @@ stores.inject(MyMetaStore, storeInstance);
26328
26350
  firstRowStripe: { style: { fillColor: colorSet.dark } },
26329
26351
  firstColumnStripe: { style: { fillColor: colorSet.dark } },
26330
26352
  });
26331
- const darkTemplateNoBorders = (colorSet) => ({
26353
+ const darkNoBorders = (colorSet) => ({
26332
26354
  category: "dark",
26333
- colorName: colorSet.name,
26355
+ templateName: "darkNoBorders",
26356
+ primaryColor: colorSet.highlight,
26334
26357
  wholeTable: { style: { fillColor: colorSet.light } },
26335
26358
  totalRow: { border: { top: { color: "#000000", style: "medium" } } }, // @compatibility: should be double line
26336
26359
  headerRow: { style: { fillColor: colorSet.highlight, textColor: "#FFFFFF" } },
26337
26360
  firstRowStripe: { style: { fillColor: colorSet.medium } },
26338
26361
  firstColumnStripe: { style: { fillColor: colorSet.medium } },
26339
26362
  });
26340
- const darkTemplateInBlack = darkTemplate(COLOR_SETS.black);
26363
+ const darkTemplateInBlack = dark(COLOR_SETS.black);
26341
26364
  darkTemplateInBlack.wholeTable.style.fillColor = "#737373";
26342
- const mediumMinimalBordersInBlack = mediumTemplateMinimalBorders(COLOR_SETS.black);
26365
+ const mediumMinimalBordersInBlack = mediumMinimalBorders(COLOR_SETS.black);
26343
26366
  mediumMinimalBordersInBlack.wholeTable.border = {
26344
26367
  ...mediumMinimalBordersInBlack.wholeTable.border,
26345
26368
  left: { color: "#000000", style: "thin" },
@@ -26347,69 +26370,92 @@ stores.inject(MyMetaStore, storeInstance);
26347
26370
  horizontal: { color: "#000000", style: "thin" },
26348
26371
  vertical: { color: "#000000", style: "thin" },
26349
26372
  };
26373
+ function buildPreset(name, template, colorSet) {
26374
+ return { ...template(colorSet), displayName: `${colorSet.name}, ${name}` };
26375
+ }
26350
26376
  const TABLE_PRESETS = {
26351
- None: { category: "none", colorName: "" },
26352
- TableStyleLight1: lightTemplateColoredText(COLOR_SETS.black),
26353
- TableStyleLight2: lightTemplateColoredText(COLOR_SETS.lightBlue),
26354
- TableStyleLight3: lightTemplateColoredText(COLOR_SETS.red),
26355
- TableStyleLight4: lightTemplateColoredText(COLOR_SETS.lightGreen),
26356
- TableStyleLight5: lightTemplateColoredText(COLOR_SETS.purple),
26357
- TableStyleLight6: lightTemplateColoredText(COLOR_SETS.gray),
26358
- TableStyleLight7: lightTemplateColoredText(COLOR_SETS.orange),
26359
- TableStyleLight8: lightTemplateWithHeader(COLOR_SETS.black),
26360
- TableStyleLight9: lightTemplateWithHeader(COLOR_SETS.lightBlue),
26361
- TableStyleLight10: lightTemplateWithHeader(COLOR_SETS.red),
26362
- TableStyleLight11: lightTemplateWithHeader(COLOR_SETS.lightGreen),
26363
- TableStyleLight12: lightTemplateWithHeader(COLOR_SETS.purple),
26364
- TableStyleLight13: lightTemplateWithHeader(COLOR_SETS.gray),
26365
- TableStyleLight14: lightTemplateWithHeader(COLOR_SETS.orange),
26366
- TableStyleLight15: lightTemplateAllBorders(COLOR_SETS.black),
26367
- TableStyleLight16: lightTemplateAllBorders(COLOR_SETS.lightBlue),
26368
- TableStyleLight17: lightTemplateAllBorders(COLOR_SETS.red),
26369
- TableStyleLight18: lightTemplateAllBorders(COLOR_SETS.lightGreen),
26370
- TableStyleLight19: lightTemplateAllBorders(COLOR_SETS.purple),
26371
- TableStyleLight20: lightTemplateAllBorders(COLOR_SETS.gray),
26372
- TableStyleLight21: lightTemplateAllBorders(COLOR_SETS.orange),
26373
- TableStyleMedium1: mediumTemplateBandedBorders(COLOR_SETS.black),
26374
- TableStyleMedium2: mediumTemplateBandedBorders(COLOR_SETS.lightBlue),
26375
- TableStyleMedium3: mediumTemplateBandedBorders(COLOR_SETS.red),
26376
- TableStyleMedium4: mediumTemplateBandedBorders(COLOR_SETS.lightGreen),
26377
- TableStyleMedium5: mediumTemplateBandedBorders(COLOR_SETS.purple),
26378
- TableStyleMedium6: mediumTemplateBandedBorders(COLOR_SETS.gray),
26379
- TableStyleMedium7: mediumTemplateBandedBorders(COLOR_SETS.orange),
26380
- TableStyleMedium8: mediumTemplateWhiteBorders(COLOR_SETS.black),
26381
- TableStyleMedium9: mediumTemplateWhiteBorders(COLOR_SETS.lightBlue),
26382
- TableStyleMedium10: mediumTemplateWhiteBorders(COLOR_SETS.red),
26383
- TableStyleMedium11: mediumTemplateWhiteBorders(COLOR_SETS.lightGreen),
26384
- TableStyleMedium12: mediumTemplateWhiteBorders(COLOR_SETS.purple),
26385
- TableStyleMedium13: mediumTemplateWhiteBorders(COLOR_SETS.gray),
26386
- TableStyleMedium14: mediumTemplateWhiteBorders(COLOR_SETS.orange),
26387
- TableStyleMedium15: mediumMinimalBordersInBlack,
26388
- TableStyleMedium16: mediumTemplateMinimalBorders(COLOR_SETS.lightBlue),
26389
- TableStyleMedium17: mediumTemplateMinimalBorders(COLOR_SETS.red),
26390
- TableStyleMedium18: mediumTemplateMinimalBorders(COLOR_SETS.lightGreen),
26391
- TableStyleMedium19: mediumTemplateMinimalBorders(COLOR_SETS.purple),
26392
- TableStyleMedium20: mediumTemplateMinimalBorders(COLOR_SETS.gray),
26393
- TableStyleMedium21: mediumTemplateMinimalBorders(COLOR_SETS.orange),
26394
- TableStyleMedium22: mediumTemplateAllBorders(COLOR_SETS.black),
26395
- TableStyleMedium23: mediumTemplateAllBorders(COLOR_SETS.lightBlue),
26396
- TableStyleMedium24: mediumTemplateAllBorders(COLOR_SETS.red),
26397
- TableStyleMedium25: mediumTemplateAllBorders(COLOR_SETS.lightGreen),
26398
- TableStyleMedium26: mediumTemplateAllBorders(COLOR_SETS.purple),
26399
- TableStyleMedium27: mediumTemplateAllBorders(COLOR_SETS.gray),
26400
- TableStyleMedium28: mediumTemplateAllBorders(COLOR_SETS.orange),
26401
- TableStyleDark1: darkTemplateInBlack,
26402
- TableStyleDark2: darkTemplate(COLOR_SETS.lightBlue),
26403
- TableStyleDark3: darkTemplate(COLOR_SETS.red),
26404
- TableStyleDark4: darkTemplate(COLOR_SETS.lightGreen),
26405
- TableStyleDark5: darkTemplate(COLOR_SETS.purple),
26406
- TableStyleDark6: darkTemplate(COLOR_SETS.gray),
26407
- TableStyleDark7: darkTemplate(COLOR_SETS.orange),
26408
- TableStyleDark8: darkTemplateNoBorders(DARK_COLOR_SETS.black),
26409
- TableStyleDark9: darkTemplateNoBorders(DARK_COLOR_SETS.redBlue),
26410
- TableStyleDark10: darkTemplateNoBorders(DARK_COLOR_SETS.purpleGreen),
26411
- TableStyleDark11: darkTemplateNoBorders(DARK_COLOR_SETS.orangeBlue),
26412
- };
26377
+ None: { category: "light", templateName: "none", primaryColor: "", displayName: "none" },
26378
+ TableStyleLight1: buildPreset("TableStyleLight1", lightColoredText, COLOR_SETS.black),
26379
+ TableStyleLight2: buildPreset("TableStyleLight2", lightColoredText, COLOR_SETS.lightBlue),
26380
+ TableStyleLight3: buildPreset("TableStyleLight3", lightColoredText, COLOR_SETS.red),
26381
+ TableStyleLight4: buildPreset("TableStyleLight4", lightColoredText, COLOR_SETS.lightGreen),
26382
+ TableStyleLight5: buildPreset("TableStyleLight5", lightColoredText, COLOR_SETS.purple),
26383
+ TableStyleLight6: buildPreset("TableStyleLight6", lightColoredText, COLOR_SETS.gray),
26384
+ TableStyleLight7: buildPreset("TableStyleLight7", lightColoredText, COLOR_SETS.orange),
26385
+ TableStyleLight8: buildPreset("TableStyleLight8", lightWithHeader, COLOR_SETS.black),
26386
+ TableStyleLight9: buildPreset("TableStyleLight9", lightWithHeader, COLOR_SETS.lightBlue),
26387
+ TableStyleLight10: buildPreset("TableStyleLight10", lightWithHeader, COLOR_SETS.red),
26388
+ TableStyleLight11: buildPreset("TableStyleLight11", lightWithHeader, COLOR_SETS.lightGreen),
26389
+ TableStyleLight12: buildPreset("TableStyleLight12", lightWithHeader, COLOR_SETS.purple),
26390
+ TableStyleLight13: buildPreset("TableStyleLight13", lightWithHeader, COLOR_SETS.gray),
26391
+ TableStyleLight14: buildPreset("TableStyleLight14", lightWithHeader, COLOR_SETS.orange),
26392
+ TableStyleLight15: buildPreset("TableStyleLight15", lightAllBorders, COLOR_SETS.black),
26393
+ TableStyleLight16: buildPreset("TableStyleLight16", lightAllBorders, COLOR_SETS.lightBlue),
26394
+ TableStyleLight17: buildPreset("TableStyleLight17", lightAllBorders, COLOR_SETS.red),
26395
+ TableStyleLight18: buildPreset("TableStyleLight18", lightAllBorders, COLOR_SETS.lightGreen),
26396
+ TableStyleLight19: buildPreset("TableStyleLight19", lightAllBorders, COLOR_SETS.purple),
26397
+ TableStyleLight20: buildPreset("TableStyleLight20", lightAllBorders, COLOR_SETS.gray),
26398
+ TableStyleLight21: buildPreset("TableStyleLight21", lightAllBorders, COLOR_SETS.orange),
26399
+ TableStyleMedium1: buildPreset("TableStyleMedium1", mediumBandedBorders, COLOR_SETS.black),
26400
+ TableStyleMedium2: buildPreset("TableStyleMedium2", mediumBandedBorders, COLOR_SETS.lightBlue),
26401
+ TableStyleMedium3: buildPreset("TableStyleMedium3", mediumBandedBorders, COLOR_SETS.red),
26402
+ TableStyleMedium4: buildPreset("TableStyleMedium4", mediumBandedBorders, COLOR_SETS.lightGreen),
26403
+ TableStyleMedium5: buildPreset("TableStyleMedium5", mediumBandedBorders, COLOR_SETS.purple),
26404
+ TableStyleMedium6: buildPreset("TableStyleMedium6", mediumBandedBorders, COLOR_SETS.gray),
26405
+ TableStyleMedium7: buildPreset("TableStyleMedium7", mediumBandedBorders, COLOR_SETS.orange),
26406
+ TableStyleMedium8: buildPreset("TableStyleMedium8", mediumWhiteBorders, COLOR_SETS.black),
26407
+ TableStyleMedium9: buildPreset("TableStyleMedium9", mediumWhiteBorders, COLOR_SETS.lightBlue),
26408
+ TableStyleMedium10: buildPreset("TableStyleMedium10", mediumWhiteBorders, COLOR_SETS.red),
26409
+ TableStyleMedium11: buildPreset("TableStyleMedium11", mediumWhiteBorders, COLOR_SETS.lightGreen),
26410
+ TableStyleMedium12: buildPreset("TableStyleMedium12", mediumWhiteBorders, COLOR_SETS.purple),
26411
+ TableStyleMedium13: buildPreset("TableStyleMedium13", mediumWhiteBorders, COLOR_SETS.gray),
26412
+ TableStyleMedium14: buildPreset("TableStyleMedium14", mediumWhiteBorders, COLOR_SETS.orange),
26413
+ TableStyleMedium15: { ...mediumMinimalBordersInBlack, displayName: "Black, TableStyleMedium15" },
26414
+ TableStyleMedium16: buildPreset("TableStyleMedium16", mediumMinimalBorders, COLOR_SETS.lightBlue),
26415
+ TableStyleMedium17: buildPreset("TableStyleMedium17", mediumMinimalBorders, COLOR_SETS.red),
26416
+ TableStyleMedium18: buildPreset("TableStyleMedium18", mediumMinimalBorders, COLOR_SETS.lightGreen),
26417
+ TableStyleMedium19: buildPreset("TableStyleMedium19", mediumMinimalBorders, COLOR_SETS.purple),
26418
+ TableStyleMedium20: buildPreset("TableStyleMedium20", mediumMinimalBorders, COLOR_SETS.gray),
26419
+ TableStyleMedium21: buildPreset("TableStyleMedium21", mediumMinimalBorders, COLOR_SETS.orange),
26420
+ TableStyleMedium22: buildPreset("TableStyleMedium22", mediumAllBorders, COLOR_SETS.black),
26421
+ TableStyleMedium23: buildPreset("TableStyleMedium23", mediumAllBorders, COLOR_SETS.lightBlue),
26422
+ TableStyleMedium24: buildPreset("TableStyleMedium24", mediumAllBorders, COLOR_SETS.red),
26423
+ TableStyleMedium25: buildPreset("TableStyleMedium25", mediumAllBorders, COLOR_SETS.lightGreen),
26424
+ TableStyleMedium26: buildPreset("TableStyleMedium26", mediumAllBorders, COLOR_SETS.purple),
26425
+ TableStyleMedium27: buildPreset("TableStyleMedium27", mediumAllBorders, COLOR_SETS.gray),
26426
+ TableStyleMedium28: buildPreset("TableStyleMedium28", mediumAllBorders, COLOR_SETS.orange),
26427
+ TableStyleDark1: { ...darkTemplateInBlack, displayName: "Black, TableStyleDark1" },
26428
+ TableStyleDark2: buildPreset("TableStyleDark2", dark, COLOR_SETS.lightBlue),
26429
+ TableStyleDark3: buildPreset("TableStyleDark3", dark, COLOR_SETS.red),
26430
+ TableStyleDark4: buildPreset("TableStyleDark4", dark, COLOR_SETS.lightGreen),
26431
+ TableStyleDark5: buildPreset("TableStyleDark5", dark, COLOR_SETS.purple),
26432
+ TableStyleDark6: buildPreset("TableStyleDark6", dark, COLOR_SETS.gray),
26433
+ TableStyleDark7: buildPreset("TableStyleDark7", dark, COLOR_SETS.orange),
26434
+ TableStyleDark8: buildPreset("TableStyleDark8", darkNoBorders, DARK_COLOR_SETS.black),
26435
+ TableStyleDark9: buildPreset("TableStyleDark9", darkNoBorders, DARK_COLOR_SETS.redBlue),
26436
+ TableStyleDark10: buildPreset("TableStyleDark10", darkNoBorders, DARK_COLOR_SETS.purpleGreen),
26437
+ TableStyleDark11: buildPreset("TableStyleDark11", darkNoBorders, DARK_COLOR_SETS.orangeBlue),
26438
+ };
26439
+ const TABLE_STYLES_TEMPLATES = {
26440
+ none: () => ({ category: "none", templateName: "none", primaryColor: "", name: "none" }),
26441
+ lightColoredText: lightColoredText,
26442
+ lightAllBorders: lightAllBorders,
26443
+ mediumAllBorders: mediumAllBorders,
26444
+ lightWithHeader: lightWithHeader,
26445
+ mediumBandedBorders: mediumBandedBorders,
26446
+ mediumMinimalBorders: mediumMinimalBorders,
26447
+ darkNoBorders: darkNoBorders,
26448
+ mediumWhiteBorders: mediumWhiteBorders,
26449
+ dark: dark,
26450
+ };
26451
+ function buildTableStyle(name, templateName, primaryColor) {
26452
+ const colorSet = generateTableColorSet("", primaryColor);
26453
+ return {
26454
+ ...TABLE_STYLES_TEMPLATES[templateName](colorSet),
26455
+ category: "custom",
26456
+ displayName: name,
26457
+ };
26458
+ }
26413
26459
 
26414
26460
  /**
26415
26461
  * Create a table on the selected zone, with UI warnings to the user if the creation fails.
@@ -26972,7 +27018,7 @@ stores.inject(MyMetaStore, storeInstance);
26972
27018
  execute: (env) => {
26973
27019
  env.openSidePanel("FindAndReplace", {});
26974
27020
  },
26975
- icon: "o-spreadsheet-Icon.FIND_AND_REPLACE",
27021
+ icon: "o-spreadsheet-Icon.SEARCH",
26976
27022
  };
26977
27023
  const deleteValues = {
26978
27024
  name: _t("Delete values"),
@@ -27472,18 +27518,18 @@ stores.inject(MyMetaStore, storeInstance);
27472
27518
  .add("delete_row", {
27473
27519
  ...deleteRow,
27474
27520
  sequence: 110,
27475
- icon: "o-spreadsheet-Icon.DELETE",
27521
+ icon: "o-spreadsheet-Icon.TRASH",
27476
27522
  })
27477
27523
  .add("delete_column", {
27478
27524
  ...deleteCol,
27479
27525
  sequence: 120,
27480
- icon: "o-spreadsheet-Icon.DELETE",
27526
+ icon: "o-spreadsheet-Icon.TRASH",
27481
27527
  })
27482
27528
  .add("delete_cell", {
27483
27529
  ...deleteCells,
27484
27530
  sequence: 130,
27485
27531
  separator: true,
27486
- icon: "o-spreadsheet-Icon.DELETE",
27532
+ icon: "o-spreadsheet-Icon.TRASH",
27487
27533
  })
27488
27534
  .addChild("delete_cell_up", ["delete_cell"], {
27489
27535
  ...deleteCellShiftUp,
@@ -27599,6 +27645,7 @@ stores.inject(MyMetaStore, storeInstance);
27599
27645
  }),
27600
27646
  execute: (env) => setFormatter(env, formatCallback(env)),
27601
27647
  isActive: (env) => isFormatSelected(env, formatCallback(env)),
27648
+ format,
27602
27649
  };
27603
27650
  }
27604
27651
  const formatNumberAutomatic = {
@@ -27951,7 +27998,9 @@ stores.inject(MyMetaStore, storeInstance);
27951
27998
 
27952
27999
  var ACTION_FORMAT = /*#__PURE__*/Object.freeze({
27953
28000
  __proto__: null,
28001
+ EXAMPLE_DATE: EXAMPLE_DATE,
27954
28002
  clearFormat: clearFormat,
28003
+ createFormatActionSpec: createFormatActionSpec,
27955
28004
  decraseDecimalPlaces: decraseDecimalPlaces,
27956
28005
  fillColor: fillColor,
27957
28006
  formatAlignment: formatAlignment,
@@ -28319,7 +28368,7 @@ stores.inject(MyMetaStore, storeInstance);
28319
28368
  .add("delete_column", {
28320
28369
  ...deleteCols,
28321
28370
  sequence: 90,
28322
- icon: "o-spreadsheet-Icon.DELETE",
28371
+ icon: "o-spreadsheet-Icon.TRASH",
28323
28372
  })
28324
28373
  .add("clear_column", {
28325
28374
  ...clearCols,
@@ -28362,64 +28411,119 @@ stores.inject(MyMetaStore, storeInstance);
28362
28411
  isVisible: (env) => canUngroupHeaders(env, "COL"),
28363
28412
  });
28364
28413
 
28365
- const numberFormatMenuRegistry = new MenuItemRegistry();
28414
+ const numberFormatMenuRegistry = new Registry();
28366
28415
  numberFormatMenuRegistry
28367
28416
  .add("format_number_automatic", {
28368
28417
  ...formatNumberAutomatic,
28418
+ id: "format_number_automatic",
28369
28419
  sequence: 10,
28370
28420
  })
28371
28421
  .add("format_number_plain_text", {
28372
28422
  ...formatNumberPlainText,
28423
+ id: "format_number_plain_text",
28373
28424
  sequence: 15,
28374
28425
  separator: true,
28375
28426
  })
28376
28427
  .add("format_number_number", {
28377
28428
  ...formatNumberNumber,
28429
+ id: "format_number_number",
28378
28430
  sequence: 20,
28379
28431
  })
28380
28432
  .add("format_number_percent", {
28381
28433
  ...formatNumberPercent,
28434
+ id: "format_number_percent",
28382
28435
  sequence: 30,
28383
28436
  separator: true,
28384
28437
  })
28385
28438
  .add("format_number_currency", {
28386
28439
  ...formatNumberCurrency,
28440
+ id: "format_number_currency",
28387
28441
  sequence: 40,
28388
28442
  })
28389
28443
  .add("format_number_currency_rounded", {
28390
28444
  ...formatNumberCurrencyRounded,
28445
+ id: "format_number_currency_rounded",
28391
28446
  sequence: 50,
28392
28447
  })
28393
28448
  .add("format_custom_currency", {
28394
28449
  ...formatCustomCurrency,
28450
+ id: "format_custom_currency",
28395
28451
  sequence: 60,
28396
28452
  separator: true,
28397
28453
  })
28398
28454
  .add("format_number_date", {
28399
28455
  ...formatNumberDate,
28456
+ id: "format_number_date",
28400
28457
  sequence: 70,
28401
28458
  })
28402
28459
  .add("format_number_time", {
28403
28460
  ...formatNumberTime,
28461
+ id: "format_number_time",
28404
28462
  sequence: 80,
28405
28463
  })
28406
28464
  .add("format_number_date_time", {
28407
28465
  ...formatNumberDateTime,
28466
+ id: "format_number_date_time",
28408
28467
  sequence: 90,
28409
28468
  })
28410
28469
  .add("format_number_duration", {
28411
28470
  ...formatNumberDuration,
28471
+ id: "format_number_duration",
28412
28472
  sequence: 100,
28413
28473
  separator: true,
28414
28474
  })
28415
28475
  .add("more_formats", {
28416
28476
  ...moreFormats,
28417
- sequence: 110,
28477
+ id: "more_formats",
28478
+ sequence: 120,
28479
+ });
28480
+ function getCustomNumberFormats(env) {
28481
+ const defaultFormats = new Set(numberFormatMenuRegistry
28482
+ .getAll()
28483
+ .map((f) => (typeof f.format === "function" ? f.format(env) : f.format)));
28484
+ const customFormats = new Map();
28485
+ for (const sheetId of env.model.getters.getSheetIds()) {
28486
+ const cells = env.model.getters.getEvaluatedCells(sheetId);
28487
+ for (const cellId in cells) {
28488
+ const cell = cells[cellId];
28489
+ if (cell.format && !customFormats.has(cell.format) && !defaultFormats.has(cell.format)) {
28490
+ const formatType = getNumberFormatType(cell.format);
28491
+ if (formatType === "date" || formatType === "currency") {
28492
+ customFormats.set(cell.format, createFormatActionSpec({
28493
+ descriptionValue: formatType === "currency" ? 1000 : EXAMPLE_DATE,
28494
+ format: cell.format,
28495
+ name: cell.format,
28496
+ }));
28497
+ }
28498
+ }
28499
+ }
28500
+ }
28501
+ return [...customFormats.values()];
28502
+ }
28503
+ const getNumberFormatType = memoize((format) => {
28504
+ if (isDateTimeFormat(format)) {
28505
+ return "date";
28506
+ }
28507
+ else if (format.includes("[$")) {
28508
+ return "currency";
28509
+ }
28510
+ return "number";
28418
28511
  });
28419
28512
  const formatNumberMenuItemSpec = {
28420
28513
  name: _t("More formats"),
28421
28514
  icon: "o-spreadsheet-Icon.NUMBER_FORMATS",
28422
- children: [() => numberFormatMenuRegistry.getAll()],
28515
+ children: [
28516
+ (env) => {
28517
+ const customFormats = getCustomNumberFormats(env).map((action) => ({
28518
+ ...action,
28519
+ sequence: 110,
28520
+ }));
28521
+ if (customFormats.length > 0) {
28522
+ customFormats[customFormats.length - 1].separator = true;
28523
+ }
28524
+ return createActions([...numberFormatMenuRegistry.getAll(), ...customFormats]);
28525
+ },
28526
+ ],
28423
28527
  };
28424
28528
 
28425
28529
  const rowMenuRegistry = new MenuItemRegistry();
@@ -28460,7 +28564,7 @@ stores.inject(MyMetaStore, storeInstance);
28460
28564
  .add("delete_row", {
28461
28565
  ...deleteRows,
28462
28566
  sequence: 70,
28463
- icon: "o-spreadsheet-Icon.DELETE",
28567
+ icon: "o-spreadsheet-Icon.TRASH",
28464
28568
  })
28465
28569
  .add("clear_row", {
28466
28570
  ...clearRows,
@@ -28611,7 +28715,7 @@ stores.inject(MyMetaStore, storeInstance);
28611
28715
  })
28612
28716
  .addChild("delete", ["edit"], {
28613
28717
  name: _t("Delete"),
28614
- icon: "o-spreadsheet-Icon.DELETE",
28718
+ icon: "o-spreadsheet-Icon.TRASH",
28615
28719
  sequence: 70,
28616
28720
  })
28617
28721
  .addChild("edit_delete_cell_values", ["edit", "delete"], {
@@ -30923,6 +31027,14 @@ stores.inject(MyMetaStore, storeInstance);
30923
31027
  css /* scss */ `
30924
31028
  .o-spreadsheet {
30925
31029
  .o-icon {
31030
+ display: flex;
31031
+ align-items: center;
31032
+ justify-content: center;
31033
+ width: ${ICON_EDGE_LENGTH}px;
31034
+ height: ${ICON_EDGE_LENGTH}px;
31035
+ font-size: ${ICON_EDGE_LENGTH}px;
31036
+ vertical-align: middle;
31037
+
30926
31038
  .small-text {
30927
31039
  font: bold 9px sans-serif;
30928
31040
  }
@@ -30930,6 +31042,9 @@ stores.inject(MyMetaStore, storeInstance);
30930
31042
  font: bold 16px sans-serif;
30931
31043
  }
30932
31044
  }
31045
+ .fa-small {
31046
+ font-size: 14px;
31047
+ }
30933
31048
  }
30934
31049
  `;
30935
31050
  // -----------------------------------------------------------------------------
@@ -33553,15 +33668,14 @@ stores.inject(MyMetaStore, storeInstance);
33553
33668
  function isStaticTable(table) {
33554
33669
  return table.type === "static" || table.type === "forceStatic";
33555
33670
  }
33556
- function getComputedTableStyle(tableConfig, numberOfCols, numberOfRows) {
33671
+ function getComputedTableStyle(tableConfig, style, numberOfCols, numberOfRows) {
33557
33672
  return {
33558
- borders: getAllTableBorders(tableConfig, numberOfCols, numberOfRows),
33559
- styles: getAllTableStyles(tableConfig, numberOfCols, numberOfRows),
33673
+ borders: getAllTableBorders(tableConfig, style, numberOfCols, numberOfRows),
33674
+ styles: getAllTableStyles(tableConfig, style, numberOfCols, numberOfRows),
33560
33675
  };
33561
33676
  }
33562
- function getAllTableBorders(tableConfig, nOfCols, nOfRows) {
33677
+ function getAllTableBorders(tableConfig, style, nOfCols, nOfRows) {
33563
33678
  const borders = generateMatrix(nOfCols, nOfRows, () => ({}));
33564
- const style = TABLE_PRESETS[tableConfig.styleId];
33565
33679
  for (const tableElement of TABLE_ELEMENTS_BY_PRIORITY) {
33566
33680
  const styleBorder = style[tableElement]?.border;
33567
33681
  if (!styleBorder)
@@ -33629,9 +33743,8 @@ stores.inject(MyMetaStore, storeInstance);
33629
33743
  return;
33630
33744
  }
33631
33745
  }
33632
- function getAllTableStyles(tableConfig, numberOfCols, numberOfRows) {
33746
+ function getAllTableStyles(tableConfig, style, numberOfCols, numberOfRows) {
33633
33747
  const styles = generateMatrix(numberOfCols, numberOfRows, () => ({}));
33634
- const style = TABLE_PRESETS[tableConfig.styleId];
33635
33748
  for (const tableElement of TABLE_ELEMENTS_BY_PRIORITY) {
33636
33749
  const tableElStyle = style[tableElement];
33637
33750
  const bold = isTableElementInBold(tableElement);
@@ -33725,8 +33838,25 @@ stores.inject(MyMetaStore, storeInstance);
33725
33838
  }
33726
33839
  return zones;
33727
33840
  }
33728
- function getTableStyleName(styleId, tableStyle) {
33729
- return tableStyle.colorName ? `${tableStyle.colorName}, ${styleId}` : styleId;
33841
+
33842
+ function createTableStyleContextMenuActions(env, styleId) {
33843
+ if (!env.model.getters.isTableStyleEditable(styleId)) {
33844
+ return [];
33845
+ }
33846
+ return createActions([
33847
+ {
33848
+ id: "editTableStyle",
33849
+ name: _t("Edit table style"),
33850
+ execute: (env) => env.openSidePanel("TableStyleEditorPanel", { styleId }),
33851
+ icon: "o-spreadsheet-Icon.EDIT_TABLE",
33852
+ },
33853
+ {
33854
+ id: "deleteTableStyle",
33855
+ name: _t("Delete table style"),
33856
+ execute: (env) => env.model.dispatch("REMOVE_TABLE_STYLE", { tableStyleId: styleId }),
33857
+ icon: "o-spreadsheet-Icon.DELETE_TABLE",
33858
+ },
33859
+ ]);
33730
33860
  }
33731
33861
 
33732
33862
  function drawPreviewTable(ctx, tableStyle, colWidth, rowHeight) {
@@ -33811,46 +33941,59 @@ stores.inject(MyMetaStore, storeInstance);
33811
33941
 
33812
33942
  class TableStylePreview extends owl.Component {
33813
33943
  static template = "o-spreadsheet-TableStylePreview";
33814
- static props = { tableConfig: Object };
33944
+ static props = { tableConfig: Object, tableStyle: { type: Object, optional: true } };
33815
33945
  canvasRef = owl.useRef("canvas");
33816
33946
  setup() {
33817
33947
  owl.onWillUpdateProps((nextProps) => {
33818
- if (!deepEquals(this.props.tableConfig, nextProps.tableConfig)) {
33819
- this.drawTable(nextProps.tableConfig);
33948
+ if (!deepEquals(this.props.tableConfig, nextProps.tableConfig) ||
33949
+ !deepEquals(this.props.tableStyle, nextProps.tableStyle)) {
33950
+ this.drawTable(nextProps);
33820
33951
  }
33821
33952
  });
33822
- owl.onMounted(() => this.drawTable(this.props.tableConfig));
33953
+ owl.onMounted(() => this.drawTable(this.props));
33823
33954
  }
33824
- drawTable(tableConfig) {
33955
+ drawTable(props) {
33825
33956
  const ctx = this.canvasRef.el.getContext("2d");
33826
33957
  const { width, height } = this.canvasRef.el.getBoundingClientRect();
33827
33958
  this.canvasRef.el.width = width;
33828
33959
  this.canvasRef.el.height = height;
33829
- const tableStyle = getComputedTableStyle(tableConfig, 5, 5);
33830
- drawPreviewTable(ctx, tableStyle, (width - 1) / 5, (height - 1) / 5);
33960
+ const computedStyle = getComputedTableStyle(props.tableConfig, props.tableStyle, 5, 5);
33961
+ drawPreviewTable(ctx, computedStyle, (width - 1) / 5, (height - 1) / 5);
33831
33962
  }
33832
33963
  }
33833
33964
 
33834
33965
  css /* scss */ `
33835
33966
  .o-table-style-popover {
33836
33967
  /** 7 tables preview + padding by line */
33837
- max-width: calc((66px + 4px * 2) * 7);
33968
+ width: calc((66px + 4px * 2) * 7);
33838
33969
  background: #fff;
33839
33970
  font-size: 14px;
33971
+ user-select: none;
33972
+
33973
+ .form-check-input {
33974
+ font-size: 12px;
33975
+ }
33976
+
33840
33977
  .o-table-style-list-item {
33841
- padding: 4px;
33842
- &.selected {
33843
- padding: 3px;
33844
- }
33978
+ padding: 3px;
33979
+ }
33980
+
33981
+ .o-table-style-popover-preview {
33982
+ width: 66px;
33983
+ height: 51px;
33984
+ }
33845
33985
 
33846
- .o-table-style-popover-preview {
33847
- width: 66px;
33848
- height: 51px;
33986
+ .o-new-table-style {
33987
+ font-size: 36px;
33988
+ color: #666;
33989
+ &:hover {
33990
+ background: #f5f5f5;
33849
33991
  }
33850
33992
  }
33851
33993
  }
33852
33994
 
33853
33995
  .o-table-style-list-item {
33996
+ border: 1px solid transparent;
33854
33997
  &.selected {
33855
33998
  border: 1px solid #007eff;
33856
33999
  background: #f5f5f5;
@@ -33863,7 +34006,7 @@ stores.inject(MyMetaStore, storeInstance);
33863
34006
  `;
33864
34007
  class TableStylesPopover extends owl.Component {
33865
34008
  static template = "o-spreadsheet-TableStylesPopover";
33866
- static components = { Popover, TableStylePreview };
34009
+ static components = { Popover, TableStylePreview, Menu };
33867
34010
  static props = {
33868
34011
  tableConfig: Object,
33869
34012
  popoverProps: { type: Object, optional: true },
@@ -33871,9 +34014,10 @@ stores.inject(MyMetaStore, storeInstance);
33871
34014
  onStylePicked: Function,
33872
34015
  selectedStyleId: { type: String, optional: true },
33873
34016
  };
33874
- stylePresets = TABLE_PRESETS;
33875
34017
  categories = TABLE_STYLE_CATEGORIES;
33876
34018
  tableStyleListRef = owl.useRef("tableStyleList");
34019
+ state = owl.useState({ selectedCategory: this.initialSelectedCategory });
34020
+ menu = owl.useState({ isOpen: false, position: null, menuItems: [] });
33877
34021
  setup() {
33878
34022
  owl.useExternalListener(window, "click", this.onExternalClick, { capture: true });
33879
34023
  }
@@ -33883,14 +34027,33 @@ stores.inject(MyMetaStore, storeInstance);
33883
34027
  ev.hasClosedTableStylesPopover = true;
33884
34028
  }
33885
34029
  }
33886
- getPresetsByCategory(category) {
33887
- return Object.keys(this.stylePresets).filter((key) => this.stylePresets[key].category === category);
34030
+ get displayedStyles() {
34031
+ const styles = this.env.model.getters.getTableStyles();
34032
+ return Object.keys(styles).filter((styleId) => styles[styleId].category === this.state.selectedCategory);
33888
34033
  }
33889
- getTableConfig(styleId) {
33890
- return { ...this.props.tableConfig, styleId: styleId };
34034
+ get initialSelectedCategory() {
34035
+ return this.props.selectedStyleId
34036
+ ? this.env.model.getters.getTableStyle(this.props.selectedStyleId).category
34037
+ : "medium";
33891
34038
  }
33892
34039
  getStyleName(styleId) {
33893
- return getTableStyleName(styleId, TABLE_PRESETS[styleId]);
34040
+ return this.env.model.getters.getTableStyle(styleId).displayName;
34041
+ }
34042
+ newTableStyle() {
34043
+ this.props.closePopover();
34044
+ this.env.openSidePanel("TableStyleEditorPanel", {
34045
+ onStylePicked: this.props.onStylePicked,
34046
+ });
34047
+ }
34048
+ onContextMenu(event, styleId) {
34049
+ this.menu.menuItems = createTableStyleContextMenuActions(this.env, styleId);
34050
+ this.menu.isOpen = true;
34051
+ this.menu.position = { x: event.clientX, y: event.clientY };
34052
+ }
34053
+ closeMenu() {
34054
+ this.menu.isOpen = false;
34055
+ this.menu.position = null;
34056
+ this.menu.menuItems = [];
33894
34057
  }
33895
34058
  }
33896
34059
 
@@ -33910,13 +34073,9 @@ stores.inject(MyMetaStore, storeInstance);
33910
34073
  }
33911
34074
 
33912
34075
  .o-table-style-list-item {
33913
- padding: 4px;
34076
+ padding: 3px;
33914
34077
  margin: 2px 1px;
33915
34078
 
33916
- &.selected {
33917
- padding: 3px;
33918
- }
33919
-
33920
34079
  .o-table-style-picker-preview {
33921
34080
  width: 61px;
33922
34081
  height: 46px;
@@ -33926,11 +34085,14 @@ stores.inject(MyMetaStore, storeInstance);
33926
34085
  `;
33927
34086
  class TableStylePicker extends owl.Component {
33928
34087
  static template = "o-spreadsheet-TableStylePicker";
33929
- static components = { TableStylesPopover, TableStylePreview };
34088
+ static components = { TableStylesPopover, TableStylePreview, Menu };
33930
34089
  static props = { table: Object };
33931
34090
  state = owl.useState({ popoverProps: undefined });
34091
+ menu = owl.useState({ isOpen: false, position: null, menuItems: [] });
33932
34092
  getDisplayedTableStyles() {
33933
- const styles = Object.keys(TABLE_PRESETS);
34093
+ const allStyles = this.env.model.getters.getTableStyles();
34094
+ const selectedStyleCategory = allStyles[this.props.table.config.styleId].category;
34095
+ const styles = Object.keys(allStyles).filter((key) => allStyles[key].category === selectedStyleCategory);
33934
34096
  const selectedStyleIndex = styles.indexOf(this.props.table.config.styleId);
33935
34097
  if (selectedStyleIndex === -1) {
33936
34098
  return styles.slice(0, 4);
@@ -33938,9 +34100,6 @@ stores.inject(MyMetaStore, storeInstance);
33938
34100
  const index = Math.floor(selectedStyleIndex / 4) * 4;
33939
34101
  return styles.slice(index, index + 4);
33940
34102
  }
33941
- getTableConfig(styleId) {
33942
- return { ...this.props.table.config, styleId: styleId };
33943
- }
33944
34103
  onStylePicked(styleId) {
33945
34104
  const sheetId = this.env.model.getters.getActiveSheetId();
33946
34105
  this.env.model.dispatch("UPDATE_TABLE", {
@@ -33967,7 +34126,17 @@ stores.inject(MyMetaStore, storeInstance);
33967
34126
  this.state.popoverProps = undefined;
33968
34127
  }
33969
34128
  getStyleName(styleId) {
33970
- return getTableStyleName(styleId, TABLE_PRESETS[styleId]);
34129
+ return this.env.model.getters.getTableStyle(styleId).displayName;
34130
+ }
34131
+ onContextMenu(event, styleId) {
34132
+ this.menu.menuItems = createTableStyleContextMenuActions(this.env, styleId);
34133
+ this.menu.isOpen = true;
34134
+ this.menu.position = { x: event.clientX, y: event.clientY };
34135
+ }
34136
+ closeMenu() {
34137
+ this.menu.isOpen = false;
34138
+ this.menu.position = null;
34139
+ this.menu.menuItems = [];
33971
34140
  }
33972
34141
  }
33973
34142
 
@@ -34157,6 +34326,103 @@ stores.inject(MyMetaStore, storeInstance);
34157
34326
  }
34158
34327
  }
34159
34328
 
34329
+ css /* scss */ `
34330
+ .o-table-style-editor-panel {
34331
+ .o-color-preview {
34332
+ width: 30px;
34333
+ height: 15px;
34334
+ margin-left: 2px;
34335
+ outline: 1px solid #3d85c6;
34336
+ outline-offset: 1px;
34337
+ margin-right: 10px;
34338
+
34339
+ cursor: pointer;
34340
+ }
34341
+
34342
+ .o-table-style-list-item {
34343
+ margin: 1px 3px;
34344
+ padding: 3px 6px;
34345
+
34346
+ .o-table-style-edit-template-preview {
34347
+ width: 81px;
34348
+ height: 61px;
34349
+ }
34350
+ }
34351
+ }
34352
+ `;
34353
+ class TableStyleEditorPanel extends owl.Component {
34354
+ static template = "o-spreadsheet-TableStyleEditorPanel";
34355
+ static components = { Section, ColorPickerWidget, TableStylePreview };
34356
+ static props = {
34357
+ onCloseSidePanel: Function,
34358
+ onStylePicked: { type: Function, optional: true },
34359
+ styleId: { type: String, optional: true },
34360
+ };
34361
+ state = owl.useState(this.getInitialState());
34362
+ setup() {
34363
+ owl.useExternalListener(window, "click", () => (this.state.pickerOpened = false));
34364
+ }
34365
+ getInitialState() {
34366
+ const editedStyle = this.props.styleId
34367
+ ? this.env.model.getters.getTableStyle(this.props.styleId)
34368
+ : null;
34369
+ return {
34370
+ pickerOpened: false,
34371
+ primaryColor: editedStyle?.primaryColor || "#3C78D8",
34372
+ selectedTemplateName: editedStyle?.templateName || "lightColoredText",
34373
+ styleName: editedStyle?.displayName || this.env.model.getters.getNewCustomTableStyleName(),
34374
+ };
34375
+ }
34376
+ togglePicker() {
34377
+ this.state.pickerOpened = !this.state.pickerOpened;
34378
+ }
34379
+ onColorPicked(color) {
34380
+ this.state.primaryColor = color;
34381
+ this.state.pickerOpened = false;
34382
+ }
34383
+ onTemplatePicked(templateName) {
34384
+ this.state.selectedTemplateName = templateName;
34385
+ }
34386
+ onConfirm() {
34387
+ const tableStyleId = this.props.styleId || this.env.model.uuidGenerator.uuidv4();
34388
+ this.env.model.dispatch("CREATE_TABLE_STYLE", {
34389
+ tableStyleId,
34390
+ tableStyleName: this.state.styleName,
34391
+ templateName: this.state.selectedTemplateName,
34392
+ primaryColor: this.state.primaryColor,
34393
+ });
34394
+ this.props.onStylePicked?.(tableStyleId);
34395
+ this.props.onCloseSidePanel();
34396
+ }
34397
+ onCancel() {
34398
+ this.props.onCloseSidePanel();
34399
+ }
34400
+ get colorPreviewStyle() {
34401
+ return cssPropertiesToCss({ background: this.state.primaryColor });
34402
+ }
34403
+ get tableTemplates() {
34404
+ return Object.keys(TABLE_STYLES_TEMPLATES).filter((templateName) => templateName !== "none");
34405
+ }
34406
+ get previewTableConfig() {
34407
+ return {
34408
+ bandedColumns: false,
34409
+ bandedRows: true,
34410
+ firstColumn: false,
34411
+ lastColumn: false,
34412
+ numberOfHeaders: 1,
34413
+ totalRow: true,
34414
+ hasFilters: true,
34415
+ styleId: "",
34416
+ };
34417
+ }
34418
+ get selectedStyle() {
34419
+ return this.computeTableStyle(this.state.selectedTemplateName);
34420
+ }
34421
+ computeTableStyle(templateName) {
34422
+ return buildTableStyle(this.state.styleName, templateName, this.state.primaryColor);
34423
+ }
34424
+ }
34425
+
34160
34426
  const sidePanelRegistry = new Registry();
34161
34427
 
34162
34428
  //------------------------------------------------------------------------------
@@ -34221,6 +34487,17 @@ stores.inject(MyMetaStore, storeInstance);
34221
34487
  return { isOpen: true, props: { table: coreTable }, key: table.id };
34222
34488
  },
34223
34489
  });
34490
+ sidePanelRegistry.add("TableStyleEditorPanel", {
34491
+ title: _t("Create custom table style"),
34492
+ Body: TableStyleEditorPanel,
34493
+ computeState: (getters, initialProps) => {
34494
+ return {
34495
+ isOpen: true,
34496
+ props: { ...initialProps },
34497
+ key: initialProps.styleId ?? "new",
34498
+ };
34499
+ },
34500
+ });
34224
34501
 
34225
34502
  class TopBarComponentRegistry extends Registry {
34226
34503
  mapping = {};
@@ -36306,6 +36583,9 @@ stores.inject(MyMetaStore, storeInstance);
36306
36583
  height: 10000px;
36307
36584
  background-color: ${SELECTION_BORDER_COLOR};
36308
36585
  }
36586
+ .o-unhide {
36587
+ color: ${ICONS_COLOR};
36588
+ }
36309
36589
  .o-unhide:hover {
36310
36590
  z-index: ${ComponentsImportance.Grid + 1};
36311
36591
  background-color: lightgrey;
@@ -36467,6 +36747,9 @@ stores.inject(MyMetaStore, storeInstance);
36467
36747
  height: 1px;
36468
36748
  background-color: ${SELECTION_BORDER_COLOR};
36469
36749
  }
36750
+ .o-unhide {
36751
+ color: ${ICONS_COLOR};
36752
+ }
36470
36753
  .o-unhide:hover {
36471
36754
  z-index: ${ComponentsImportance.Grid + 1};
36472
36755
  background-color: lightgrey;
@@ -39609,6 +39892,13 @@ stores.inject(MyMetaStore, storeInstance);
39609
39892
  }
39610
39893
  return parseInt(hex.replace("#", ""), 16);
39611
39894
  }
39895
+ /**
39896
+ * When defining style (fontColor, borderColor for instance)
39897
+ * Excel will specify rgb="FF000000"
39898
+ * In that case, We should not consider this value as user-defined but
39899
+ * rather like an instruction: "Use your system default"
39900
+ */
39901
+ const DEFAULT_SYSTEM_COLOR = "FF000000";
39612
39902
 
39613
39903
  /**
39614
39904
  * Get the relative path between two files
@@ -39647,18 +39937,6 @@ stores.inject(MyMetaStore, storeInstance);
39647
39937
  }
39648
39938
  return obj;
39649
39939
  }
39650
- /**
39651
- * Convert an object whose keys are numbers to an array were the element index was their key in the object.
39652
- *
39653
- * eg. : {0:"a", 2:"b"} => ["a", undefined, "b"]
39654
- */
39655
- function objectToArray(obj) {
39656
- const arr = [];
39657
- for (let key of Object.keys(obj).map(Number)) {
39658
- arr[key] = obj[key];
39659
- }
39660
- return arr;
39661
- }
39662
39940
  /**
39663
39941
  * In xlsx we can have string with unicode characters with the format _x00fa_.
39664
39942
  * Replace with characters understandable by JS
@@ -40127,7 +40405,7 @@ stores.inject(MyMetaStore, storeInstance);
40127
40405
  vertical: style.verticalAlign
40128
40406
  ? V_ALIGNMENT_EXPORT_CONVERSION_MAP[style.verticalAlign]
40129
40407
  : undefined,
40130
- wrapText: style.wrapping === "wrap",
40408
+ wrapText: style.wrapping === "wrap" || undefined,
40131
40409
  },
40132
40410
  };
40133
40411
  styles.font["strike"] = !!style?.strikethrough || undefined;
@@ -40731,128 +41009,54 @@ stores.inject(MyMetaStore, storeInstance);
40731
41009
  : sheet.rows.find((row) => row.index === index);
40732
41010
  }
40733
41011
 
40734
- const TABLE_HEADER_STYLE = {
40735
- fillColor: "#000000",
40736
- textColor: "#ffffff",
40737
- bold: true,
40738
- };
40739
- const TABLE_HIGHLIGHTED_CELL_STYLE = {
40740
- bold: true,
40741
- };
40742
- const TABLE_BORDER_STYLE = { style: "thin", color: "#000000FF" };
40743
41012
  /**
40744
- * Convert the imported XLSX tables.
40745
- *
40746
- * We will create a Table if the imported table have filters, then apply a style in all the cells of the table
40747
- * and convert the table-specific formula references into standard references.
41013
+ * Convert the imported XLSX tables and pivots convert the table-specific formula references into standard references.
40748
41014
  *
40749
41015
  * Change the converted data in-place.
40750
41016
  */
40751
41017
  function convertTables(convertedData, xlsxData) {
40752
41018
  for (const xlsxSheet of xlsxData.sheets) {
41019
+ const sheet = convertedData.sheets.find((sheet) => sheet.name === xlsxSheet.sheetName);
41020
+ if (!sheet)
41021
+ continue;
41022
+ if (!sheet.tables)
41023
+ sheet.tables = [];
40753
41024
  for (const table of xlsxSheet.tables) {
40754
- const sheet = convertedData.sheets.find((sheet) => sheet.name === xlsxSheet.sheetName);
40755
- if (!sheet || !table.autoFilter)
40756
- continue;
40757
- if (!sheet.tables)
40758
- sheet.tables = [];
40759
- sheet.tables.push({ range: table.ref });
41025
+ sheet.tables.push({ range: table.ref, config: convertTableConfig(table) });
41026
+ }
41027
+ for (const pivotTable of xlsxSheet.pivotTables) {
41028
+ sheet.tables.push({
41029
+ range: pivotTable.location.ref,
41030
+ config: convertPivotTableConfig(pivotTable),
41031
+ });
40760
41032
  }
40761
41033
  }
40762
- applyTableStyle(convertedData, xlsxData);
40763
41034
  convertTableFormulaReferences(convertedData.sheets, xlsxData.sheets);
40764
41035
  }
40765
- /**
40766
- * Apply a style to all the cells that are in a table, and add the created styles in the converted data.
40767
- *
40768
- * In XLSXs, the style of the cells of a table are not directly in the sheet, but rather deduced from the style of
40769
- * the table that is defined in the table's XML file. The style of the table is a string referencing a standard style
40770
- * defined in the OpenXML specifications. As there are 80+ different styles, we won't implement every one of them but
40771
- * we will just define a style that will be used for all the imported tables.
40772
- */
40773
- function applyTableStyle(convertedData, xlsxData) {
40774
- const styles = objectToArray(convertedData.styles);
40775
- const borders = objectToArray(convertedData.borders);
40776
- for (let xlsxSheet of xlsxData.sheets) {
40777
- for (let table of xlsxSheet.tables) {
40778
- const sheet = convertedData.sheets.find((sheet) => sheet.name === xlsxSheet.sheetName);
40779
- if (!sheet)
40780
- continue;
40781
- const tableZone = toZone(table.ref);
40782
- // Table style
40783
- for (let i = 0; i < table.headerRowCount; i++) {
40784
- applyStyleToZone(TABLE_HEADER_STYLE, { ...tableZone, bottom: tableZone.top + i }, sheet.cells, styles);
40785
- }
40786
- for (let i = 0; i < table.totalsRowCount; i++) {
40787
- applyStyleToZone(TABLE_HIGHLIGHTED_CELL_STYLE, { ...tableZone, top: tableZone.bottom - i }, sheet.cells, styles);
40788
- }
40789
- if (table.style?.showFirstColumn) {
40790
- applyStyleToZone(TABLE_HIGHLIGHTED_CELL_STYLE, { ...tableZone, right: tableZone.left }, sheet.cells, styles);
40791
- }
40792
- if (table.style?.showLastColumn) {
40793
- applyStyleToZone(TABLE_HIGHLIGHTED_CELL_STYLE, { ...tableZone, left: tableZone.right }, sheet.cells, styles);
40794
- }
40795
- // Table borders
40796
- // Borders at : table outline + col(/row) if showColumnStripes(/showRowStripes) + border above totalRow
40797
- for (let col = tableZone.left; col <= tableZone.right; col++) {
40798
- for (let row = tableZone.top; row <= tableZone.bottom; row++) {
40799
- const xc = toXC(col, row);
40800
- const cell = sheet.cells[xc];
40801
- const border = {
40802
- left: col === tableZone.left || table.style?.showColumnStripes
40803
- ? TABLE_BORDER_STYLE
40804
- : undefined,
40805
- right: col === tableZone.right ? TABLE_BORDER_STYLE : undefined,
40806
- top: row === tableZone.top ||
40807
- table.style?.showRowStripes ||
40808
- row > tableZone.bottom - table.totalsRowCount
40809
- ? TABLE_BORDER_STYLE
40810
- : undefined,
40811
- bottom: row === tableZone.bottom ? TABLE_BORDER_STYLE : undefined,
40812
- };
40813
- const newBorder = cell?.border ? { ...borders[cell.border], ...border } : border;
40814
- let borderIndex = borders.findIndex((border) => deepEquals(border, newBorder));
40815
- if (borderIndex === -1) {
40816
- borderIndex = borders.length;
40817
- borders.push(newBorder);
40818
- }
40819
- if (cell) {
40820
- cell.border = borderIndex;
40821
- }
40822
- else {
40823
- sheet.cells[xc] = { border: borderIndex };
40824
- }
40825
- }
40826
- }
40827
- }
40828
- }
40829
- convertedData.styles = arrayToObject(styles);
40830
- convertedData.borders = arrayToObject(borders);
41036
+ function convertTableConfig(table) {
41037
+ const styleId = table.style?.name || "";
41038
+ return {
41039
+ hasFilters: table.autoFilter !== undefined,
41040
+ numberOfHeaders: table.headerRowCount,
41041
+ totalRow: table.totalsRowCount > 0,
41042
+ firstColumn: table.style?.showFirstColumn || false,
41043
+ lastColumn: table.style?.showLastColumn || false,
41044
+ bandedRows: table.style?.showRowStripes || false,
41045
+ bandedColumns: table.style?.showColumnStripes || false,
41046
+ styleId: TABLE_PRESETS[styleId] ? styleId : DEFAULT_TABLE_CONFIG.styleId,
41047
+ };
40831
41048
  }
40832
- /**
40833
- * Apply a style to all the cells in the zone. The applied style WILL NOT overwrite values in existing style of the cell.
40834
- *
40835
- * If a style that was not in the styles array was applied, push it into the style array.
40836
- */
40837
- function applyStyleToZone(appliedStyle, zone, cells, styles) {
40838
- for (let col = zone.left; col <= zone.right; col++) {
40839
- for (let row = zone.top; row <= zone.bottom; row++) {
40840
- const xc = toXC(col, row);
40841
- const cell = cells[xc];
40842
- const newStyle = cell?.style ? { ...styles[cell.style], ...appliedStyle } : appliedStyle;
40843
- let styleIndex = styles.findIndex((style) => deepEquals(style, newStyle));
40844
- if (styleIndex === -1) {
40845
- styleIndex = styles.length;
40846
- styles.push(newStyle);
40847
- }
40848
- if (cell) {
40849
- cell.style = styleIndex;
40850
- }
40851
- else {
40852
- cells[xc] = { style: styleIndex };
40853
- }
40854
- }
40855
- }
41049
+ function convertPivotTableConfig(pivotTable) {
41050
+ return {
41051
+ hasFilters: false,
41052
+ numberOfHeaders: pivotTable.location.firstDataRow,
41053
+ totalRow: pivotTable.rowGrandTotals,
41054
+ firstColumn: true,
41055
+ lastColumn: pivotTable.style?.showLastColumn || false,
41056
+ bandedRows: pivotTable.style?.showRowStripes || false,
41057
+ bandedColumns: pivotTable.style?.showColStripes || false,
41058
+ styleId: DEFAULT_TABLE_CONFIG.styleId,
41059
+ };
40856
41060
  }
40857
41061
  /**
40858
41062
  * In all the sheets, replace the table-only references in the formula cells with standard references.
@@ -41021,7 +41225,7 @@ stores.inject(MyMetaStore, storeInstance);
41021
41225
  fillId: 0,
41022
41226
  numFmtId: 0,
41023
41227
  borderId: 0,
41024
- alignment: { vertical: "bottom" },
41228
+ alignment: {},
41025
41229
  },
41026
41230
  ],
41027
41231
  fonts: [
@@ -41029,7 +41233,7 @@ stores.inject(MyMetaStore, storeInstance);
41029
41233
  size: DEFAULT_FONT_SIZE,
41030
41234
  family: 2,
41031
41235
  color: { rgb: "000000" },
41032
- name: "Calibri",
41236
+ name: "Arial",
41033
41237
  },
41034
41238
  ],
41035
41239
  fills: [{ reservedAttribute: "none" }, { reservedAttribute: "gray125" }],
@@ -41321,9 +41525,10 @@ stores.inject(MyMetaStore, storeInstance);
41321
41525
  }
41322
41526
  else {
41323
41527
  rgb = this.extractAttr(colorElement, "rgb")?.asString();
41528
+ rgb = rgb === DEFAULT_SYSTEM_COLOR ? undefined : rgb;
41324
41529
  }
41325
41530
  const color = {
41326
- rgb,
41531
+ rgb: rgb || defaultColor,
41327
41532
  auto: this.extractAttr(colorElement, "auto")?.asBool(),
41328
41533
  indexed: this.extractAttr(colorElement, "indexed")?.asNum(),
41329
41534
  tint: this.extractAttr(colorElement, "tint")?.asNum(),
@@ -41760,20 +41965,48 @@ stores.inject(MyMetaStore, storeInstance);
41760
41965
  // pivotTableDefinition elements.
41761
41966
  { query: ":root", parent: this.rootFile.file.xml }, (pivotElement) => {
41762
41967
  return {
41763
- displayName: this.extractAttr(pivotElement, "name", { required: true }).asString(),
41764
- id: this.extractAttr(pivotElement, "name", { required: true }).asString(),
41765
- ref: this.extractChildAttr(pivotElement, "location", "ref", {
41968
+ name: this.extractAttr(pivotElement, "name", { required: true }).asString(),
41969
+ rowGrandTotals: this.extractAttr(pivotElement, "rowGrandTotals", {
41970
+ default: true,
41971
+ }).asBool(),
41972
+ location: this.extractPivotLocation(pivotElement),
41973
+ style: this.extractPivotStyleInfo(pivotElement),
41974
+ };
41975
+ })[0];
41976
+ }
41977
+ extractPivotLocation(pivotElement) {
41978
+ return this.mapOnElements({ query: "location", parent: pivotElement }, (pivotStyleElement) => {
41979
+ return {
41980
+ ref: this.extractAttr(pivotStyleElement, "ref", { required: true }).asString(),
41981
+ firstHeaderRow: this.extractAttr(pivotStyleElement, "firstHeaderRow", {
41982
+ required: true,
41983
+ }).asNum(),
41984
+ firstDataRow: this.extractAttr(pivotStyleElement, "firstDataRow", {
41985
+ required: true,
41986
+ }).asNum(),
41987
+ firstDataCol: this.extractAttr(pivotStyleElement, "firstDataCol", {
41766
41988
  required: true,
41767
- }).asString(),
41768
- headerRowCount: this.extractChildAttr(pivotElement, "location", "firstDataRow", {
41769
- default: 0,
41770
41989
  }).asNum(),
41771
- totalsRowCount: 1,
41772
- cols: [],
41773
- style: {
41774
- showFirstColumn: true,
41775
- showRowStripes: true,
41776
- },
41990
+ };
41991
+ })[0];
41992
+ }
41993
+ extractPivotStyleInfo(pivotElement) {
41994
+ return this.mapOnElements({ query: "pivotTableStyleInfo", parent: pivotElement }, (pivotStyleElement) => {
41995
+ return {
41996
+ name: this.extractAttr(pivotStyleElement, "name", { required: true }).asString(),
41997
+ showRowHeaders: this.extractAttr(pivotStyleElement, "showRowHeaders", {
41998
+ required: true,
41999
+ }).asBool(),
42000
+ showColHeaders: this.extractAttr(pivotStyleElement, "showColHeaders", {
42001
+ required: true,
42002
+ }).asBool(),
42003
+ showRowStripes: this.extractAttr(pivotStyleElement, "showRowStripes", {
42004
+ required: true,
42005
+ }).asBool(),
42006
+ showColStripes: this.extractAttr(pivotStyleElement, "showColStripes", {
42007
+ required: true,
42008
+ }).asBool(),
42009
+ showLastColumn: this.extractAttr(pivotStyleElement, "showLastColumn")?.asBool(),
41777
42010
  };
41778
42011
  })[0];
41779
42012
  }
@@ -41870,7 +42103,8 @@ stores.inject(MyMetaStore, storeInstance);
41870
42103
  cfs: this.extractConditionalFormats(),
41871
42104
  figures: this.extractFigures(sheetElement),
41872
42105
  hyperlinks: this.extractHyperLinks(sheetElement),
41873
- tables: [...this.extractTables(sheetElement), ...this.extractPivotTables()],
42106
+ tables: this.extractTables(sheetElement),
42107
+ pivotTables: this.extractPivotTables(),
41874
42108
  isVisible: sheetWorkbookInfo.state === "visible" ? true : false,
41875
42109
  };
41876
42110
  })[0];
@@ -42480,6 +42714,8 @@ stores.inject(MyMetaStore, storeInstance);
42480
42714
  if (!data) {
42481
42715
  return createEmptyWorkbookData();
42482
42716
  }
42717
+ console.group("Loading data");
42718
+ const start = performance.now();
42483
42719
  if (data["[Content_Types].xml"]) {
42484
42720
  const reader = new XlsxReader(data);
42485
42721
  data = reader.convertXlsx();
@@ -42492,17 +42728,22 @@ stores.inject(MyMetaStore, storeInstance);
42492
42728
  // apply migrations, if needed
42493
42729
  if ("version" in data) {
42494
42730
  if (data.version < CURRENT_VERSION) {
42731
+ console.info("Migrating data from version", data.version);
42495
42732
  data = migrate(data);
42496
42733
  }
42497
42734
  }
42498
42735
  data = repairData(data);
42736
+ console.info("Data loaded in", performance.now() - start, "ms");
42737
+ console.groupEnd();
42499
42738
  return data;
42500
42739
  }
42501
42740
  function migrate(data) {
42741
+ const start = performance.now();
42502
42742
  const index = MIGRATIONS.findIndex((m) => m.from === data.version);
42503
42743
  for (let i = index; i < MIGRATIONS.length; i++) {
42504
42744
  data = MIGRATIONS[i].applyMigration(data);
42505
42745
  }
42746
+ console.info("Data migrated in", performance.now() - start, "ms");
42506
42747
  return data;
42507
42748
  }
42508
42749
  const MIGRATIONS = [
@@ -43014,6 +43255,7 @@ stores.inject(MyMetaStore, storeInstance);
43014
43255
  settings: { locale: DEFAULT_LOCALE },
43015
43256
  pivots: {},
43016
43257
  pivotNextId: 1,
43258
+ customTableStyles: {},
43017
43259
  };
43018
43260
  return data;
43019
43261
  }
@@ -43222,7 +43464,7 @@ stores.inject(MyMetaStore, storeInstance);
43222
43464
  this.clearBorders(cmd.sheetId, cmd.target);
43223
43465
  break;
43224
43466
  case "REMOVE_COLUMNS_ROWS":
43225
- for (let el of cmd.elements) {
43467
+ for (let el of [...cmd.elements].sort((a, b) => b - a)) {
43226
43468
  if (cmd.dimension === "COL") {
43227
43469
  this.shiftBordersHorizontally(cmd.sheetId, el + 1, -1);
43228
43470
  }
@@ -47490,10 +47732,9 @@ stores.inject(MyMetaStore, storeInstance);
47490
47732
  for (const tableId in tables) {
47491
47733
  const table = tables[tableId];
47492
47734
  if (table && cmd.target.some((zone) => isZoneInside(table.range.zone, zone))) {
47493
- delete tables[tableId];
47735
+ this.dispatch("REMOVE_TABLE", { sheetId: cmd.sheetId, target: [table.range.zone] });
47494
47736
  }
47495
47737
  }
47496
- this.history.update("tables", cmd.sheetId, tables);
47497
47738
  break;
47498
47739
  }
47499
47740
  }
@@ -47590,9 +47831,6 @@ stores.inject(MyMetaStore, storeInstance);
47590
47831
  if (config.numberOfHeaders !== undefined && config.numberOfHeaders < 0) {
47591
47832
  return "InvalidTableConfig" /* CommandResult.InvalidTableConfig */;
47592
47833
  }
47593
- if (config.styleId && !TABLE_PRESETS[config.styleId]) {
47594
- return "InvalidTableConfig" /* CommandResult.InvalidTableConfig */;
47595
- }
47596
47834
  if (config.hasFilters && config.numberOfHeaders === 0) {
47597
47835
  return "InvalidTableConfig" /* CommandResult.InvalidTableConfig */;
47598
47836
  }
@@ -47810,7 +48048,12 @@ stores.inject(MyMetaStore, storeInstance);
47810
48048
  }
47811
48049
  }
47812
48050
  exportForExcel(data) {
47813
- this.export(data);
48051
+ for (const sheet of data.sheets) {
48052
+ for (const table of this.getCoreTables(sheet.id)) {
48053
+ const range = zoneToXc(table.range.zone);
48054
+ sheet.tables.push({ range, filters: [], config: table.config });
48055
+ }
48056
+ }
47814
48057
  }
47815
48058
  }
47816
48059
 
@@ -48681,6 +48924,108 @@ stores.inject(MyMetaStore, storeInstance);
48681
48924
  }
48682
48925
  }
48683
48926
 
48927
+ class TableStylePlugin extends CorePlugin {
48928
+ static getters = [
48929
+ "getNewCustomTableStyleName",
48930
+ "getTableStyle",
48931
+ "getTableStyles",
48932
+ "isTableStyleEditable",
48933
+ ];
48934
+ styles = {};
48935
+ allowDispatch(cmd) {
48936
+ switch (cmd.type) {
48937
+ case "CREATE_TABLE":
48938
+ case "UPDATE_TABLE":
48939
+ if (cmd.config?.styleId && !this.styles[cmd.config.styleId]) {
48940
+ return "InvalidTableConfig" /* CommandResult.InvalidTableConfig */;
48941
+ }
48942
+ break;
48943
+ case "CREATE_TABLE_STYLE":
48944
+ if (!TABLE_STYLES_TEMPLATES[cmd.templateName]) {
48945
+ return "InvalidTableStyle" /* CommandResult.InvalidTableStyle */;
48946
+ }
48947
+ try {
48948
+ toHex(cmd.primaryColor);
48949
+ }
48950
+ catch (e) {
48951
+ return "InvalidTableStyle" /* CommandResult.InvalidTableStyle */;
48952
+ }
48953
+ break;
48954
+ }
48955
+ return "Success" /* CommandResult.Success */;
48956
+ }
48957
+ handle(cmd) {
48958
+ switch (cmd.type) {
48959
+ case "CREATE_TABLE_STYLE":
48960
+ const style = buildTableStyle(cmd.tableStyleName, cmd.templateName, cmd.primaryColor);
48961
+ this.history.update("styles", cmd.tableStyleId, style);
48962
+ break;
48963
+ case "REMOVE_TABLE_STYLE":
48964
+ const styles = { ...this.styles };
48965
+ delete styles[cmd.tableStyleId];
48966
+ this.history.update("styles", styles);
48967
+ for (const sheetId of this.getters.getSheetIds()) {
48968
+ for (const table of this.getters.getCoreTables(sheetId)) {
48969
+ if (table.config.styleId === cmd.tableStyleId) {
48970
+ this.dispatch("UPDATE_TABLE", {
48971
+ sheetId,
48972
+ zone: table.range.zone,
48973
+ config: { styleId: DEFAULT_TABLE_CONFIG.styleId },
48974
+ });
48975
+ }
48976
+ }
48977
+ }
48978
+ break;
48979
+ }
48980
+ }
48981
+ getTableStyle(styleId) {
48982
+ if (!this.styles[styleId]) {
48983
+ throw new Error(`Table style ${styleId} does not exist`);
48984
+ }
48985
+ return this.styles[styleId];
48986
+ }
48987
+ getTableStyles() {
48988
+ return this.styles;
48989
+ }
48990
+ getNewCustomTableStyleName() {
48991
+ let name = _t("Custom Table Style");
48992
+ const styleNames = new Set(Object.values(this.styles).map((style) => style.displayName));
48993
+ if (!styleNames.has(name)) {
48994
+ return name;
48995
+ }
48996
+ let i = 2;
48997
+ while (styleNames.has(`${name} ${i}`)) {
48998
+ i++;
48999
+ }
49000
+ return `${name} ${i}`;
49001
+ }
49002
+ isTableStyleEditable(styleId) {
49003
+ return !TABLE_PRESETS[styleId];
49004
+ }
49005
+ import(data) {
49006
+ for (const presetStyleId in TABLE_PRESETS) {
49007
+ this.styles[presetStyleId] = TABLE_PRESETS[presetStyleId];
49008
+ }
49009
+ for (const styleId in data.customTableStyles) {
49010
+ const styleData = data.customTableStyles[styleId];
49011
+ this.styles[styleId] = buildTableStyle(styleData.displayName, styleData.templateName, styleData.primaryColor);
49012
+ }
49013
+ }
49014
+ export(data) {
49015
+ const exportedStyles = {};
49016
+ for (const styleId in this.styles) {
49017
+ if (!TABLE_PRESETS[styleId]) {
49018
+ exportedStyles[styleId] = {
49019
+ displayName: this.styles[styleId].displayName,
49020
+ templateName: this.styles[styleId].templateName,
49021
+ primaryColor: this.styles[styleId].primaryColor,
49022
+ };
49023
+ }
49024
+ }
49025
+ data.customTableStyles = exportedStyles;
49026
+ }
49027
+ }
49028
+
48684
49029
  /**
48685
49030
  * UI plugins handle any transient data required to display a spreadsheet.
48686
49031
  * They can draw on the grid canvas.
@@ -48741,19 +49086,17 @@ stores.inject(MyMetaStore, storeInstance);
48741
49086
  * function for which this parameter is used, we just return the string of the parameter.
48742
49087
  * The `compute` of the formula's function must process it completely
48743
49088
  */
48744
- refFn(range, isMeta, functionName, paramNumber) {
48745
- this.assertRangeValid(range);
49089
+ refFn(range, isMeta) {
49090
+ const rangeError = this.getRangeError(range);
49091
+ if (rangeError) {
49092
+ return rangeError;
49093
+ }
48746
49094
  if (isMeta) {
48747
49095
  // Use zoneToXc of zone instead of getRangeString to avoid sending unbounded ranges
48748
49096
  const sheetName = this.getters.getSheetName(range.sheetId);
48749
49097
  return { value: getFullReference(sheetName, zoneToXc(range.zone)) };
48750
49098
  }
48751
- // if the formula definition could have accepted a range, we would pass through the _range function and not here
48752
- if (range.zone.bottom !== range.zone.top || range.zone.left !== range.zone.right) {
48753
- throw new EvaluationError(paramNumber
48754
- ? _t("Function %s expects the parameter %s to be a single value or a single cell reference, not a range.", functionName.toString(), paramNumber.toString())
48755
- : _t("Function %s expects its parameters to be single values or single cell references, not ranges.", functionName.toString()));
48756
- }
49099
+ // the compiler guarantees only single cell ranges reach this part of the code
48757
49100
  const position = { sheetId: range.sheetId, col: range.zone.left, row: range.zone.top };
48758
49101
  return this.computeCell(position);
48759
49102
  }
@@ -48766,7 +49109,10 @@ stores.inject(MyMetaStore, storeInstance);
48766
49109
  * that are actually present in the grid.
48767
49110
  */
48768
49111
  range(range) {
48769
- this.assertRangeValid(range);
49112
+ const rangeError = this.getRangeError(range);
49113
+ if (rangeError) {
49114
+ return [[rangeError]];
49115
+ }
48770
49116
  const sheetId = range.sheetId;
48771
49117
  const zone = range.zone;
48772
49118
  // Performance issue: Avoid fetching data on positions that are out of the spreadsheet
@@ -48796,13 +49142,14 @@ stores.inject(MyMetaStore, storeInstance);
48796
49142
  this.rangeCache[cacheKey] = matrix;
48797
49143
  return matrix;
48798
49144
  }
48799
- assertRangeValid(range) {
49145
+ getRangeError(range) {
48800
49146
  if (!isZoneValid(range.zone)) {
48801
- throw new InvalidReferenceError();
49147
+ return new InvalidReferenceError();
48802
49148
  }
48803
49149
  if (range.invalidSheetName) {
48804
- throw new EvaluationError(_t("Invalid sheet name: %s", range.invalidSheetName));
49150
+ return new EvaluationError(_t("Invalid sheet name: %s", range.invalidSheetName));
48805
49151
  }
49152
+ return undefined;
48806
49153
  }
48807
49154
  }
48808
49155
 
@@ -49604,7 +49951,7 @@ stores.inject(MyMetaStore, storeInstance);
49604
49951
  }
49605
49952
  }
49606
49953
  /**
49607
- * Return the cell and all cells that depend on it,
49954
+ * Return all the cells that depend on the provided ranges,
49608
49955
  * in the correct order they should be evaluated.
49609
49956
  * This is called a topological ordering (excluding cycles)
49610
49957
  */
@@ -49615,11 +49962,19 @@ stores.inject(MyMetaStore, storeInstance);
49615
49962
  const range = queue.pop();
49616
49963
  visited.addMany(positions(range.zone).map((position) => ({ sheetId: range.sheetId, ...position })));
49617
49964
  const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
49965
+ const nextInQueue = {};
49618
49966
  for (const position of impactedPositions) {
49619
49967
  if (!visited.has(position)) {
49620
- queue.push({ sheetId: position.sheetId, zone: positionToZone(position) });
49968
+ if (!nextInQueue[position.sheetId]) {
49969
+ nextInQueue[position.sheetId] = [];
49970
+ }
49971
+ nextInQueue[position.sheetId].push(positionToZone(position));
49621
49972
  }
49622
49973
  }
49974
+ for (const sheetId in nextInQueue) {
49975
+ const zones = recomputeZones(nextInQueue[sheetId], []);
49976
+ queue.push(...zones.map((zone) => ({ sheetId, zone })));
49977
+ }
49623
49978
  }
49624
49979
  visited.deleteMany(ranges.flatMap((r) => positions(r.zone).map((position) => ({ sheetId: r.sheetId, ...position }))));
49625
49980
  return visited;
@@ -49943,6 +50298,7 @@ stores.inject(MyMetaStore, storeInstance);
49943
50298
  return new PositionSet(sheetSizes);
49944
50299
  }
49945
50300
  evaluateCells(positions) {
50301
+ const start = performance.now();
49946
50302
  const cellsToCompute = this.createEmptyPositionSet();
49947
50303
  cellsToCompute.addMany(positions);
49948
50304
  const arrayFormulasPositions = this.getArrayFormulasImpactedByChangesOf(positions);
@@ -49950,6 +50306,7 @@ stores.inject(MyMetaStore, storeInstance);
49950
50306
  cellsToCompute.addMany(arrayFormulasPositions);
49951
50307
  cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
49952
50308
  this.evaluate(cellsToCompute);
50309
+ console.info("evaluate Cells", performance.now() - start, "ms");
49953
50310
  }
49954
50311
  getArrayFormulasImpactedByChangesOf(positions) {
49955
50312
  const impactedPositions = this.createEmptyPositionSet();
@@ -49962,7 +50319,7 @@ stores.inject(MyMetaStore, storeInstance);
49962
50319
  }
49963
50320
  if (!content) {
49964
50321
  // The previous content could have blocked some array formulas
49965
- impactedPositions.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(position));
50322
+ impactedPositions.addMany(this.getArrayFormulasBlockedBy(position));
49966
50323
  }
49967
50324
  }
49968
50325
  return impactedPositions;
@@ -49984,8 +50341,10 @@ stores.inject(MyMetaStore, storeInstance);
49984
50341
  });
49985
50342
  }
49986
50343
  evaluateAllCells() {
50344
+ const start = performance.now();
49987
50345
  this.evaluatedCells = new PositionMap();
49988
50346
  this.evaluate(this.getAllCells());
50347
+ console.info("evaluate all cells", performance.now() - start, "ms");
49989
50348
  }
49990
50349
  evaluateFormula(sheetId, formulaString) {
49991
50350
  const compiledFormula = compile(formulaString);
@@ -50005,14 +50364,23 @@ stores.inject(MyMetaStore, storeInstance);
50005
50364
  positions.fillAllPositions();
50006
50365
  return positions;
50007
50366
  }
50008
- getArrayFormulasBlockedByOrSpreadingOn(position) {
50367
+ /**
50368
+ * Return the position of formulas blocked by the given position
50369
+ * as well as all their dependencies.
50370
+ */
50371
+ getArrayFormulasBlockedBy(position) {
50009
50372
  if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
50010
50373
  return [];
50011
50374
  }
50012
50375
  const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
50013
50376
  const positions = this.createEmptyPositionSet();
50014
50377
  positions.addMany(arrayFormulas);
50015
- positions.addMany(this.getCellsDependingOn(arrayFormulas));
50378
+ const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
50379
+ if (arrayFormulaPosition) {
50380
+ // ignore the formula spreading on the position. Keep only the blocked ones
50381
+ positions.delete(arrayFormulaPosition);
50382
+ }
50383
+ positions.addMany(this.getCellsDependingOn(positions));
50016
50384
  return positions;
50017
50385
  }
50018
50386
  nextPositionsToUpdate = new PositionSet({});
@@ -50153,7 +50521,7 @@ stores.inject(MyMetaStore, storeInstance);
50153
50521
  }
50154
50522
  this.evaluatedCells.delete(child);
50155
50523
  this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
50156
- this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
50524
+ this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedBy(child));
50157
50525
  }
50158
50526
  }
50159
50527
  // ----------------------------------------------------------
@@ -50658,7 +51026,7 @@ stores.inject(MyMetaStore, storeInstance);
50658
51026
  const tables = this.getters.getTables(sheetId);
50659
51027
  return tables.flatMap((table) => {
50660
51028
  const config = table.config;
50661
- const style = TABLE_PRESETS[config.styleId];
51029
+ const style = this.getters.getTableStyle(config.styleId);
50662
51030
  return [
50663
51031
  this.getTableStyleElementColors(style.wholeTable),
50664
51032
  config.numberOfHeaders > 0 ? this.getTableStyleElementColors(style.headerRow) : [],
@@ -52600,6 +52968,7 @@ stores.inject(MyMetaStore, storeInstance);
52600
52968
  otRegistry.addTransformation("REMOVE_COLUMNS_ROWS", ["FREEZE_COLUMNS", "FREEZE_ROWS"], freezeTransformation);
52601
52969
  otRegistry.addTransformation("ADD_COLUMNS_ROWS", ["UPDATE_TABLE"], updateTableTransformation);
52602
52970
  otRegistry.addTransformation("REMOVE_COLUMNS_ROWS", ["UPDATE_TABLE"], updateTableTransformation);
52971
+ otRegistry.addTransformation("REMOVE_TABLE_STYLE", ["CREATE_TABLE", "UPDATE_TABLE"], removeTableStyleTransform);
52603
52972
  otRegistry.addTransformation("ADD_COLUMNS_ROWS", ["GROUP_HEADERS", "UNGROUP_HEADERS", "FOLD_HEADER_GROUP", "UNFOLD_HEADER_GROUP"], groupHeadersTransformation);
52604
52973
  otRegistry.addTransformation("REMOVE_COLUMNS_ROWS", ["GROUP_HEADERS", "UNGROUP_HEADERS", "FOLD_HEADER_GROUP", "UNFOLD_HEADER_GROUP"], groupHeadersTransformation);
52605
52974
  otRegistry.addTransformation("REMOVE_PIVOT", ["RENAME_PIVOT", "DUPLICATE_PIVOT", "INSERT_PIVOT", "UPDATE_PIVOT"], pivotTransformation);
@@ -52696,6 +53065,15 @@ stores.inject(MyMetaStore, storeInstance);
52696
53065
  : undefined;
52697
53066
  return { ...toTransform, newTableRange, zone: newCmdZone };
52698
53067
  }
53068
+ function removeTableStyleTransform(toTransform, executed) {
53069
+ if (toTransform.config?.styleId !== executed.tableStyleId) {
53070
+ return toTransform;
53071
+ }
53072
+ return {
53073
+ ...toTransform,
53074
+ config: { ...toTransform.config, styleId: DEFAULT_TABLE_CONFIG.styleId },
53075
+ };
53076
+ }
52699
53077
  /**
52700
53078
  * Transform ADD_COLUMNS_ROWS command if some headers were added/removed
52701
53079
  */
@@ -53078,11 +53456,14 @@ stores.inject(MyMetaStore, storeInstance);
53078
53456
  this.transportService.onNewMessage(this.clientId, this.onMessageReceived.bind(this));
53079
53457
  }
53080
53458
  loadInitialMessages(messages) {
53459
+ const start = performance.now();
53460
+ const numberOfCommands = messages.reduce((acc, message) => acc + (message.type === "REMOTE_REVISION" ? message.commands.length : 1), 0);
53081
53461
  this.isReplayingInitialRevisions = true;
53082
53462
  for (const message of messages) {
53083
53463
  this.onMessageReceived(message);
53084
53464
  }
53085
53465
  this.isReplayingInitialRevisions = false;
53466
+ console.info("Replayed", numberOfCommands, "commands in", performance.now() - start, "ms");
53086
53467
  }
53087
53468
  /**
53088
53469
  * Notify the server that the user client left the collaborative session
@@ -54155,7 +54536,7 @@ stores.inject(MyMetaStore, storeInstance);
54155
54536
  }
54156
54537
  }
54157
54538
 
54158
- class TableStylePlugin extends UIPlugin {
54539
+ class TableComputedStylePlugin extends UIPlugin {
54159
54540
  static getters = ["getCellTableStyle", "getCellTableBorder"];
54160
54541
  tableStyles = {};
54161
54542
  handle(cmd) {
@@ -54166,7 +54547,12 @@ stores.inject(MyMetaStore, storeInstance);
54166
54547
  return;
54167
54548
  }
54168
54549
  if (doesCommandInvalidatesTableStyle(cmd)) {
54169
- delete this.tableStyles[cmd.sheetId];
54550
+ if ("sheetId" in cmd) {
54551
+ delete this.tableStyles[cmd.sheetId];
54552
+ }
54553
+ else {
54554
+ this.tableStyles = {};
54555
+ }
54170
54556
  return;
54171
54557
  }
54172
54558
  }
@@ -54199,7 +54585,8 @@ stores.inject(MyMetaStore, storeInstance);
54199
54585
  computeTableStyle(sheetId, table) {
54200
54586
  return lazy(() => {
54201
54587
  const { config, numberOfCols, numberOfRows } = this.getTableRuntimeConfig(sheetId, table);
54202
- const relativeTableStyle = getComputedTableStyle(config, numberOfCols, numberOfRows);
54588
+ const style = this.getters.getTableStyle(table.config.styleId);
54589
+ const relativeTableStyle = getComputedTableStyle(config, style, numberOfCols, numberOfRows);
54203
54590
  // Return the style with sheet coordinates instead of tables coordinates
54204
54591
  const mapping = this.getTableMapping(sheetId, table);
54205
54592
  const absoluteTableStyle = { borders: {}, styles: {} };
@@ -54300,6 +54687,8 @@ stores.inject(MyMetaStore, storeInstance);
54300
54687
  "UPDATE_FILTER",
54301
54688
  "REMOVE_TABLE",
54302
54689
  "RESIZE_TABLE",
54690
+ "CREATE_TABLE_STYLE",
54691
+ "REMOVE_TABLE_STYLE",
54303
54692
  ];
54304
54693
  const invalidateTableStyleCommandsSet = new Set(invalidateTableStyleCommands);
54305
54694
  function doesCommandInvalidatesTableStyle(cmd) {
@@ -54319,8 +54708,14 @@ stores.inject(MyMetaStore, storeInstance);
54319
54708
  return;
54320
54709
  }
54321
54710
  if (doesCommandInvalidatesTableStyle(cmd)) {
54322
- delete this.styles[cmd.sheetId];
54323
- delete this.borders[cmd.sheetId];
54711
+ if ("sheetId" in cmd) {
54712
+ delete this.styles[cmd.sheetId];
54713
+ delete this.borders[cmd.sheetId];
54714
+ }
54715
+ else {
54716
+ this.styles = {};
54717
+ this.borders = {};
54718
+ }
54324
54719
  return;
54325
54720
  }
54326
54721
  if (invalidateCFEvaluationCommands.has(cmd.type)) {
@@ -55669,9 +56064,7 @@ stores.inject(MyMetaStore, storeInstance);
55669
56064
  };
55670
56065
  const filteredValues = this.getFilterHiddenValues(position);
55671
56066
  const filter = this.getters.getFilter(position);
55672
- if (!filter)
55673
- continue;
55674
- const valuesInFilterZone = filter.filteredRange
56067
+ const valuesInFilterZone = filter?.filteredRange
55675
56068
  ? positions(filter.filteredRange.zone).map((position) => this.getters.getEvaluatedCell({ sheetId, ...position }).formattedValue)
55676
56069
  : [];
55677
56070
  if (filteredValues.length) {
@@ -55684,17 +56077,12 @@ stores.inject(MyMetaStore, storeInstance);
55684
56077
  displayBlanks: !filteredValues.includes("") && valuesInFilterZone.some((val) => !val),
55685
56078
  });
55686
56079
  }
55687
- // In xlsx, filter header should ALWAYS be a string and should be unique in the table
55688
- const headerPosition = {
55689
- col: filter.col,
55690
- row: filter.rangeWithHeaders.zone.top,
55691
- sheetId,
55692
- };
55693
- const headerString = this.getters.getEvaluatedCell(headerPosition).formattedValue;
56080
+ // In xlsx, column header should ALWAYS be a string and should be unique in the table
56081
+ const headerString = this.getters.getEvaluatedCell(position).formattedValue;
55694
56082
  const headerName = this.getUniqueColNameForExcel(i, headerString, headerNames);
55695
56083
  headerNames.push(headerName);
55696
- sheetData.cells[toXC(headerPosition.col, headerPosition.row)] = {
55697
- ...sheetData.cells[toXC(headerPosition.col, headerPosition.row)],
56084
+ sheetData.cells[toXC(position.col, position.row)] = {
56085
+ ...sheetData.cells[toXC(position.col, position.row)],
55698
56086
  content: headerName,
55699
56087
  value: headerName,
55700
56088
  isFormula: false,
@@ -55741,7 +56129,6 @@ stores.inject(MyMetaStore, storeInstance);
55741
56129
  "getSelection",
55742
56130
  "getActivePosition",
55743
56131
  "getSheetPosition",
55744
- "isSelected",
55745
56132
  "isSingleColSelected",
55746
56133
  "getElementsFromSelection",
55747
56134
  "tryGetActiveSheetId",
@@ -56023,9 +56410,6 @@ stores.inject(MyMetaStore, storeInstance);
56023
56410
  : this.getters.getNextVisibleCellPosition({ sheetId, col: 0, row: 0 });
56024
56411
  }
56025
56412
  }
56026
- isSelected(zone) {
56027
- return !!this.getters.getSelectedZones().find((z) => isEqual(z, zone));
56028
- }
56029
56413
  isSingleColSelected() {
56030
56414
  const selection = this.getters.getSelectedZones();
56031
56415
  if (selection.length !== 1 || selection[0].left !== selection[0].right) {
@@ -57409,7 +57793,8 @@ stores.inject(MyMetaStore, storeInstance);
57409
57793
  .add("figures", FigurePlugin)
57410
57794
  .add("chart", ChartPlugin)
57411
57795
  .add("image", ImagePlugin)
57412
- .add("pivot_core", PivotCorePlugin);
57796
+ .add("pivot_core", PivotCorePlugin)
57797
+ .add("tableStyle", TableStylePlugin);
57413
57798
  // Plugins which handle a specific feature, without handling any core commands
57414
57799
  const featurePluginRegistry = new Registry()
57415
57800
  .add("ui_sheet", SheetUIPlugin)
@@ -57430,8 +57815,8 @@ stores.inject(MyMetaStore, storeInstance);
57430
57815
  .add("selection", GridSelectionPlugin)
57431
57816
  .add("evaluation_filter", FilterEvaluationPlugin)
57432
57817
  .add("header_visibility_ui", HeaderVisibilityUIPlugin)
57433
- .add("table_style", TableStylePlugin)
57434
57818
  .add("cell_computed_style", CellComputedStylePlugin)
57819
+ .add("table_computed_style", TableComputedStylePlugin)
57435
57820
  .add("header_positions", HeaderPositionsUIPlugin)
57436
57821
  .add("viewport", SheetViewPlugin)
57437
57822
  .add("clipboard", ClipboardPlugin);
@@ -58249,6 +58634,7 @@ stores.inject(MyMetaStore, storeInstance);
58249
58634
  .o-icon {
58250
58635
  height: 18px;
58251
58636
  width: 18px;
58637
+ font-size: 18px;
58252
58638
  }
58253
58639
  }
58254
58640
  }
@@ -59790,6 +60176,8 @@ stores.inject(MyMetaStore, storeInstance);
59790
60176
  display: grid;
59791
60177
  grid-template-columns: auto 350px;
59792
60178
  color: #333;
60179
+ font-size: 14px;
60180
+
59793
60181
  input {
59794
60182
  background-color: white;
59795
60183
  }
@@ -59867,12 +60255,6 @@ stores.inject(MyMetaStore, storeInstance);
59867
60255
  grid-column: 1 / 3;
59868
60256
  }
59869
60257
 
59870
- .o-icon {
59871
- width: ${ICON_EDGE_LENGTH}px;
59872
- height: ${ICON_EDGE_LENGTH}px;
59873
- vertical-align: middle;
59874
- }
59875
-
59876
60258
  .o-cf-icon {
59877
60259
  width: ${CF_ICON_EDGE_LENGTH}px;
59878
60260
  height: ${CF_ICON_EDGE_LENGTH}px;
@@ -62775,20 +63157,14 @@ stores.inject(MyMetaStore, storeInstance);
62775
63157
  `;
62776
63158
  }
62777
63159
 
62778
- const TABLE_DEFAULT_ATTRS = [
62779
- ["name", "TableStyleLight8"],
62780
- ["showFirstColumn", "0"],
62781
- ["showLastColumn", "0"],
62782
- ["showRowStripes", "0"],
62783
- ["showColumnStripes", "0"],
62784
- ];
62785
- const TABLE_DEFAULT_STYLE = escapeXml /*xml*/ `<tableStyleInfo ${formatAttributes(TABLE_DEFAULT_ATTRS)}/>`;
62786
63160
  function createTable(table, tableId, sheetData) {
62787
63161
  const tableAttributes = [
62788
63162
  ["id", tableId],
62789
63163
  ["name", `Table${tableId}`],
62790
63164
  ["displayName", `Table${tableId}`],
62791
63165
  ["ref", table.range],
63166
+ ["headerRowCount", table.config.numberOfHeaders],
63167
+ ["totalsRowCount", table.config.totalRow ? 1 : 0],
62792
63168
  ["xmlns", NAMESPACE.table],
62793
63169
  ["xmlns:xr", NAMESPACE.revision],
62794
63170
  ["xmlns:xr3", NAMESPACE.revision3],
@@ -62796,9 +63172,9 @@ stores.inject(MyMetaStore, storeInstance);
62796
63172
  ];
62797
63173
  const xml = escapeXml /*xml*/ `
62798
63174
  <table ${formatAttributes(tableAttributes)}>
62799
- ${addAutoFilter(table)}
63175
+ ${table.config.hasFilters ? addAutoFilter(table) : ""}
62800
63176
  ${addTableColumns(table, sheetData)}
62801
- ${TABLE_DEFAULT_STYLE}
63177
+ ${addTableStyle(table)}
62802
63178
  </table>
62803
63179
  `;
62804
63180
  return parseXML(xml);
@@ -62850,6 +63226,16 @@ stores.inject(MyMetaStore, storeInstance);
62850
63226
  </tableColumns>
62851
63227
  `;
62852
63228
  }
63229
+ function addTableStyle(table) {
63230
+ const tableStyleAttrs = [
63231
+ ["name", table.config.styleId],
63232
+ ["showFirstColumn", table.config.firstColumn ? 1 : 0],
63233
+ ["showLastColumn", table.config.lastColumn ? 1 : 0],
63234
+ ["showRowStripes", table.config.bandedRows ? 1 : 0],
63235
+ ["showColumnStripes", table.config.bandedColumns ? 1 : 0],
63236
+ ];
63237
+ return escapeXml /*xml*/ `<tableStyleInfo ${formatAttributes(tableStyleAttrs)}/>`;
63238
+ }
62853
63239
 
62854
63240
  function addColumns(cols) {
62855
63241
  if (!Object.values(cols).length) {
@@ -62906,7 +63292,10 @@ stores.inject(MyMetaStore, storeInstance);
62906
63292
  const attributes = [["r", xc]];
62907
63293
  // style
62908
63294
  const id = normalizeStyle(construct, extractStyle(cell, data));
62909
- attributes.push(["s", id]);
63295
+ // don't add style if default
63296
+ if (id) {
63297
+ attributes.push(["s", id]);
63298
+ }
62910
63299
  let additionalAttrs = [];
62911
63300
  let cellNode = escapeXml ``;
62912
63301
  // Either formula or static value inside the cell
@@ -63404,6 +63793,8 @@ stores.inject(MyMetaStore, storeInstance);
63404
63793
  uiHandlers = [];
63405
63794
  coreHandlers = [];
63406
63795
  constructor(data = {}, config = {}, stateUpdateMessages = [], uuidGenerator = new UuidGenerator(), verboseImport = true) {
63796
+ const start = performance.now();
63797
+ console.group("Model creation");
63407
63798
  super();
63408
63799
  stateUpdateMessages = repairInitialMessages(data, stateUpdateMessages);
63409
63800
  const workbookData = load(data, verboseImport);
@@ -63473,12 +63864,17 @@ stores.inject(MyMetaStore, storeInstance);
63473
63864
  this.setupSessionEvents();
63474
63865
  this.joinSession();
63475
63866
  if (config.snapshotRequested) {
63867
+ const startSnapshot = performance.now();
63868
+ console.info("Snapshot requested");
63476
63869
  this.session.snapshot(this.exportData());
63477
63870
  this.garbageCollectExternalResources();
63871
+ console.info("Snapshot taken in", performance.now() - startSnapshot, "ms");
63478
63872
  }
63479
63873
  // mark all models as "raw", so they will not be turned into reactive objects
63480
63874
  // by owl, since we do not rely on reactivity
63481
63875
  owl.markRaw(this);
63876
+ console.info("Model created in", performance.now() - start, "ms");
63877
+ console.groupEnd();
63482
63878
  }
63483
63879
  joinSession() {
63484
63880
  this.session.join(this.config.client);
@@ -63693,11 +64089,16 @@ stores.inject(MyMetaStore, storeInstance);
63693
64089
  }
63694
64090
  this.status = 1 /* Status.Running */;
63695
64091
  const { changes, commands } = this.state.recordChanges(() => {
64092
+ const start = performance.now();
63696
64093
  if (isCoreCommand(command)) {
63697
64094
  this.state.addCommand(command);
63698
64095
  }
63699
64096
  this.dispatchToHandlers(this.handlers, command);
63700
64097
  this.finalize();
64098
+ const time = performance.now() - start;
64099
+ if (time > 5) {
64100
+ console.info(type, time, "ms");
64101
+ }
63701
64102
  });
63702
64103
  this.session.save(command, commands, changes);
63703
64104
  this.status = 0 /* Status.Ready */;
@@ -64146,9 +64547,9 @@ stores.inject(MyMetaStore, storeInstance);
64146
64547
  exports.tokenize = tokenize;
64147
64548
 
64148
64549
 
64149
- __info__.version = "17.3.0-alpha.5";
64150
- __info__.date = "2024-04-18T14:38:57.166Z";
64151
- __info__.hash = "cff0ef9";
64550
+ __info__.version = "17.3.0-alpha.6";
64551
+ __info__.date = "2024-04-26T07:39:53.611Z";
64552
+ __info__.hash = "f58a0d5";
64152
64553
 
64153
64554
 
64154
64555
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);