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

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.11
5
+ @date 2026-05-11T13:51:07.177Z
6
+ @hash 7fa6ba6
7
7
  -->
8
8
  <odoo>
9
9
  <t t-name="o-spreadsheet-ValidationMessages">
@@ -58,12 +58,12 @@
58
58
  t-att-class="{'o-topbar-responsive': !env.model.getters.isReadonly()}"
59
59
  t-ref="toolBarContainer">
60
60
  <div
61
- class="o-topbar-toolbar d-flex"
61
+ class="o-topbar-toolbar d-flex flex-grow-1"
62
62
  t-att-class="{'flex-shrink-0': env.model.getters.isReadonly()}">
63
63
  <!-- Toolbar -->
64
64
  <div
65
65
  t-if="env.model.getters.isReadonly()"
66
- class="o-readonly-toolbar d-flex align-items-center text-muted">
66
+ class="o-readonly-toolbar d-flex flex-grow-1 align-items-center text-muted">
67
67
  <span>
68
68
  <i class="fa fa-eye"/>
69
69
  Readonly Access
@@ -188,7 +188,7 @@
188
188
  <FontSizeEditor
189
189
  currentFontSize="currentFontSize"
190
190
  onFontSizeChanged.bind="this.setFontSize"
191
- class="props.class"
191
+ class="class"
192
192
  onToggle.bind="this.onToggle"
193
193
  onFocusInput.bind="this.onFocusInput"
194
194
  />
@@ -217,7 +217,9 @@
217
217
  </div>
218
218
 
219
219
  <t t-name="o-spreadsheet-ColorEditor">
220
- <div class="d-flex align-items-center">
220
+ <div
221
+ class="d-flex align-items-center"
222
+ t-att-class="{'o-disabled': env.model.getters.isCurrentSheetLocked() }">
221
223
  <ColorPickerWidget
222
224
  currentColor="currentColor"
223
225
  toggleColorPicker.bind="onClick"
@@ -364,12 +366,12 @@
364
366
  </t>
365
367
 
366
368
  <t t-name="o-spreadsheet-TableDropdownButton">
367
- <div class="o-table-widget d-flex align-item-center" t-att-class="props.class">
369
+ <div class="o-table-widget d-flex align-item-center" t-att-class="class">
368
370
  <ActionButton
369
371
  action="action"
370
372
  hasTriangleDownIcon="true"
371
373
  t-on-click="onClick"
372
- class="'o-hoverable-button'"
374
+ class="'opacity-100'"
373
375
  />
374
376
  </div>
375
377
  <TableStylesPopover
@@ -524,7 +526,7 @@
524
526
  class="o-delete o-button-danger o-button">
525
527
  Delete
526
528
  </button>
527
- <button t-on-click="onCancel" class="o-cancel o-button">Cancel</button>
529
+ <button t-on-click="onCancel" class="o-cancel o-button">Discard</button>
528
530
  <button t-on-click="onConfirm" class="o-confirm o-button primary">Confirm</button>
529
531
  </div>
530
532
  </Section>
@@ -651,16 +653,12 @@
651
653
  <t t-name="o-spreadsheet-SplitIntoColumnsPanel">
652
654
  <div class="o-split-to-cols-panel">
653
655
  <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>
656
+ <Select
657
+ class="'mb-3'"
658
+ values="separators"
659
+ selectedValue="state.separatorValue"
660
+ onChange.bind="onSeparatorChange"
661
+ />
664
662
 
665
663
  <input
666
664
  class="o-input mb-3"
@@ -722,11 +720,9 @@
722
720
  </div>
723
721
  <div class="o-sidePanelTitle o-fw-bold ms-2" t-esc="getTitle()"/>
724
722
  <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">
723
+ t-if="props.isPinned"
724
+ class="o-pin-panel o-sidePanelAction ms-auto rounded active"
725
+ t-on-click="props.onTogglePinPanel">
730
726
  <i class="fa fa-thumb-tack"/>
731
727
  </div>
732
728
  <div class="o-sidePanelClose o-sidePanelAction rounded" t-on-click="props.onCloseSidePanel">
@@ -767,11 +763,9 @@
767
763
  <i class="fa fa-angle-double-left"/>
768
764
  </div>
769
765
  <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">
766
+ t-if="props.isPinned"
767
+ class="o-pin-panel o-sidePanelAction ms-auto rounded active"
768
+ t-on-click="props.onTogglePinPanel">
775
769
  <i class="fa fa-thumb-tack"/>
776
770
  </div>
777
771
  <div
@@ -788,16 +782,11 @@
788
782
  <t t-name="o-spreadsheet-SettingsPanel">
789
783
  <div class="o-settings-panel">
790
784
  <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>
785
+ <Select
786
+ selectedValue="currentLocale.code"
787
+ values="selectOptions"
788
+ onChange="(value) => this.onLocaleChange(value)"
789
+ />
801
790
  <div class="o-locale-preview mt-4 p-3 rounded border">
802
791
  <div>
803
792
  <span class="o-fw-bold me-1">Number:</span>
@@ -825,24 +814,6 @@
825
814
  </div>
826
815
  </t>
827
816
 
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
817
  <t t-name="o-spreadsheet-RemoveDuplicatesPanel">
847
818
  <div class="o-remove-duplicates">
848
819
  <Section>
@@ -929,15 +900,15 @@
929
900
  </div>
930
901
  </div>
931
902
 
932
- <div class="o-panel-content overflow-y-auto h-100" t-ref="panelContent">
933
- <div class="h-100" t-att-class="{ 'd-none': state.panel !== 'configuration' }">
903
+ <div class="o-panel-content overflow-hidden h-100">
904
+ <div class="h-100" t-att-class="state.panel !== 'configuration' ? 'd-none' : ''">
934
905
  <t
935
906
  t-component="sidePanelEditor"
936
907
  pivotId="props.pivotId"
937
908
  onCloseSidePanel="props.onCloseSidePanel"
938
909
  />
939
910
  </div>
940
- <div t-att-class="state.panel !== 'design' ? 'd-none' : ''">
911
+ <div class="h-100" t-att-class="state.panel !== 'design' ? 'd-none' : ''">
941
912
  <PivotDesignPanel pivotId="props.pivotId"/>
942
913
  </div>
943
914
  </div>
@@ -994,120 +965,117 @@
994
965
  </t>
995
966
 
996
967
  <t t-name="o-spreadsheet-PivotDesignPanel">
997
- <Section class="'o-pivot-design'" title.translate="Display options">
998
- <div>
999
- <div class="container-fluid p-0 pt-2">
1000
- <div class="row mb-2 align-items-center">
1001
- <div class="col-6">Max rows:</div>
1002
- <div class="col-6 d-flex align-items-center">
1003
- <NumberInput
1004
- value="pivotStyle.numberOfRows ?? ''"
1005
- class="'o-pivot-n-of-rows'"
1006
- placeholder.translate="e.g. 10"
1007
- onChange="(value) => this.updatePivotStyleNumberProperty(value, 'numberOfRows')"
1008
- />
968
+ <div class="h-100 overflow-y-auto">
969
+ <Section class="'o-pivot-design'" title.translate="Display options">
970
+ <div>
971
+ <div class="container-fluid p-0 pt-2">
972
+ <div class="row mb-2 align-items-center">
973
+ <div class="col-6">Max rows:</div>
974
+ <div class="col-6 d-flex align-items-center">
975
+ <NumberInput
976
+ value="pivotStyle.numberOfRows ?? ''"
977
+ class="'o-pivot-n-of-rows'"
978
+ placeholder.translate="e.g. 10"
979
+ onChange="(value) => this.updatePivotStyleNumberProperty(value, 'numberOfRows')"
980
+ />
981
+ </div>
1009
982
  </div>
1010
- </div>
1011
- <div class="row mb-2 align-items-center">
1012
- <div class="col-6">Max columns:</div>
1013
- <div class="col-6 d-flex align-items-center">
1014
- <NumberInput
1015
- value="pivotStyle.numberOfColumns ?? ''"
1016
- class="'o-pivot-n-of-columns'"
1017
- placeholder.translate="e.g. 5"
1018
- onChange="(value) => this.updatePivotStyleNumberProperty(value, 'numberOfColumns')"
1019
- />
983
+ <div class="row mb-2 align-items-center">
984
+ <div class="col-6">Max columns:</div>
985
+ <div class="col-6 d-flex align-items-center">
986
+ <NumberInput
987
+ value="pivotStyle.numberOfColumns ?? ''"
988
+ class="'o-pivot-n-of-columns'"
989
+ placeholder.translate="e.g. 5"
990
+ onChange="(value) => this.updatePivotStyleNumberProperty(value, 'numberOfColumns')"
991
+ />
992
+ </div>
1020
993
  </div>
