@odoo/o-spreadsheet 18.3.0-alpha.0 → 18.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * This file is generated by o-spreadsheet build tools. Do not edit it.
4
4
  * @see https://github.com/odoo/o-spreadsheet
5
- * @version 18.3.0-alpha.0
6
- * @date 2025-02-18T09:02:28.625Z
7
- * @hash 9b88da0
5
+ * @version 18.3.0-alpha.1
6
+ * @date 2025-02-25T06:00:14.885Z
7
+ * @hash be4d957
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -993,6 +993,9 @@
993
993
  }
994
994
  return newText;
995
995
  }
996
+ function isFormula(content) {
997
+ return content.startsWith("=") || content.startsWith("+");
998
+ }
996
999
 
997
1000
  const RBA_REGEX = /rgba?\(|\s+|\)/gi;
998
1001
  const HEX_MATCH = /^#([A-F\d]{2}){3,4}$/;
@@ -4459,7 +4462,7 @@
4459
4462
  * @param reverseSearch if true, search in the array starting from the end.
4460
4463
 
4461
4464
  */
4462
- function linearSearch(data, target, mode, numberOfValues, getValueInData, reverseSearch = false) {
4465
+ function linearSearch(data, target, mode, numberOfValues, getValueInData, lookupCaches, reverseSearch = false) {
4463
4466
  if (target === undefined || target.value === null) {
4464
4467
  return -1;
4465
4468
  }
@@ -4468,17 +4471,48 @@
4468
4471
  }
4469
4472
  const _target = normalizeValue(target.value);
4470
4473
  const getValue = reverseSearch
4471
- ? (data, i) => getValueInData(data, numberOfValues - i - 1)
4472
- : getValueInData;
4474
+ ? (data, i) => normalizeValue(getValueInData(data, numberOfValues - i - 1))
4475
+ : (data, i) => normalizeValue(getValueInData(data, i));
4476
+ // first check if the target is in the cache
4477
+ const isNotWildcardTarget = mode !== "wildcard" ||
4478
+ typeof _target !== "string" ||
4479
+ !(_target.includes("*") || _target.includes("?"));
4480
+ if (lookupCaches && isNotWildcardTarget) {
4481
+ const searchMode = reverseSearch ? "reverseSearch" : "forwardSearch";
4482
+ let cache = lookupCaches[searchMode].get(data);
4483
+ if (cache === undefined) {
4484
+ // build the cache for all the values
4485
+ cache = new Map();
4486
+ for (let i = 0; i < numberOfValues; i++) {
4487
+ const value = getValue(data, i) ?? null;
4488
+ if (!cache.has(value)) {
4489
+ cache.set(value, i);
4490
+ }
4491
+ }
4492
+ lookupCaches[searchMode].set(data, cache);
4493
+ }
4494
+ if (cache.has(_target)) {
4495
+ const resultIndex = cache.get(_target);
4496
+ return reverseSearch ? numberOfValues - resultIndex - 1 : resultIndex;
4497
+ }
4498
+ if (mode === "strict") {
4499
+ return -1;
4500
+ }
4501
+ }
4502
+ // else perform the linear search
4503
+ const resultIndex = _linearSearch(data, _target, mode, numberOfValues, getValue);
4504
+ return reverseSearch && resultIndex !== -1 ? numberOfValues - resultIndex - 1 : resultIndex;
4505
+ }
4506
+ function _linearSearch(data, _target, mode, numberOfValues, getNormalizeValue) {
4473
4507
  let indexMatchTarget = (i) => {
4474
- return normalizeValue(getValue(data, i)) === _target;
4508
+ return getNormalizeValue(data, i) === _target;
4475
4509
  };
4476
4510
  if (mode === "wildcard" &&
4477
4511
  typeof _target === "string" &&
4478
4512
  (_target.includes("*") || _target.includes("?"))) {
4479
4513
  const regExp = wildcardToRegExp(_target);
4480
4514
  indexMatchTarget = (i) => {
4481
- const value = normalizeValue(getValue(data, i));
4515
+ const value = getNormalizeValue(data, i);
4482
4516
  if (typeof value === "string") {
4483
4517
  return regExp.test(value);
4484
4518
  }
@@ -4489,7 +4523,7 @@
4489
4523
  let closestMatchIndex = -1;
4490
4524
  if (mode === "nextSmaller") {
4491
4525
  indexMatchTarget = (i) => {
4492
- const value = normalizeValue(getValue(data, i));
4526
+ const value = getNormalizeValue(data, i);
4493
4527
  if ((!closestMatch && compareCellValues(_target, value) >= 0) ||
4494
4528
  (compareCellValues(_target, value) >= 0 && compareCellValues(value, closestMatch) > 0)) {
4495
4529
  closestMatch = value;
@@ -4500,7 +4534,7 @@
4500
4534
  }
4501
4535
  if (mode === "nextGreater") {
4502
4536
  indexMatchTarget = (i) => {
4503
- const value = normalizeValue(getValue(data, i));
4537
+ const value = getNormalizeValue(data, i);
4504
4538
  if ((!closestMatch && compareCellValues(_target, value) <= 0) ||
4505
4539
  (compareCellValues(_target, value) <= 0 && compareCellValues(value, closestMatch) < 0)) {
4506
4540
  closestMatch = value;
@@ -4511,12 +4545,10 @@
4511
4545
  }
4512
4546
  for (let i = 0; i < numberOfValues; i++) {
4513
4547
  if (indexMatchTarget(i)) {
4514
- return reverseSearch ? numberOfValues - i - 1 : i;
4548
+ return i;
4515
4549
  }
4516
4550
  }
4517
- return reverseSearch && closestMatchIndex !== -1
4518
- ? numberOfValues - closestMatchIndex - 1
4519
- : closestMatchIndex;
4551
+ return closestMatchIndex;
4520
4552
  }
4521
4553
  /**
4522
4554
  * Normalize a value.
@@ -4632,8 +4664,8 @@
4632
4664
  function urlRepresentation(link, getters) {
4633
4665
  return findMatchingSpec(link.url).urlRepresentation(link.url, getters);
4634
4666
  }
4635
- function openLink(link, env) {
4636
- findMatchingSpec(link.url).open(link.url, env);
4667
+ function openLink(link, env, isMiddleClick) {
4668
+ findMatchingSpec(link.url).open(link.url, env, isMiddleClick);
4637
4669
  }
4638
4670
  function detectLink(value) {
4639
4671
  if (typeof value !== "string") {
@@ -6490,10 +6522,11 @@
6490
6522
  *
6491
6523
  */
6492
6524
  smallUuid() {
6493
- //@ts-ignore
6494
- if (window.crypto && window.crypto.getRandomValues) {
6495
- //@ts-ignore
6496
- return ([1e7] + -1e3).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
6525
+ if (window.crypto) {
6526
+ return "10000000-1000".replace(/[01]/g, (c) => {
6527
+ const n = Number(c);
6528
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6529
+ });
6497
6530
  }
6498
6531
  else {
6499
6532
  // mainly for jest and other browsers that do not have the crypto functionality
@@ -6508,10 +6541,11 @@
6508
6541
  * This method should be used when you need to avoid collisions at all costs, like the id of a revision.
6509
6542
  */
6510
6543
  uuidv4() {
6511
- //@ts-ignore
6512
- if (window.crypto && window.crypto.getRandomValues) {
6513
- //@ts-ignore
6514
- return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16));
6544
+ if (window.crypto) {
6545
+ return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => {
6546
+ const n = Number(c);
6547
+ return (n ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (n / 4)))).toString(16);
6548
+ });
6515
6549
  }
6516
6550
  else {
6517
6551
  // mainly for jest and other browsers that do not have the crypto functionality
@@ -6919,7 +6953,7 @@
6919
6953
  * canonicalizeNumberContent("02/12/2012", FR_LOCALE) // "02/12/2012"
6920
6954
  */
6921
6955
  function canonicalizeNumberContent(content, locale) {
6922
- return content.startsWith("=")
6956
+ return isFormula(content)
6923
6957
  ? canonicalizeFormula$1(content, locale)
6924
6958
  : canonicalizeNumberLiteral(content, locale);
6925
6959
  }
@@ -6934,7 +6968,7 @@
6934
6968
  * canonicalizeContent("02-12-2012", FR_LOCALE) // "12/02/2012"
6935
6969
  */
6936
6970
  function canonicalizeContent(content, locale) {
6937
- return content.startsWith("=")
6971
+ return isFormula(content)
6938
6972
  ? canonicalizeFormula$1(content, locale)
6939
6973
  : canonicalizeLiteral(content, locale);
6940
6974
  }
@@ -6953,7 +6987,7 @@
6953
6987
  }
6954
6988
  /** Change a formula to its canonical form (en_US locale) */
6955
6989
  function canonicalizeFormula$1(formula, locale) {
6956
- return _localizeFormula$1(formula, locale, DEFAULT_LOCALE);
6990
+ return _localizeFormula$1(formula.startsWith("+") ? "=" + formula.slice(1) : formula, locale, DEFAULT_LOCALE);
6957
6991
  }
6958
6992
  /** Change a formula from the canonical form to the given locale */
6959
6993
  function localizeFormula(formula, locale) {
@@ -7116,13 +7150,13 @@
7116
7150
 
7117
7151
  /** Change a number string to its canonical form (en_US locale) */
7118
7152
  function canonicalizeNumberValue(content, locale) {
7119
- return content.startsWith("=")
7153
+ return isFormula(content)
7120
7154
  ? canonicalizeFormula(content, locale)
7121
7155
  : canonicalizeNumberLiteral(content, locale);
7122
7156
  }
7123
7157
  /** Change a formula to its canonical form (en_US locale) */
7124
7158
  function canonicalizeFormula(formula, locale) {
7125
- return _localizeFormula(formula, locale, DEFAULT_LOCALE);
7159
+ return _localizeFormula(formula.startsWith("+") ? "=" + formula.slice(1) : formula, locale, DEFAULT_LOCALE);
7126
7160
  }
7127
7161
  function _localizeFormula(formula, fromLocale, toLocale) {
7128
7162
  if (fromLocale.formulaArgSeparator === toLocale.formulaArgSeparator &&
@@ -10876,7 +10910,7 @@ stores.inject(MyMetaStore, storeInstance);
10876
10910
 
10877
10911
  autoCompleteProviders.add("dataValidation", {
10878
10912
  getProposals(tokenAtCursor, content) {
10879
- if (content.startsWith("=")) {
10913
+ if (isFormula(content)) {
10880
10914
  return [];
10881
10915
  }
10882
10916
  if (!this.composer.currentEditedCell) {
@@ -18685,7 +18719,7 @@ stores.inject(MyMetaStore, storeInstance);
18685
18719
  const _isSorted = toBoolean(isSorted.value);
18686
18720
  const colIndex = _isSorted
18687
18721
  ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range.length, getValueFromRange)
18688
- : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange);
18722
+ : linearSearch(_range, searchKey, "wildcard", _range.length, getValueFromRange, this.lookupCaches);
18689
18723
  const col = _range[colIndex];
18690
18724
  if (col === undefined) {
18691
18725
  return valueNotAvailable(searchKey);
@@ -18840,7 +18874,7 @@ stores.inject(MyMetaStore, storeInstance);
18840
18874
  index = dichotomicSearch(_range, searchKey, "nextSmaller", "asc", rangeLen, getElement);
18841
18875
  break;
18842
18876
  case 0:
18843
- index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement);
18877
+ index = linearSearch(_range, searchKey, "wildcard", rangeLen, getElement, this.lookupCaches);
18844
18878
  break;
18845
18879
  case -1:
18846
18880
  index = dichotomicSearch(_range, searchKey, "nextGreater", "desc", rangeLen, getElement);
@@ -18908,7 +18942,7 @@ stores.inject(MyMetaStore, storeInstance);
18908
18942
  const _isSorted = toBoolean(isSorted.value);
18909
18943
  const rowIndex = _isSorted
18910
18944
  ? dichotomicSearch(_range, searchKey, "nextSmaller", "asc", _range[0].length, getValueFromRange)
18911
- : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange);
18945
+ : linearSearch(_range, searchKey, "wildcard", _range[0].length, getValueFromRange, this.lookupCaches);
18912
18946
  const value = _range[_index - 1][rowIndex];
18913
18947
  if (value === undefined) {
18914
18948
  return valueNotAvailable(searchKey);
@@ -18964,7 +18998,7 @@ stores.inject(MyMetaStore, storeInstance);
18964
18998
  const reverseSearch = _searchMode === -1;
18965
18999
  const index = _searchMode === 2 || _searchMode === -2
18966
19000
  ? dichotomicSearch(_lookupRange, searchKey, mode, _searchMode === 2 ? "asc" : "desc", rangeLen, getElement)
18967
- : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, reverseSearch);
19001
+ : linearSearch(_lookupRange, searchKey, mode, rangeLen, getElement, this.lookupCaches, reverseSearch);
18968
19002
  if (index !== -1) {
18969
19003
  return lookupDirection === "col"
18970
19004
  ? _returnRange.map((col) => [col[index]])
@@ -20422,7 +20456,7 @@ stores.inject(MyMetaStore, storeInstance);
20422
20456
  return [];
20423
20457
  }
20424
20458
  const searchTerm = tokenAtCursor.value;
20425
- if (!this.composer.currentContent.startsWith("=")) {
20459
+ if (!isFormula(this.composer.currentContent)) {
20426
20460
  return [];
20427
20461
  }
20428
20462
  const values = Object.entries(functionRegistry.content)
@@ -20830,7 +20864,7 @@ stores.inject(MyMetaStore, storeInstance);
20830
20864
  return;
20831
20865
  }
20832
20866
  if (content) {
20833
- if (content.startsWith("=")) {
20867
+ if (isFormula(content)) {
20834
20868
  const left = this.currentTokens.filter((t) => t.type === "LEFT_PAREN").length;
20835
20869
  const right = this.currentTokens.filter((t) => t.type === "RIGHT_PAREN").length;
20836
20870
  const missing = left - right;
@@ -20884,7 +20918,7 @@ stores.inject(MyMetaStore, storeInstance);
20884
20918
  }
20885
20919
  if (isNewCurrentContent || this.editionMode !== "inactive") {
20886
20920
  const locale = this.getters.getLocale();
20887
- this.currentTokens = text.startsWith("=") ? composerTokenize(text, locale) : [];
20921
+ this.currentTokens = isFormula(text) ? composerTokenize(text, locale) : [];
20888
20922
  if (this.currentTokens.length > 100) {
20889
20923
  if (raise) {
20890
20924
  this.notificationStore.raiseError(_t("This formula has over 100 parts. It can't be processed properly, consider splitting it into multiple cells"));
@@ -21084,7 +21118,7 @@ stores.inject(MyMetaStore, storeInstance);
21084
21118
  }
21085
21119
  }
21086
21120
  updateRangeColor() {
21087
- if (!this._currentContent.startsWith("=") || this.editionMode === "inactive") {
21121
+ if (!isFormula(this._currentContent) || this.editionMode === "inactive") {
21088
21122
  return;
21089
21123
  }
21090
21124
  const editionSheetId = this.sheetId;
@@ -21113,7 +21147,7 @@ stores.inject(MyMetaStore, storeInstance);
21113
21147
  * Highlight all ranges that can be found in the composer content.
21114
21148
  */
21115
21149
  get highlights() {
21116
- if (!this.currentContent.startsWith("=") || this.editionMode === "inactive") {
21150
+ if (!isFormula(this.currentContent) || this.editionMode === "inactive") {
21117
21151
  return [];
21118
21152
  }
21119
21153
  const editionSheetId = this.sheetId;
@@ -21147,7 +21181,7 @@ stores.inject(MyMetaStore, storeInstance);
21147
21181
  }
21148
21182
  get autocompleteProvider() {
21149
21183
  const content = this.currentContent;
21150
- const tokenAtCursor = content.startsWith("=")
21184
+ const tokenAtCursor = isFormula(content)
21151
21185
  ? this.tokenAtCursor
21152
21186
  : { type: "STRING", value: content };
21153
21187
  if (this.editionMode === "inactive" ||
@@ -21204,7 +21238,7 @@ stores.inject(MyMetaStore, storeInstance);
21204
21238
  * - Previous and next tokens can be separated by spaces
21205
21239
  */
21206
21240
  canStartComposerRangeSelection() {
21207
- if (this._currentContent.startsWith("=")) {
21241
+ if (isFormula(this._currentContent)) {
21208
21242
  const tokenAtCursor = this.tokenAtCursor;
21209
21243
  if (!tokenAtCursor) {
21210
21244
  return false;
@@ -28303,7 +28337,7 @@ stores.inject(MyMetaStore, storeInstance);
28303
28337
  }
28304
28338
  function getPyramidChartData(definition, dataSets, labelRange, getters) {
28305
28339
  const barChartData = getBarChartData(definition, dataSets, labelRange, getters);
28306
- const barDataset = barChartData.dataSetsValues;
28340
+ const barDataset = barChartData.dataSetsValues.filter((ds) => !ds.hidden);
28307
28341
  const pyramidDatasetValues = [];
28308
28342
  if (barDataset[0]) {
28309
28343
  const pyramidData = barDataset[0].data.map((value) => (value > 0 ? value : 0));
@@ -28780,10 +28814,8 @@ stores.inject(MyMetaStore, storeInstance);
28780
28814
  function getChartDatasetValues(getters, dataSets) {
28781
28815
  const datasetValues = [];
28782
28816
  for (const [dsIndex, ds] of Object.entries(dataSets)) {
28783
- if (getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left)) {
28784
- continue;
28785
- }
28786
28817
  let label;
28818
+ let hidden = getters.isColHidden(ds.dataRange.sheetId, ds.dataRange.zone.left);
28787
28819
  if (ds.labelCell) {
28788
28820
  const labelRange = ds.labelCell;
28789
28821
  const cell = labelRange
@@ -28810,9 +28842,9 @@ stores.inject(MyMetaStore, storeInstance);
28810
28842
  data.fill(1);
28811
28843
  }
28812
28844
  else if (data.every((cell) => cell === undefined || cell === null || !isNumber(cell.toString(), DEFAULT_LOCALE))) {
28813
- continue;
28845
+ hidden = true;
28814
28846
  }
28815
- datasetValues.push({ data, label });
28847
+ datasetValues.push({ data, label, hidden });
28816
28848
  }
28817
28849
  return datasetValues;
28818
28850
  }
@@ -28823,12 +28855,13 @@ stores.inject(MyMetaStore, storeInstance);
28823
28855
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28824
28856
  const trendDatasets = [];
28825
28857
  for (const index in dataSetsValues) {
28826
- let { label, data } = dataSetsValues[index];
28858
+ let { label, data, hidden } = dataSetsValues[index];
28827
28859
  label = definition.dataSets?.[index].label || label;
28828
28860
  const backgroundColor = colors.next();
28829
28861
  const dataset = {
28830
28862
  label,
28831
28863
  data,
28864
+ hidden,
28832
28865
  borderColor: definition.background || BACKGROUND_CHART_COLOR,
28833
28866
  borderWidth: definition.stacked ? 1 : 0,
28834
28867
  backgroundColor,
@@ -28861,6 +28894,9 @@ stores.inject(MyMetaStore, storeInstance);
28861
28894
  const labelsWithSubTotals = [];
28862
28895
  let lastValue = 0;
28863
28896
  for (const dataSetsValue of dataSetsValues) {
28897
+ if (dataSetsValue.hidden) {
28898
+ continue;
28899
+ }
28864
28900
  for (let i = 0; i < dataSetsValue.data.length; i++) {
28865
28901
  const data = dataSetsValue.data[i];
28866
28902
  labelsWithSubTotals.push(labels[i]);
@@ -28896,7 +28932,7 @@ stores.inject(MyMetaStore, storeInstance);
28896
28932
  const trendDatasets = [];
28897
28933
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28898
28934
  for (let index = 0; index < dataSetsValues.length; index++) {
28899
- let { label, data } = dataSetsValues[index];
28935
+ let { label, data, hidden } = dataSetsValues[index];
28900
28936
  label = definition.dataSets?.[index].label || label;
28901
28937
  const color = colors.next();
28902
28938
  if (axisType && ["linear", "time"].includes(axisType)) {
@@ -28906,6 +28942,7 @@ stores.inject(MyMetaStore, storeInstance);
28906
28942
  const dataset = {
28907
28943
  label,
28908
28944
  data,
28945
+ hidden,
28909
28946
  tension: 0, // 0 -> render straight lines, which is much faster
28910
28947
  borderColor: color,
28911
28948
  backgroundColor: areaChart ? setColorAlpha(color, LINE_FILL_TRANSPARENCY) : color,
@@ -28938,7 +28975,9 @@ stores.inject(MyMetaStore, storeInstance);
28938
28975
  const dataSets = [];
28939
28976
  const dataSetsLength = Math.max(0, ...dataSetsValues.map((ds) => ds?.data?.length ?? 0));
28940
28977
  const backgroundColor = getPieColors(new ColorGenerator(dataSetsLength), dataSetsValues);
28941
- for (const { label, data } of dataSetsValues) {
28978
+ for (const { label, data, hidden } of dataSetsValues) {
28979
+ if (hidden)
28980
+ continue;
28942
28981
  const dataset = {
28943
28982
  label,
28944
28983
  data,
@@ -28956,7 +28995,7 @@ stores.inject(MyMetaStore, storeInstance);
28956
28995
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28957
28996
  const trendDatasets = [];
28958
28997
  for (let index = 0; index < dataSetsValues.length; index++) {
28959
- let { label, data } = dataSetsValues[index];
28998
+ let { label, data, hidden } = dataSetsValues[index];
28960
28999
  label = definition.dataSets?.[index].label || label;
28961
29000
  const design = definition.dataSets?.[index];
28962
29001
  const color = colors.next();
@@ -28964,6 +29003,7 @@ stores.inject(MyMetaStore, storeInstance);
28964
29003
  const dataset = {
28965
29004
  label: label,
28966
29005
  data,
29006
+ hidden,
28967
29007
  borderColor: color,
28968
29008
  backgroundColor: color,
28969
29009
  yAxisID: definition.dataSets?.[index].yAxisId || "y",
@@ -28988,7 +29028,7 @@ stores.inject(MyMetaStore, storeInstance);
28988
29028
  const fill = definition.fillArea ?? false;
28989
29029
  const colors = getChartColorsGenerator(definition, dataSetsValues.length);
28990
29030
  for (let i = 0; i < dataSetsValues.length; i++) {
28991
- let { label, data } = dataSetsValues[i];
29031
+ let { label, data, hidden } = dataSetsValues[i];
28992
29032
  if (definition.dataSets?.[i]?.label) {
28993
29033
  label = definition.dataSets[i].label;
28994
29034
  }
@@ -28996,6 +29036,7 @@ stores.inject(MyMetaStore, storeInstance);
28996
29036
  const dataset = {
28997
29037
  label,
28998
29038
  data,
29039
+ hidden,
28999
29040
  borderColor,
29000
29041
  backgroundColor: borderColor,
29001
29042
  };
@@ -29141,6 +29182,11 @@ stores.inject(MyMetaStore, storeInstance);
29141
29182
  hidden: false,
29142
29183
  lineWidth: 2,
29143
29184
  })),
29185
+ filter: (legendItem, data) => {
29186
+ return "datasetIndex" in legendItem
29187
+ ? !data.datasets[legendItem.datasetIndex].hidden
29188
+ : true;
29189
+ },
29144
29190
  },
29145
29191
  };
29146
29192
  }
@@ -29202,6 +29248,11 @@ stores.inject(MyMetaStore, storeInstance);
29202
29248
  }
29203
29249
  return legendValues;
29204
29250
  },
29251
+ filter: (legendItem, data) => {
29252
+ return "datasetIndex" in legendItem
29253
+ ? !data.datasets[legendItem.datasetIndex].hidden
29254
+ : true;
29255
+ },
29205
29256
  },
29206
29257
  onClick: () => { }, // Disables click interaction with the waterfall chart legend items
29207
29258
  };
@@ -29285,6 +29336,11 @@ stores.inject(MyMetaStore, storeInstance);
29285
29336
  ...legendLabelConfig,
29286
29337
  };
29287
29338
  }),
29339
+ filter: (legendItem, data) => {
29340
+ return "datasetIndex" in legendItem
29341
+ ? !data.datasets[legendItem.datasetIndex].hidden
29342
+ : true;
29343
+ },
29288
29344
  },
29289
29345
  };
29290
29346
  }
@@ -29618,7 +29674,7 @@ stores.inject(MyMetaStore, storeInstance);
29618
29674
  <div
29619
29675
  class="o-chart-custom-tooltip border rounded px-2 py-1 pe-none mw-100 position-absolute text-nowrap shadow opacity-100">
29620
29676
  <table class="overflow-hidden m-0">
29621
- <thead>
29677
+ <thead t-if="title">
29622
29678
  <tr>
29623
29679
  <th class="o-tooltip-title align-baseline border-0 text-truncate" t-esc="title" t-attf-style="max-width: {{ labelsMaxWidth }}"/>
29624
29680
  </tr>
@@ -29679,8 +29735,8 @@ stores.inject(MyMetaStore, storeInstance);
29679
29735
  ? undefined
29680
29736
  : "";
29681
29737
  },
29738
+ beforeLabel: (tooltipItem) => tooltipItem.dataset?.label || tooltipItem.label,
29682
29739
  label: function (tooltipItem) {
29683
- const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
29684
29740
  const horizontalChart = definition.horizontal;
29685
29741
  let yLabel = horizontalChart ? tooltipItem.parsed.x : tooltipItem.parsed.y;
29686
29742
  if (yLabel === undefined || yLabel === null) {
@@ -29688,7 +29744,7 @@ stores.inject(MyMetaStore, storeInstance);
29688
29744
  }
29689
29745
  const axisId = horizontalChart ? tooltipItem.dataset.xAxisID : tooltipItem.dataset.yAxisID;
29690
29746
  const yLabelStr = formatChartDatasetValue(args.axisFormats, args.locale)(yLabel, axisId);
29691
- return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
29747
+ return yLabelStr;
29692
29748
  },
29693
29749
  },
29694
29750
  };
@@ -29713,21 +29769,18 @@ stores.inject(MyMetaStore, storeInstance);
29713
29769
  const formattedX = formatValue(label, { locale, format: labelFormat });
29714
29770
  const axisId = tooltipItem.dataset.yAxisID || "y";
29715
29771
  const formattedY = formatValue(dataSetPoint, { locale, format: axisFormats?.[axisId] });
29716
- const dataSetTitle = tooltipItem.dataset.label;
29717
- return formattedX
29718
- ? `${dataSetTitle}: (${formattedX}, ${formattedY})`
29719
- : `${dataSetTitle}: ${formattedY}`;
29772
+ return formattedX ? `(${formattedX}, ${formattedY})` : `${formattedY}`;
29720
29773
  };
29721
29774
  }
29722
29775
  else {
29723
29776
  tooltip.callbacks.label = function (tooltipItem) {
29724
- const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
29725
29777
  const yLabel = tooltipItem.parsed.y;
29726
29778
  const axisId = tooltipItem.dataset.yAxisID;
29727
29779
  const yLabelStr = formatChartDatasetValue(axisFormats, locale)(yLabel, axisId);
29728
- return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
29780
+ return yLabelStr;
29729
29781
  };
29730
29782
  }
29783
+ tooltip.callbacks.beforeLabel = (tooltipItem) => tooltipItem.dataset?.label || tooltipItem.label;
29731
29784
  tooltip.callbacks.title = function (tooltipItems) {
29732
29785
  const displayTooltipTitle = axisType !== "linear" &&
29733
29786
  tooltipItems.some((item) => item.dataset.xAxisID !== TREND_LINE_XAXIS_ID);
@@ -29745,17 +29798,15 @@ stores.inject(MyMetaStore, storeInstance);
29745
29798
  title: function (tooltipItems) {
29746
29799
  return tooltipItems[0].dataset.label;
29747
29800
  },
29801
+ beforeLabel: (tooltipItem) => tooltipItem.label || tooltipItem.dataset.label,
29748
29802
  label: function (tooltipItem) {
29749
29803
  const data = tooltipItem.dataset.data;
29750
29804
  const dataIndex = tooltipItem.dataIndex;
29751
29805
  const percentage = calculatePercentage(data, dataIndex);
29752
- const xLabel = tooltipItem.label || tooltipItem.dataset.label;
29753
29806
  const yLabel = tooltipItem.parsed.y ?? tooltipItem.parsed;
29754
29807
  const toolTipFormat = !format && yLabel >= 1000 ? "#,##" : format;
29755
29808
  const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
29756
- return xLabel
29757
- ? `${xLabel}: ${yLabelStr} (${percentage}%)`
29758
- : `${yLabelStr} (${percentage}%)`;
29809
+ return `${yLabelStr} (${percentage}%)`;
29759
29810
  },
29760
29811
  },
29761
29812
  };
@@ -29768,16 +29819,17 @@ stores.inject(MyMetaStore, storeInstance);
29768
29819
  enabled: false,
29769
29820
  external: customTooltipHandler,
29770
29821
  callbacks: {
29771
- label: function (tooltipItem) {
29772
- const [lastValue, currentValue] = tooltipItem.raw;
29773
- const yLabel = currentValue - lastValue;
29822
+ beforeLabel: function (tooltipItem) {
29774
29823
  const dataSeriesIndex = labels.length
29775
29824
  ? Math.floor(tooltipItem.dataIndex / labels.length)
29776
29825
  : 0;
29777
- const dataSeriesLabel = dataSeriesLabels[dataSeriesIndex];
29826
+ return dataSeriesLabels[dataSeriesIndex];
29827
+ },
29828
+ label: function (tooltipItem) {
29829
+ const [lastValue, currentValue] = tooltipItem.raw;
29830
+ const yLabel = currentValue - lastValue;
29778
29831
  const toolTipFormat = !format && Math.abs(yLabel) > 1000 ? "#,##" : format;
29779
- const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
29780
- return dataSeriesLabel ? `${dataSeriesLabel}: ${yLabelStr}` : yLabelStr;
29832
+ return formatValue(yLabel, { format: toolTipFormat, locale });
29781
29833
  },
29782
29834
  },
29783
29835
  };
@@ -29801,11 +29853,10 @@ stores.inject(MyMetaStore, storeInstance);
29801
29853
  enabled: false,
29802
29854
  external: customTooltipHandler,
29803
29855
  callbacks: {
29856
+ beforeLabel: (tooltipItem) => tooltipItem.dataset?.label || tooltipItem.label,
29804
29857
  label: function (tooltipItem) {
29805
- const xLabel = tooltipItem.dataset?.label || tooltipItem.label;
29806
29858
  const yLabel = tooltipItem.parsed.r;
29807
- const formattedY = formatValue(yLabel, { format: axisFormats?.r, locale });
29808
- return xLabel ? `${xLabel}: ${formattedY}` : formattedY;
29859
+ return formatValue(yLabel, { format: axisFormats?.r, locale });
29809
29860
  },
29810
29861
  },
29811
29862
  };
@@ -29820,13 +29871,12 @@ stores.inject(MyMetaStore, storeInstance);
29820
29871
  return tooltipItem.raw.value !== undefined;
29821
29872
  },
29822
29873
  callbacks: {
29874
+ beforeLabel: (tooltipItem) => tooltipItem.raw.feature.properties.name,
29823
29875
  label: function (tooltipItem) {
29824
29876
  const rawItem = tooltipItem.raw;
29825
- const xLabel = rawItem.feature.properties.name;
29826
29877
  const yLabel = rawItem.value;
29827
29878
  const toolTipFormat = !format && Math.abs(yLabel) >= 1000 ? "#,##" : format;
29828
- const yLabelStr = formatValue(yLabel, { format: toolTipFormat, locale });
29829
- return xLabel ? `${xLabel}: ${yLabelStr}` : yLabelStr;
29879
+ return formatValue(yLabel, { format: toolTipFormat, locale });
29830
29880
  },
29831
29881
  },
29832
29882
  };
@@ -29846,7 +29896,8 @@ stores.inject(MyMetaStore, storeInstance);
29846
29896
  return;
29847
29897
  }
29848
29898
  const tooltipItems = tooltip.body.map((body, index) => {
29849
- let [label, value] = body.lines[0].split(":").map((str) => str.trim());
29899
+ let label = body.before[0];
29900
+ let value = body.lines[0];
29850
29901
  if (!value) {
29851
29902
  value = label;
29852
29903
  label = "";
@@ -33007,6 +33058,187 @@ stores.inject(MyMetaStore, storeInstance);
33007
33058
  },
33008
33059
  };
33009
33060
 
33061
+ const macRegex = /Mac/i;
33062
+ const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
33063
+ /**
33064
+ * Return true if the event was triggered from
33065
+ * a child element.
33066
+ */
33067
+ function isChildEvent(parent, ev) {
33068
+ if (!parent)
33069
+ return false;
33070
+ return !!ev.target && parent.contains(ev.target);
33071
+ }
33072
+ function gridOverlayPosition() {
33073
+ const spreadsheetElement = document.querySelector(".o-grid-overlay");
33074
+ if (spreadsheetElement) {
33075
+ const { top, left } = spreadsheetElement?.getBoundingClientRect();
33076
+ return { top, left };
33077
+ }
33078
+ throw new Error("Can't find spreadsheet position");
33079
+ }
33080
+ function getBoundingRectAsPOJO(el) {
33081
+ const rect = el.getBoundingClientRect();
33082
+ return {
33083
+ x: rect.x,
33084
+ y: rect.y,
33085
+ width: rect.width,
33086
+ height: rect.height,
33087
+ };
33088
+ }
33089
+ /**
33090
+ * Iterate over all the children of `el` in the dom tree starting at `el`, depth first.
33091
+ */
33092
+ function* iterateChildren(el) {
33093
+ yield el;
33094
+ if (el.hasChildNodes()) {
33095
+ for (let child of el.childNodes) {
33096
+ yield* iterateChildren(child);
33097
+ }
33098
+ }
33099
+ }
33100
+ function getOpenedMenus() {
33101
+ return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
33102
+ }
33103
+ function getCurrentSelection(el) {
33104
+ let { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
33105
+ let startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
33106
+ let endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
33107
+ return {
33108
+ start: startSizeBefore,
33109
+ end: endSizeBefore,
33110
+ };
33111
+ }
33112
+ function getStartAndEndSelection(el) {
33113
+ const selection = document.getSelection();
33114
+ return {
33115
+ startElement: selection.anchorNode || el,
33116
+ startSelectionOffset: selection.anchorOffset,
33117
+ endElement: selection.focusNode || el,
33118
+ endSelectionOffset: selection.focusOffset,
33119
+ };
33120
+ }
33121
+ /**
33122
+ * Computes the text 'index' inside this.el based on the currently selected node and its offset.
33123
+ * The selected node is either a Text node or an Element node.
33124
+ *
33125
+ * case 1 -Text node:
33126
+ * the offset is the number of characters from the start of the node. We have to add this offset to the
33127
+ * content length of all previous nodes.
33128
+ *
33129
+ * case 2 - Element node:
33130
+ * the offset is the number of child nodes before the selected node. We have to add the content length of
33131
+ * all the nodes prior to the selected node as well as the content of the child node before the offset.
33132
+ *
33133
+ * See the MDN documentation for more details.
33134
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
33135
+ * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
33136
+ *
33137
+ */
33138
+ function findSelectionIndex(el, nodeToFind, nodeOffset) {
33139
+ let usedCharacters = 0;
33140
+ let it = iterateChildren(el);
33141
+ let current = it.next();
33142
+ let isFirstParagraph = true;
33143
+ while (!current.done && current.value !== nodeToFind) {
33144
+ if (!current.value.hasChildNodes()) {
33145
+ if (current.value.textContent) {
33146
+ usedCharacters += current.value.textContent.length;
33147
+ }
33148
+ }
33149
+ // One new paragraph = one new line character, except for the first paragraph
33150
+ if (current.value.nodeName === "P" ||
33151
+ (current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
33152
+ ) {
33153
+ if (isFirstParagraph) {
33154
+ isFirstParagraph = false;
33155
+ }
33156
+ else {
33157
+ usedCharacters++;
33158
+ }
33159
+ }
33160
+ current = it.next();
33161
+ }
33162
+ if (current.value !== nodeToFind) {
33163
+ /** This situation can happen if the code is called while the selection is not currently on the element.
33164
+ * In this case, we return 0 because we don't know the size of the text before the selection.
33165
+ *
33166
+ * A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
33167
+ */
33168
+ return 0;
33169
+ }
33170
+ else {
33171
+ if (!current.value.hasChildNodes()) {
33172
+ usedCharacters += nodeOffset;
33173
+ }
33174
+ else {
33175
+ const children = [...current.value.childNodes].slice(0, nodeOffset);
33176
+ usedCharacters += children.reduce((acc, child, index) => {
33177
+ if (child.textContent !== null) {
33178
+ // need to account for paragraph nodes that implicitly add a new line
33179
+ // except for the last paragraph
33180
+ let chars = child.textContent.length;
33181
+ if (child.nodeName === "P" && index !== children.length - 1) {
33182
+ chars++;
33183
+ }
33184
+ return acc + chars;
33185
+ }
33186
+ else {
33187
+ return acc;
33188
+ }
33189
+ }, 0);
33190
+ }
33191
+ }
33192
+ if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
33193
+ usedCharacters++;
33194
+ }
33195
+ return usedCharacters;
33196
+ }
33197
+ const letterRegex = /^[a-zA-Z]$/;
33198
+ /**
33199
+ * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
33200
+ *
33201
+ * @argument ev - The keyboard event to transform
33202
+ * @argument mode - Use either ev.key of ev.code to get the string shortcut
33203
+ *
33204
+ * @example
33205
+ * event : { ctrlKey: true, key: "a" } => "Ctrl+A"
33206
+ * event : { shift: true, alt: true, key: "Home" } => "Alt+Shift+Home"
33207
+ */
33208
+ function keyboardEventToShortcutString(ev, mode = "key") {
33209
+ let keyDownString = "";
33210
+ if (!MODIFIER_KEYS.includes(ev.key)) {
33211
+ if (isCtrlKey(ev))
33212
+ keyDownString += "Ctrl+";
33213
+ if (ev.altKey)
33214
+ keyDownString += "Alt+";
33215
+ if (ev.shiftKey)
33216
+ keyDownString += "Shift+";
33217
+ }
33218
+ const key = mode === "key" ? ev.key : ev.code;
33219
+ keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
33220
+ return keyDownString;
33221
+ }
33222
+ function isMacOS() {
33223
+ return Boolean(macRegex.test(navigator.userAgent));
33224
+ }
33225
+ /**
33226
+ * @param {KeyboardEvent | MouseEvent} ev
33227
+ * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
33228
+ * On Mac, this is the "meta" or "command" key.
33229
+ */
33230
+ function isCtrlKey(ev) {
33231
+ return isMacOS() ? ev.metaKey : ev.ctrlKey;
33232
+ }
33233
+ /**
33234
+ * @param {MouseEvent} ev - The mouse event.
33235
+ * @returns {boolean} Returns true if the event was triggered by a middle-click
33236
+ * or a Ctrl + Click (Cmd + Click on Mac).
33237
+ */
33238
+ function isMiddleClickOrCtrlClick(ev) {
33239
+ return ev.button === 1 || (isCtrlKey(ev) && ev.button === 0);
33240
+ }
33241
+
33010
33242
  const LINK_TOOLTIP_HEIGHT = 32;
33011
33243
  const LINK_TOOLTIP_WIDTH = 220;
33012
33244
  css /* scss */ `
@@ -33081,8 +33313,8 @@ stores.inject(MyMetaStore, storeInstance);
33081
33313
  getUrlRepresentation(link) {
33082
33314
  return urlRepresentation(link, this.env.model.getters);
33083
33315
  }
33084
- openLink() {
33085
- openLink(this.link, this.env);
33316
+ openLink(ev) {
33317
+ openLink(this.link, this.env, isMiddleClickOrCtrlClick(ev));
33086
33318
  }
33087
33319
  edit() {
33088
33320
  const { col, row } = this.props.cellPosition;
@@ -33211,179 +33443,6 @@ stores.inject(MyMetaStore, storeInstance);
33211
33443
  sequence: 10,
33212
33444
  });
33213
33445
 
33214
- const macRegex = /Mac/i;
33215
- const MODIFIER_KEYS = ["Shift", "Control", "Alt", "Meta"];
33216
- /**
33217
- * Return true if the event was triggered from
33218
- * a child element.
33219
- */
33220
- function isChildEvent(parent, ev) {
33221
- if (!parent)
33222
- return false;
33223
- return !!ev.target && parent.contains(ev.target);
33224
- }
33225
- function gridOverlayPosition() {
33226
- const spreadsheetElement = document.querySelector(".o-grid-overlay");
33227
- if (spreadsheetElement) {
33228
- const { top, left } = spreadsheetElement?.getBoundingClientRect();
33229
- return { top, left };
33230
- }
33231
- throw new Error("Can't find spreadsheet position");
33232
- }
33233
- function getBoundingRectAsPOJO(el) {
33234
- const rect = el.getBoundingClientRect();
33235
- return {
33236
- x: rect.x,
33237
- y: rect.y,
33238
- width: rect.width,
33239
- height: rect.height,
33240
- };
33241
- }
33242
- /**
33243
- * Iterate over all the children of `el` in the dom tree starting at `el`, depth first.
33244
- */
33245
- function* iterateChildren(el) {
33246
- yield el;
33247
- if (el.hasChildNodes()) {
33248
- for (let child of el.childNodes) {
33249
- yield* iterateChildren(child);
33250
- }
33251
- }
33252
- }
33253
- function getOpenedMenus() {
33254
- return Array.from(document.querySelectorAll(".o-spreadsheet .o-menu"));
33255
- }
33256
- function getCurrentSelection(el) {
33257
- let { startElement, endElement, startSelectionOffset, endSelectionOffset } = getStartAndEndSelection(el);
33258
- let startSizeBefore = findSelectionIndex(el, startElement, startSelectionOffset);
33259
- let endSizeBefore = findSelectionIndex(el, endElement, endSelectionOffset);
33260
- return {
33261
- start: startSizeBefore,
33262
- end: endSizeBefore,
33263
- };
33264
- }
33265
- function getStartAndEndSelection(el) {
33266
- const selection = document.getSelection();
33267
- return {
33268
- startElement: selection.anchorNode || el,
33269
- startSelectionOffset: selection.anchorOffset,
33270
- endElement: selection.focusNode || el,
33271
- endSelectionOffset: selection.focusOffset,
33272
- };
33273
- }
33274
- /**
33275
- * Computes the text 'index' inside this.el based on the currently selected node and its offset.
33276
- * The selected node is either a Text node or an Element node.
33277
- *
33278
- * case 1 -Text node:
33279
- * the offset is the number of characters from the start of the node. We have to add this offset to the
33280
- * content length of all previous nodes.
33281
- *
33282
- * case 2 - Element node:
33283
- * the offset is the number of child nodes before the selected node. We have to add the content length of
33284
- * all the nodes prior to the selected node as well as the content of the child node before the offset.
33285
- *
33286
- * See the MDN documentation for more details.
33287
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/startOffset
33288
- * https://developer.mozilla.org/en-US/docs/Web/API/Range/endOffset
33289
- *
33290
- */
33291
- function findSelectionIndex(el, nodeToFind, nodeOffset) {
33292
- let usedCharacters = 0;
33293
- let it = iterateChildren(el);
33294
- let current = it.next();
33295
- let isFirstParagraph = true;
33296
- while (!current.done && current.value !== nodeToFind) {
33297
- if (!current.value.hasChildNodes()) {
33298
- if (current.value.textContent) {
33299
- usedCharacters += current.value.textContent.length;
33300
- }
33301
- }
33302
- // One new paragraph = one new line character, except for the first paragraph
33303
- if (current.value.nodeName === "P" ||
33304
- (current.value.nodeName === "DIV" && current.value !== el) // On paste, the HTML may contain <div> instead of <p>
33305
- ) {
33306
- if (isFirstParagraph) {
33307
- isFirstParagraph = false;
33308
- }
33309
- else {
33310
- usedCharacters++;
33311
- }
33312
- }
33313
- current = it.next();
33314
- }
33315
- if (current.value !== nodeToFind) {
33316
- /** This situation can happen if the code is called while the selection is not currently on the element.
33317
- * In this case, we return 0 because we don't know the size of the text before the selection.
33318
- *
33319
- * A known occurrence is triggered since the introduction of commit d4663158 (PR #2038).
33320
- */
33321
- return 0;
33322
- }
33323
- else {
33324
- if (!current.value.hasChildNodes()) {
33325
- usedCharacters += nodeOffset;
33326
- }
33327
- else {
33328
- const children = [...current.value.childNodes].slice(0, nodeOffset);
33329
- usedCharacters += children.reduce((acc, child, index) => {
33330
- if (child.textContent !== null) {
33331
- // need to account for paragraph nodes that implicitly add a new line
33332
- // except for the last paragraph
33333
- let chars = child.textContent.length;
33334
- if (child.nodeName === "P" && index !== children.length - 1) {
33335
- chars++;
33336
- }
33337
- return acc + chars;
33338
- }
33339
- else {
33340
- return acc;
33341
- }
33342
- }, 0);
33343
- }
33344
- }
33345
- if (nodeToFind.nodeName === "P" && !isFirstParagraph && nodeToFind.textContent === "") {
33346
- usedCharacters++;
33347
- }
33348
- return usedCharacters;
33349
- }
33350
- const letterRegex = /^[a-zA-Z]$/;
33351
- /**
33352
- * Transform a keyboard event into a shortcut string that represent this event. The letters keys will be uppercased.
33353
- *
33354
- * @argument ev - The keyboard event to transform
33355
- * @argument mode - Use either ev.key of ev.code to get the string shortcut
33356
- *
33357
- * @example
33358
- * event : { ctrlKey: true, key: "a" } => "Ctrl+A"
33359
- * event : { shift: true, alt: true, key: "Home" } => "Alt+Shift+Home"
33360
- */
33361
- function keyboardEventToShortcutString(ev, mode = "key") {
33362
- let keyDownString = "";
33363
- if (!MODIFIER_KEYS.includes(ev.key)) {
33364
- if (isCtrlKey(ev))
33365
- keyDownString += "Ctrl+";
33366
- if (ev.altKey)
33367
- keyDownString += "Alt+";
33368
- if (ev.shiftKey)
33369
- keyDownString += "Shift+";
33370
- }
33371
- const key = mode === "key" ? ev.key : ev.code;
33372
- keyDownString += letterRegex.test(key) ? key.toUpperCase() : key;
33373
- return keyDownString;
33374
- }
33375
- function isMacOS() {
33376
- return Boolean(macRegex.test(navigator.userAgent));
33377
- }
33378
- /**
33379
- * @param {KeyboardEvent | MouseEvent} ev
33380
- * @returns Returns true if the event was triggered with the "ctrl" modifier pressed.
33381
- * On Mac, this is the "meta" or "command" key.
33382
- */
33383
- function isCtrlKey(ev) {
33384
- return isMacOS() ? ev.metaKey : ev.ctrlKey;
33385
- }
33386
-
33387
33446
  /**
33388
33447
  * Repeatedly calls a callback function with a time delay between calls.
33389
33448
  */
@@ -33578,8 +33637,8 @@ stores.inject(MyMetaStore, storeInstance);
33578
33637
  getIconColor(menu) {
33579
33638
  return cssPropertiesToCss({ color: menu.iconColor });
33580
33639
  }
33581
- async activateMenu(menu) {
33582
- const result = await menu.execute?.(this.env);
33640
+ async activateMenu(menu, isMiddleClick) {
33641
+ const result = await menu.execute?.(this.env, isMiddleClick);
33583
33642
  this.close();
33584
33643
  this.props.onMenuClicked?.({ detail: result });
33585
33644
  }
@@ -33642,13 +33701,14 @@ stores.inject(MyMetaStore, storeInstance);
33642
33701
  this.subMenu.parentMenu = undefined;
33643
33702
  }
33644
33703
  onClickMenu(menu, ev) {
33645
- if (this.isEnabled(menu)) {
33646
- if (this.isRoot(menu)) {
33647
- this.openSubMenu(menu, ev.currentTarget);
33648
- }
33649
- else {
33650
- this.activateMenu(menu);
33651
- }
33704
+ if (!this.isEnabled(menu)) {
33705
+ return;
33706
+ }
33707
+ if (this.isRoot(menu)) {
33708
+ this.openSubMenu(menu, ev.currentTarget);
33709
+ }
33710
+ else {
33711
+ this.activateMenu(menu, isMiddleClickOrCtrlClick(ev));
33652
33712
  }
33653
33713
  }
33654
33714
  onMouseOver(menu, ev) {
@@ -40276,7 +40336,7 @@ stores.inject(MyMetaStore, storeInstance);
40276
40336
  const content = this.props.composerStore.currentContent;
40277
40337
  if (this.props.focus === "cellFocus" &&
40278
40338
  !this.autoCompleteState.provider &&
40279
- !content.startsWith("=")) {
40339
+ !isFormula(content)) {
40280
40340
  this.props.composerStore.stopEdition();
40281
40341
  return;
40282
40342
  }
@@ -40493,7 +40553,7 @@ stores.inject(MyMetaStore, storeInstance);
40493
40553
  return;
40494
40554
  }
40495
40555
  const composerContent = this.props.composerStore.currentContent;
40496
- const isValidFormula = composerContent.startsWith("=");
40556
+ const isValidFormula = isFormula(composerContent);
40497
40557
  if (isValidFormula) {
40498
40558
  const tokens = this.props.composerStore.currentTokens;
40499
40559
  const currentSelection = this.contentHelper.getCurrentSelection();
@@ -40550,7 +40610,7 @@ stores.inject(MyMetaStore, storeInstance);
40550
40610
  */
40551
40611
  getContentLines() {
40552
40612
  let value = this.props.composerStore.currentContent;
40553
- const isValidFormula = value.startsWith("=");
40613
+ const isValidFormula = isFormula(value);
40554
40614
  if (value === "") {
40555
40615
  return [];
40556
40616
  }
@@ -40639,7 +40699,7 @@ stores.inject(MyMetaStore, storeInstance);
40639
40699
  this.autoCompleteState.useProvider(autoCompleteProvider);
40640
40700
  }
40641
40701
  const token = this.props.composerStore.tokenAtCursor;
40642
- if (content.startsWith("=") && token && token.type !== "SYMBOL") {
40702
+ if (isFormula(content) && token && token.type !== "SYMBOL") {
40643
40703
  const tokenContext = token.functionContext;
40644
40704
  const parentFunction = tokenContext?.parent.toUpperCase();
40645
40705
  if (tokenContext &&
@@ -48697,7 +48757,7 @@ stores.inject(MyMetaStore, storeInstance);
48697
48757
  }
48698
48758
  break;
48699
48759
  case "ACTIVATE_SHEET":
48700
- if (!this._currentContent.startsWith("=")) {
48760
+ if (!isFormula(this._currentContent)) {
48701
48761
  this._cancelEdition();
48702
48762
  this.resetContent();
48703
48763
  }
@@ -48773,7 +48833,7 @@ stores.inject(MyMetaStore, storeInstance);
48773
48833
  if (content) {
48774
48834
  const sheetId = this.getters.getActiveSheetId();
48775
48835
  const cell = this.getters.getEvaluatedCell({ sheetId, col: this.col, row: this.row });
48776
- if (cell.link && !content.startsWith("=")) {
48836
+ if (cell.link && !isFormula(content)) {
48777
48837
  content = markdownLink(content, cell.link.url);
48778
48838
  }
48779
48839
  this.addHeadersForSpreadingFormula(content);
@@ -48833,7 +48893,7 @@ stores.inject(MyMetaStore, storeInstance);
48833
48893
  }
48834
48894
  /** Add headers at the end of the sheet so the formula in the composer has enough space to spread */
48835
48895
  addHeadersForSpreadingFormula(content) {
48836
- if (!content.startsWith("=")) {
48896
+ if (!isFormula(content)) {
48837
48897
  return;
48838
48898
  }
48839
48899
  const evaluated = this.getters.evaluateFormula(this.sheetId, content);
@@ -48866,7 +48926,7 @@ stores.inject(MyMetaStore, storeInstance);
48866
48926
  checkDataValidation() {
48867
48927
  const cellPosition = { sheetId: this.sheetId, col: this.col, row: this.row };
48868
48928
  const content = this.getCurrentCanonicalContent();
48869
- const cellValue = content.startsWith("=")
48929
+ const cellValue = isFormula(content)
48870
48930
  ? this.getters.evaluateFormula(this.sheetId, content)
48871
48931
  : parseLiteral(content, this.getters.getLocale());
48872
48932
  if (isMatrix(cellValue)) {
@@ -48988,23 +49048,23 @@ stores.inject(MyMetaStore, storeInstance);
48988
49048
  if (this.composerStore.editionMode === "inactive") {
48989
49049
  return `z-index: -1000;`;
48990
49050
  }
48991
- const isFormula = this.composerStore.currentContent.startsWith("=");
49051
+ const _isFormula = isFormula(this.composerStore.currentContent);
48992
49052
  const cell = this.env.model.getters.getActiveCell();
48993
49053
  const position = this.env.model.getters.getActivePosition();
48994
49054
  const style = this.env.model.getters.getCellComputedStyle(position);
48995
49055
  // position style
48996
49056
  const { x: left, y: top, width, height } = this.rect;
48997
49057
  // color style
48998
- const background = (!isFormula && style.fillColor) || "#ffffff";
48999
- const color = (!isFormula && style.textColor) || "#000000";
49058
+ const background = (!_isFormula && style.fillColor) || "#ffffff";
49059
+ const color = (!_isFormula && style.textColor) || "#000000";
49000
49060
  // font style
49001
- const fontSize = (!isFormula && style.fontSize) || 10;
49002
- const fontWeight = !isFormula && style.bold ? "bold" : undefined;
49003
- const fontStyle = !isFormula && style.italic ? "italic" : "normal";
49004
- const textDecoration = !isFormula ? getTextDecoration(style) : "none";
49061
+ const fontSize = (!_isFormula && style.fontSize) || 10;
49062
+ const fontWeight = !_isFormula && style.bold ? "bold" : undefined;
49063
+ const fontStyle = !_isFormula && style.italic ? "italic" : "normal";
49064
+ const textDecoration = !_isFormula ? getTextDecoration(style) : "none";
49005
49065
  // align style
49006
49066
  let textAlign = "left";
49007
- if (!isFormula) {
49067
+ if (!_isFormula) {
49008
49068
  textAlign = style.align || cell.defaultAlign;
49009
49069
  }
49010
49070
  const maxHeight = this.props.gridDims.height - this.rect.y;
@@ -51638,8 +51698,8 @@ stores.inject(MyMetaStore, storeInstance);
51638
51698
  css /* scss */ `
51639
51699
  .o-corner {
51640
51700
  position: absolute;
51641
- height: 6px;
51642
- width: 6px;
51701
+ height: 8px;
51702
+ width: 8px;
51643
51703
  border: 1px solid white;
51644
51704
  }
51645
51705
  .o-corner-nw,
@@ -53204,7 +53264,7 @@ stores.inject(MyMetaStore, storeInstance);
53204
53264
  this.history.update("borders", sheetId, col, row, "left", border?.left);
53205
53265
  if (border?.left &&
53206
53266
  col > 0 &&
53207
- !deepEquals(this.getCellBorder({ sheetId, col: col - 1, row })?.right, border?.left)) {
53267
+ !deepEquals(this.borders[sheetId]?.[col - 1]?.[row]?.right, border?.left)) {
53208
53268
  this.history.update("borders", sheetId, col - 1, row, "right", undefined);
53209
53269
  }
53210
53270
  }
@@ -53212,7 +53272,7 @@ stores.inject(MyMetaStore, storeInstance);
53212
53272
  this.history.update("borders", sheetId, col, row, "top", border?.top);
53213
53273
  if (border?.top &&
53214
53274
  row > 0 &&
53215
- !deepEquals(this.getCellBorder({ sheetId, col, row: row - 1 })?.bottom, border?.top)) {
53275
+ !deepEquals(this.borders[sheetId]?.[col]?.[row - 1]?.bottom, border?.top)) {
53216
53276
  this.history.update("borders", sheetId, col, row - 1, "bottom", undefined);
53217
53277
  }
53218
53278
  }
@@ -53220,7 +53280,7 @@ stores.inject(MyMetaStore, storeInstance);
53220
53280
  this.history.update("borders", sheetId, col, row, "right", border?.right);
53221
53281
  if (border?.right &&
53222
53282
  col < maxCol &&
53223
- !deepEquals(this.getCellBorder({ sheetId, col: col + 1, row })?.left, border?.right)) {
53283
+ !deepEquals(this.borders[sheetId]?.[col + 1]?.[row]?.left, border?.right)) {
53224
53284
  this.history.update("borders", sheetId, col + 1, row, "left", undefined);
53225
53285
  }
53226
53286
  }
@@ -53228,7 +53288,7 @@ stores.inject(MyMetaStore, storeInstance);
53228
53288
  this.history.update("borders", sheetId, col, row, "bottom", border?.bottom);
53229
53289
  if (border?.bottom &&
53230
53290
  row < maxRow &&
53231
- !deepEquals(this.getCellBorder({ sheetId, col, row: row + 1 })?.top, border?.bottom)) {
53291
+ !deepEquals(this.borders[sheetId]?.[col]?.[row + 1]?.top, border?.bottom)) {
53232
53292
  this.history.update("borders", sheetId, col, row + 1, "top", undefined);
53233
53293
  }
53234
53294
  }
@@ -60000,6 +60060,10 @@ stores.inject(MyMetaStore, storeInstance);
60000
60060
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
60001
60061
  this.compilationParams.evalContext.updateDependencies = this.updateDependencies.bind(this);
60002
60062
  this.compilationParams.evalContext.addDependencies = this.addDependencies.bind(this);
60063
+ this.compilationParams.evalContext.lookupCaches = {
60064
+ forwardSearch: new Map(),
60065
+ reverseSearch: new Map(),
60066
+ };
60003
60067
  }
60004
60068
  createEmptyPositionSet() {
60005
60069
  const sheetSizes = {};
@@ -68616,7 +68680,7 @@ stores.inject(MyMetaStore, storeInstance);
68616
68680
  condition: (position, getters) => {
68617
68681
  return !!getters.getEvaluatedCell(position).link;
68618
68682
  },
68619
- execute: (position, env) => openLink(env.model.getters.getEvaluatedCell(position).link, env),
68683
+ execute: (position, env, isMiddleClick) => openLink(env.model.getters.getEvaluatedCell(position).link, env, isMiddleClick),
68620
68684
  sequence: 5,
68621
68685
  });
68622
68686
 
@@ -69601,9 +69665,9 @@ stores.inject(MyMetaStore, storeInstance);
69601
69665
  getClickableCells() {
69602
69666
  return owl.toRaw(this.clickableCellsStore.clickableCells);
69603
69667
  }
69604
- selectClickableCell(clickableCell) {
69668
+ selectClickableCell(ev, clickableCell) {
69605
69669
  const { position, action } = clickableCell;
69606
- action(position, this.env);
69670
+ action(position, this.env, isMiddleClickOrCtrlClick(ev));
69607
69671
  }
69608
69672
  onClosePopover() {
69609
69673
  this.cellPopovers.close();
@@ -75610,9 +75674,9 @@ stores.inject(MyMetaStore, storeInstance);
75610
75674
  exports.tokenize = tokenize;
75611
75675
 
75612
75676
 
75613
- __info__.version = "18.3.0-alpha.0";
75614
- __info__.date = "2025-02-18T09:02:28.625Z";
75615
- __info__.hash = "9b88da0";
75677
+ __info__.version = "18.3.0-alpha.1";
75678
+ __info__.date = "2025-02-25T06:00:14.885Z";
75679
+ __info__.hash = "be4d957";
75616
75680
 
75617
75681
 
75618
75682
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);