@odoo/o-spreadsheet 19.2.0-alpha.2 → 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.2
5
- @date 2026-01-07T16:22:28.451Z
6
- @hash ac2fa3e
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">
@@ -271,15 +271,17 @@
271
271
  t-esc="categories[category_value]"
272
272
  />
273
273
  </div>
274
- <div class="d-flex flex-wrap px-4">
274
+ <div
275
+ class="d-flex flex-wrap px-4"
276
+ t-att-class="props.type === 'pivot' ? 'o-pivot-previews' : 'o-table-previews'">
275
277
  <t t-foreach="displayedStyles" t-as="styleId" t-key="styleId">
276
278
  <TableStylePreview
277
- class="'o-table-style-popover-preview'"
278
279
  styleId="styleId"
279
280
  selected="styleId === props.selectedStyleId"
280
281
  tableConfig="props.tableConfig"
281
- tableStyle="env.model.getters.getTableStyle(styleId)"
282
+ tableStyle="props.tableStyles[styleId]"
282
283
  onClick="() => this.props.onStylePicked(styleId)"
284
+ type="props.type"
283
285
  />
284
286
  </t>
285
287
  <div
@@ -301,7 +303,7 @@
301
303
  t-att-title="styleName"
302
304
  t-on-click="props.onClick"
303
305
  t-on-contextmenu.prevent="(ev) => this.onContextMenu(ev)">
304
- <div t-att-class="props.class">
306
+ <div class="o-table-preview">
305
307
  <canvas t-ref="canvas" class="w-100 h-100"/>
306
308
  </div>
307
309
  <div
@@ -322,15 +324,17 @@
322
324
 
323
325
  <t t-name="o-spreadsheet-TableStylePicker">
324
326
  <div class="o-table-style-picker d-flex flew-row justify-content-between ps-1 border rounded">
325
- <div class="d-flex flex-row overflow-hidden ps-2">
327
+ <div
328
+ class="d-flex flex-row overflow-hidden"
329
+ t-att-class="props.type === 'pivot' ? 'o-pivot-previews' : 'o-table-previews ps-2'">
326
330
  <t t-foreach="getDisplayedTableStyles()" t-as="styleId" t-key="styleId">
327
331
  <TableStylePreview
328
- class="'o-table-style-picker-preview'"
329
- selected="styleId === props.table.config.styleId"
330
- tableConfig="props.table.config"
331
- tableStyle="env.model.getters.getTableStyle(styleId)"
332
+ selected="styleId === props.tableConfig.styleId"
333
+ tableConfig="props.tableConfig"
334
+ tableStyle="props.tableStyles[styleId]"
332
335
  styleId="styleId"
333
336
  onClick="() => this.onStylePicked(styleId)"
337
+ type="props.type"
334
338
  />
335
339
  </t>
336
340
  </div>
@@ -341,11 +345,13 @@
341
345
  </div>
342
346
  </div>
343
347
  <TableStylesPopover
344
- tableConfig="props.table.config"
345
- selectedStyleId="props.table.config.styleId"
348
+ tableConfig="props.tableConfig"
349
+ selectedStyleId="props.tableConfig.styleId"
346
350
  onStylePicked.bind="onStylePicked"
347
351
  popoverProps="state.popoverProps"
348
352
  closePopover.bind="closePopover"
353
+ tableStyles="props.tableStyles"
354
+ type="props.type"
349
355
  />
350
356
  </t>
351
357
 
@@ -371,6 +377,8 @@
371
377
  onStylePicked.bind="onStylePicked"
372
378
  popoverProps="state.popoverProps"
373
379
  closePopover.bind="closePopover"
380
+ tableStyles="tableStyles"
381
+ type="'table'"
374
382
  />
375
383
  </t>
376
384
 
@@ -499,11 +507,11 @@
499
507
  <div class="d-flex flex-wrap">
500
508
  <t t-foreach="tableTemplates" t-as="templateName" t-key="templateName">
501
509
  <TableStylePreview
502
- class="'o-table-style-edit-template-preview'"
503
510
  selected="templateName === state.selectedTemplateName"
504
511
  tableConfig="previewTableConfig"
505
512
  tableStyle="computeTableStyle(templateName)"
506
513
  onClick="() => this.onTemplatePicked(templateName)"
514
+ type="'table'"
507
515
  />
508
516
  </t>
509
517
  </div>
@@ -587,7 +595,12 @@
587
595
  </div>
588
596
  </Section>
589
597
  <Section>
590
- <TableStylePicker table="props.table"/>
598
+ <TableStylePicker
599
+ tableConfig="props.table.config"
600
+ onStylePicked.bind="onStylePicked"
601
+ tableStyles="env.model.getters.getTableStyles()"
602
+ type="'table'"
603
+ />
591
604
  </Section>
592
605
  <Section title.translate="Data range">
593
606
  <SelectionInput
@@ -638,16 +651,12 @@
638
651
  <t t-name="o-spreadsheet-SplitIntoColumnsPanel">
639
652
  <div class="o-split-to-cols-panel">
640
653
  <Section title.translate="Separator">
641
- <select class="o-input mb-3" t-on-change="(ev) => this.onSeparatorChange(ev.target.value)">
642
- <option
643
- t-foreach="separators"
644
- t-as="separator"
645
- t-key="separator.value"
646
- t-att-value="separator.value"
647
- t-esc="separator.name"
648
- t-att-selected="state.separatorValue === separator.value"
649
- />
650
- </select>
654
+ <Select
655
+ class="'mb-3'"
656
+ values="separators"
657
+ selectedValue="state.separatorValue"
658
+ onChange.bind="onSeparatorChange"
659
+ />
651
660
 