1021
- </div>
1022
- <div class="row mb-2 align-items-center">
1023
- <div class="col-6">Show totals:</div>
1024
- <div class="col-6 d-flex align-items-center">
1025
- <Checkbox
1026
- name="'displayTotals'"
1027
- value="pivotStyle.displayTotals ?? defaultStyle.displayTotals"
1028
- onChange="(val) => this.updatePivotStyleProperty('displayTotals', val)"
1029
- />
994
+ <div class="row mb-2 align-items-center">
995
+ <div class="col-6">Show totals:</div>
996
+ <div class="col-6 d-flex align-items-center">
997
+ <Checkbox
998
+ name="'displayTotals'"
999
+ value="pivotStyle.displayTotals ?? defaultStyle.displayTotals"
1000
+ onChange="(val) => this.updatePivotStyleProperty('displayTotals', val)"
1001
+ />
1002
+ </div>
1030
1003
  </div>
1031
- </div>
1032
- <div class="row mb-2 align-items-center">
1033
- <div class="col-6">Show column titles:</div>
1034
- <div class="col-6 d-flex align-items-center">
1035
- <Checkbox
1036
- name="'displayColumnHeaders'"
1037
- value="pivotStyle.displayColumnHeaders ?? defaultStyle.displayColumnHeaders"
1038
- onChange="(val) => this.updatePivotStyleProperty('displayColumnHeaders', val)"
1039
- />
1004
+ <div class="row mb-2 align-items-center">
1005
+ <div class="col-6">Show column titles:</div>
1006
+ <div class="col-6 d-flex align-items-center">
1007
+ <Checkbox
1008
+ name="'displayColumnHeaders'"
1009
+ value="pivotStyle.displayColumnHeaders ?? defaultStyle.displayColumnHeaders"
1010
+ onChange="(val) => this.updatePivotStyleProperty('displayColumnHeaders', val)"
1011
+ />
1012
+ </div>
1040
1013
  </div>
1041
- </div>
1042
- <div class="row mb-2 align-items-center">
1043
- <div class="col-6">Show measure titles:</div>
1044
- <div class="col-6 d-flex align-items-center">
1045
- <Checkbox
1046
- name="'displayMeasuresRow'"
1047
- value="pivotStyle.displayMeasuresRow ?? defaultStyle.displayMeasuresRow"
1048
- onChange="(val) => this.updatePivotStyleProperty('displayMeasuresRow', val)"
1049
- />
1014
+ <div class="row mb-2 align-items-center">
1015
+ <div class="col-6">Show measure titles:</div>
1016
+ <div class="col-6 d-flex align-items-center">
1017
+ <Checkbox
1018
+ name="'displayMeasuresRow'"
1019
+ value="pivotStyle.displayMeasuresRow ?? defaultStyle.displayMeasuresRow"
1020
+ onChange="(val) => this.updatePivotStyleProperty('displayMeasuresRow', val)"
1021
+ />
1022
+ </div>
1050
1023
  </div>
1051
1024
  </div>
1052
1025
  </div>
1053
- </div>
1054
- </Section>
1026
+ </Section>
1055
1027
 
1056
- <Section class="'o-pivot-table-style'" title.translate="Pivot table style">
1057
- <div class="row mb-2 align-items-center pt-2">
1058
- <div class="col-6">Banded rows:</div>
1059
- <div class="col-6 d-flex align-items-center">
1060
- <Checkbox
1061
- name="'bandedRows'"
1062
- value="pivotStyle.bandedRows ?? defaultStyle.bandedRows"
1063
- onChange="(val) => this.updatePivotStyleProperty('bandedRows', val)"
1064
- />
1028
+ <Section class="'o-pivot-table-style'" title.translate="Pivot table style">
1029
+ <div class="row mb-2 align-items-center pt-2">
1030
+ <div class="col-6">Banded rows:</div>
1031
+ <div class="col-6 d-flex align-items-center">
1032
+ <Checkbox
1033
+ name="'bandedRows'"
1034
+ value="pivotStyle.bandedRows ?? defaultStyle.bandedRows"
1035
+ onChange="(val) => this.updatePivotStyleProperty('bandedRows', val)"
1036
+ />
1037
+ </div>
1065
1038
  </div>
1066
- </div>
1067
- <div class="row mb-2 align-items-center">
1068
- <div class="col-6">Banded columns</div>
1069
- <div class="col-6 d-flex align-items-center">
1070
- <Checkbox
1071
- name="'bandedColumns'"
1072
- value="pivotStyle.bandedColumns ?? defaultStyle.bandedColumns"
1073
- onChange="(val) => this.updatePivotStyleProperty('bandedColumns', val)"
1074
- />
1039
+ <div class="row mb-2 align-items-center">
1040
+ <div class="col-6">Banded columns</div>
1041
+ <div class="col-6 d-flex align-items-center">
1042
+ <Checkbox
1043
+ name="'bandedColumns'"
1044
+ value="pivotStyle.bandedColumns ?? defaultStyle.bandedColumns"
1045
+ onChange="(val) => this.updatePivotStyleProperty('bandedColumns', val)"
1046
+ />
1047
+ </div>
1075
1048
  </div>
1076
- </div>
1077
- <div class="row mb-2 align-items-center">
1078
- <div class="col-6">Filter button</div>
1079
- <div class="col-6 d-flex align-items-center">
1080
- <Checkbox
1081
- name="'hasFilters'"
1082
- value="pivotStyle.hasFilters ?? defaultStyle.hasFilters"
1083
- onChange="(val) => this.updatePivotStyleProperty('hasFilters', val)"
1049
+ <div class="row mb-2 align-items-center">
1050
+ <div class="col-6">Filter button</div>
1051
+ <div class="col-6 d-flex align-items-center">
1052
+ <Checkbox
1053
+ name="'hasFilters'"
1054
+ value="pivotStyle.hasFilters ?? defaultStyle.hasFilters"
1055
+ onChange="(val) => this.updatePivotStyleProperty('hasFilters', val)"
1056
+ />
1057
+ </div>
1058
+ </div>
1059
+ <div class="mt-4">
1060
+ <TableStylePicker
1061
+ tableConfig="tableConfig"
1062
+ onStylePicked.bind="onStylePicked"
1063
+ tableStyles="tableStyles"
1064
+ type="'pivot'"
1084
1065
  />
1085
1066
  </div>
1086
- </div>
1087
- <div class="mt-4">
1088
- <TableStylePicker
1089
- tableConfig="tableConfig"
1090
- onStylePicked.bind="onStylePicked"
1091
- tableStyles="tableStyles"
1092
- type="'pivot'"
1093
- />
1094
- </div>
1095
- </Section>
1067
+ </Section>
1068
+ </div>
1096
1069
  </t>
1097
1070
 
1098
1071
  <t t-name="o-spreadsheet-PivotMeasureDisplayPanel">
1099
1072
  <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>
1073
+ <Select
1074
+ class="'o-pivot-measure-display-type'"
1075
+ values="measureDisplayTypeOptions"
1076
+ selectedValue="store.measureDisplay.type"
1077
+ onChange="(value) => this.store.updateMeasureDisplayType(value)"
1078
+ />
1111
1079
  <div
1112
1080
  class="o-pivot-measure-display-description mt-3 ps-3 border-start"
1113
1081
  t-esc="measureDisplayDescription[store.measureDisplay.type]"
@@ -1146,7 +1114,7 @@
1146
1114
 
1147
1115
  <Section>
1148
1116
  <div class="o-sidePanelButtons">
1149
- <button t-on-click="onCancel" class="o-pivot-measure-cancel o-button">Cancel</button>
1117
+ <button t-on-click="onCancel" class="o-pivot-measure-cancel o-button">Discard</button>
1150
1118
  <button t-on-click="onSave" class="o-pivot-measure-save o-button primary">Save</button>
1151
1119
  </div>
1152
1120
  </Section>
@@ -1321,24 +1289,17 @@
1321
1289
  <div class="d-flex flex-row">
1322
1290
  <div class="d-flex py-1 px-2 w-100 small">
1323
1291
  <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>
1292
+ <Select
1293
+ class="'flex-grow-1'"
1294
+ values="aggregatorOptions"
1295
+ selectedValue="props.measure.aggregator"
1296
+ onChange.bind="updateAggregator"
1297
+ />
1298
+ </div>
1299
+ </div>
1300
+ <div class="d-flex flex-row">
1301
+ <div class="d-flex py-1 px-2 w-100 small text-muted o-measure-description">
1302
+ <i t-esc="getMeasureDescription(measure)"/>
1342
1303
  </div>
1343
1304
  </div>
1344
1305
  </PivotDimension>
@@ -1348,18 +1309,12 @@
1348
1309
  <div class="d-flex">
