@odoo/o-spreadsheet 18.5.0-alpha.3 → 18.5.0-alpha.5

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.5.0-alpha.3
6
- * @date 2025-07-28T13:43:05.981Z
7
- * @hash 53dfee8
5
+ * @version 18.5.0-alpha.5
6
+ * @date 2025-08-04T06:53:29.412Z
7
+ * @hash 71c9a36
8
8
  */
9
9
 
10
10
  (function (exports, owl) {
@@ -1057,16 +1057,21 @@
1057
1057
  "#001f3f",
1058
1058
  ];
1059
1059
  /*
1060
- * transform a color number (R * 256^2 + G * 256 + B) into classic hex6 value
1060
+ * transform a color number (R * 256^2 + G * 256 + B) into classic hex (+alpha) value
1061
1061
  * */
1062
- function colorNumberString(color) {
1063
- return toHex(color.toString(16).padStart(6, "0"));
1062
+ function colorNumberToHex(color, alpha = 1) {
1063
+ const alphaHex = alpha !== 1
1064
+ ? Math.round(alpha * 255)
1065
+ .toString(16)
1066
+ .padStart(2, "0")
1067
+ : "";
1068
+ return toHex(color.toString(16).padStart(6, "0")) + alphaHex;
1064
1069
  }
1065
1070
  function colorToNumber(color) {
1066
1071
  if (typeof color === "number") {
1067
1072
  return color;
1068
1073
  }
1069
- return Number.parseInt(toHex(color).slice(1), 16);
1074
+ return Number.parseInt(toHex(color).slice(1, 7), 16);
1070
1075
  }
1071
1076
  /**
1072
1077
  * Converts any CSS color value to a standardized hex6 value.
@@ -1325,6 +1330,12 @@
1325
1330
  function hexToHSLA(hex) {
1326
1331
  return rgbaToHSLA(colorToRGBA(hex));
1327
1332
  }
1333
+ function colorOrNumberToRGBA(color) {
1334
+ if (typeof color === "number") {
1335
+ return colorToRGBA(colorNumberToHex(color));
1336
+ }
1337
+ return colorToRGBA(color);
1338
+ }
1328
1339
  /**
1329
1340
  * Will compare two color strings
1330
1341
  * A tolerance can be provided to account for small differences that could
@@ -1606,6 +1617,8 @@
1606
1617
  const sortedColorScalePoints = [...colorScalePoints.sort((a, b) => a.value - b.value)];
1607
1618
  const thresholds = [];
1608
1619
  for (let i = 1; i < sortedColorScalePoints.length; i++) {
1620
+ const minColorAlpha = colorOrNumberToRGBA(sortedColorScalePoints[i - 1].color).a;
1621
+ const maxColorAlpha = colorOrNumberToRGBA(sortedColorScalePoints[i].color).a;
1609
1622
  const minColor = colorToNumber(sortedColorScalePoints[i - 1].color);
1610
1623
  const maxColor = colorToNumber(sortedColorScalePoints[i].color);
1611
1624
  thresholds.push({
@@ -1613,19 +1626,21 @@
1613
1626
  max: sortedColorScalePoints[i].value,
1614
1627
  minColor,
1615
1628
  maxColor,
1629
+ minColorAlpha: minColorAlpha,
1630
+ maxColorAlpha: maxColorAlpha,
1616
1631
  colorDiff: computeColorDiffUnits(sortedColorScalePoints[i - 1].value, sortedColorScalePoints[i].value, minColor, maxColor),
1617
1632
  });
1618
1633
  }
1619
1634
  return (value) => {
1620
1635
  if (value < thresholds[0].min) {
1621
- return colorNumberString(thresholds[0].minColor);
1636
+ return colorNumberToHex(thresholds[0].minColor, thresholds[0].minColorAlpha);
1622
1637
  }
1623
1638
  for (const threshold of thresholds) {
1624
1639
  if (value >= threshold.min && value <= threshold.max) {
1625
- return colorNumberString(colorCell(value, threshold.min, threshold.minColor, threshold.colorDiff));
1640
+ return colorNumberToHex(colorCell(value, threshold.min, threshold.minColor, threshold.colorDiff), threshold.maxColorAlpha);
1626
1641
  }
1627
1642
  }
1628
- return colorNumberString(thresholds[thresholds.length - 1].maxColor);
1643
+ return colorNumberToHex(thresholds[thresholds.length - 1].maxColor, thresholds[thresholds.length - 1].maxColorAlpha);
1629
1644
  };
1630
1645
  }
1631
1646
  function computeColorDiffUnits(minValue, maxValue, minColor, maxColor) {
@@ -8639,12 +8654,12 @@
8639
8654
  avg: _t("Average"),
8640
8655
  sum: _t("Sum"),
8641
8656
  };
8642
- const NUMBER_CHAR_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8657
+ const DEFAULT_AGGREGATORS = ["max", "min", "avg", "sum", "count_distinct", "count"];
8643
8658
  const AGGREGATORS_BY_FIELD_TYPE = {
8644
- integer: NUMBER_CHAR_AGGREGATORS,
8645
- char: NUMBER_CHAR_AGGREGATORS,
8659
+ integer: DEFAULT_AGGREGATORS,
8660
+ char: DEFAULT_AGGREGATORS,
8661
+ datetime: DEFAULT_AGGREGATORS,
8646
8662
  boolean: ["count_distinct", "count", "bool_and", "bool_or"],
8647
- datetime: ["max", "min", "count_distinct", "count"],
8648
8663
  };
8649
8664
  const AGGREGATORS = {};
8650
8665
  for (const type in AGGREGATORS_BY_FIELD_TYPE) {
@@ -20477,6 +20492,54 @@ stores.inject(MyMetaStore, storeInstance);
20477
20492
  },
20478
20493
  isExported: true,
20479
20494
  };
20495
+ const REGEXEXTRACT_DEFAULT_MODE = 0;
20496
+ const REGEXEXTRACT_DEFAULT_CASE_SENSITIVITY = 0;
20497
+ // -----------------------------------------------------------------------------
20498
+ // REGEXEXTRACT
20499
+ // -----------------------------------------------------------------------------
20500
+ const REGEXEXTRACT = {
20501
+ description: _t("Extract text from a string based on the supplied regular expression."),
20502
+ args: [
20503
+ arg("text (string)", _t("The string on which you want to extract text.")),
20504
+ arg("pattern (string)", _t("The regular expression pattern to match against the text.")),
20505
+ arg(`return_mode (number, default=${REGEXEXTRACT_DEFAULT_MODE})`, _t("0 = first match, 1 = all matches as an array, 2 = capturing groups from the first match as an array.")),
20506
+ arg(`case_sensitivity (number, default=${REGEXEXTRACT_DEFAULT_CASE_SENSITIVITY})`, _t("0 = case-sensitive, 1 = case-insensitive.")),
20507
+ ],
20508
+ compute: function (text, pattern, return_mode = { value: REGEXEXTRACT_DEFAULT_MODE }, newText = { value: REGEXEXTRACT_DEFAULT_CASE_SENSITIVITY }) {
20509
+ const _text = toString(text);
20510
+ const _pattern = toString(pattern);
20511
+ const _returnMode = toNumber(return_mode, this.locale);
20512
+ const _caseSensitivity = toNumber(newText, this.locale);
20513
+ if (_text === "" || _pattern === "") {
20514
+ return { value: "" };
20515
+ }
20516
+ if (_returnMode < 0 || _returnMode > 2) {
20517
+ return new EvaluationError(_t("The return_mode (%s) must be 0, 1 or 2.", _returnMode));
20518
+ }
20519
+ if (_caseSensitivity !== 0 && _caseSensitivity !== 1) {
20520
+ return new EvaluationError(_t("The case_sensitivity (%s) must be 0 or 1.", _caseSensitivity));
20521
+ }
20522
+ const flags = _caseSensitivity === 1 ? "gi" : "g";
20523
+ const regex = new RegExp(_pattern, flags);
20524
+ const matches = [..._text.matchAll(regex)];
20525
+ if (matches.length === 0) {
20526
+ return { value: CellErrorType.NotAvailable, message: _t("No matches found.") };
20527
+ }
20528
+ if (_returnMode === 0) {
20529
+ return matches[0][0];
20530
+ }
20531
+ else if (_returnMode === 1) {
20532
+ return matches.map((match) => [match[0]]);
20533
+ }
20534
+ else {
20535
+ if (matches[0].length < 2) {
20536
+ return new EvaluationError(_t("No capturing groups found."));
20537
+ }
20538
+ return matches[0].slice(1).map((s) => [s]);
20539
+ }
20540
+ },
20541
+ isExported: true,
20542
+ };
20480
20543
  // -----------------------------------------------------------------------------
20481
20544
  // REPLACE
20482
20545
  // -----------------------------------------------------------------------------
@@ -20753,6 +20816,105 @@ stores.inject(MyMetaStore, storeInstance);
20753
20816
  },
20754
20817
  isExported: true,
20755
20818
  };
20819
+ // -----------------------------------------------------------------------------
20820
+ // TEXTAFTER
20821
+ // -----------------------------------------------------------------------------
20822
+ const TEXT_FN_DEFAULT_INSTANCE = 1;
20823
+ const TEXT_FN_DEFAULT_MATCH_MODE = 0;
20824
+ const TEXT_FN_DEFAULT_MATCH_END = 0;
20825
+ const TEXTAFTER = {
20826
+ description: _t("Returns text that occurs after a given substring or delimiter."),
20827
+ args: [
20828
+ arg("text (string)", _t("The source text.")),
20829
+ arg("delimiter (string)", _t("The substring after which text will be returned.")),
20830
+ arg(`instance_num (number, default=${TEXT_FN_DEFAULT_INSTANCE})`, _t("The desired instance of the delimiter after which we extract the text. A negative number searches from the end.")),
20831
+ arg(`match_mode (number, default=${TEXT_FN_DEFAULT_MATCH_MODE})`, _t("0 = case-sensitive, 1 = case-insensitive.")),
20832
+ arg(`match_end (number, default=${TEXT_FN_DEFAULT_MATCH_END}))`, _t("Whether to treat the end of text as a delimiter.")),
20833
+ arg(`if_not_found (string, default="${CellErrorType.NotAvailable}")`, _t("Value to return if the delimiter is not found.")),
20834
+ ],
20835
+ compute: function (text, delimiter, matchIndex = { value: TEXT_FN_DEFAULT_INSTANCE }, matchMode = { value: TEXT_FN_DEFAULT_MATCH_MODE }, matchEnd = { value: TEXT_FN_DEFAULT_MATCH_END }, ifNotFound = new NotAvailableError()) {
20836
+ const _text = toString(text);
20837
+ const _matchIndex = toNumber(matchIndex, this.locale);
20838
+ const _matchMode = toNumber(matchMode, this.locale);
20839
+ const _matchEnd = toNumber(matchEnd, this.locale);
20840
+ if (_matchIndex === 0) {
20841
+ return new EvaluationError(_t("The instance_num (%s) must not be zero.", _matchIndex));
20842
+ }
20843
+ if (_matchMode !== 0 && _matchMode !== 1) {
20844
+ return new EvaluationError(_t("match_mode should have a value of 0 or 1."));
20845
+ }
20846
+ if (_matchEnd !== 0 && _matchEnd !== 1) {
20847
+ return new EvaluationError(_t("match_end should have a value of 0 or 1."));
20848
+ }
20849
+ const _delimiter = toString(delimiter);
20850
+ if (_delimiter === "") {
20851
+ return Math.sign(_matchIndex) > 0 ? { value: _text } : { value: "" };
20852
+ }
20853
+ const flags = _matchMode === 1 ? "gi" : "g";
20854
+ const pattern = escapeRegExp(_delimiter);
20855
+ const regexp = new RegExp(pattern, flags);
20856
+ let matchIndices = [..._text.matchAll(regexp)].map((match) => match.index + pattern.length);
20857
+ if (_matchIndex < 0) {
20858
+ matchIndices = matchIndices.reverse();
20859
+ }
20860
+ // If _matchEnd, we act like the text is appended by the delimiter (or prepended if negative index)
20861
+ if (_matchEnd && Math.abs(_matchIndex) === matchIndices.length + 1) {
20862
+ return Math.sign(_matchIndex) > 0 ? { value: "" } : { value: _text };
20863
+ }
20864
+ const targetIndex = matchIndices[Math.abs(_matchIndex) - 1];
20865
+ return targetIndex === undefined ? ifNotFound : { value: _text.substring(targetIndex) };
20866
+ },
20867
+ isExported: true,
20868
+ };
20869
+ // -----------------------------------------------------------------------------
20870
+ // TEXTBEFORE
20871
+ // -----------------------------------------------------------------------------
20872
+ const TEXTBEFORE = {
20873
+ description: _t("Returns text that occurs before a given substring or delimiter."),
20874
+ args: [
20875
+ arg("text (string)", _t("The source text.")),
20876
+ arg("delimiter (string)", _t("The substring after which text will be returned.")),
20877
+ arg(`instance_num (number, default=${TEXT_FN_DEFAULT_INSTANCE})`, _t("The desired instance of the delimiter before which we extract the text. A negative number searches from the end.")),
20878
+ arg(`match_mode (number, default=${TEXT_FN_DEFAULT_MATCH_MODE})`, _t("0 = case-sensitive, 1 = case-insensitive.")),
20879
+ arg(`match_end (number, default=${TEXT_FN_DEFAULT_MATCH_END}))`, _t("Whether to match a delimiter against the end of the text.")),
20880
+ arg(`if_not_found (string, default="${CellErrorType.NotAvailable}")`, _t("Value to return if the delimiter is not found.")),
20881
+ ],
20882
+ compute: function (text, delimiter, matchIndex = { value: TEXT_FN_DEFAULT_INSTANCE }, matchMode = { value: TEXT_FN_DEFAULT_MATCH_MODE }, matchEnd = { value: TEXT_FN_DEFAULT_MATCH_END }, ifNotFound = new NotAvailableError()) {
20883
+ const _text = toString(text);
20884
+ const _matchIndex = toNumber(matchIndex, this.locale);
20885
+ const _matchMode = toNumber(matchMode, this.locale);
20886
+ const _matchEnd = toNumber(matchEnd, this.locale);
20887
+ if (_matchIndex === 0) {
20888
+ return new EvaluationError(_t("The instance_num (%s) must not be zero.", _matchIndex));
20889
+ }
20890
+ if (_matchMode !== 0 && _matchMode !== 1) {
20891
+ return new EvaluationError(_t("match_mode should have a value of 0 or 1."));
20892
+ }
20893
+ if (_matchEnd !== 0 && _matchEnd !== 1) {
20894
+ return new EvaluationError(_t("match_end should have a value of 0 or 1."));
20895
+ }
20896
+ const _delimiter = toString(delimiter);
20897
+ if (_delimiter === "") {
20898
+ return Math.sign(_matchIndex) > 0 ? { value: "" } : { value: _text };
20899
+ }
20900
+ const flags = _matchMode === 1 ? "gi" : "g";
20901
+ const pattern = escapeRegExp(_delimiter);
20902
+ const regexp = new RegExp(pattern, flags);
20903
+ let matchIndices = [..._text.matchAll(regexp)].map((match) => match.index + pattern.length);
20904
+ if (_matchIndex < 0) {
20905
+ matchIndices = matchIndices.reverse();
20906
+ }
20907
+ // If _matchEnd, we act like the text is appended by the delimiter (or prepended if negative index)
20908
+ if (_matchEnd && Math.abs(_matchIndex) === matchIndices.length + 1) {
20909
+ return Math.sign(_matchIndex) > 0 ? { value: _text } : { value: "" };
20910
+ }
20911
+ const targetIndex = matchIndices[Math.abs(_matchIndex) - 1];
20912
+ return targetIndex === undefined
20913
+ ? ifNotFound
20914
+ : { value: _text.substring(0, targetIndex - _delimiter.length) };
20915
+ },
20916
+ isExported: true,
20917
+ };
20756
20918
 
20757
20919
  var text = /*#__PURE__*/Object.freeze({
20758
20920
  __proto__: null,
@@ -20767,12 +20929,15 @@ stores.inject(MyMetaStore, storeInstance);
20767
20929
  LOWER: LOWER,
20768
20930
  MID: MID,
20769
20931
  PROPER: PROPER,
20932
+ REGEXEXTRACT: REGEXEXTRACT,
20770
20933
  REPLACE: REPLACE,
20771
20934
  RIGHT: RIGHT,
20772
20935
  SEARCH: SEARCH,
20773
20936
  SPLIT: SPLIT,
20774
20937
  SUBSTITUTE: SUBSTITUTE,
20775
20938
  TEXT: TEXT,
20939
+ TEXTAFTER: TEXTAFTER,
20940
+ TEXTBEFORE: TEXTBEFORE,
20776
20941
  TEXTJOIN: TEXTJOIN,
20777
20942
  TEXTSPLIT: TEXTSPLIT,
20778
20943
  TRIM: TRIM,
@@ -22776,6 +22941,7 @@ stores.inject(MyMetaStore, storeInstance);
22776
22941
  this.animationStore = useStore(ChartAnimationStore);
22777
22942
  }
22778
22943
  owl.onMounted(() => {
22944
+ registerChartJSExtensions();
22779
22945
  const runtime = this.chartRuntime;
22780
22946
  this.currentRuntime = runtime;
22781
22947
  // Note: chartJS modify the runtime in place, so it's important to give it a copy
@@ -30516,28 +30682,17 @@ stores.inject(MyMetaStore, storeInstance);
30516
30682
  }
30517
30683
  }
30518
30684
 
30519
- class ChartDashboardMenu extends owl.Component {
30520
- static template = "spreadsheet.ChartDashboardMenu";
30521
- static components = { MenuPopover };
30522
- static props = { figureUI: Object };
30685
+ class ChartDashboardMenuStore extends SpreadsheetStore {
30686
+ chartId;
30687
+ mutators = ["reset"];
30523
30688
  originalChartDefinition;
30524
- fullScreenFigureStore;
30525
- menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
30526
- setup() {
30527
- super.setup();
30528
- this.fullScreenFigureStore = useStore(FullScreenChartStore);
30529
- this.originalChartDefinition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
30530
- owl.onWillUpdateProps(({ figureUI }) => {
30531
- if (figureUI.id !== this.props.figureUI.id) {
30532
- this.originalChartDefinition = this.env.model.getters.getChartDefinition(figureUI.id);
30533
- }
30534
- });
30535
- }
30536
- getMenuItems() {
30537
- return [this.fullScreenMenuItem, ...this.changeChartTypeMenuItems].filter(isDefined);
30689
+ constructor(get, chartId) {
30690
+ super(get);
30691
+ this.chartId = chartId;
30692
+ this.originalChartDefinition = this.getters.getChartDefinition(this.chartId);
30538
30693
  }
30539
30694
  get changeChartTypeMenuItems() {
30540
- const definition = this.env.model.getters.getChartDefinition(this.props.figureUI.id);
30695
+ const definition = this.getters.getChartDefinition(this.chartId);
30541
30696
  if (!["line", "bar", "pie"].includes(definition.type)) {
30542
30697
  return [];
30543
30698
  }
@@ -30546,27 +30701,19 @@ stores.inject(MyMetaStore, storeInstance);
30546
30701
  return {
30547
30702
  id: item.chartType,
30548
30703
  label: item.displayName,
30549
- onClick: () => this.onTypeChange(item.chartType),
30550
- isSelected: item.chartType === this.selectedChartType,
30704
+ onClick: () => this.updateType(item.chartType),
30705
+ isSelected: item.chartType === this.getters.getChartDefinition(this.chartId).type,
30551
30706
  iconClass: this.getIconClasses(item.chartType),
30552
30707
  };
30553
30708
  });
30554
30709
  }
30555
- getIconClasses(type) {
30556
- if (type.includes("bar")) {
30557
- return "fa fa-bar-chart";
30558
- }
30559
- if (type.includes("line")) {
30560
- return "fa fa-line-chart";
30561
- }
30562
- if (type.includes("pie")) {
30563
- return "fa fa-pie-chart";
30564
- }
30565
- return "";
30710
+ reset(chartId) {
30711
+ this.chartId = chartId;
30712
+ this.originalChartDefinition = this.getters.getChartDefinition(chartId);
30566
30713
  }
30567
- onTypeChange(type) {
30568
- const figureId = this.props.figureUI.id;
30569
- const currentDefinition = this.env.model.getters.getChartDefinition(figureId);
30714
+ updateType(type) {
30715
+ const figureId = this.chartId;
30716
+ const currentDefinition = this.getters.getChartDefinition(figureId);
30570
30717
  if (currentDefinition.type === type) {
30571
30718
  return;
30572
30719
  }
@@ -30577,7 +30724,7 @@ stores.inject(MyMetaStore, storeInstance);
30577
30724
  else {
30578
30725
  const newChartInfo = chartSubtypeRegistry.get(type);
30579
30726
  const ChartClass = chartRegistry.get(newChartInfo.chartType);
30580
- const chartCreationContext = this.env.model.getters.getContextCreationChart(figureId);
30727
+ const chartCreationContext = this.getters.getContextCreationChart(figureId);
30581
30728
  if (!chartCreationContext)
30582
30729
  return;
30583
30730
  definition = {
@@ -30585,14 +30732,45 @@ stores.inject(MyMetaStore, storeInstance);
30585
30732
  ...newChartInfo.subtypeDefinition,
30586
30733
  };
30587
30734
  }
30588
- this.env.model.dispatch("UPDATE_CHART", {
30735
+ this.model.dispatch("UPDATE_CHART", {
30589
30736
  definition,
30590
30737
  figureId,
30591
- sheetId: this.env.model.getters.getActiveSheetId(),
30738
+ sheetId: this.getters.getActiveSheetId(),
30592
30739
  });
30593
30740
  }
30594
- get selectedChartType() {
30595
- return this.env.model.getters.getChartDefinition(this.props.figureUI.id).type;
30741
+ getIconClasses(type) {
30742
+ if (type.includes("bar")) {
30743
+ return "fa fa-bar-chart";
30744
+ }
30745
+ if (type.includes("line")) {
30746
+ return "fa fa-line-chart";
30747
+ }
30748
+ if (type.includes("pie")) {
30749
+ return "fa fa-pie-chart";
30750
+ }
30751
+ return "";
30752
+ }
30753
+ }
30754
+
30755
+ class ChartDashboardMenu extends owl.Component {
30756
+ static template = "o-spreadsheet-ChartDashboardMenu";
30757
+ static components = { MenuPopover };
30758
+ static props = { figureUI: Object };
30759
+ fullScreenFigureStore;
30760
+ store;
30761
+ menuState = owl.useState({ isOpen: false, anchorRect: null, menuItems: [] });
30762
+ setup() {
30763
+ super.setup();
30764
+ this.store = useLocalStore(ChartDashboardMenuStore, this.props.figureUI.id);
30765
+ this.fullScreenFigureStore = useStore(FullScreenChartStore);
30766
+ owl.onWillUpdateProps(({ figureUI }) => {
30767
+ if (figureUI.id !== this.props.figureUI.id) {
30768
+ this.store.reset(figureUI.id);
30769
+ }
30770
+ });
30771
+ }
30772
+ getMenuItems() {
30773
+ return [this.fullScreenMenuItem, ...this.store.changeChartTypeMenuItems].filter(isDefined);
30596
30774
  }
30597
30775
  get backgroundColor() {
30598
30776
  const color = this.env.model.getters.getChartDefinition(this.props.figureUI.id).background;
@@ -31734,7 +31912,7 @@ stores.inject(MyMetaStore, storeInstance);
31734
31912
  }
31735
31913
  const criterionValues = getters.getDataValidationRangeValues(sheetId, criterion);
31736
31914
  return criterionValues
31737
- .map((value) => value.toLowerCase())
31915
+ .map((value) => value.value.toLowerCase())
31738
31916
  .includes(value.toString().toLowerCase());
31739
31917
  },
31740
31918
  getErrorString: (criterion) => _t("The value must be a value in the range %s", String(criterion.values[0])),
@@ -32166,40 +32344,6 @@ stores.inject(MyMetaStore, storeInstance);
32166
32344
  return sameColor && sameClass && sameContent;
32167
32345
  }
32168
32346
 
32169
- // -----------------------------------------------------------------------------
32170
- // Formula Assistant component
32171
- // -----------------------------------------------------------------------------
32172
- css /* scss */ `
32173
- .o-formula-assistant {
32174
- background: #ffffff;
32175
- .o-formula-assistant-head {
32176
- background-color: #f2f2f2;
32177
- padding: 10px;
32178
- }
32179
- .collapsed {
32180
- transform: rotate(180deg);
32181
- }
32182
- .o-formula-assistant-core {
32183
- border-bottom: 1px solid gray;
32184
- }
32185
- .o-formula-assistant-arg-description {
32186
- font-size: 85%;
32187
- }
32188
- .o-formula-assistant-focus {
32189
- div:first-child,
32190
- span {
32191
- color: ${COMPOSER_ASSISTANT_COLOR};
32192
- text-shadow: 0px 0px 1px ${COMPOSER_ASSISTANT_COLOR};
32193
- }
32194
- div:last-child {
32195
- color: black;
32196
- }
32197
- }
32198
- .o-formula-assistant-gray {
32199
- color: gray;
32200
- }
32201
- }
32202
- `;
32203
32347
  class FunctionDescriptionProvider extends owl.Component {
32204
32348
  static template = "o-spreadsheet-FunctionDescriptionProvider";
32205
32349
  static props = {
@@ -51912,16 +52056,16 @@ stores.inject(MyMetaStore, storeInstance);
51912
52056
  return cssPropertiesToCss(cellStyleToCss(rule.style));
51913
52057
  }
51914
52058
  else if (rule.type === "ColorScaleRule") {
51915
- const minColor = colorNumberString(rule.minimum.color);
51916
- const midColor = rule.midpoint ? colorNumberString(rule.midpoint.color) : null;
51917
- const maxColor = colorNumberString(rule.maximum.color);
52059
+ const minColor = colorNumberToHex(rule.minimum.color);
52060
+ const midColor = rule.midpoint ? colorNumberToHex(rule.midpoint.color) : null;
52061
+ const maxColor = colorNumberToHex(rule.maximum.color);
51918
52062
  const baseString = "background-image: linear-gradient(to right, ";
51919
52063
  return midColor
51920
52064
  ? baseString + minColor + ", " + midColor + ", " + maxColor + ")"
51921
52065
  : baseString + minColor + ", " + maxColor + ")";
51922
52066
  }
51923
52067
  else if (rule.type === "DataBarRule") {
51924
- const color = colorNumberString(rule.color);
52068
+ const color = colorNumberToHex(rule.color);
51925
52069
  return `background-image: linear-gradient(to right, ${color} 50%, white 50%)`;
51926
52070
  }
51927
52071
  return "";
@@ -52111,7 +52255,8 @@ stores.inject(MyMetaStore, storeInstance);
52111
52255
  static props = {
52112
52256
  editedCf: Object,
52113
52257
  onCancel: Function,
52114
- onSave: Function,
52258
+ onExit: Function,
52259
+ isNewCf: Boolean,
52115
52260
  };
52116
52261
  static components = {
52117
52262
  SelectionInput,
@@ -52128,7 +52273,7 @@ stores.inject(MyMetaStore, storeInstance);
52128
52273
  icons = ICONS;
52129
52274
  iconSets = ICON_SETS;
52130
52275
  getTextDecoration = getTextDecoration;
52131
- colorNumberString = colorNumberString;
52276
+ colorNumberToHex = colorNumberToHex;
52132
52277
  state;
52133
52278
  setup() {
52134
52279
  this.state = owl.useState({
@@ -52136,6 +52281,7 @@ stores.inject(MyMetaStore, storeInstance);
52136
52281
  currentCFType: this.props.editedCf.rule.type,
52137
52282
  ranges: this.props.editedCf.ranges,
52138
52283
  rules: this.getDefaultRules(),
52284
+ hasEditedCf: this.props.isNewCf,
52139
52285
  });
52140
52286
  switch (this.props.editedCf.rule.type) {
52141
52287
  case "CellIsRule":
@@ -52187,6 +52333,9 @@ stores.inject(MyMetaStore, storeInstance);
52187
52333
  ranges: ranges.map((xc) => this.env.model.getters.getRangeDataFromXc(sheetId, xc)),
52188
52334
  sheetId,
52189
52335
  });
52336
+ if (result.isSuccessful) {
52337
+ this.state.hasEditedCf = true;
52338
+ }
52190
52339
  const reasons = result.reasons.filter((r) => r !== "NoChanges" /* CommandResult.NoChanges */);
52191
52340
  if (!newCf.suppressErrors) {
52192
52341
  this.state.errors = reasons;
@@ -52208,7 +52357,15 @@ stores.inject(MyMetaStore, storeInstance);
52208
52357
  onSave() {
52209
52358
  const result = this.updateConditionalFormat({});
52210
52359
  if (result.length === 0) {
52211
- this.props.onSave();
52360
+ this.props.onExit();
52361
+ }
52362
+ }
52363
+ onCancel() {
52364
+ if (this.state.hasEditedCf) {
52365
+ this.props.onCancel();
52366
+ }
52367
+ else {
52368
+ this.props.onExit();
52212
52369
  }
52213
52370
  }
52214
52371
  getDefaultRules() {
@@ -52367,9 +52524,9 @@ stores.inject(MyMetaStore, storeInstance);
52367
52524
  }
52368
52525
  getPreviewGradient() {
52369
52526
  const rule = this.state.rules.colorScale;
52370
- const minColor = colorNumberString(rule.minimum.color);
52371
- const midColor = colorNumberString(rule.midpoint?.color || DEFAULT_COLOR_SCALE_MIDPOINT_COLOR);
52372
- const maxColor = colorNumberString(rule.maximum.color);
52527
+ const minColor = colorNumberToHex(rule.minimum.color);
52528
+ const midColor = colorNumberToHex(rule.midpoint?.color || DEFAULT_COLOR_SCALE_MIDPOINT_COLOR);
52529
+ const maxColor = colorNumberToHex(rule.maximum.color);
52373
52530
  const baseString = "background-image: linear-gradient(to right, ";
52374
52531
  return rule.midpoint === undefined
52375
52532
  ? baseString + minColor + ", " + maxColor + ")"
@@ -52377,8 +52534,8 @@ stores.inject(MyMetaStore, storeInstance);
52377
52534
  }
52378
52535
  getThresholdColor(threshold) {
52379
52536
  return threshold
52380
- ? colorNumberString(threshold.color)
52381
- : colorNumberString(DEFAULT_COLOR_SCALE_MIDPOINT_COLOR);
52537
+ ? colorNumberToHex(threshold.color)
52538
+ : colorNumberToHex(DEFAULT_COLOR_SCALE_MIDPOINT_COLOR);
52382
52539
  }
52383
52540
  onMidpointChange(ev) {
52384
52541
  const type = ev.target.value;
@@ -66602,9 +66759,9 @@ stores.inject(MyMetaStore, storeInstance);
66602
66759
  formatColors.push(rule.style.fillColor);
66603
66760
  }
66604
66761
  else if (rule.type === "ColorScaleRule") {
66605
- formatColors.push(colorNumberString(rule.minimum.color));
66606
- formatColors.push(rule.midpoint ? colorNumberString(rule.midpoint.color) : undefined);
66607
- formatColors.push(colorNumberString(rule.maximum.color));
66762
+ formatColors.push(colorNumberToHex(rule.minimum.color));
66763
+ formatColors.push(rule.midpoint ? colorNumberToHex(rule.midpoint.color) : undefined);
66764
+ formatColors.push(colorNumberToHex(rule.maximum.color));
66608
66765
  }
66609
66766
  }
66610
66767
  return formatColors.filter(isDefined);
@@ -66975,7 +67132,7 @@ stores.inject(MyMetaStore, storeInstance);
66975
67132
  if (!computedDataBars[col])
66976
67133
  computedDataBars[col] = [];
66977
67134
  computedDataBars[col][row] = {
66978
- color: colorNumberString(color),
67135
+ color: colorNumberToHex(color),
66979
67136
  percentage: (cell.value * 100) / max,
66980
67137
  };
66981
67138
  }
@@ -67104,8 +67261,16 @@ stores.inject(MyMetaStore, storeInstance);
67104
67261
  }
67105
67262
  getDataValidationRangeValues(sheetId, criterion) {
67106
67263
  const range = this.getters.getRangeFromSheetXC(sheetId, String(criterion.values[0]));
67107
- const criterionValues = this.getters.getRangeValues(range);
67108
- return criterionValues.map((value) => value?.toString()).filter(isDefined);
67264
+ const values = [];
67265
+ const labelsSet = new Set();
67266
+ for (const p of positions(range.zone)) {
67267
+ const cell = this.getters.getEvaluatedCell({ ...p, sheetId: range.sheetId });
67268
+ if (cell.formattedValue && !labelsSet.has(cell.formattedValue)) {
67269
+ labelsSet.add(cell.formattedValue);
67270
+ values.push({ label: cell.formattedValue, value: cell.value?.toString() || "" });
67271
+ }
67272
+ }
67273
+ return values;
67109
67274
  }
67110
67275
  isCellValidCheckbox(cellPosition) {
67111
67276
  if (!this.getters.isMainCellPosition(cellPosition)) {
@@ -75727,25 +75892,30 @@ stores.inject(MyMetaStore, storeInstance);
75727
75892
  }
75728
75893
  const sheetId = this.composer.currentEditedCell.sheetId;
75729
75894
  const values = rule.criterion.type === "isValueInRange"
75730
- ? Array.from(new Set(this.getters.getDataValidationRangeValues(sheetId, rule.criterion)))
75731
- : rule.criterion.values;
75895
+ ? this.getters.getDataValidationRangeValues(sheetId, rule.criterion)
75896
+ : rule.criterion.values.map((value) => ({ label: value, value }));
75732
75897
  const isChip = rule.criterion.displayStyle === "chip";
75733
75898
  if (!isChip) {
75734
- return values.map((value) => ({ text: value }));
75899
+ return values.map((value) => ({
75900
+ text: value.value,
75901
+ fuzzySearchKey: value.label,
75902
+ htmlContent: [{ value: value.label }],
75903
+ }));
75735
75904
  }
75736
75905
  const colors = rule.criterion.colors;
75737
75906
  return values.map((value) => {
75738
- const color = colors?.[value];
75907
+ const color = colors?.[value.value];
75739
75908
  return {
75740
- text: value,
75909
+ text: value.value,
75741
75910
  htmlContent: [
75742
75911
  {
75743
- value,
75912
+ value: value.label,
75744
75913
  color: color ? chipTextColor(color) : undefined,
75745
75914
  backgroundColor: color || GRAY_200,
75746
75915
  classes: ["badge rounded-pill fs-6 fw-normal w-100 mt-1 text-start"],
75747
75916
  },
75748
75917
  ],
75918
+ fuzzySearchKey: value.label,
75749
75919
  };
75750
75920
  });
75751
75921
  },
@@ -79986,7 +80156,6 @@ stores.inject(MyMetaStore, storeInstance);
79986
80156
  }
79987
80157
  }
79988
80158
 
79989
- .o-spreadsheet-topbar-wrapper,
79990
80159
  .o-spreadsheet-bottombar-wrapper {
79991
80160
  z-index: ${ComponentsImportance.ScrollBar + 1};
79992
80161
  }
@@ -80101,7 +80270,6 @@ stores.inject(MyMetaStore, storeInstance);
80101
80270
  this.checkViewportSize();
80102
80271
  stores.on("store-updated", this, render);
80103
80272
  resizeObserver.observe(this.spreadsheetRef.el);
80104
- registerChartJSExtensions();
80105
80273
  });
80106
80274
  owl.onWillUnmount(() => {
80107
80275
  this.unbindModelEvents();
@@ -82747,7 +82915,7 @@ stores.inject(MyMetaStore, storeInstance);
82747
82915
  <dataBar>
82748
82916
  <cfvo type="min" val="0"/>
82749
82917
  <cfvo type="max" val="100"/>
82750
- <color rgb="${toXlsxHexColor(colorNumberString(rule.color))}"/>
82918
+ <color rgb="${toXlsxHexColor(colorNumberToHex(rule.color))}"/>
82751
82919
  </dataBar>
82752
82920
  </cfRule>
82753
82921
  </conditionalFormatting>
@@ -82775,7 +82943,7 @@ stores.inject(MyMetaStore, storeInstance);
82775
82943
  continue;
82776
82944
  }
82777
82945
  cfValueObject.push(thresholdAttributes(threshold, position));
82778
- colors.push([["rgb", toXlsxHexColor(colorNumberString(threshold.color))]]);
82946
+ colors.push([["rgb", toXlsxHexColor(colorNumberToHex(threshold.color))]]);
82779
82947
  }
82780
82948
  if (!canExport) {
82781
82949
  console.warn("Conditional formats with formula rules are not supported at the moment. The rule is therefore skipped.");
@@ -84719,6 +84887,8 @@ stores.inject(MyMetaStore, storeInstance);
84719
84887
  WaterfallChartDesignPanel,
84720
84888
  ComboChartDesignPanel,
84721
84889
  FunnelChartDesignPanel,
84890
+ SunburstChartDesignPanel,
84891
+ TreeMapChartDesignPanel,
84722
84892
  ChartTypePicker,
84723
84893
  FigureComponent,
84724
84894
  MenuPopover,
@@ -84748,6 +84918,7 @@ stores.inject(MyMetaStore, storeInstance);
84748
84918
  };
84749
84919
  const stores = {
84750
84920
  useStoreProvider,
84921
+ ChartDashboardMenuStore,
84751
84922
  DependencyContainer,
84752
84923
  CellPopoverStore,
84753
84924
  ComposerFocusStore,
@@ -84834,9 +85005,9 @@ stores.inject(MyMetaStore, storeInstance);
84834
85005
  exports.tokenize = tokenize;
84835
85006
 
84836
85007
 
84837
- __info__.version = "18.5.0-alpha.3";
84838
- __info__.date = "2025-07-28T13:43:05.981Z";
84839
- __info__.hash = "53dfee8";
85008
+ __info__.version = "18.5.0-alpha.5";
85009
+ __info__.date = "2025-08-04T06:53:29.412Z";
85010
+ __info__.hash = "71c9a36";
84840
85011
 
84841
85012
 
84842
85013
  })(this.o_spreadsheet = this.o_spreadsheet || {}, owl);