@odoo/o-spreadsheet 19.2.0-alpha.3 → 19.2.0-alpha.4

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.
@@ -1,9 +1,9 @@
1
1
  <!--
2
2
  This file is generated by o-spreadsheet build tools. Do not edit it.
3
3
  @see https://github.com/odoo/o-spreadsheet
4
- @version 19.2.0-alpha.3
5
- @date 2026-01-14T10:03:04.642Z
6
- @hash e5cbf18
4
+ @version 19.2.0-alpha.4
5
+ @date 2026-01-21T11:09:20.618Z
6
+ @hash fa080c2
7
7
  -->
8
8
  <odoo>
9
9
  <t t-name="o-spreadsheet-ValidationMessages">
@@ -651,16 +651,12 @@
651
651
  <t t-name="o-spreadsheet-SplitIntoColumnsPanel">
652
652
  <div class="o-split-to-cols-panel">
653
653
  <Section title.translate="Separator">
654
- <select class="o-input mb-3" t-on-change="(ev) => this.onSeparatorChange(ev.target.value)">
655
- <option
656
- t-foreach="separators"
657
- t-as="separator"
658
- t-key="separator.value"
659
- t-att-value="separator.value"
660
- t-esc="separator.name"
661
- t-att-selected="state.separatorValue === separator.value"
662
- />
663
- </select>
654
+ <Select
655
+ class="'mb-3'"
656
+ values="separators"
657
+ selectedValue="state.separatorValue"
658
+ onChange.bind="onSeparatorChange"
659
+ />
664
660
 
665
661
  <input
666
662
  class="o-input mb-3"
@@ -722,11 +718,9 @@
722
718
  </div>
723
719
  <div class="o-sidePanelTitle o-fw-bold ms-2" t-esc="getTitle()"/>
724
720
  <div
725
- t-if="props.onTogglePinPanel"
726
- class="o-pin-panel o-sidePanelAction ms-auto rounded"
727
- t-att-class="{'active': props.isPinned}"
728
- t-on-click="props.onTogglePinPanel"
729
- t-att-title="pinInfoMessage">
721
+ t-if="props.isPinned"
722
+ class="o-pin-panel o-sidePanelAction ms-auto rounded active"
723
+ t-on-click="props.onTogglePinPanel">
730
724
  <i class="fa fa-thumb-tack"/>
731
725
  </div>
732
726
  <div class="o-sidePanelClose o-sidePanelAction rounded" t-on-click="props.onCloseSidePanel">
@@ -767,11 +761,9 @@
767
761
  <i class="fa fa-angle-double-left"/>
768
762
  </div>
769
763
  <div
770
- t-if="props.onTogglePinPanel"
771
- class="o-pin-panel o-sidePanelAction rounded mb-1"
772
- t-att-class="{'active': props.isPinned}"
773
- t-on-click.stop="props.onTogglePinPanel"
774
- t-att-title="pinInfoMessage">
764
+ t-if="props.isPinned"
765
+ class="o-pin-panel o-sidePanelAction ms-auto rounded active"
766
+ t-on-click="props.onTogglePinPanel">
775
767
  <i class="fa fa-thumb-tack"/>
776
768
  </div>
777
769
  <div
@@ -788,16 +780,11 @@
788
780
  <t t-name="o-spreadsheet-SettingsPanel">
789
781
  <div class="o-settings-panel">
790
782
  <Section title.translate="Locale">
791
- <select class="o-input" t-on-change="(ev) => this.onLocaleChange(ev.target.value)">
792
- <option
793
- t-foreach="supportedLocales"
794
- t-as="locale"
795
- t-key="locale.code"
796
- t-att-value="locale.code"
797
- t-esc="locale.name"
798
- t-att-selected="currentLocale.code === locale.code"
799
- />
800
- </select>
783
+ <Select
784
+ selectedValue="currentLocale.code"
785
+ values="selectOptions"
786
+ onChange="(value) => this.onLocaleChange(value)"
787
+ />
801
788
  <div class="o-locale-preview mt-4 p-3 rounded border">
802
789
  <div>
803
790
  <span class="o-fw-bold me-1">Number:</span>
@@ -825,24 +812,6 @@
825
812
  </div>
826
813
  </t>
827
814
 
828
- <t t-name="o-spreadsheet-SelectMenu">
829
- <select
830
- t-att-class="props.class"
831
- t-ref="select"
832
- t-on-pointerdown.stop.prevent=""
833
- t-on-click="onClick">
834
- <option selected="true" t-esc="props.selectedValue"/>
835
- </select>
836
- <MenuPopover
837
- t-if="state.isMenuOpen"
838
- menuItems="props.menuItems"
839
- anchorRect="menuAnchorRect"
840
- onClose.bind="onMenuClosed"
841
- menuId="menuId"
842
- popoverPositioning="'bottom-left'"
843
- />
844
- </t>
845
-
846
815
  <t t-name="o-spreadsheet-RemoveDuplicatesPanel">
847
816
  <div class="o-remove-duplicates">
848
817
  <Section>
@@ -1097,17 +1066,12 @@
1097
1066
 
1098
1067
  <t t-name="o-spreadsheet-PivotMeasureDisplayPanel">
1099
1068
  <Section title.translate="Show measure as:">