1349
1310
  <div class="d-flex py-1 px-2 w-100 small">
1350
1311
  <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>
1312
+ <Select
1313
+ class="'flex-grow-1'"
1314
+ values="orderSelectOptions"
1315
+ selectedValue="props.dimension.order || ''"
1316
+ onChange="(value) => props.onUpdated(props.dimension, value)"
1317
+ />
1363
1318
  </div>
1364
1319
  </div>
1365
1320
  </t>
@@ -1369,19 +1324,12 @@
1369
1324
  <div class="d-flex flex-row py-1 px-2 w-100 small">
1370
1325
  <t t-set="granularityProps" t-value="props.dimension.granularity || 'month'"/>
1371
1326
  <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>
1327
+ <Select
1328
+ class="'flex-grow-1'"
1329
+ values="granularityOptions"
1330
+ selectedValue="props.dimension.granularity || 'month'"
1331
+ onChange="(value) => props.onUpdated(props.dimension, value)"
1332
+ />
1385
1333
  </div>
1386
1334
  </div>
1387
1335
  </t>
@@ -1391,7 +1339,9 @@
1391
1339
  class="py-1 px-2 d-flex flex-column shadow-sm pivot-dimension border rounded"
1392
1340
  t-att-class="{'pivot-dimension-invalid': !props.dimension.isValid}">
1393
1341
  <div class="d-flex flex-row justify-content-between align-items-center">
1394
- <div class="d-flex align-items-center overflow-hidden text-nowrap">
1342
+ <div
1343
+ class="d-flex align-items-center overflow-hidden text-nowrap"
1344
+ t-att-title="props.dimension.displayName">
1395
1345
  <span class="text-danger me-1" t-if="!props.dimension.isValid">
1396
1346
  <t t-call="o-spreadsheet-Icon.TRIANGLE_EXCLAMATION"/>
1397
1347
  </span>
@@ -1402,7 +1352,7 @@
1402
1352
  class="'o-fw-bold'"
1403
1353
  selectContentOnFocus="true"
1404
1354
  />
1405
- <span t-else="1" class="o-fw-bold" t-esc="props.dimension.displayName"/>
1355
+ <span t-else="1" class="o-fw-bold text-truncate" t-esc="dimensionDisplayName"/>
1406
1356
  </div>
1407
1357
  <div class="d-flex flex-rows" t-on-pointerdown.stop="">
1408
1358
  <t t-slot="upper-right-icons"/>
@@ -1556,17 +1506,12 @@
1556
1506
  <t t-name="o-spreadsheet-CustomCurrencySection">
1557
1507
  <t t-set="availableCurrencies" t-value="store.availableCurrencies"/>
1558
1508
  <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>
1509
+ <Select
1510
+ class="'o-available-currencies'"
1511
+ values="availableCurrenciesOptions"
1512
+ selectedValue="store.selectedCurrencyIndex.toString()"
1513
+ onChange.bind="updateSelectCurrency"
1514
+ />
1570
1515
  </Section>
1571
1516
  <Section>
1572
1517
  <div class="o-subsection-left">
@@ -1598,7 +1543,7 @@
1598
1543
  </t>
1599
1544
 
1600
1545
  <t t-name="o-spreadsheet-FindAndReplacePanel">
1601
- <div class="o-find-and-replace">
1546
+ <div class="o-find-and-replace" t-on-keydown="onKeydownPanel">
1602
1547
  <Section title.translate="Search">
1603
1548
  <div class="o-input-search-container">
1604
1549
  <input
@@ -1631,14 +1576,13 @@
1631
1576
  </button>
1632
1577
  </div>
1633
1578
  </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>
1579
+ <Select
1580
+ class="'o-type-range-selector mt-3 mb-3'"
1581
+ values="searchScopeOptions"
1582
+ selectedValue="searchOptions.searchScope"
1583
+ onChange.bind="changeSearchScope"
1584
+ />
1585
+
1642
1586
  <div t-if="searchOptions.searchScope === 'specificRange'">
1643
1587
  <SelectionInput
1644
1588
  t-key="selectionInputKey"
@@ -1675,33 +1619,35 @@
1675
1619
  <ValidationMessages msgType="'info'" messages="searchInfo" singleBox="true"/>
1676
1620
  </div>
1677
1621
  </Section>
1678
- <Section class="'pt-0'" t-if="!env.model.getters.isReadonly()" title.translate="Replace">
1679
- <div class="o-input-search-container">
1680
- <input
1681
- type="text"
1682
- class="o-input o-input-without-count o-replace"
1683
- t-on-keydown="onKeydownReplace"
1684
- t-model="store.toReplace"
1685
- placeholder="e.g. 'replace me'"
1686
- />
1687
- </div>
1688
- </Section>
1689
- <Section>
1690
- <div class="o-sidePanelButtons" t-if="!env.model.getters.isReadonly()">
1691
- <button
1692
- t-att-disabled="store.selectedMatchIndex === null"
1693
- t-on-click="() => store.replace()"
1694
- class="o-button o-replace">
1695
- Replace
1696
- </button>
1697
- <button
1698
- t-att-disabled="store.selectedMatchIndex === null"
1699
- t-on-click="() => store.replaceAll()"
1700
- class="o-button o-replace-all">
1701
- Replace all
1702
- </button>
1703
- </div>
1704
- </Section>
1622
+ <t t-if="!env.model.getters.isReadonly()">
1623
+ <Section class="'pt-0'" title.translate="Replace">
1624
+ <div class="o-input-search-container">
1625
+ <input
1626
+ type="text"
1627
+ class="o-input o-input-without-count o-replace"
1628
+ t-on-keydown="onKeydownReplace"
1629
+ t-model="store.toReplace"
1630
+ placeholder="e.g. 'replace me'"
1631
+ />
1632
+ </div>
1633
+ </Section>
1634
+ <Section>
1635
+ <div class="o-sidePanelButtons">
1636
+ <button
1637
+ t-att-disabled="store.selectedMatchIndex === null"
1638
+ t-on-click="() => store.replace()"
1639
+ class="o-button o-replace">
1640
+ Replace
1641
+ </button>
1642
+ <button
1643
+ t-att-disabled="store.selectedMatchIndex === null"
1644
+ t-on-click="() => store.replaceAll()"
1645
+ class="o-button o-replace-all">
1646
+ Replace all
1647
+ </button>
1648
+ </div>
1649
+ </Section>
1650
+ </t>
1705
1651
  </div>
1706
1652
  </t>
1707
1653
 
@@ -1745,12 +1691,12 @@
1745
1691
  <Section class="'pt-0'">
1746
1692
  <div class="o-subsection o-dv-settings">
1747
1693
  <div class="o-section-title">Criteria</div>
1748
- <SelectMenu
1749
- class="'o-dv-type o-input mb-2'"
1750
- menuItems="dvCriterionMenuItems"
1751
- selectedValue="selectedCriterionName"
1694
+ <Select
1695
+ class="'o-dv-type mb-2'"
1696
+ values="dvCriterionOptions"
1697
+ selectedValue="state.rule.criterion.type"
1698
+ onChange.bind="onCriterionTypeChanged"
1752
1699
  />
1753
-
1754
1700
  <t
1755
1701
  t-if="criterionComponent"
1756
1702
  t-component="criterionComponent"
@@ -1763,15 +1709,17 @@
1763
1709
  </Section>
1764
1710
 
1765
1711
  <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>
1712
+ <Select
1713
+ class="'o-dv-reject-input pt-0'"
1714
+ values="isRuleBlockingSelectOptions"
1715
+ selectedValue="state.rule.isBlocking ? 'true' : 'false'"
1716
+ onChange.bind="changeRuleIsBlocking"
1717
+ />
1770
1718
  </Section>
1771
1719
 
1772
1720
  <Section>
1773
1721
  <div class="o-sidePanelButtons">
1774
- <button t-on-click="onCancel" class="o-dv-cancel o-button">Cancel</button>
1722
+ <button t-on-click="onCancel" class="o-dv-cancel o-button">Discard</button>
1775
1723
  <button t-on-click="onSave" class="o-dv-save o-button primary">Save</button>
1776
1724
  </div>
1777
1725
  </Section>
@@ -1802,36 +1750,35 @@
1802
1750
  </t>
1803
1751
 
1804
1752
  <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>
1753
+ <Select
1754
+ class="'o-dv-display-style'"
1755
+ values="displayTypeOptions"
1756
+ selectedValue="props.criterion.displayStyle"
1757
+ onChange.bind="onChangedDisplayStyle"
1758
+ />
1812
1759
  </t>
1813
1760
 
1814
1761
  <t t-name="o-spreadsheet-ListCriterionForm">
1815
- <t t-foreach="displayedValues" t-as="value" t-key="value_index">
1762
+ <t t-foreach="this.state.items" t-as="item" t-key="item_index">
1816
1763
  <div class="o-dv-list-values d-flex align-items-center">