652
661
  <input
653
662
  class="o-input mb-3"
@@ -709,11 +718,9 @@
709
718
  </div>
710
719
  <div class="o-sidePanelTitle o-fw-bold ms-2" t-esc="getTitle()"/>
711
720
  <div
712
- t-if="props.onTogglePinPanel"
713
- class="o-pin-panel o-sidePanelAction ms-auto rounded"
714
- t-att-class="{'active': props.isPinned}"
715
- t-on-click="props.onTogglePinPanel"
716
- 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">
717
724
  <i class="fa fa-thumb-tack"/>
718
725
  </div>
719
726
  <div class="o-sidePanelClose o-sidePanelAction rounded" t-on-click="props.onCloseSidePanel">
@@ -754,11 +761,9 @@
754
761
  <i class="fa fa-angle-double-left"/>
755
762
  </div>
756
763
  <div
757
- t-if="props.onTogglePinPanel"
758
- class="o-pin-panel o-sidePanelAction rounded mb-1"
759
- t-att-class="{'active': props.isPinned}"
760
- t-on-click.stop="props.onTogglePinPanel"
761
- 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">
762
767
  <i class="fa fa-thumb-tack"/>
763
768
  </div>
764
769
  <div
@@ -775,16 +780,11 @@
775
780
  <t t-name="o-spreadsheet-SettingsPanel">
776
781
  <div class="o-settings-panel">
777
782
  <Section title.translate="Locale">
778
- <select class="o-input" t-on-change="(ev) => this.onLocaleChange(ev.target.value)">
779
- <option
780
- t-foreach="supportedLocales"
781
- t-as="locale"
782
- t-key="locale.code"
783
- t-att-value="locale.code"
784
- t-esc="locale.name"
785
- t-att-selected="currentLocale.code === locale.code"
786
- />
787
- </select>
783
+ <Select
784
+ selectedValue="currentLocale.code"
785
+ values="selectOptions"
786
+ onChange="(value) => this.onLocaleChange(value)"
787
+ />
788
788
  <div class="o-locale-preview mt-4 p-3 rounded border">
789
789
  <div>
790
790
  <span class="o-fw-bold me-1">Number:</span>
@@ -812,24 +812,6 @@
812
812
  </div>
813
813
  </t>
814
814
 
815
- <t t-name="o-spreadsheet-SelectMenu">
816
- <select
817
- t-att-class="props.class"
818
- t-ref="select"
819
- t-on-pointerdown.stop.prevent=""
820
- t-on-click="onClick">
821
- <option selected="true" t-esc="props.selectedValue"/>
822
- </select>
823
- <MenuPopover
824
- t-if="state.isMenuOpen"
825
- menuItems="props.menuItems"
826
- anchorRect="menuAnchorRect"
827
- onClose.bind="onMenuClosed"
828
- menuId="menuId"
829
- popoverPositioning="'bottom-left'"
830
- />
831
- </t>
832
-
833
815
  <t t-name="o-spreadsheet-RemoveDuplicatesPanel">
834
816
  <div class="o-remove-duplicates">
835
817
  <Section>
@@ -898,7 +880,7 @@
898
880
  </t>
899
881
 
900
882
  <t t-name="o-spreadsheet-PivotSidePanel">
901
- <div class="d-flex flex-column h-100">
883
+ <div class="o-pivot-panel d-flex flex-column h-100">
902
884
  <div class="o-panel d-flex">
903
885
  <div
904
886
  class="o-sidePanel-tab o-panel-configuration d-flew flex-grow-1 text-center"
@@ -918,7 +900,11 @@
918
900
 
919
901
  <div class="o-panel-content overflow-y-auto h-100" t-ref="panelContent">
920
902
  <div class="h-100" t-att-class="{ 'd-none': state.panel !== 'configuration' }">
921
- <t t-component="sidePanelEditor" t-props="props"/>
903
+ <t
904
+ t-component="sidePanelEditor"
905
+ pivotId="props.pivotId"
906
+ onCloseSidePanel="props.onCloseSidePanel"
907
+ />
922
908
  </div>
923
909
  <div t-att-class="state.panel !== 'design' ? 'd-none' : ''">
924
910
  <PivotDesignPanel pivotId="props.pivotId"/>
@@ -1035,21 +1021,57 @@
1035
1021
  </div>
1036
1022
  </div>
1037
1023
  </Section>