1100
- <select
1101
- class="o-pivot-measure-display-type o-input"
1102
- t-on-change="(ev) => this.store.updateMeasureDisplayType(ev.target.value)">
1103
- <t t-foreach="measureDisplayTypeLabels" t-as="measureType" t-key="measureType">
1104
- <option
1105
- t-att-value="measureType"
1106
- t-att-selected="measureType === store.measureDisplay.type"
1107
- t-esc="measureType_value"
1108
- />
1109
- </t>
1110
- </select>
1069
+ <Select
1070
+ class="'o-pivot-measure-display-type'"
1071
+ values="measureDisplayTypeOptions"
1072
+ selectedValue="store.measureDisplay.type"
1073
+ onChange="(value) => this.store.updateMeasureDisplayType(value)"
1074
+ />
1111
1075
  <div
1112
1076
  class="o-pivot-measure-display-description mt-3 ps-3 border-start"
1113
1077
  t-esc="measureDisplayDescription[store.measureDisplay.type]"
@@ -1321,24 +1285,12 @@
1321
1285
  <div class="d-flex flex-row">
1322
1286
  <div class="d-flex py-1 px-2 w-100 small">
1323
1287
  <div class="pivot-dim-operator-label">Aggregated by</div>
1324
- <select
1325
- class="o-input flex-grow-1"
1326
- t-on-change="(ev) => this.updateAggregator(ev.target.value)">
1327
- <option
1328
- t-foreach="Object.keys(props.aggregators[measure.type])"
1329
- t-as="agg"
1330
- t-key="agg"
1331
- t-att-value="agg"
1332
- t-att-selected="agg === measure.aggregator"
1333
- t-esc="props.aggregators[measure.type][agg]"
1334
- />
1335
- <option
1336
- t-if="measure.computedBy"
1337
- t-att-value="''"
1338
- t-att-selected="'' === measure.aggregator">
1339
- Compute from totals
1340
- </option>
1341
- </select>
1288
+ <Select
1289
+ class="'flex-grow-1'"
1290
+ values="aggregatorOptions"
1291
+ selectedValue="props.measure.aggregator"
1292
+ onChange.bind="updateAggregator"
1293
+ />
1342
1294
  </div>
1343
1295
  </div>
1344
1296
  </PivotDimension>
@@ -1348,18 +1300,12 @@
1348
1300
  <div class="d-flex">
1349
1301
  <div class="d-flex py-1 px-2 w-100 small">
1350
1302
  <div class="pivot-dim-operator-label">Order by</div>
1351
- <select
1352
- class="o-input flex-grow-1"
1353
- t-on-change="(ev) => props.onUpdated(props.dimension, ev.target.value)">
1354
- <option value="asc" t-att-selected="props.dimension.order === 'asc'">Ascending</option>
1355
- <option value="desc" t-att-selected="props.dimension.order === 'desc'">Descending</option>
1356
- <option
1357
- t-if="props.dimension.type !== 'date'"
1358
- value=""
1359
- t-att-selected="props.dimension.order === undefined">
1360
- Unsorted
1361
- </option>
1362
- </select>
1303
+ <Select
1304
+ class="'flex-grow-1'"
1305
+ values="orderSelectOptions"
1306
+ selectedValue="props.dimension.order || ''"
1307
+ onChange="(value) => props.onUpdated(props.dimension, value)"
1308
+ />
1363
1309
  </div>
1364
1310
  </div>
1365
1311
  </t>
@@ -1369,19 +1315,12 @@
1369
1315
  <div class="d-flex flex-row py-1 px-2 w-100 small">
1370
1316
  <t t-set="granularityProps" t-value="props.dimension.granularity || 'month'"/>
1371
1317
  <div class="pivot-dim-operator-label">Granularity</div>
1372
- <select
1373
- class="o-input flex-grow-1"
1374
- t-on-change="(ev) => props.onUpdated(props.dimension, ev.target.value)">
1375
- <option
1376
- t-foreach="props.allGranularities"
1377
- t-as="granularity"
1378
- t-key="granularity"
1379
- t-if="props.availableGranularities.has(granularity) || granularity === granularityProps"
1380
- t-att-value="granularity"
1381
- t-esc="periods[granularity]"
1382
- t-att-selected="granularity === granularityProps or (granularity === 'month' and !granularityProps)"
1383
- />
1384
- </select>
1318
+ <Select
1319
+ class="'flex-grow-1'"
1320
+ values="granularityOptions"
1321
+ selectedValue="props.dimension.granularity || 'month'"
1322
+ onChange="(value) => props.onUpdated(props.dimension, value)"
1323
+ />
1385
1324
  </div>
1386
1325
  </div>
1387
1326
  </t>
@@ -1556,17 +1495,12 @@
1556
1495
  <t t-name="o-spreadsheet-CustomCurrencySection">
1557
1496
  <t t-set="availableCurrencies" t-value="store.availableCurrencies"/>
1558
1497
  <Section t-if="availableCurrencies.length > 1" title.translate="Currency">