1817
1764
  <div class="me-1">
1818
1765
  <RoundColorPicker
1819
- currentColor="props.criterion.colors?.[value] || '#E7E9ED'"
1820
- onColorPicked="(c) => this.onColorChanged(c, value)"
1766
+ currentColor="item.color || '#E7E9ED'"
1767
+ onColorPicked="(c) => this.onColorChanged(item_index, c)"
1821
1768
  />
1822
1769
  </div>
1823
1770
  <CriterionInput
1824
- value="props.criterion.values[value_index]"
1825
- onValueChanged="(v) => this.onValueChanged(v, value_index)"
1771
+ value="item.value"
1772
+ onValueChanged="(v) => this.onValueChanged(item_index, v)"
1826
1773
  criterionType="props.criterion.type"
1827
- onKeyDown="(ev) => this.onKeyDown(ev, value_index)"
1828
- focused="value_index === state.focusedValueIndex"
1774
+ onKeyDown="(ev) => this.onKeyDown(ev, item_index)"
1775
+ focused="item_index === this.state.focusedValueIndex"
1829
1776
  onBlur.bind="onBlurInput"
1830
1777
  disableFormulas="props.disableFormulas"
1831
1778
  />
1832
1779
  <div
1833
1780
  class="o-dv-list-item-delete ms-2 o-button-icon"
1834
- t-on-click="() => this.removeItem(value_index)">
1781
+ t-on-click="() => this.removeItem(item_index)">
1835
1782
  <t t-call="o-spreadsheet-Icon.TRASH_FILLED"/>
1836
1783
  </div>
1837
1784
  </div>
@@ -1842,21 +1789,22 @@
1842
1789
  </button>
1843
1790
 
1844
1791
  <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>
1792
+ <Select
1793
+ class="'o-dv-display-style'"
1794
+ values="displayTypeOptions"
1795
+ selectedValue="props.criterion.displayStyle"
1796
+ onChange.bind="onChangedDisplayStyle"
1797
+ />
1852
1798
  </t>
1853
1799
 
1854
1800
  <t t-name="o-spreadsheet-Top10CriterionForm">
1855
1801
  <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>
1802
+ <Select
1803
+ class="'o-top-10-select-values flex-shrink-0'"
1804
+ values="isBottomSelectOptions"
1805
+ selectedValue="props.criterion.isBottom ? 'bottom' : 'top'"
1806
+ onChange.bind="updateIsBottom"
1807
+ />
1860
1808
  <CriterionInput
1861
1809
  value="props.criterion.values[0]"
1862
1810
  onValueChanged.bind="onValueChanged"
@@ -1864,10 +1812,12 @@
1864
1812
  disableFormulas="true"
1865
1813
  focused="props.autofocus"
1866
1814
  />
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>
1815
+ <Select
1816
+ class="'o-top-10-select-mode flex-shrink-0'"
1817
+ values="isPercentSelectOptions"
1818
+ selectedValue="props.criterion.isPercent ? 'percent' : 'values'"
1819
+ onChange.bind="updateIsPercent"
1820
+ />
1871
1821
  </div>
1872
1822
  </t>
1873
1823
 
@@ -1898,16 +1848,12 @@
1898
1848
  </t>
1899
1849
 
1900
1850
  <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>
1851
+ <Select
1852
+ class="'o-dv-date-value mb-4'"
1853
+ values="dateValues"
1854
+ selectedValue="currentDateValue"
1855
+ onChange.bind="onDateValueChanged"
1856
+ />
1911
1857
 
1912
1858
  <CriterionInput
1913
1859
  t-if="currentDateValue === 'exactDate'"
@@ -2055,17 +2001,12 @@
2055
2001
  </td>
2056
2002
  <td>When value is</td>
2057
2003
  <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>
2004
+ <Select
2005
+ selectedValue="inflectionPointValue.operator"
2006
+ name="'valueType'"
2007
+ values="getIconSetOperatorSelectOptions()"
2008
+ onChange.bind="(value) => store.setInflectionOperator(inflectionPoint, value)"
2009
+ />
2069
2010
  </td>
2070
2011
  <td>
2071
2012
  <div class="ms-2 me-2">
@@ -2081,23 +2022,13 @@
2081
2022
  </div>
2082
2023
  </td>
2083
2024
  <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>
2025
+ <Select
2026
+ selectedValue="inflectionPointValue.type"
2027
+ popoverClass="'o-icon-set-type-select-dropdown'"
2028
+ name="'valueType'"
2029
+ values="getThresholdTypeSelectOptions('iconSet')"
2030
+ onChange.bind="(value) => store.setInflectionType(inflectionPoint, value)"
2031
+ />
2101
2032
  </td>
2102
2033
  </tr>
2103
2034
  </t>
@@ -2187,37 +2118,22 @@
2187
2118
  <div
2188
2119
  t-attf-class="o-threshold o-threshold-{{thresholdType}} d-flex align-items-center flex-row">
2189
2120
  <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>
2121
+ <Select
2122
+ selectedValue="threshold?.type || 'none'"
2123
+ class="'me-2' + (threshold and threshold.type !== 'value' ? ' o-select-with-input' : '')"
2124
+ name="'valueType'"
2125
+ values="getThresholdTypeSelectOptions(thresholdType)"
2126
+ onChange.bind="(value) => store.onMidpointChange(value)"
2127
+ />
2203
2128
  </t>
2204
2129
  <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>
2130
+ <Select
2131
+ selectedValue="threshold?.type || 'none'"
2132
+ class="'me-2' + (threshold and threshold.type !== 'value' ? ' o-select-with-input' : '')"
2133
+ name="'valueType'"
2134
+ values="getThresholdTypeSelectOptions(thresholdType)"
2135
+ onChange.bind="(value) => store.updateThresholdType(thresholdType, value)"
2136
+ />
2221
2137
  </t>
2222
2138
  <div class="o-threshold-value me-2" t-if="threshold and threshold.type !== 'value'">
2223
2139
  <input
@@ -2307,7 +2223,7 @@
2307
2223
  </Section>
2308
2224
  <Section class="'pt-1'">
2309
2225
  <div class="o-sidePanelButtons">
2310
- <button t-on-click="onCancel" class="o-button o-cf-cancel">Cancel</button>
2226
+ <button t-on-click="onCancel" class="o-button o-cf-cancel">Discard</button>
2311
2227
  <button
2312
2228
  t-on-click="onSave"
2313
2229
  class="o-button primary o-cf-save"
@@ -2341,10 +2257,11 @@
2341
2257
  <t t-set="state" t-value="store.state"/>
2342
2258
  <div class="o-cf-cell-is-rule">
2343
2259
  <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"
2260
+ <Select
2261
+ class="'o-cell-is-operator mb-2'"
2262
+ values="store.cfCriterions"
2263
+ selectedValue="state.rules.cellIs.operator"
2264
+ onChange="(operator) => store.editOperator(operator)"
2348
2265
  />
2349
2266
 
2350
2267
  <t
@@ -2926,15 +2843,11 @@
2926
2843
  onSelectionConfirmed="() => this.updateBaselineRange()"
2927
2844
  />
2928
2845
  <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>
2846
+ <Select
2847
+ selectedValue="props.definition.baselineMode"
2848
+ values="baselineModeOptions"
2849
+ onChange.bind="this.updateBaselineMode"
2850
+ />
2938
2851
  </Section>
2939
2852
 
2940
2853
  <ChartErrorSection t-if="errorMessages.length" messages="errorMessages"/>
@@ -3020,27 +2933,31 @@
3020
2933
  </div>
3021
2934
 
3022
2935
  <t t-set="definition" t-value="getChartDefinition(this.chartId)"/>
3023
- <div class="o-panel-content" t-ref="panelContent">
3024
- <div t-att-class="store.panel !== 'configuration' ? 'd-none' : ''">
3025
- <ChartTypePicker chartId="chartId" chartPanelStore="store"/>
3026
- <t
3027
- t-component="chartPanel.configuration"
3028
- definition="definition"
3029
- chartId="chartId"
3030
- updateChart.bind="updateChart"
3031
- canUpdateChart.bind="canUpdateChart"
3032
- t-key="chartId + definition.type"
3033
- />
2936
+ <div class="o-panel-content h-100 overflow-y-hidden">
2937
+ <div class="h-100" t-att-class="store.panel !== 'configuration' ? 'd-none' : ''">
2938
+ <div class="h-100 overflow-y-auto">
2939
+ <ChartTypePicker chartId="chartId" chartPanelStore="store"/>
2940
+ <t
2941
+ t-component="chartPanel.configuration"
2942
+ definition="definition"
2943
+ chartId="chartId"
2944
+ updateChart.bind="updateChart"
2945
+ canUpdateChart.bind="canUpdateChart"
2946
+ t-key="chartId + definition.type"
2947
+ />
2948
+ </div>
3034
2949
  </div>