1024
+
1025
+ <Section class="'o-pivot-table-style'" title.translate="Pivot table style">
1026
+ <div class="row mb-2 align-items-center pt-2">
1027
+ <div class="col-6">Banded rows:</div>
1028
+ <div class="col-6 d-flex align-items-center">
1029
+ <Checkbox
1030
+ name="'bandedRows'"
1031
+ value="pivotStyle.bandedRows ?? defaultStyle.bandedRows"
1032
+ onChange="(val) => this.updatePivotStyleProperty('bandedRows', val)"
1033
+ />
1034
+ </div>
1035
+ </div>
1036
+ <div class="row mb-2 align-items-center">
1037
+ <div class="col-6">Banded columns</div>
1038
+ <div class="col-6 d-flex align-items-center">
1039
+ <Checkbox
1040
+ name="'bandedColumns'"
1041
+ value="pivotStyle.bandedColumns ?? defaultStyle.bandedColumns"
1042
+ onChange="(val) => this.updatePivotStyleProperty('bandedColumns', val)"
1043
+ />
1044
+ </div>
1045
+ </div>
1046
+ <div class="row mb-2 align-items-center">
1047
+ <div class="col-6">Filter button</div>
1048
+ <div class="col-6 d-flex align-items-center">
1049
+ <Checkbox
1050
+ name="'hasFilters'"
1051
+ value="pivotStyle.hasFilters ?? defaultStyle.hasFilters"
1052
+ onChange="(val) => this.updatePivotStyleProperty('hasFilters', val)"
1053
+ />
1054
+ </div>
1055
+ </div>
1056
+ <div class="mt-4">
1057
+ <TableStylePicker
1058
+ tableConfig="tableConfig"
1059
+ onStylePicked.bind="onStylePicked"
1060
+ tableStyles="tableStyles"
1061
+ type="'pivot'"
1062
+ />
1063
+ </div>
1064
+ </Section>
1038
1065
  </t>
1039
1066
 
1040
1067
  <t t-name="o-spreadsheet-PivotMeasureDisplayPanel">
1041
1068
  <Section title.translate="Show measure as:">
1042
- <select
1043
- class="o-pivot-measure-display-type o-input"
1044
- t-on-change="(ev) => this.store.updateMeasureDisplayType(ev.target.value)">
1045
- <t t-foreach="measureDisplayTypeLabels" t-as="measureType" t-key="measureType">
1046
- <option
1047
- t-att-value="measureType"
1048
- t-att-selected="measureType === store.measureDisplay.type"
1049
- t-esc="measureType_value"
1050
- />
1051
- </t>
1052
- </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
+ />
1053
1075
  <div
1054
1076
  class="o-pivot-measure-display-description mt-3 ps-3 border-start"
1055
1077
  t-esc="measureDisplayDescription[store.measureDisplay.type]"
@@ -1263,24 +1285,12 @@
1263
1285
  <div class="d-flex flex-row">
1264
1286
  <div class="d-flex py-1 px-2 w-100 small">
1265
1287
  <div class="pivot-dim-operator-label">Aggregated by</div>
1266
- <select
1267
- class="o-input flex-grow-1"
1268
- t-on-change="(ev) => this.updateAggregator(ev.target.value)">
1269
- <option
1270
- t-foreach="Object.keys(props.aggregators[measure.type])"
1271
- t-as="agg"
1272
- t-key="agg"
1273
- t-att-value="agg"
1274
- t-att-selected="agg === measure.aggregator"
1275
- t-esc="props.aggregators[measure.type][agg]"
1276
- />
1277
- <option
1278
- t-if="measure.computedBy"
1279
- t-att-value="''"
1280
- t-att-selected="'' === measure.aggregator">
1281
- Compute from totals
1282
- </option>
1283
- </select>
1288
+ <Select
1289
+ class="'flex-grow-1'"
1290
+ values="aggregatorOptions"
1291
+ selectedValue="props.measure.aggregator"
1292
+ onChange.bind="updateAggregator"
1293
+ />
1284
1294
  </div>
1285
1295
  </div>
1286
1296
  </PivotDimension>
@@ -1290,18 +1300,12 @@
1290
1300
  <div class="d-flex">
1291
1301
  <div class="d-flex py-1 px-2 w-100 small">
1292
1302
  <div class="pivot-dim-operator-label">Order by</div>
1293
- <select
1294
- class="o-input flex-grow-1"
1295
- t-on-change="(ev) => props.onUpdated(props.dimension, ev.target.value)">
1296
- <option value="asc" t-att-selected="props.dimension.order === 'asc'">Ascending</option>
1297
- <option value="desc" t-att-selected="props.dimension.order === 'desc'">Descending</option>
1298
- <option
1299
- t-if="props.dimension.type !== 'date'"
1300
- value=""
1301
- t-att-selected="props.dimension.order === undefined">
1302
- Unsorted
1303
- </option>
1304
- </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
+ />
1305
1309
  </div>
1306
1310
  </div>
1307
1311
  </t>
@@ -1311,19 +1315,12 @@
1311
1315
  <div class="d-flex flex-row py-1 px-2 w-100 small">
1312
1316
  <t t-set="granularityProps" t-value="props.dimension.granularity || 'month'"/>
1313
1317
  <div class="pivot-dim-operator-label">Granularity</div>
1314
- <select
1315
- class="o-input flex-grow-1"
1316
- t-on-change="(ev) => props.onUpdated(props.dimension, ev.target.value)">
1317
- <option
1318
- t-foreach="props.allGranularities"
1319
- t-as="granularity"
1320
- t-key="granularity"
1321
- t-if="props.availableGranularities.has(granularity) || granularity === granularityProps"
1322
- t-att-value="granularity"
1323
- t-esc="periods[granularity]"
1324
- t-att-selected="granularity === granularityProps or (granularity === 'month' and !granularityProps)"
1325
- />
1326
- </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
+ />
1327
1324
  </div>
1328
1325
  </div>
1329
1326
  </t>
@@ -1498,17 +1495,12 @@
1498
1495
  <t t-name="o-spreadsheet-CustomCurrencySection">
1499
1496
  <t t-set="availableCurrencies" t-value="store.availableCurrencies"/>
1500
1497
  <Section t-if="availableCurrencies.length > 1" title.translate="Currency">