1559
- <select
1560
- class="o-input o-available-currencies"
1561
- t-on-change="(ev) => this.updateSelectCurrency(ev)">
1562
- <t t-foreach="availableCurrencies" t-as="currency" t-key="currency_index">
1563
- <option
1564
- t-att-value="currency_index"
1565
- t-esc="currencyDisplayName(currency)"
1566
- t-att-selected="currency_index === store.selectedCurrencyIndex"
1567
- />
1568
- </t>
1569
- </select>
1498
+ <Select
1499
+ class="'o-available-currencies'"
1500
+ values="availableCurrenciesOptions"
1501
+ selectedValue="store.selectedCurrencyIndex.toString()"
1502
+ onChange.bind="updateSelectCurrency"
1503
+ />
1570
1504
  </Section>
1571
1505
  <Section>
1572
1506
  <div class="o-subsection-left">
@@ -1631,14 +1565,13 @@
1631
1565
  </button>
1632
1566
  </div>
1633
1567
  </div>
1634
- <select
1635
- class="o-input o-type-range-selector mt-3 mb-3"
1636
- t-on-change="changeSearchScope"
1637
- t-att-value="searchOptions.searchScope">
1638
- <option value="allSheets">All sheets</option>
1639
- <option value="activeSheet">Current sheet</option>
1640
- <option value="specificRange">Specific range</option>
1641
- </select>
1568
+ <Select
1569
+ class="'o-type-range-selector mt-3 mb-3'"
1570
+ values="searchScopeOptions"
1571
+ selectedValue="searchOptions.searchScope"
1572
+ onChange.bind="changeSearchScope"
1573
+ />
1574
+
1642
1575
  <div t-if="searchOptions.searchScope === 'specificRange'">
1643
1576
  <SelectionInput
1644
1577
  t-key="selectionInputKey"
@@ -1745,12 +1678,12 @@
1745
1678
  <Section class="'pt-0'">
1746
1679
  <div class="o-subsection o-dv-settings">
1747
1680
  <div class="o-section-title">Criteria</div>
1748
- <SelectMenu
1749
- class="'o-dv-type o-input mb-2'"
1750
- menuItems="dvCriterionMenuItems"
1751
- selectedValue="selectedCriterionName"
1681
+ <Select
1682
+ class="'o-dv-type mb-2'"
1683
+ values="dvCriterionOptions"
1684
+ selectedValue="state.rule.criterion.type"
1685
+ onChange.bind="onCriterionTypeChanged"
1752
1686
  />
1753
-
1754
1687
  <t
1755
1688
  t-if="criterionComponent"
1756
1689
  t-component="criterionComponent"
@@ -1763,10 +1696,12 @@
1763
1696
  </Section>
1764
1697
 
1765
1698
  <Section class="'o-dv-invalid-option pt-0'" title.translate="If the data is invalid">
1766
- <select class="o-dv-reject-input o-input" t-on-change="changeRuleIsBlocking">
1767
- <option t-att-selected="!state.rule.isBlocking" value="false">Show a warning</option>
1768
- <option t-att-selected="state.rule.isBlocking" value="true">Reject the input</option>
1769
- </select>
1699
+ <Select
1700
+ class="'o-dv-reject-input pt-0'"
1701
+ values="isRuleBlockingSelectOptions"
1702
+ selectedValue="state.rule.isBlocking ? 'true' : 'false'"
1703
+ onChange.bind="changeRuleIsBlocking"
1704
+ />
1770
1705
  </Section>
1771
1706
 
1772
1707
  <Section>
@@ -1802,13 +1737,12 @@
1802
1737
  </t>
1803
1738
 
1804
1739
  <div class="o-section-subtitle mt-4">Display style</div>
1805
- <select class="o-dv-display-style o-input" t-on-change="onChangedDisplayStyle">
1806
- <option t-att-selected="props.criterion.displayStyle === 'chip'" value="chip">Chip</option>
1807
- <option t-att-selected="props.criterion.displayStyle === 'arrow'" value="arrow">Arrow</option>
1808
- <option t-att-selected="props.criterion.displayStyle === 'plainText'" value="plainText">
1809
- Plain text
1810
- </option>
1811
- </select>
1740
+ <Select
1741
+ class="'o-dv-display-style'"
1742
+ values="displayTypeOptions"
1743
+ selectedValue="props.criterion.displayStyle"
1744
+ onChange.bind="onChangedDisplayStyle"
1745
+ />
1812
1746
  </t>
1813
1747
 
1814
1748
  <t t-name="o-spreadsheet-ListCriterionForm">
@@ -1842,21 +1776,22 @@
1842
1776
  </button>
1843
1777
 
1844
1778
  <div class="o-section-subtitle">Display style</div>
1845
- <select class="o-dv-display-style o-input" t-on-change="onChangedDisplayStyle">
1846
- <option t-att-selected="props.criterion.displayStyle === 'chip'" value="chip">Chip</option>
1847
- <option t-att-selected="props.criterion.displayStyle === 'arrow'" value="arrow">Arrow</option>
1848
- <option t-att-selected="props.criterion.displayStyle === 'plainText'" value="plainText">
1849
- Plain text
1850
- </option>
1851
- </select>
1779
+ <Select
1780
+ class="'o-dv-display-style'"
1781
+ values="displayTypeOptions"
1782
+ selectedValue="props.criterion.displayStyle"
1783
+ onChange.bind="onChangedDisplayStyle"
1784
+ />
1852
1785
  </t>
1853
1786
 