3035
- <div t-att-class="store.panel !== 'design' ? 'd-none' : ''">
3036
- <t
3037
- t-component="chartPanel.design"
3038
- definition="definition"
3039
- chartId="chartId"
3040
- updateChart.bind="updateChart"
3041
- canUpdateChart.bind="canUpdateChart"
3042
- t-key="chartId + definition.type"
3043
- />
2950
+ <div class="h-100" t-att-class="store.panel !== 'design' ? 'd-none' : ''">
2951
+ <div class="h-100 overflow-y-auto">
2952
+ <t
2953
+ t-component="chartPanel.design"
2954
+ definition="definition"
2955
+ chartId="chartId"
2956
+ updateChart.bind="updateChart"
2957
+ canUpdateChart.bind="canUpdateChart"
2958
+ t-key="chartId + definition.type"
2959
+ />
2960
+ </div>
3044
2961
  </div>
3045
2962
  </div>
3046
2963
  </div>
@@ -3145,15 +3062,11 @@
3145
3062
 
3146
3063
  <t t-name="o-spreadsheet-GeoChartRegionSelectSection">
3147
3064
  <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>
3065
+ <Select
3066
+ selectedValue="selectedRegion"
3067
+ values="regionOptions"
3068
+ onChange.bind="this.updateSelectedRegion"
3069
+ />
3157
3070
  </Section>
3158
3071
  </t>
3159
3072
 
@@ -3161,16 +3074,12 @@
3161
3074
  <GeneralDesignEditor t-props="props">
3162
3075
  <t t-set-slot="general-extension">
3163
3076
  <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>
3077
+ <Select
3078
+ selectedValue="props.definition.legendPosition ?? 'bottom'"
3079
+ values="legendValues"
3080
+ class="'o-chart-legend-position'"
3081
+ onChange.bind="this.updateLegendPosition"
3082
+ />
3174
3083
  </Section>
3175
3084
  <Section class="'pt-0'" title.translate="Number formatting">
3176
3085
  <ChartHumanizeNumbers t-props="props"/>
@@ -3287,7 +3196,7 @@
3287
3196
  <t t-call="o-spreadsheet-GaugeChartColorSectionTemplateRow">
3288
3197
  <t t-set="sectionColor" t-value="sectionRule.colors.lowerColor"/>
3289
3198
  <t t-set="sectionType" t-value="'lowerColor'"/>
3290
- <t t-set="inflectionPoint" t-value="sectionRule.lowerInflectionPoint"/>
3199
+ <t t-set="inflectionPoint" t-value="'lowerInflectionPoint'"/>
3291
3200
  <t t-set="isInvalid" t-value="isLowerInflectionPointInvalid"/>
3292
3201
  <t t-set="inflectionPointName" t-value="'lowerInflectionPoint'"/>
3293
3202
  </t>
@@ -3295,7 +3204,7 @@
3295
3204
  <t t-call="o-spreadsheet-GaugeChartColorSectionTemplateRow">
3296
3205
  <t t-set="sectionColor" t-value="sectionRule.colors.middleColor"/>
3297
3206
  <t t-set="sectionType" t-value="'middleColor'"/>
3298
- <t t-set="inflectionPoint" t-value="sectionRule.upperInflectionPoint"/>
3207
+ <t t-set="inflectionPoint" t-value="'upperInflectionPoint'"/>
3299
3208
  <t t-set="isInvalid" t-value="isUpperInflectionPointInvalid"/>
3300
3209
  <t t-set="inflectionPointName" t-value="'upperInflectionPoint'"/>
3301
3210
  </t>
@@ -3328,28 +3237,23 @@
3328
3237
  <td class="pe-2">
3329
3238
  <t t-set="below">below</t>
3330
3239
  <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>
3240
+ <Select
3241
+ selectedValue="sectionRule[inflectionPoint].operator"
3242
+ name="'operatorType'"
3243
+ values="inflectionPointOperators"
3244
+ onChange.bind="(value) => this.updateSectionRuleOperator(inflectionPoint, value)"
3245
+ />
3340
3246
  </td>
3341
3247
  <td class="pe-2">
3342
3248
  <StandaloneComposer t-props="getGaugeInflectionComposerProps(sectionType)"/>
3343
3249
  </td>
3344
3250
  <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>
3251
+ <Select
3252
+ selectedValue="sectionRule[inflectionPoint].type"
3253
+ name="'valueType'"
3254
+ values="inflectionPointTypes"
3255
+ onChange.bind="(value) => this.updateSectionRulePointType(inflectionPoint, value)"
3256
+ />
3353
3257
  </td>
3354
3258
  </tr>
3355
3259
  </t>
@@ -3918,14 +3822,12 @@
3918
3822
  <GeneralDesignEditor t-props="props">
3919
3823
  <t t-set-slot="general-extension">
3920
3824
  <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>
3825
+ <Select
3826
+ selectedValue="props.definition.legendPosition ?? 'left'"
3827
+ values="legendValues"
3828
+ class="'o-chart-legend-position'"
3829
+ onChange.bind="this.updateLegendPosition"
3830
+ />
3929
3831
  </Section>
3930
3832
  <Section class="'pt-0'" title.translate="Values">
3931
3833
  <ChartShowValues t-props="props"/>
@@ -3978,33 +3880,21 @@
3978
3880
  <Section title.translate="Fields to group by">
3979
3881
  <div class="d-flex">
3980
3882
  <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>
3883
+ <Select
3884
+ selectedValue="getGroupByType('horizontal') || ''"
3885
+ values="getGroupByOptions()"
3886
+ class="'o-horizontal-group-by'"
3887
+ onChange="(value) => this.updateGroupBy('horizontal', value)"
3888
+ />
3993
3889
  </div>
3994
3890
  <div class="d-flex">
3995
3891
  <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>
3892
+ <Select
3893
+ selectedValue="getGroupByType('vertical') || ''"
3894
+ values="getGroupByOptions()"
3895
+ class="'o-vertical-group-by'"
3896
+ onChange="(value) => this.updateGroupBy('vertical', value)"
3897
+ />
4008
3898
  </div>
4009
3899
  </Section>
4010
3900
 
@@ -4141,25 +4031,12 @@
4141
4031
  <div class="d-flex py-2">
4142
4032
  <div class="w-100">
4143
4033
  <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>
4034
+ <Select
4035
+ selectedValue="trendType"
4036
+ values="trendOptions"
4037
+ class="'trend-type-selector'"
4038
+ onChange="(value) => this.onChangeTrendType(index, value)"
4039
+ />
4163
4040
  </div>
4164
4041
  <div class="w-50 ms-3" t-if="trendType === 'trailingMovingAverage'">
4165
4042
  <span class="o-section-subtitle">Window</span>
@@ -4172,16 +4049,12 @@
4172
4049
  </div>
4173
4050
  <div class="w-50 ms-3" t-if="trendType === 'polynomial'">
4174
4051
  <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>
4052
+ <Select
4053
+ selectedValue="trend.order.toString()"
4054
+ values="getPolynomialDegrees(index)"
4055
+ class="'trend-order-input'"
4056
+ onChange="(value) => this.onChangePolynomialDegree(index, value)"
4057
+ />
4185
4058
  </div>
4186
4059
  </div>
4187
4060
  <div class="d-flex align-items-center">
@@ -4202,18 +4075,12 @@
4202
4075
  <SidePanelCollapsible isInitiallyCollapsed="true" title.translate="Data Series">
4203
4076
  <t t-set-slot="content">
4204
4077
  <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>
4078
+ <Select
4079
+ selectedValue="state.index.toString()"
4080
+ values="selectOptions"
4081
+ class="'data-series-selector'"
4082
+ onChange.bind="this.updateEditedSeries"
4083
+ />
4217
4084
  <Section class="'px-0'">
4218
4085
  <div class="d-flex align-items-center">
4219
4086
  <span class="o-section-title mb-0 pe-2">Series color</span>
@@ -4254,16 +4121,12 @@
4254
4121
 
4255
4122
  <t t-name="o-spreadsheet-ChartLegend">
4256
4123
  <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>
4124
+ <Select
4125
+ selectedValue="props.definition.legendPosition ?? 'top'"
4126
+ values="legendValues"
4127
+ class="'o-chart-legend-position'"
4128
+ onChange.bind="this.updateLegendPosition"
4129
+ />
4267
4130
  </Section>
4268
4131
  </t>
4269
4132
 
@@ -4663,7 +4526,7 @@
4663
4526
  spellcheck="false"
4664
4527
  placeholder="e.g. A1:A2"