1501
- <select
1502
- class="o-input o-available-currencies"
1503
- t-on-change="(ev) => this.updateSelectCurrency(ev)">
1504
- <t t-foreach="availableCurrencies" t-as="currency" t-key="currency_index">
1505
- <option
1506
- t-att-value="currency_index"
1507
- t-esc="currencyDisplayName(currency)"
1508
- t-att-selected="currency_index === store.selectedCurrencyIndex"
1509
- />
1510
- </t>
1511
- </select>
1498
+ <Select
1499
+ class="'o-available-currencies'"
1500
+ values="availableCurrenciesOptions"
1501
+ selectedValue="store.selectedCurrencyIndex.toString()"
1502
+ onChange.bind="updateSelectCurrency"
1503
+ />
1512
1504
  </Section>
1513
1505
  <Section>
1514
1506
  <div class="o-subsection-left">
@@ -1573,17 +1565,17 @@
1573
1565
  </button>
1574
1566
  </div>
1575
1567
  </div>
1576
- <select
1577
- class="o-input o-type-range-selector mt-3 mb-3"
1578
- t-on-change="changeSearchScope"
1579
- t-att-value="searchOptions.searchScope">
1580
- <option value="allSheets">All sheets</option>
1581
- <option value="activeSheet">Current sheet</option>
1582
- <option value="specificRange">Specific range</option>
1583
- </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
+
1584
1575
  <div t-if="searchOptions.searchScope === 'specificRange'">
1585
1576
  <SelectionInput
1586
- ranges="[this.state.dataRange]"
1577
+ t-key="selectionInputKey"
1578
+ ranges="[specificRange]"
1587
1579
  onSelectionChanged="(ranges) => this.onSearchRangeChanged(ranges)"
1588
1580
  onSelectionConfirmed.bind="updateDataRange"
1589
1581
  hasSingleRange="true"
@@ -1686,12 +1678,12 @@
1686
1678
  <Section class="'pt-0'">
1687
1679
  <div class="o-subsection o-dv-settings">
1688
1680
  <div class="o-section-title">Criteria</div>
1689
- <SelectMenu
1690
- class="'o-dv-type o-input mb-2'"
1691
- menuItems="dvCriterionMenuItems"
1692
- selectedValue="selectedCriterionName"
1681
+ <Select
1682
+ class="'o-dv-type mb-2'"
1683
+ values="dvCriterionOptions"
1684
+ selectedValue="state.rule.criterion.type"
1685
+ onChange.bind="onCriterionTypeChanged"
1693
1686
  />
1694
-
1695
1687
  <t
1696
1688
  t-if="criterionComponent"
1697
1689
  t-component="criterionComponent"
@@ -1704,10 +1696,12 @@
1704
1696
  </Section>
1705
1697
 
1706
1698
  <Section class="'o-dv-invalid-option pt-0'" title.translate="If the data is invalid">
1707
- <select class="o-dv-reject-input o-input" t-on-change="changeRuleIsBlocking">
1708
- <option t-att-selected="!state.rule.isBlocking" value="false">Show a warning</option>
1709
- <option t-att-selected="state.rule.isBlocking" value="true">Reject the input</option>
1710
- </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
+ />
1711
1705
  </Section>
1712
1706
 
1713
1707
  <Section>
@@ -1743,13 +1737,12 @@
1743
1737
  </t>
1744
1738
 
1745
1739
  <div class="o-section-subtitle mt-4">Display style</div>
1746
- <select class="o-dv-display-style o-input" t-on-change="onChangedDisplayStyle">
1747
- <option t-att-selected="props.criterion.displayStyle === 'chip'" value="chip">Chip</option>
1748
- <option t-att-selected="props.criterion.displayStyle === 'arrow'" value="arrow">Arrow</option>
1749
- <option t-att-selected="props.criterion.displayStyle === 'plainText'" value="plainText">
1750
- Plain text
1751
- </option>
1752
- </select>
1740
+ <Select
1741
+ class="'o-dv-display-style'"
1742
+ values="displayTypeOptions"
1743
+ selectedValue="props.criterion.displayStyle"
1744
+ onChange.bind="onChangedDisplayStyle"
1745
+ />
1753
1746
  </t>
1754
1747
 
1755
1748
  <t t-name="o-spreadsheet-ListCriterionForm">
@@ -1783,21 +1776,22 @@
1783
1776
  </button>
1784
1777
 
1785
1778
  <div class="o-section-subtitle">Display style</div>
1786
- <select class="o-dv-display-style o-input" t-on-change="onChangedDisplayStyle">
1787
- <option t-att-selected="props.criterion.displayStyle === 'chip'" value="chip">Chip</option>
1788
- <option t-att-selected="props.criterion.displayStyle === 'arrow'" value="arrow">Arrow</option>
1789
- <option t-att-selected="props.criterion.displayStyle === 'plainText'" value="plainText">
1790
- Plain text
1791
- </option>
1792
- </select>
1779
+ <Select
1780
+ class="'o-dv-display-style'"
1781
+ values="displayTypeOptions"
1782
+ selectedValue="props.criterion.displayStyle"
1783
+ onChange.bind="onChangedDisplayStyle"
1784
+ />
1793
1785
  </t>
1794
1786
 
1795
1787
  <t t-name="o-spreadsheet-Top10CriterionForm">
1796
1788
  <div class="o-top-10-criterion d-flex flex-row align-items-center gap-2 w-100 mt-2">
1797
- <select class="o-top-10-select-values o-input flex-shrink-0" t-on-change="updateIsBottom">
1798
- <option value="top" t-att-selected="!props.criterion.isBottom">Top</option>
1799
- <option value="bottom" t-att-selected="props.criterion.isBottom">Bottom</option>
1800
- </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
+ />
1801
1795
  <CriterionInput