1854
1787
  <t t-name="o-spreadsheet-Top10CriterionForm">
1855
1788
  <div class="o-top-10-criterion d-flex flex-row align-items-center gap-2 w-100 mt-2">
1856
- <select class="o-top-10-select-values o-input flex-shrink-0" t-on-change="updateIsBottom">
1857
- <option value="top" t-att-selected="!props.criterion.isBottom">Top</option>
1858
- <option value="bottom" t-att-selected="props.criterion.isBottom">Bottom</option>
1859
- </select>
1789
+ <Select
1790
+ class="'o-top-10-select-values flex-shrink-0'"
1791
+ values="isBottomSelectOptions"
1792
+ selectedValue="props.criterion.isBottom ? 'bottom' : 'top'"
1793
+ onChange.bind="updateIsBottom"
1794
+ />
1860
1795
  <CriterionInput
1861
1796
  value="props.criterion.values[0]"
1862
1797
  onValueChanged.bind="onValueChanged"
@@ -1864,10 +1799,12 @@
1864
1799
  disableFormulas="true"
1865
1800
  focused="props.autofocus"
1866
1801
  />
1867
- <select class="o-top-10-select-mode o-input flex-shrink-0" t-on-change="updateIsPercent">
1868
- <option value="values" t-att-selected="!props.criterion.isPercent">Values</option>
1869
- <option value="percent" t-att-selected="props.criterion.isPercent">Percent</option>
1870
- </select>
1802
+ <Select
1803
+ class="'o-top-10-select-mode flex-shrink-0'"
1804
+ values="isPercentSelectOptions"
1805
+ selectedValue="props.criterion.isPercent ? 'percent' : 'values'"
1806
+ onChange.bind="updateIsPercent"
1807
+ />
1871
1808
  </div>
1872
1809
  </t>
1873
1810
 
@@ -1898,16 +1835,12 @@
1898
1835
  </t>
1899
1836
 
1900
1837
  <t t-name="o-spreadsheet-DataValidationDateCriterion">
1901
- <select class="o-dv-date-value o-input mb-4" t-on-change="onDateValueChanged">
1902
- <option
1903
- t-foreach="dateValues"
1904
- t-as="dateValue"
1905
- t-key="dateValue.value"
1906
- t-att-value="dateValue.value"
1907
- t-esc="dateValue.title"
1908
- t-att-selected="dateValue.value === currentDateValue"
1909
- />
1910
- </select>
1838
+ <Select
1839
+ class="'o-dv-date-value mb-4'"
1840
+ values="dateValues"
1841
+ selectedValue="currentDateValue"
1842
+ onChange.bind="onDateValueChanged"
1843
+ />
1911
1844
 
1912
1845
  <CriterionInput
1913
1846
  t-if="currentDateValue === 'exactDate'"
@@ -2055,17 +1988,12 @@
2055
1988
  </td>
2056
1989
  <td>When value is</td>
2057
1990
  <td>
2058
- <select
2059
- class="o-input"
2060
- name="valueType"
2061
- t-on-change="(ev) => store.setInflectionOperator(inflectionPoint, ev.target.value)">
2062
- <option value="gt" t-att-selected="inflectionPointValue.operator === 'gt'">
2063
- <span>&#62;</span>
2064
- </option>
2065
- <option value="ge" t-att-selected="inflectionPointValue.operator === 'ge'">
2066
- <span>&#8805;</span>
2067
- </option>
2068
- </select>
1991
+ <Select
1992
+ selectedValue="inflectionPointValue.operator"
1993
+ name="'valueType'"
1994
+ values="getIconSetOperatorSelectOptions()"
1995
+ onChange.bind="(value) => store.setInflectionOperator(inflectionPoint, value)"
1996
+ />
2069
1997
  </td>
2070
1998
  <td>
2071
1999
  <div class="ms-2 me-2">
@@ -2081,23 +2009,13 @@
2081
2009
  </div>
2082
2010
  </td>
2083
2011
  <td>
2084
- <select
2085
- class="o-input"
2086
- name="valueType"
2087
- t-on-change="(ev) => store.setInflectionType(inflectionPoint, ev.target.value)">
2088
- <option value="number" t-att-selected="inflectionPointValue.type === 'number'">
2089
- Number
2090
- </option>
2091
- <option value="percentage" t-att-selected="inflectionPointValue.type === 'percentage'">
2092
- Percentage
2093
- </option>
2094
- <option value="percentile" t-att-selected="inflectionPointValue.type === 'percentile'">
2095
- Percentile
2096
- </option>
2097
- <option value="formula" t-att-selected="inflectionPointValue.type === 'formula'">
2098
- Formula
2099
- </option>
2100
- </select>
2012
+ <Select
2013
+ selectedValue="inflectionPointValue.type"
2014
+ popoverClass="'o-icon-set-type-select-dropdown'"
2015
+ name="'valueType'"
2016
+ values="getThresholdTypeSelectOptions('iconSet')"
2017
+ onChange.bind="(value) => store.setInflectionType(inflectionPoint, value)"
2018
+ />
2101
2019
  </td>
2102
2020
  </tr>
2103
2021
  </t>
@@ -2187,37 +2105,22 @@
2187
2105
  <div