4665
4528
  t-on-input="(ev) => this.onInputChanged(range.id, ev)"
4666
- t-on-focus="() => this.focus(range.id)"
4529
+ t-on-click="() => this.focus(range.id)"
4667
4530
  t-on-keydown="onKeydown"
4668
4531
  t-att-value="range.xc"
4669
4532
  t-att-style="getColor(range)"
@@ -4672,7 +4535,7 @@
4672
4535
  'o-disabled-ranges' : range.disabled and !range.isFocused,
4673
4536
  'o-focused' : range.isFocused,
4674
4537
  'o-invalid border-danger position-relative': isInvalid || !range.isValidRange,
4675
- 'text-decoration-underline': range.xc and range.isFocused and state.mode === 'select-range'
4538
+ 'text-decoration-underline': range.xc and range.isFocused and store.mode === 'select-range'
4676
4539
  }"
4677
4540
  t-ref="{{range.isFocused ? 'focusedInput' : 'unfocusedInput' + range_index}}"
4678
4541
  />
@@ -4700,13 +4563,12 @@
4700
4563
  <button class="o-button o-add-selection" t-if="canAddRange" t-on-click="addEmptyInput">
4701
4564
  Add range
4702
4565
  </button>
4703
- <div class="ms-auto" t-if="store.hasFocus">
4566
+ <div class="ms-auto" t-if="store.hasFocus or isResettable">
4704
4567
  <button class="o-button o-selection-ko" t-if="isResettable" t-on-click="reset">
4705
4568
  Reset
4706
4569
  </button>
4707
4570
  <button
4708
4571
  class="o-button primary ms-2 o-selection-ok"
4709
- t-if="store.hasFocus"
4710
4572
  t-att-disabled="!isConfirmable"
4711
4573
  t-on-click="confirm">
4712
4574
  Confirm
@@ -4720,6 +4582,41 @@
4720
4582
  <Highlight t-props="highlightProps"/>
4721
4583
  </t>
4722
4584
 
4585
+ <t t-name="o-spreadsheet-Select">
4586
+ <div
4587
+ tabIndex="0"
4588
+ t-ref="selectRef"
4589
+ class="o-input o-select"
4590
+ t-att-class="props.class"
4591
+ t-att-name="props.name"
4592
+ t-on-pointerdown="onMouseDown"
4593
+ t-on-keydown="onKeyDown"
4594
+ t-esc="selectedLabel"
4595
+ />
4596
+
4597
+ <Popover t-if="state.isPopoverOpen" t-props="popoverProps">
4598
+ <div
4599
+ class="o-select-dropdown bg-white overflow-hidden py-2"
4600
+ t-att-class="props.popoverClass"
4601
+ t-ref="dropdownRef">
4602
+ <t t-foreach="props.values" t-as="value" t-key="value_index">
4603
+ <div
4604
+ class="o-select-option"
4605
+ t-att-class="{
4606
+ 'o-active': value.value === activeValue
4607
+ }"
4608
+ t-att-data-id="value.value"
4609
+ t-esc="value.label"
4610
+ t-att-title="value.label"
4611
+ t-on-click="() => this.onOptionClick(value.value)"
4612
+ t-on-mouseenter="() => this.onOptionHover(value.value)"
4613
+ />
4614
+ <div t-if="value.separator and !value_last" class="o-separator border-bottom"/>
4615
+ </t>
4616
+ </div>
4617
+ </Popover>
4618
+ </t>
4619
+
4723
4620
  <t t-name="o-spreadsheet-ScrollBar">
4724
4621
  <div class="o-scrollbar" t-on-scroll="onScroll" t-ref="scrollbar" t-att-style="positionCss">
4725
4622
  <div t-att-style="sizeCss"/>
@@ -4794,7 +4691,7 @@
4794
4691
  <span
4795
4692
  class="o-menu-item-button"
4796
4693
  title="Paint Format"
4797
- t-att-class="{active: isActive}"
4694
+ t-att-class="{'active': isActive, 'o-disabled': env.model.getters.isCurrentSheetLocked()}"
4798
4695
  t-attf-class="{{props.class}}"
4799
4696
  t-on-click="togglePaintFormat"
4800
4697
  t-on-dblclick="onDblClick">
@@ -4859,7 +4756,7 @@
4859
4756
  </t>
4860
4757
 
4861
4758
  <t t-name="o-spreadsheet-Menu-Popover">
4862
- <Popover t-if="props.menuItems" t-props="popoverProps">
4759
+ <Popover t-if="menuItems.length" t-props="popoverProps">
4863
4760
  <div
4864
4761
  t-ref="menu"
4865
4762
  class="o-menu-wrapper bg-white"
@@ -4892,7 +4789,7 @@
4892
4789
  t-on-pointerdown.prevent=""
4893
4790
  t-on-click.stop=""
4894
4791
  t-on-contextmenu.prevent="">
4895
- <t t-foreach="menuItemsAndSeparators" t-as="menuItem" t-key="menuItem_index">
4792
+ <t t-foreach="props.menuItems" t-as="menuItem" t-key="menuItem_index">
4896
4793
  <div t-if="menuItem === 'separator'" class="o-separator border-bottom"/>
4897
4794
  <t t-else="">
4898
4795
  <t t-set="isMenuRoot" t-value="isRoot(menuItem)"/>
@@ -4916,7 +4813,7 @@
4916
4813
  <t t-if="getIconName(menuItem)" t-call="{{getIconName(menuItem)}}"/>
4917
4814
  </div>
4918
4815
  <div class="o-menu-item-name align-middle text-truncate" t-esc="getName(menuItem)"/>
4919
- <t t-set="description" t-value="menuItem.description(env)"/>
4816
+ <t t-set="description" t-value="menuItem.description(env) or menuItem.shortcut"/>
4920
4817
  <div
4921
4818
  t-if="description"
4922
4819
  class="o-menu-item-description ms-auto text-truncate"
@@ -4998,7 +4895,7 @@
4998
4895
  onClose="() => this.menu.isOpen=false"
4999
4896
  />
5000
4897
  <div class="o-buttons">
5001
- <button t-on-click="cancel" class="o-button o-cancel me-2">Cancel</button>
4898
+ <button t-on-click="cancel" class="o-button o-cancel me-2">Discard</button>
5002
4899
  <button t-on-click="save" class="o-button primary o-save" t-att-disabled="!link.url">
5003
4900
  Confirm
5004
4901
  </button>
@@ -5756,6 +5653,11 @@
5756
5653
  <circle fill="currentColor" cx="14" cy="9" r="4"/>
5757
5654
  </svg>
5758
5655
  </t>
5656
+ <t t-name="o-spreadsheet-Icon.CARET_SORT">
5657
+ <svg class="o-icon" viewBox="0 0 512 512">
5658
+ <path fill="currentColor" d="M40 240 h320 l-160 -160 M40 280 h320 l-160 160"/>
5659
+ </svg>
5660
+ </t>
5759
5661
  <t t-name="o-spreadsheet-Icon.SORT_RANGE">
5760
5662
  <svg class="o-icon">
5761
5663
  <path
@@ -5984,10 +5886,10 @@
5984
5886
  </svg>
5985
5887
  </t>
5986
5888
  <t t-name="o-spreadsheet-Icon.PIVOT">
5987
- <svg class="o-icon" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
5889
+ <svg class="o-icon">
5988
5890
  <path
5989
5891
  fill="currentColor"
5990
- d="M17 2v14H1V6h4V2h12m-1 1H6v3h10M2 7v2h3V7m-3 3v2h3v-2m-3 3v2h3v-2m1-6v2h6V7m-6 3v2h6v-2m-6 3v2h6v-2m1-6v2h3V7m-3 3v2h3v-2m-3 3v2h3v-2"
5892
+ d="M15.5 2A1.5 1.5 0 0 1 17 3.5V14a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 1 14V7.5A1.5 1.5 0 0 1 2.5 6H5V3.5A1.5 1.5 0 0 1 6.5 2H17m-1.5 1.5h-9V6h9m-13 1.5V10H5V7.5m-2.5 4V14H5v-2.5m1.5-4V10h9V7.5m-9 4V14h9v-2.5"
5991
5893
  />
5992
5894
  </svg>
5993
5895
  </t>
@@ -6017,7 +5919,7 @@
6017
5919
  </defs>
6018
5920
  <path
6019
5921
  fill="currentColor"
6020
- d="M17 2v14H1V6h4V2h12m-1 1H6v3h10M2 7v2h3V7m-3 3v2h3v-2m-3 3v2h3v-2m1-6v2h6V7m-6 3v2h6v-2m-6 3v2h6v-2m1-6v2h3V7m-3 3v2h3v-2m-3 3v2h3v-2"
5922
+ d="M15.5 2A1.5 1.5 0 0 1 17 3.5V14a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 1 14V7.5A1.5 1.5 0 0 1 2.5 6H5V3.5A1.5 1.5 0 0 1 6.5 2H17m-1.5 1.5h-9V6h9m-13 1.5V10H5V7.5m-2.5 4V14H5v-2.5m1.5-4V10h9V7.5m-9 4V14h9v-2.5"
6021
5923
  mask="url(#a)"