1802
1796
  value="props.criterion.values[0]"
1803
1797
  onValueChanged.bind="onValueChanged"
@@ -1805,10 +1799,12 @@
1805
1799
  disableFormulas="true"
1806
1800
  focused="props.autofocus"
1807
1801
  />
1808
- <select class="o-top-10-select-mode o-input flex-shrink-0" t-on-change="updateIsPercent">
1809
- <option value="values" t-att-selected="!props.criterion.isPercent">Values</option>
1810
- <option value="percent" t-att-selected="props.criterion.isPercent">Percent</option>
1811
- </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
+ />
1812
1808
  </div>
1813
1809
  </t>
1814
1810
 
@@ -1839,16 +1835,12 @@
1839
1835
  </t>
1840
1836
 
1841
1837
  <t t-name="o-spreadsheet-DataValidationDateCriterion">
1842
- <select class="o-dv-date-value o-input mb-4" t-on-change="onDateValueChanged">
1843
- <option
1844
- t-foreach="dateValues"
1845
- t-as="dateValue"
1846
- t-key="dateValue.value"
1847
- t-att-value="dateValue.value"
1848
- t-esc="dateValue.title"
1849
- t-att-selected="dateValue.value === currentDateValue"
1850
- />
1851
- </select>
1838
+ <Select
1839
+ class="'o-dv-date-value mb-4'"
1840
+ values="dateValues"
1841
+ selectedValue="currentDateValue"
1842
+ onChange.bind="onDateValueChanged"
1843
+ />
1852
1844
 
1853
1845
  <CriterionInput
1854
1846
  t-if="currentDateValue === 'exactDate'"
@@ -1996,17 +1988,12 @@
1996
1988
  </td>
1997
1989
  <td>When value is</td>
1998
1990
  <td>
1999
- <select
2000
- class="o-input"
2001
- name="valueType"
2002
- t-on-change="(ev) => store.setInflectionOperator(inflectionPoint, ev.target.value)">
2003
- <option value="gt" t-att-selected="inflectionPointValue.operator === 'gt'">
2004
- <span>&#62;</span>
2005
- </option>
2006
- <option value="ge" t-att-selected="inflectionPointValue.operator === 'ge'">
2007
- <span>&#8805;</span>
2008
- </option>
2009
- </select>
1991
+ <Select
1992
+ selectedValue="inflectionPointValue.operator"
1993
+ name="'valueType'"
1994
+ values="getIconSetOperatorSelectOptions()"
1995
+ onChange.bind="(value) => store.setInflectionOperator(inflectionPoint, value)"
1996
+ />
2010
1997
  </td>
2011
1998
  <td>
2012
1999
  <div class="ms-2 me-2">
@@ -2022,23 +2009,13 @@
2022
2009
  </div>
2023
2010
  </td>
2024
2011
  <td>
2025
- <select
2026
- class="o-input"
2027
- name="valueType"
2028
- t-on-change="(ev) => store.setInflectionType(inflectionPoint, ev.target.value)">
2029
- <option value="number" t-att-selected="inflectionPointValue.type === 'number'">
2030
- Number
2031
- </option>
2032
- <option value="percentage" t-att-selected="inflectionPointValue.type === 'percentage'">
2033
- Percentage
2034
- </option>
2035
- <option value="percentile" t-att-selected="inflectionPointValue.type === 'percentile'">
2036
- Percentile
2037
- </option>
2038
- <option value="formula" t-att-selected="inflectionPointValue.type === 'formula'">
2039
- Formula
2040
- </option>
2041
- </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
+ />
2042
2019
  </td>
2043
2020
  </tr>
2044
2021
  </t>
@@ -2128,37 +2105,22 @@
2128
2105
  <div
2129
2106
  t-attf-class="o-threshold o-threshold-{{thresholdType}} d-flex align-items-center flex-row">
2130
2107
  <t t-if="thresholdType === 'midpoint'">
2131
- <t t-set="type" t-value="threshold and threshold.type"/>
2132
- <select
2133
- class="o-input me-2"
2134
- name="valueType"
2135
- t-on-change="(ev) => store.onMidpointChange(ev.target.value)"
2136
- t-att-class="{ 'o-select-with-input': threshold and threshold.type !== 'value' }"
2137
- t-on-click="() => store.closeMenus()">
2138
- <option value="none" t-att-selected="threshold === undefined">None</option>
2139
- <option value="number" t-att-selected="type === 'number'">Fixed Number</option>
2140
- <option value="percentage" t-att-selected="type === 'percentage'">Percentage</option>
2141
- <option value="percentile" t-att-selected="type === 'percentile'">Percentile</option>
2142
- <option value="formula" t-att-selected="type === 'formula'">Formula</option>
2143
- </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
+ />
2144
2115
  </t>
2145
2116
  <t t-else="">
2146
- <select
2147
- class="o-input me-2"
2148
- name="valueType"
2149
- t-on-change="(ev) => store.updateThresholdType(thresholdType, ev.target.value)"
2150
- t-on-click="() => store.closeMenus()"
2151
- t-att-class="{ 'o-select-with-input': threshold?.type !== 'value' }">
2152
- <option value="value" t-att-selected="threshold.type === 'value'">Cell values</option>
2153
- <option value="number" t-att-selected="threshold.type === 'number'">Number</option>
2154
- <option value="percentage" t-att-selected="threshold.type === 'percentage'">
2155
- Percentage
2156
- </option>
2157
- <option value="percentile" t-att-selected="threshold.type === 'percentile'">
2158
- Percentile
2159
- </option>
2160
- <option value="formula" t-att-selected="threshold.type === 'formula'">Formula</option>
2161
- </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
+ />
2162
2124
  </t>