2188
2106
  t-attf-class="o-threshold o-threshold-{{thresholdType}} d-flex align-items-center flex-row">
2189
2107
  <t t-if="thresholdType === 'midpoint'">
2190
- <t t-set="type" t-value="threshold and threshold.type"/>
2191
- <select
2192
- class="o-input me-2"
2193
- name="valueType"
2194
- t-on-change="(ev) => store.onMidpointChange(ev.target.value)"
2195
- t-att-class="{ 'o-select-with-input': threshold and threshold.type !== 'value' }"
2196
- t-on-click="() => store.closeMenus()">
2197
- <option value="none" t-att-selected="threshold === undefined">None</option>
2198
- <option value="number" t-att-selected="type === 'number'">Fixed Number</option>
2199
- <option value="percentage" t-att-selected="type === 'percentage'">Percentage</option>
2200
- <option value="percentile" t-att-selected="type === 'percentile'">Percentile</option>
2201
- <option value="formula" t-att-selected="type === 'formula'">Formula</option>
2202
- </select>
2108
+ <Select
2109
+ selectedValue="threshold?.type || 'none'"
2110
+ class="'me-2' + (threshold and threshold.type !== 'value' ? ' o-select-with-input' : '')"
2111
+ name="'valueType'"
2112
+ values="getThresholdTypeSelectOptions(thresholdType)"
2113
+ onChange.bind="(value) => store.onMidpointChange(value)"
2114
+ />
2203
2115
  </t>
2204
2116
  <t t-else="">
2205
- <select
2206
- class="o-input me-2"
2207
- name="valueType"
2208
- t-on-change="(ev) => store.updateThresholdType(thresholdType, ev.target.value)"
2209
- t-on-click="() => store.closeMenus()"
2210
- t-att-class="{ 'o-select-with-input': threshold?.type !== 'value' }">
2211
- <option value="value" t-att-selected="threshold.type === 'value'">Cell values</option>
2212
- <option value="number" t-att-selected="threshold.type === 'number'">Number</option>
2213
- <option value="percentage" t-att-selected="threshold.type === 'percentage'">
2214
- Percentage
2215
- </option>
2216
- <option value="percentile" t-att-selected="threshold.type === 'percentile'">
2217
- Percentile
2218
- </option>
2219
- <option value="formula" t-att-selected="threshold.type === 'formula'">Formula</option>
2220
- </select>
2117
+ <Select
2118
+ selectedValue="threshold?.type || 'none'"
2119
+ class="'me-2' + (threshold and threshold.type !== 'value' ? ' o-select-with-input' : '')"
2120
+ name="'valueType'"
2121
+ values="getThresholdTypeSelectOptions(thresholdType)"
2122
+ onChange.bind="(value) => store.updateThresholdType(thresholdType, value)"
2123
+ />
2221
2124
  </t>
2222
2125
  <div class="o-threshold-value me-2" t-if="threshold and threshold.type !== 'value'">
2223
2126
  <input
@@ -2341,10 +2244,11 @@
2341
2244
  <t t-set="state" t-value="store.state"/>
2342
2245
  <div class="o-cf-cell-is-rule">
2343
2246
  <div class="o-section-subtitle">Format cells if...</div>
2344
- <SelectMenu
2345
- class="'o-cell-is-operator o-input mb-2'"
2346
- menuItems="store.cfCriterionMenuItems"
2347
- selectedValue="store.selectedCriterionName"
2247
+ <Select
2248
+ class="'o-cell-is-operator mb-2'"
2249
+ values="store.cfCriterions"
2250
+ selectedValue="state.rules.cellIs.operator"
2251
+ onChange="(operator) => store.editOperator(operator)"
2348
2252
  />
2349
2253
 
2350
2254
  <t
@@ -2926,15 +2830,11 @@
2926
2830
  onSelectionConfirmed="() => this.updateBaselineRange()"
2927
2831
  />
2928
2832
  <div class="o-section-subtitle">Format</div>
2929
- <select
2930
- t-att-value="props.definition.baselineMode"
2931
- class="o-input"
2932
- t-on-change="(ev) => this.updateBaselineMode(ev)">
2933
- <option value="text">Absolute value</option>
2934
- <option value="difference">Value change from key value</option>
2935
- <option value="percentage">Percentage change from key value</option>
2936
- <option value="progress">Progress bar</option>
2937
- </select>
2833
+ <Select
2834
+ selectedValue="props.definition.baselineMode"
2835
+ values="baselineModeOptions"
2836
+ onChange.bind="this.updateBaselineMode"
2837
+ />
2938
2838
  </Section>
2939
2839
 
2940
2840
  <ChartErrorSection t-if="errorMessages.length" messages="errorMessages"/>
@@ -3145,15 +3045,11 @@
3145
3045
 
3146
3046
  <t t-name="o-spreadsheet-GeoChartRegionSelectSection">
3147
3047
  <Section class="'o-geo-region'" title.translate="Region">
3148
- <select class="o-input" t-on-change="this.updateSelectedRegion">
3149
- <t t-foreach="availableRegions" t-as="region" t-key="region.id">
3150
- <option
3151
- t-att-value="region.id"
3152
- t-esc="region.label"
3153
- t-att-selected="region.id === selectedRegion"
3154
- />
3155
- </t>
3156
- </select>
3048
+ <Select
3049
+ selectedValue="selectedRegion"
3050
+ values="regionOptions"
3051
+ onChange.bind="this.updateSelectedRegion"
3052
+ />
3157
3053
  </Section>