6022
5924
  />
6023
5925
  <path
@@ -6068,7 +5970,7 @@
6068
5970
  <svg class="o-icon" viewBox="0 0 122.88 99.75">
6069
5971
  <path
6070
5972
  fill="currentColor"
6071
- d="M29.09,0h64.7A5.21,5.21,0,0,1,99,5.18v89.4a5.19,5.19,0,0,1-5.18,5.17H29.09a5.19,5.19,0,0,1-5.17-5.18V5.18A5.21,5.21,0,0,1,29.09,0Zm78.52,12.46,10.59-1.52a4.71,4.71,0,0,1,4.68,4.69v68.5a4.71,4.71,0,0,1-4.68,4.68L107.77,88a1.35,1.35,0,0,1-1.31-1.34V83.14a1.34,1.34,0,0,1,1.44-1.23l8.91.73V17.22l-9,1.34a1.34,1.34,0,0,1-1.34-1.34V13.78a1.34,1.34,0,0,1,1.15-1.32ZM5,11l10.31,1.49a1.33,1.33,0,0,1,1.14,1.32v3.44a1.34,1.34,0,0,1-1.34,1.34l-9-1.34V82.64L15,81.91a1.33,1.33,0,0,1,1.43,1.23v3.49A1.35,1.35,0,0,1,15.11,88l-10.43.84A4.71,4.71,0,0,1,0,84.13V15.63a4.73,4.73,0,0,1,4.68-4.69L5,11Zm87.93-4.9H30v87.6h62.9V6.07Z"
5973
+ d="M29.09 0h65.7A5.2 5.2 0 0 1 99 5.2v89.4a5.2 5.2 0 0 1-5.2 5.2H29.1a5.2 5.2 0 0 1-5.2-5.2V5.2A5.2 5.2 0 0 1 29.1 0m89 9a4.7 4.7 0 0 1 4.7 4.7v71a4.7 4.7 0 0 1-4.7 4.7L106.8 91a1.4 1.35 0 0 1-1.3-1.3V81a1.3 1.3 0 0 1 1.4-1.2l6.9.7V17.2l-7 1a1.3 1.3 0 0 1-1.3-1.3V9a1.3 1.3 0 0 1 1.2-1.3ZM5 9l11.3-1a1.3 1.3 0 0 1 1.1 1.3v7.4a1.3 1.3 0 0 1-1.3 1.3l-7-1v64l6.9-1a1.3 1.3 0 0 1 1.4 1.2v9.5a1.4 1.4 0 0 1-2.3.3L4.7 89.2a4.7 4.7 0 0 1-4.7-5V13.7A4.7 4.7 0 0 1 4.7 9zm83 1H35v78h53Z"
6072
5974
  />
6073
5975
  </svg>
6074
5976
  </t>
@@ -6110,51 +6012,61 @@
6110
6012
  </div>
6111
6013
  </t>
6112
6014
  <t t-name="o-spreadsheet-Icon.ROTATION-0">
6113
- <svg
6114
- width="18"
6115
- height="18"
6116
- viewBox="0 0 18 18"
6117
- transform="rotate(270)"
6118
- xmlns="http://www.w3.org/2000/svg">
6015
+ <svg class="o-icon" viewBox="0 0 18 18">
6119
6016
  <path
6120
- d="M5 2h1v12h1.5l-2 2-2-2H5m6-5h1v5h1.5l-2 2-2-2H11M8 2l7 2.8V6L8 8.8l-.43-1.12 1.9-.7V3.8l-1.9-.7L8 1.98m2.7 2.25v2.3l2.8-1.1z"
6017
+ d="M1.5 14v-2h11v-2l4 3-4 3v-2m-4-8V4h4V2l4 3-4 3V6m-11 3 2.8-7h1.2l2.8 7-1.12.43-.7-1.9H3.3l-.7 1.9L1.48 9m2.25-2.7h2.3l-1.1-2.8z"
6121
6018
  fill="currentColor"
6122
6019
  />
6123
6020
  </svg>
6124
6021
  </t>
6125
6022
  <t t-name="o-spreadsheet-Icon.ROTATION-45">
6126
- <svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
6023
+ <svg class="o-icon" viewBox="0 0 18 18">
6127
6024
  <path
6128
- d="m1.95 6.879.707-.707 8.485 8.485 1.06-1.06v2.828H9.375l1.061-1.061m.706-7.778.707-.707 3.536 3.535 1.06-1.06v2.828h-2.828l1.06-1.06M4.071 4.757l6.93-2.97.848.849-2.97 6.93-1.096-.488.849-1.839-2.249-2.248-1.838.848-.488-1.096m3.5-.318 1.626 1.626 1.203-2.757z"
6025
+ d="m.743 7.086 1.414-1.414 7.778 7.778 1.414-1.414.707 4.95-4.95-.708 1.415-1.414m2.121-7.778 1.414-1.414L14.885 8.5l1.414-1.414.707 4.95-4.95-.708 1.415-1.414m-9.9-5.657 6.93-2.97.848.849-2.97 6.93-1.096-.488.849-1.839-2.249-2.248-1.838.848-.488-1.096m3.5-.318 1.626 1.626 1.203-2.757z"
6129
6026
  fill="currentColor"
6130
6027
  />
6131
6028
  </svg>
6132
6029
  </t>
6133
6030
  <t t-name="o-spreadsheet-Icon.ROTATION-90">
6134
- <svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
6031
+ <svg class="o-icon" viewBox="0 0 18 18">
6135
6032
  <path
6136
- d="M5 2h1v12h1.5l-2 2-2-2H5m6-5h1v5h1.5l-2 2-2-2H11M8 2l7 2.8V6L8 8.8l-.43-1.12 1.9-.7V3.8l-1.9-.7L8 1.98m2.7 2.25v2.3l2.8-1.1z"
6033
+ d="M4 1.5h2v11h2l-3 4-3-4h2m8-4h2v4h2l-3 4-3-4h2m-3-11 7 2.8v1.2L9 8.3l-.43-1.12 1.9-.7V3.3l-1.9-.7L9 1.48m2.7 2.25v2.3l2.8-1.1z"
6137
6034
  fill="currentColor"
6138
6035
  />
6139
6036
  </svg>
6140
6037
  </t>
6141
6038
 
6142
6039
  <t t-name="o-spreadsheet-Icon.ROTATION-270">
6143
- <svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
6040
+ <svg class="o-icon" viewBox="0 0 18 18">
6144
6041
  <path
6145
- d="M13 16h-1V4h-1.5l2-2 2 2H13M7 9H6V4H4.5l2-2 2 2H7m3 12-7-2.8V12l7-2.8.43 1.12-1.9.7v3.18l1.9.7-.43 1.12m-2.7-2.25v-2.3l-2.8 1.1z"
6042
+ d="M14 16.5h-2v-11h-2l3-4 3 4h-2m-8 4H4v-4H2l3-4 3 4H6m3 11-7-2.8v-1.2l7-2.8.43 1.12-1.9.7v3.18l1.9.7L9 16.52m-2.7-2.25v-2.3l-2.8 1.1z"
6146
6043
  fill="currentColor"
6147
6044
  />
6148
6045
  </svg>
6149
6046
  </t>
6150
6047
  <t t-name="o-spreadsheet-Icon.ROTATION-315">
6151
- <svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
6048
+ <svg class="o-icon" viewBox="0 0 18 18">
6152
6049
  <path
6153
- d="m6.879 16.55-.707-.707 8.485-8.485-1.06-1.06h2.828v2.828l-1.061-1.061m-7.778-.707-.707-.707 3.535-3.536-1.06-1.06h2.828v2.828l-1.06-1.06M4.757 14.429l-2.97-6.93.849-.848 6.93 2.97-.488 1.096-1.839-.849-2.248 2.249.848 1.838-1.096.488m-.318-3.5 1.626-1.626-2.757-1.203z"
6050
+ d="m6.836 17.257-1.414-1.414L13.2 8.065l-1.414-1.414 4.95-.707-.708 4.95-1.414-1.415M6.836 7.358 5.422 5.944 8.25 3.115 6.836 1.701l4.95-.707-.708 4.95-1.414-1.415m-5.657 9.9-2.97-6.93.849-.848 6.93 2.97-.488 1.096-1.839-.849-2.248 2.249.848 1.838-1.096.488m-.318-3.5 1.626-1.626-2.757-1.203z"
6154
6051
  fill="currentColor"