2163
2125
  <div class="o-threshold-value me-2" t-if="threshold and threshold.type !== 'value'">
2164
2126
  <input
@@ -2282,10 +2244,11 @@
2282
2244
  <t t-set="state" t-value="store.state"/>
2283
2245
  <div class="o-cf-cell-is-rule">
2284
2246
  <div class="o-section-subtitle">Format cells if...</div>
2285
- <SelectMenu
2286
- class="'o-cell-is-operator o-input mb-2'"
2287
- menuItems="store.cfCriterionMenuItems"
2288
- 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)"
2289
2252
  />
2290
2253
 
2291
2254
  <t
@@ -2867,15 +2830,11 @@
2867
2830
  onSelectionConfirmed="() => this.updateBaselineRange()"
2868
2831
  />
2869
2832
  <div class="o-section-subtitle">Format</div>
2870
- <select
2871
- t-att-value="props.definition.baselineMode"
2872
- class="o-input"
2873
- t-on-change="(ev) => this.updateBaselineMode(ev)">
2874
- <option value="text">Absolute value</option>
2875
- <option value="difference">Value change from key value</option>
2876
- <option value="percentage">Percentage change from key value</option>
2877
- <option value="progress">Progress bar</option>
2878
- </select>
2833
+ <Select
2834
+ selectedValue="props.definition.baselineMode"
2835
+ values="baselineModeOptions"
2836
+ onChange.bind="this.updateBaselineMode"
2837
+ />
2879
2838
  </Section>
2880
2839
 
2881
2840
  <ChartErrorSection t-if="errorMessages.length" messages="errorMessages"/>
@@ -3086,15 +3045,11 @@
3086
3045
 
3087
3046
  <t t-name="o-spreadsheet-GeoChartRegionSelectSection">
3088
3047
  <Section class="'o-geo-region'" title.translate="Region">
3089
- <select class="o-input" t-on-change="this.updateSelectedRegion">
3090
- <t t-foreach="availableRegions" t-as="region" t-key="region.id">
3091
- <option
3092
- t-att-value="region.id"
3093
- t-esc="region.label"
3094
- t-att-selected="region.id === selectedRegion"
3095
- />
3096
- </t>
3097
- </select>
3048
+ <Select
3049
+ selectedValue="selectedRegion"
3050
+ values="regionOptions"
3051
+ onChange.bind="this.updateSelectedRegion"
3052
+ />
3098
3053
  </Section>
3099
3054
  </t>
3100
3055
 
@@ -3102,16 +3057,12 @@
3102
3057
  <GeneralDesignEditor t-props="props">
3103
3058
  <t t-set-slot="general-extension">
3104
3059
  <Section class="'pt-0'" title.translate="Legend position">
3105
- <select
3106
- t-att-value="props.definition.legendPosition ?? 'bottom-left'"
3107
- class="o-input o-chart-legend-position"
3108
- t-on-change="this.updateLegendPosition">
3109
- <option value="none">None</option>
3110
- <option value="top">Top left</option>
3111
- <option value="right">Top right</option>
3112
- <option value="bottom">Bottom right</option>
3113
- <option value="left">Bottom left</option>
3114
- </select>
3060
+ <Select
3061
+ selectedValue="props.definition.legendPosition ?? 'bottom'"
3062
+ values="legendValues"
3063
+ class="'o-chart-legend-position'"
3064
+ onChange.bind="this.updateLegendPosition"
3065
+ />
3115
3066
  </Section>
3116
3067
  <Section class="'pt-0'" title.translate="Number formatting">
3117
3068
  <ChartHumanizeNumbers t-props="props"/>
@@ -3228,7 +3179,7 @@
3228
3179
  <t t-call="o-spreadsheet-GaugeChartColorSectionTemplateRow">
3229
3180
  <t t-set="sectionColor" t-value="sectionRule.colors.lowerColor"/>
3230
3181
  <t t-set="sectionType" t-value="'lowerColor'"/>
3231
- <t t-set="inflectionPoint" t-value="sectionRule.lowerInflectionPoint"/>
3182
+ <t t-set="inflectionPoint" t-value="'lowerInflectionPoint'"/>
3232
3183
  <t t-set="isInvalid" t-value="isLowerInflectionPointInvalid"/>
3233
3184
  <t t-set="inflectionPointName" t-value="'lowerInflectionPoint'"/>
3234
3185
  </t>
@@ -3236,7 +3187,7 @@
3236
3187
  <t t-call="o-spreadsheet-GaugeChartColorSectionTemplateRow">
3237
3188
  <t t-set="sectionColor" t-value="sectionRule.colors.middleColor"/>
3238
3189
  <t t-set="sectionType" t-value="'middleColor'"/>
3239
- <t t-set="inflectionPoint" t-value="sectionRule.upperInflectionPoint"/>
3190
+ <t t-set="inflectionPoint" t-value="'upperInflectionPoint'"/>
3240
3191
  <t t-set="isInvalid" t-value="isUpperInflectionPointInvalid"/>
3241
3192
  <t t-set="inflectionPointName" t-value="'upperInflectionPoint'"/>
3242
3193
  </t>