3158
3054
  </t>
3159
3055
 
@@ -3161,16 +3057,12 @@
3161
3057
  <GeneralDesignEditor t-props="props">
3162
3058
  <t t-set-slot="general-extension">
3163
3059
  <Section class="'pt-0'" title.translate="Legend position">
3164
- <select
3165
- t-att-value="props.definition.legendPosition ?? 'bottom-left'"
3166
- class="o-input o-chart-legend-position"
3167
- t-on-change="this.updateLegendPosition">
3168
- <option value="none">None</option>
3169
- <option value="top">Top left</option>
3170
- <option value="right">Top right</option>
3171
- <option value="bottom">Bottom right</option>
3172
- <option value="left">Bottom left</option>
3173
- </select>
3060
+ <Select
3061
+ selectedValue="props.definition.legendPosition ?? 'bottom'"
3062
+ values="legendValues"
3063
+ class="'o-chart-legend-position'"
3064
+ onChange.bind="this.updateLegendPosition"
3065
+ />
3174
3066
  </Section>
3175
3067
  <Section class="'pt-0'" title.translate="Number formatting">
3176
3068
  <ChartHumanizeNumbers t-props="props"/>
@@ -3287,7 +3179,7 @@
3287
3179
  <t t-call="o-spreadsheet-GaugeChartColorSectionTemplateRow">
3288
3180
  <t t-set="sectionColor" t-value="sectionRule.colors.lowerColor"/>
3289
3181
  <t t-set="sectionType" t-value="'lowerColor'"/>
3290
- <t t-set="inflectionPoint" t-value="sectionRule.lowerInflectionPoint"/>
3182
+ <t t-set="inflectionPoint" t-value="'lowerInflectionPoint'"/>
3291
3183
  <t t-set="isInvalid" t-value="isLowerInflectionPointInvalid"/>
3292
3184
  <t t-set="inflectionPointName" t-value="'lowerInflectionPoint'"/>
3293
3185
  </t>
@@ -3295,7 +3187,7 @@
3295
3187
  <t t-call="o-spreadsheet-GaugeChartColorSectionTemplateRow">
3296
3188
  <t t-set="sectionColor" t-value="sectionRule.colors.middleColor"/>
3297
3189
  <t t-set="sectionType" t-value="'middleColor'"/>
3298
- <t t-set="inflectionPoint" t-value="sectionRule.upperInflectionPoint"/>
3190
+ <t t-set="inflectionPoint" t-value="'upperInflectionPoint'"/>
3299
3191
  <t t-set="isInvalid" t-value="isUpperInflectionPointInvalid"/>
3300
3192
  <t t-set="inflectionPointName" t-value="'upperInflectionPoint'"/>
3301
3193
  </t>
@@ -3328,28 +3220,23 @@
3328
3220
  <td class="pe-2">
3329
3221
  <t t-set="below">below</t>
3330
3222
  <t t-set="belowOrEqualTo">below or equal to</t>
3331
- <select
3332
- class="o-input"
3333
- name="operatorType"
3334
- t-att-title="inflectionPoint.operator === '&lt;' ? below : belowOrEqualTo"
3335
- t-model="inflectionPoint.operator"
3336
- t-on-change="() => this.updateSectionRule(state.sectionRule)">
3337
- <option title="below" value="&lt;">&lt;</option>
3338
- <option title="below or equal to" value="&lt;=">&lt;=</option>
3339
- </select>
3223
+ <Select
3224
+ selectedValue="sectionRule[inflectionPoint].operator"
3225
+ name="'operatorType'"
3226
+ values="inflectionPointOperators"
3227
+ onChange.bind="(value) => this.updateSectionRuleOperator(inflectionPoint, value)"
3228
+ />
3340
3229
  </td>
3341
3230
  <td class="pe-2">
3342
3231
  <StandaloneComposer t-props="getGaugeInflectionComposerProps(sectionType)"/>
3343
3232
  </td>
3344
3233
  <td>
3345
- <select
3346
- class="o-input"
3347
- name="valueType"
3348
- t-model="inflectionPoint.type"
3349
- t-on-change="(ev) => this.updateSectionRule(state.sectionRule)">
3350
- <option value="number">Number</option>
3351
- <option value="percentage">Percentage</option>
3352
- </select>
3234
+ <Select
3235
+ selectedValue="sectionRule[inflectionPoint].type"
3236
+ name="'valueType'"
3237
+ values="inflectionPointTypes"
3238
+ onChange.bind="(value) => this.updateSectionRulePointType(inflectionPoint, value)"
3239
+ />
3353
3240
  </td>
3354
3241
  </tr>
3355
3242
  </t>
@@ -3918,14 +3805,12 @@
3918
3805
  <GeneralDesignEditor t-props="props">
3919
3806
  <t t-set-slot="general-extension">
3920
3807
  <Section class="'pt-0'" title.translate="Legend position">
