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