@@ -3269,28 +3220,23 @@
3269
3220
  <td class="pe-2">
3270
3221
  <t t-set="below">below</t>
3271
3222
  <t t-set="belowOrEqualTo">below or equal to</t>
3272
- <select
3273
- class="o-input"
3274
- name="operatorType"
3275
- t-att-title="inflectionPoint.operator === '&lt;' ? below : belowOrEqualTo"
3276
- t-model="inflectionPoint.operator"
3277
- t-on-change="() => this.updateSectionRule(state.sectionRule)">
3278
- <option title="below" value="&lt;">&lt;</option>
3279
- <option title="below or equal to" value="&lt;=">&lt;=</option>
3280
- </select>
3223
+ <Select
3224
+ selectedValue="sectionRule[inflectionPoint].operator"
3225
+ name="'operatorType'"
3226
+ values="inflectionPointOperators"
3227
+ onChange.bind="(value) => this.updateSectionRuleOperator(inflectionPoint, value)"
3228
+ />
3281
3229
  </td>
3282
3230
  <td class="pe-2">
3283
3231
  <StandaloneComposer t-props="getGaugeInflectionComposerProps(sectionType)"/>
3284
3232
  </td>
3285
3233
  <td>
3286
- <select
3287
- class="o-input"
3288
- name="valueType"
3289
- t-model="inflectionPoint.type"
3290
- t-on-change="(ev) => this.updateSectionRule(state.sectionRule)">
3291
- <option value="number">Number</option>
3292
- <option value="percentage">Percentage</option>
3293
- </select>
3234
+ <Select
3235
+ selectedValue="sectionRule[inflectionPoint].type"
3236
+ name="'valueType'"
3237
+ values="inflectionPointTypes"
3238
+ onChange.bind="(value) => this.updateSectionRulePointType(inflectionPoint, value)"
3239
+ />
3294
3240
  </td>
3295
3241
  </tr>
3296
3242
  </t>
@@ -3859,14 +3805,12 @@
3859
3805
  <GeneralDesignEditor t-props="props">
3860
3806
  <t t-set-slot="general-extension">
3861
3807
  <Section class="'pt-0'" title.translate="Legend position">
3862
- <select
3863
- t-att-value="props.definition.legendPosition ?? 'bottom-left'"
3864
- class="o-input o-chart-legend-position"
3865
- t-on-change="this.updateLegendPosition">
3866
- <option value="none">None</option>
3867
- <option value="right">Right</option>
3868
- <option value="left">Left</option>
3869
- </select>
3808
+ <Select
3809
+ selectedValue="props.definition.legendPosition ?? 'left'"
3810
+ values="legendValues"
3811
+ class="'o-chart-legend-position'"
3812
+ onChange.bind="this.updateLegendPosition"
3813
+ />
3870
3814
  </Section>
3871
3815
  <Section class="'pt-0'" title.translate="Values">
3872
3816
  <ChartShowValues t-props="props"/>
@@ -3919,33 +3863,21 @@
3919
3863
  <Section title.translate="Fields to group by">
3920
3864
  <div class="d-flex">
3921
3865
  <span class="w-100">Horizontal axis</span>
3922
- <select
3923
- t-att-value="getGroupByType('horizontal')"
3924
- class="o-input o-horizontal-group-by"
3925
- t-on-change="ev => this.updateGroupBy('horizontal', ev.target.value)">
3926
- <t t-foreach="getGroupByOptions()" t-as="groupBy" t-key="groupBy.value">
3927
- <option
3928
- t-att-selected="getGroupByType('horizontal') === groupBy.value"
3929
- t-att-value="groupBy.value">
3930
- <t t-esc="groupBy.label"/>
3931
- </option>
3932
- </t>
3933
- </select>
3866
+ <Select
3867
+ selectedValue="getGroupByType('horizontal') || ''"
3868
+ values="getGroupByOptions()"
3869
+ class="'o-horizontal-group-by'"
3870
+ onChange="(value) => this.updateGroupBy('horizontal', value)"
3871
+ />
3934
3872
  </div>
3935
3873
  <div class="d-flex">
3936
3874
  <span class="w-100">Vertical axis</span>
3937
- <select
3938
- t-att-value="getGroupByType('vertical')"
3939
- class="o-input o-vertical-group-by"
3940
- t-on-change="ev => this.updateGroupBy('vertical', ev.target.value)">
3941
- <t t-foreach="getGroupByOptions()" t-as="groupBy" t-key="groupBy.value">
3942
- <option
3943
- t-att-selected="getGroupByType('vertical') === groupBy.value"
3944
- t-att-value="groupBy.value">
3945
- <t t-esc="groupBy.label"/>
3946
- </option>
3947
- </t>
3948
- </select>
3875
+ <Select
3876
+ selectedValue="getGroupByType('vertical') || ''"
3877
+ values="getGroupByOptions()"
3878
+ class="'o-vertical-group-by'"
3879
+ onChange="(value) => this.updateGroupBy('vertical', value)"
3880
+ />
3949
3881
  </div>
3950
3882
  </Section>
3951
3883
 
@@ -4082,25 +4014,12 @@
4082
4014
  <div class="d-flex py-2">
4083
4015
  <div class="w-100">
4084
4016
  <span class="o-section-subtitle">Type</span>