3921
- <select
3922
- t-att-value="props.definition.legendPosition ?? 'bottom-left'"
3923
- class="o-input o-chart-legend-position"
3924
- t-on-change="this.updateLegendPosition">
3925
- <option value="none">None</option>
3926
- <option value="right">Right</option>
3927
- <option value="left">Left</option>
3928
- </select>
3808
+ <Select
3809
+ selectedValue="props.definition.legendPosition ?? 'left'"
3810
+ values="legendValues"
3811
+ class="'o-chart-legend-position'"
3812
+ onChange.bind="this.updateLegendPosition"
3813
+ />
3929
3814
  </Section>
3930
3815
  <Section class="'pt-0'" title.translate="Values">
3931
3816
  <ChartShowValues t-props="props"/>
@@ -3978,33 +3863,21 @@
3978
3863
  <Section title.translate="Fields to group by">
3979
3864
  <div class="d-flex">
3980
3865
  <span class="w-100">Horizontal axis</span>
3981
- <select
3982
- t-att-value="getGroupByType('horizontal')"
3983
- class="o-input o-horizontal-group-by"
3984
- t-on-change="ev => this.updateGroupBy('horizontal', ev.target.value)">
3985
- <t t-foreach="getGroupByOptions()" t-as="groupBy" t-key="groupBy.value">
3986
- <option
3987
- t-att-selected="getGroupByType('horizontal') === groupBy.value"
3988
- t-att-value="groupBy.value">
3989
- <t t-esc="groupBy.label"/>
3990
- </option>
3991
- </t>
3992
- </select>
3866
+ <Select
3867
+ selectedValue="getGroupByType('horizontal') || ''"
3868
+ values="getGroupByOptions()"
3869
+ class="'o-horizontal-group-by'"
3870
+ onChange="(value) => this.updateGroupBy('horizontal', value)"
3871
+ />
3993
3872
  </div>
3994
3873
  <div class="d-flex">
3995
3874
  <span class="w-100">Vertical axis</span>
3996
- <select
3997
- t-att-value="getGroupByType('vertical')"
3998
- class="o-input o-vertical-group-by"
3999
- t-on-change="ev => this.updateGroupBy('vertical', ev.target.value)">
4000
- <t t-foreach="getGroupByOptions()" t-as="groupBy" t-key="groupBy.value">
4001
- <option
4002
- t-att-selected="getGroupByType('vertical') === groupBy.value"
4003
- t-att-value="groupBy.value">
4004
- <t t-esc="groupBy.label"/>
4005
- </option>
4006
- </t>
4007
- </select>
3875
+ <Select
3876
+ selectedValue="getGroupByType('vertical') || ''"
3877
+ values="getGroupByOptions()"
3878
+ class="'o-vertical-group-by'"
3879
+ onChange="(value) => this.updateGroupBy('vertical', value)"
3880
+ />
4008
3881
  </div>
4009
3882
  </Section>
4010
3883
 
@@ -4141,25 +4014,12 @@
4141
4014
  <div class="d-flex py-2">
4142
4015
  <div class="w-100">
4143
4016
  <span class="o-section-subtitle">Type</span>
4144
- <select
4145
- class="o-input trend-type-selector"
4146
- t-on-change="(ev) => this.onChangeTrendType(index, ev)">
4147
- <option value="linear" t-att-selected="trendType === 'linear'">Linear</option>
4148
- <option value="exponential" t-att-selected="trendType === 'exponential'">
4149
- Exponential
4150
- </option>
4151
- <option value="polynomial" t-att-selected="trendType === 'polynomial'">
4152
- Polynomial
4153
- </option>
4154
- <option value="logarithmic" t-att-selected="trendType === 'logarithmic'">
4155
- Logarithmic
4156
- </option>
4157
- <option
4158
- value="trailingMovingAverage"
4159
- t-att-selected="trendType === 'trailingMovingAverage'">
4160
- Trailing moving average
4161
- </option>
4162
- </select>
4017
+ <Select
4018
+ selectedValue="trendType"
4019
+ values="trendOptions"
4020
+ class="'trend-type-selector'"
4021
+ onChange="(value) => this.onChangeTrendType(index, value)"
4022
+ />
4163
4023
  </div>
4164
4024
  <div class="w-50 ms-3" t-if="trendType === 'trailingMovingAverage'">
4165
4025
  <span class="o-section-subtitle">Window</span>
@@ -4172,16 +4032,12 @@
4172
4032
  </div>
4173
4033
  <div class="w-50 ms-3" t-if="trendType === 'polynomial'">
4174
4034
  <span class="o-section-subtitle">Degree</span>
4175
- <select
4176
- t-att-value="trend.order"
4177
- class="o-input trend-order-input"
4178
- t-on-change="(ev) => this.onChangePolynomialDegree(index, ev)">
4179
- <t t-foreach="getPolynomialDegrees(index)" t-as="degree" t-key="degree">
4180
- <option t-att-value="degree">
4181
- <t t-esc="degree"/>
4182
- </option>
4183
- </t>
4184
- </select>
4035
+ <Select
4036
+ selectedValue="trend.order.toString()"
4037
+ values="getPolynomialDegrees(index)"
4038
+ class="'trend-order-input'"
4039
+ onChange="(value) => this.onChangePolynomialDegree(index, value)"
4040
+ />
4185
4041
  </div>
4186
4042
  </div>