6155
6052
  />
6156
6053
  </svg>
6157
6054
  </t>
6055
+ <t t-name="o-spreadsheet-Icon.THUMB_TACK">
6056
+ <div class="o-icon">
6057
+ <i class="fa fa-thumb-tack"/>
6058
+ </div>
6059
+ </t>
6060
+ <t t-name="o-spreadsheet-Icon.LOCK">
6061
+ <div class="o-icon">
6062
+ <i class="fa fa-lock"/>
6063
+ </div>
6064
+ </t>
6065
+ <t t-name="o-spreadsheet-Icon.UNLOCK">
6066
+ <div class="o-icon">
6067
+ <i class="fa fa-unlock"/>
6068
+ </div>
6069
+ </t>
6158
6070
 
6159
6071
  <t t-name="o-spreadsheet-IconPicker">
6160
6072
  <div class="o-icon-picker bg-white">
@@ -6209,7 +6121,7 @@
6209
6121
  <t t-name="o-spreadsheet-Corner">
6210
6122
  <div
6211
6123
  class="o-corner d-flex justify-content-center align-items-center"
6212
- t-on-pointerdown.prevent="onMouseDown"
6124
+ t-on-pointerdown.prevent.stop="onMouseDown"
6213
6125
  t-on-touchstart.prevent.stop=""
6214
6126
  t-att-style="handlerStyle">
6215
6127
  <div
@@ -6620,7 +6532,11 @@
6620
6532
  anchorRect="menuState.anchorRect"
6621
6533
  onClose="() => this.closeMenu()"
6622
6534
  />
6623
- <t t-foreach="staticTables" t-as="table" t-key="table.id">
6535
+ <t
6536
+ t-if="!env.model.getters.isReadonly()"
6537
+ t-foreach="staticTables"
6538
+ t-as="table"
6539
+ t-key="table.id">
6624
6540
  <TableResizer table="table"/>
6625
6541
  </t>
6626
6542
  <VerticalScrollBar topOffset="HEADER_HEIGHT"/>
@@ -6677,8 +6593,8 @@
6677
6593
 
6678
6594
  <t t-name="o-spreadsheet-FilterMenuValueList">
6679
6595
  <div class="o-filter-menu-actions d-flex">
6680
- <div class="o-button-link me-4" t-on-click="selectAll">Select all</div>
6681
- <div class="o-button-link me-4" t-on-click="clearAll">Clear</div>
6596
+ <div class="o-button-link me-4" t-on-click="this.selectAll">Select all</div>
6597
+ <div class="o-button-link me-4" t-on-click="this.clearAll">Clear</div>
6682
6598
  </div>
6683
6599
  <div class="position-relative">
6684
6600
  <input
@@ -6745,10 +6661,11 @@
6745
6661
  </t>
6746
6662
 
6747
6663
  <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"
6664
+ <Select
6665
+ class="'o-filter-criterion-type m-1 mb-2'"
6666
+ values="criterionOptions"
6667
+ selectedValue="state.criterion.type"
6668
+ onChange.bind="onCriterionTypeChange"
6752
6669
  />
6753
6670
 
6754
6671
  <t
@@ -6805,7 +6722,7 @@
6805
6722
  </SidePanelCollapsible>
6806
6723
 
6807
6724
  <div class="o-filter-menu-buttons d-flex justify-content-end">
6808
- <button class="o-button o-filter-menu-cancel me-2" t-on-click="cancel">Cancel</button>
6725
+ <button class="o-button o-filter-menu-cancel me-2" t-on-click="cancel">Discard</button>
6809
6726
  <button class="o-button primary o-filter-menu-confirm" t-on-click="confirm">
6810
6727
  Confirm
6811
6728
  </button>
@@ -6972,7 +6889,7 @@
6972
6889
  t-att-style="props.style"
6973
6890
  t-att-data-id="props.figureUI.id"
6974
6891
  tabindex="0"
6975
- t-on-keydown="(ev) => this.onKeyDown(ev)"
6892
+ t-on-keydown.stop="(ev) => this.onKeyDown(ev)"
6976
6893
  t-on-keyup.stop="">
6977
6894
  <t
6978
6895
  t-component="figureRegistry.get(props.figureUI.tag).Component"
@@ -7020,7 +6937,7 @@
7020
6937
  />
7021
6938
  </div>
7022
6939
  </div>
7023
- <t t-if="isSelected and !env.isMobile()">
6940
+ <t t-if="isFigureResizable">
7024
6941
  <div
7025
6942
  class="o-fig-anchor o-top pe-auto"
7026
6943
  t-att-style="this.getResizerPosition('top')"
@@ -7105,7 +7022,7 @@
7105
7022
  </t>
7106
7023
 
7107
7024
  <t t-name="o-spreadsheet-ChartJsComponent">
7108
- <canvas class="o-figure-canvas w-100 h-100" t-att-style="canvasStyle" t-ref="graphContainer"/>
7025
+ <canvas class="o-figure-canvas w-100 h-100" t-ref="graphContainer"/>
7109
7026
  </t>
7110
7027
 
7111
7028
  <t t-name="o-spreadsheet-ZoomableChartJsComponent">
@@ -7122,7 +7039,7 @@
7122
7039
  t-ref="masterChartCanvas"
7123
7040
  t-on-dblclick="onMasterChartDoubleClick"
7124
7041
  t-on-pointerdown="onMasterChartPointerDown"
7125
- t-on-pointermove="onMasterChartPointerMove"
7042
+ t-on-pointermove="updateMasterChartCursor"
7126
7043
  t-on-mouseleave="onMasterChartMouseLeave"
7127
7044
  />
7128
7045
  </div>
@@ -7156,7 +7073,12 @@
7156
7073
  </t>
7157
7074
 
7158
7075
  <t t-name="o-spreadsheet-SpreadsheetDashboard">
7159
- <div class="o-grid o-two-columns" t-ref="dashboard" tabindex="-1" t-on-wheel="onMouseWheel">
7076
+ <div
7077
+ class="o-grid o-two-columns o-zoomable"
7078
+ t-ref="dashboard"
7079
+ tabindex="-1"
7080
+ t-on-wheel="onMouseWheel"
7081
+ t-att-style="dashboardStyle">
7160
7082
  <div class="mx-auto h-100 position-relative" t-ref="grid" t-att-style="gridContainer">
7161
7083
  <GridOverlay
7162
7084
  onGridResized.bind="onGridResized"
@@ -7196,9 +7118,9 @@
7196
7118
  <div class="w-100 h-100 d-flex flex-column align-items-end" t-att-class="verticalJustifyClass">
7197
7119
  <span
7198
7120
  t-if="props.sortDirection === 'none'"
7199
- class="o-icon sorting-icon pb-1"
7121
+ class="o-icon sorting-icon mb-1"
7200
7122
  t-att-style="style">
7201
- <i class="fa fa-small fa-sort"/>
7123
+ <t t-call="o-spreadsheet-Icon.CARET_SORT"/>
7202
7124
  </span>
7203
7125
  </div>
7204
7126
  </t>
@@ -7704,6 +7626,9 @@
7704
7626
  t-att-title="sheetName"
7705
7627
  t-att-data-id="props.sheetId"
7706
7628
  t-att-class="{active: isSheetActive}">
7629
+ <span t-if="isSheetLocked" class="me-1">
7630
+ <t t-call="o-spreadsheet-Icon.LOCK"/>
7631
+ </span>
7707
7632
  <span
7708
7633
  class="o-sheet-name"
7709
7634
  t-att-class="{'o-sheet-name-editable': state.isEditing }"
@@ -7717,8 +7642,9 @@
7717
7642
  t-att-contenteditable="state.isEditing ? 'plaintext-only': 'false'"
7718
7643
  />
7719
7644
  <span
7720
- class="o-sheet-icon ms-1"
7645
+ class="o-sheet-icon ms-1 rounded"
7721
7646
  tabindex="-1"
7647
+ t-ref="icon"
7722
7648
  t-on-click.stop="(ev) => this.onIconClick(ev)">
7723
7649
  <t t-call="o-spreadsheet-Icon.CARET_DOWN"/>
7724
7650
  </span>
@@ -7738,7 +7664,10 @@
7738
7664
  </t>
7739
7665
 
7740
7666
  <t t-name="o-spreadsheet-BorderEditorWidget">
7741
- <div class="d-flex position-relative" title="Borders">
7667
+ <div
7668
+ class="d-flex position-relative"
7669
+ t-att-class="{'o-disabled': env.model.getters.isCurrentSheetLocked()}"
7670
+ title="Borders">
7742
7671
  <span
7743
7672
  t-ref="borderEditorButton"
7744
7673
  t-on-click.stop="toggleBorderEditor"