4085
- <select
4086
- class="o-input trend-type-selector"
4087
- t-on-change="(ev) => this.onChangeTrendType(index, ev)">
4088
- <option value="linear" t-att-selected="trendType === 'linear'">Linear</option>
4089
- <option value="exponential" t-att-selected="trendType === 'exponential'">
4090
- Exponential
4091
- </option>
4092
- <option value="polynomial" t-att-selected="trendType === 'polynomial'">
4093
- Polynomial
4094
- </option>
4095
- <option value="logarithmic" t-att-selected="trendType === 'logarithmic'">
4096
- Logarithmic
4097
- </option>
4098
- <option
4099
- value="trailingMovingAverage"
4100
- t-att-selected="trendType === 'trailingMovingAverage'">
4101
- Trailing moving average
4102
- </option>
4103
- </select>
4017
+ <Select
4018
+ selectedValue="trendType"
4019
+ values="trendOptions"
4020
+ class="'trend-type-selector'"
4021
+ onChange="(value) => this.onChangeTrendType(index, value)"
4022
+ />
4104
4023
  </div>
4105
4024
  <div class="w-50 ms-3" t-if="trendType === 'trailingMovingAverage'">
4106
4025
  <span class="o-section-subtitle">Window</span>
@@ -4113,16 +4032,12 @@
4113
4032
  </div>
4114
4033
  <div class="w-50 ms-3" t-if="trendType === 'polynomial'">
4115
4034
  <span class="o-section-subtitle">Degree</span>
4116
- <select
4117
- t-att-value="trend.order"
4118
- class="o-input trend-order-input"
4119
- t-on-change="(ev) => this.onChangePolynomialDegree(index, ev)">
4120
- <t t-foreach="getPolynomialDegrees(index)" t-as="degree" t-key="degree">
4121
- <option t-att-value="degree">
4122
- <t t-esc="degree"/>
4123
- </option>
4124
- </t>
4125
- </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
+ />
4126
4041
  </div>
4127
4042
  </div>
4128
4043
  <div class="d-flex align-items-center">
@@ -4143,18 +4058,12 @@
4143
4058
  <SidePanelCollapsible isInitiallyCollapsed="true" title.translate="Data Series">
4144
4059
  <t t-set-slot="content">
4145
4060
  <Section class="'pt-0 pb-0'">
4146
- <select
4147
- class="o-input data-series-selector"
4148
- t-model="state.label"
4149
- t-on-change="(ev) => this.updateEditedSeries(ev)">
4150
- <t t-foreach="getDataSeries()" t-as="serie" t-key="serie_index">
4151
- <option
4152
- t-att-value="serie"
4153
- t-att-selected="state.index === serie_index"
4154
- t-esc="serie"
4155
- />
4156
- </t>
4157
- </select>
4061
+ <Select
4062
+ selectedValue="state.index.toString()"
4063
+ values="selectOptions"
4064
+ class="'data-series-selector'"
4065
+ onChange.bind="this.updateEditedSeries"
4066
+ />
4158
4067
  <Section class="'px-0'">
4159
4068
  <div class="d-flex align-items-center">
4160
4069
  <span class="o-section-title mb-0 pe-2">Series color</span>
@@ -4195,16 +4104,12 @@
4195
4104
 
4196
4105
  <t t-name="o-spreadsheet-ChartLegend">
4197
4106
  <Section class="'pt-0'" title.translate="Legend position">
4198
- <select
4199
- t-att-value="props.definition.legendPosition ?? 'top'"
4200
- class="o-input o-chart-legend-position"
4201
- t-on-change="this.updateLegendPosition">
4202
- <option value="none">None</option>
4203
- <option value="top">Top</option>
4204
- <option value="bottom">Bottom</option>
4205
- <option value="left">Left</option>
4206
- <option value="right">Right</option>
4207
- </select>
4107
+ <Select
4108
+ selectedValue="props.definition.legendPosition ?? 'top'"
4109
+ values="legendValues"
4110
+ class="'o-chart-legend-position'"
4111
+ onChange.bind="this.updateLegendPosition"
4112
+ />
4208
4113
  </Section>
4209
4114
  </t>
4210
4115
 
@@ -4661,6 +4566,41 @@
4661
4566
  <Highlight t-props="highlightProps"/>
4662
4567
  </t>
4663
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
+
4664
4604
  <t t-name="o-spreadsheet-ScrollBar">
4665
4605
  <div class="o-scrollbar" t-on-scroll="onScroll" t-ref="scrollbar" t-att-style="positionCss">
4666
4606
  <div t-att-style="sizeCss"/>
@@ -6096,6 +6036,11 @@
6096
6036
  />
6097
6037
  </svg>
6098
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>
6099
6044
 
6100
6045
  <t t-name="o-spreadsheet-IconPicker">
6101
6046
  <div class="o-icon-picker bg-white">
@@ -6686,10 +6631,11 @@
6686
6631
  </t>
6687
6632
 
6688
6633
  <t t-name="o-spreadsheet-FilterMenuCriterion">
6689
- <SelectMenu
6690
- class="'o-filter-criterion-type o-input m-1 mb-2'"
6691
- menuItems="criterionMenuItems"
6692
- 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"
6693
6639
  />
6694
6640
 
6695
6641
  <t
@@ -6833,6 +6779,7 @@
6833
6779
  <div
6834
6780
  class="o-carousel-tab text-truncate px-2 mt-1 flex-shrink-0"
6835
6781
  t-att-class="{ 'selected': isItemSelected(item) }"
6782
+ t-att-data-type="item.type"
6836
6783
  t-esc="getItemTitle(item)"
6837
6784
  t-on-click.stop="() => this.onCarouselTabClick(item)"
6838
6785
  />