4187
4043
  <div class="d-flex align-items-center">
@@ -4202,18 +4058,12 @@
4202
4058
  <SidePanelCollapsible isInitiallyCollapsed="true" title.translate="Data Series">
4203
4059
  <t t-set-slot="content">
4204
4060
  <Section class="'pt-0 pb-0'">
4205
- <select
4206
- class="o-input data-series-selector"
4207
- t-model="state.label"
4208
- t-on-change="(ev) => this.updateEditedSeries(ev)">
4209
- <t t-foreach="getDataSeries()" t-as="serie" t-key="serie_index">
4210
- <option
4211
- t-att-value="serie"
4212
- t-att-selected="state.index === serie_index"
4213
- t-esc="serie"
4214
- />
4215
- </t>
4216
- </select>
4061
+ <Select
4062
+ selectedValue="state.index.toString()"
4063
+ values="selectOptions"
4064
+ class="'data-series-selector'"
4065
+ onChange.bind="this.updateEditedSeries"
4066
+ />
4217
4067
  <Section class="'px-0'">
4218
4068
  <div class="d-flex align-items-center">
4219
4069
  <span class="o-section-title mb-0 pe-2">Series color</span>
@@ -4254,16 +4104,12 @@
4254
4104
 
4255
4105
  <t t-name="o-spreadsheet-ChartLegend">
4256
4106
  <Section class="'pt-0'" title.translate="Legend position">
4257
- <select
4258
- t-att-value="props.definition.legendPosition ?? 'top'"
4259
- class="o-input o-chart-legend-position"
4260
- t-on-change="this.updateLegendPosition">
4261
- <option value="none">None</option>
4262
- <option value="top">Top</option>
4263
- <option value="bottom">Bottom</option>
4264
- <option value="left">Left</option>
4265
- <option value="right">Right</option>
4266
- </select>
4107
+ <Select
4108
+ selectedValue="props.definition.legendPosition ?? 'top'"
4109
+ values="legendValues"
4110
+ class="'o-chart-legend-position'"
4111
+ onChange.bind="this.updateLegendPosition"
4112
+ />
4267
4113
  </Section>
4268
4114
  </t>
4269
4115
 
@@ -4720,6 +4566,41 @@
4720
4566
  <Highlight t-props="highlightProps"/>
4721
4567
  </t>
4722
4568
 
4569
+ <t t-name="o-spreadsheet-Select">
4570
+ <div
4571
+ tabIndex="0"
4572
+ t-ref="selectRef"
4573
+ class="o-input o-select"
4574
+ t-att-class="props.class"
4575
+ t-att-name="props.name"
4576
+ t-on-pointerdown="onMouseDown"
4577
+ t-on-keydown="onKeyDown"
4578
+ t-esc="selectedLabel"
4579
+ />
4580
+
4581
+ <Popover t-if="state.isPopoverOpen" t-props="popoverProps">
4582
+ <div
4583
+ class="o-select-dropdown bg-white overflow-hidden py-2"
4584
+ t-att-class="props.popoverClass"
4585
+ t-ref="dropdownRef">
4586
+ <t t-foreach="props.values" t-as="value" t-key="value_index">
4587
+ <div
4588
+ class="o-select-option"
4589
+ t-att-class="{
4590
+ 'o-active': value.value === activeValue
4591
+ }"
4592
+ t-att-data-id="value.value"
4593
+ t-esc="value.label"
4594
+ t-att-title="value.label"
4595
+ t-on-click="() => this.onOptionClick(value.value)"
4596
+ t-on-mouseenter="() => this.onOptionHover(value.value)"
4597
+ />
4598
+ <div t-if="value.separator and !value_last" class="o-separator border-bottom"/>
4599
+ </t>
4600
+ </div>
4601
+ </Popover>
4602
+ </t>
4603
+
4723
4604
  <t t-name="o-spreadsheet-ScrollBar">
4724
4605
  <div class="o-scrollbar" t-on-scroll="onScroll" t-ref="scrollbar" t-att-style="positionCss">
4725
4606
  <div t-att-style="sizeCss"/>
@@ -6155,6 +6036,11 @@
6155
6036
  />
6156
6037
  </svg>
6157
6038
  </t>
6039
+ <t t-name="o-spreadsheet-Icon.THUMB_TACK">
6040
+ <div class="o-icon">
6041
+ <i class="fa fa-thumb-tack"/>
6042
+ </div>
6043
+ </t>
6158
6044
 
6159
6045
  <t t-name="o-spreadsheet-IconPicker">
6160
6046
  <div class="o-icon-picker bg-white">
@@ -6745,10 +6631,11 @@
6745
6631
  </t>
6746
6632
 
6747
6633
  <t t-name="o-spreadsheet-FilterMenuCriterion">
6748
- <SelectMenu
6749
- class="'o-filter-criterion-type o-input m-1 mb-2'"
6750
- menuItems="criterionMenuItems"
6751
- selectedValue="selectedCriterionName"
6634
+ <Select
6635
+ class="'o-filter-criterion-type m-1 mb-2'"
6636
+ values="criterionOptions"
6637
+ selectedValue="state.criterion.type"
6638
+ onChange.bind="onCriterionTypeChange"
6752
6639
  />
6753
6640
 
6754
6641
  <t