@odoo/o-spreadsheet 19.2.0-alpha.4 → 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.4
5
- @date 2026-01-21T11:09:20.618Z
6
- @hash fa080c2
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>
@@ -898,15 +900,15 @@
898
900
  </div>
899
901
  </div>
900
902
 
901
- <div class="o-panel-content overflow-y-auto h-100" t-ref="panelContent">
902
- <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' : ''">
903
905
  <t
904
906
  t-component="sidePanelEditor"
905
907
  pivotId="props.pivotId"
906
908
  onCloseSidePanel="props.onCloseSidePanel"
907
909
  />
908
910
  </div>
909
- <div t-att-class="state.panel !== 'design' ? 'd-none' : ''">
911
+ <div class="h-100" t-att-class="state.panel !== 'design' ? 'd-none' : ''">
910
912
  <PivotDesignPanel pivotId="props.pivotId"/>
911
913
  </div>
912
914
  </div>
@@ -963,105 +965,107 @@
963
965
  </t>
964
966
 
965
967
  <t t-name="o-spreadsheet-PivotDesignPanel">
966
- <Section class="'o-pivot-design'" title.translate="Display options">
967
- <div>
968
- <div class="container-fluid p-0 pt-2">
969
- <div class="row mb-2 align-items-center">
970
- <div class="col-6">Max rows:</div>
971
- <div class="col-6 d-flex align-items-center">
972
- <NumberInput
973
- value="pivotStyle.numberOfRows ?? ''"
974
- class="'o-pivot-n-of-rows'"
975
- placeholder.translate="e.g. 10"
976
- onChange="(value) => this.updatePivotStyleNumberProperty(value, 'numberOfRows')"
977
- />
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>
978
982
  </div>
979
- </div>
980
- <div class="row mb-2 align-items-center">
981
- <div class="col-6">Max columns:</div>
982
- <div class="col-6 d-flex align-items-center">
983
- <NumberInput
984
- value="pivotStyle.numberOfColumns ?? ''"
985
- class="'o-pivot-n-of-columns'"
986
- placeholder.translate="e.g. 5"
987
- onChange="(value) => this.updatePivotStyleNumberProperty(value, 'numberOfColumns')"
988
- />
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>
989
993
  </div>
990
- </div>
991
- <div class="row mb-2 align-items-center">
992
- <div class="col-6">Show totals:</div>
993
- <div class="col-6 d-flex align-items-center">
994
- <Checkbox
995
- name="'displayTotals'"
996
- value="pivotStyle.displayTotals ?? defaultStyle.displayTotals"
997
- onChange="(val) => this.updatePivotStyleProperty('displayTotals', val)"
998
- />
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>
999
1003
  </div>
1000
- </div>
1001
- <div class="row mb-2 align-items-center">
1002
- <div class="col-6">Show column titles:</div>
1003
- <div class="col-6 d-flex align-items-center">
1004
- <Checkbox
1005
- name="'displayColumnHeaders'"
1006
- value="pivotStyle.displayColumnHeaders ?? defaultStyle.displayColumnHeaders"
1007
- onChange="(val) => this.updatePivotStyleProperty('displayColumnHeaders', val)"
1008
- />
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>
1009
1013
  </div>
1010
- </div>
1011
- <div class="row mb-2 align-items-center">
1012
- <div class="col-6">Show measure titles:</div>
1013
- <div class="col-6 d-flex align-items-center">
1014
- <Checkbox
1015
- name="'displayMeasuresRow'"
1016
- value="pivotStyle.displayMeasuresRow ?? defaultStyle.displayMeasuresRow"
1017
- onChange="(val) => this.updatePivotStyleProperty('displayMeasuresRow', val)"
1018
- />
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>
1019
1023
  </div>
1020
1024
  </div>
1021
1025
  </div>
1022
- </div>
1023
- </Section>
1026
+ </Section>
1024
1027
 
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
- />
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>
1034
1038
  </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
- />
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>
1044
1048
  </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)"
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'"
1053
1065
  />
1054
1066
  </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>
1067
+ </Section>
1068
+ </div>
1065
1069
  </t>
1066
1070
 
1067
1071
  <t t-name="o-spreadsheet-PivotMeasureDisplayPanel">
@@ -1110,7 +1114,7 @@
1110
1114
 
1111
1115
  <Section>
1112
1116
  <div class="o-sidePanelButtons">
1113
- <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>
1114
1118
  <button t-on-click="onSave" class="o-pivot-measure-save o-button primary">Save</button>
1115
1119
  </div>
1116
1120
  </Section>
@@ -1293,6 +1297,11 @@
1293
1297
  />
1294
1298
  </div>
1295
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)"/>
1303
+ </div>
1304
+ </div>
1296
1305
  </PivotDimension>
1297
1306
  </t>
1298
1307
 
@@ -1330,7 +1339,9 @@
1330
1339
  class="py-1 px-2 d-flex flex-column shadow-sm pivot-dimension border rounded"
1331
1340
  t-att-class="{'pivot-dimension-invalid': !props.dimension.isValid}">
1332
1341
  <div class="d-flex flex-row justify-content-between align-items-center">
1333
- <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">
1334
1345
  <span class="text-danger me-1" t-if="!props.dimension.isValid">
1335
1346
  <t t-call="o-spreadsheet-Icon.TRIANGLE_EXCLAMATION"/>
1336
1347
  </span>
@@ -1341,7 +1352,7 @@
1341
1352
  class="'o-fw-bold'"
1342
1353
  selectContentOnFocus="true"
1343
1354
  />
1344
- <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"/>
1345
1356
  </div>
1346
1357
  <div class="d-flex flex-rows" t-on-pointerdown.stop="">
1347
1358
  <t t-slot="upper-right-icons"/>
@@ -1532,7 +1543,7 @@
1532
1543
  </t>
1533
1544
 
1534
1545
  <t t-name="o-spreadsheet-FindAndReplacePanel">
1535
- <div class="o-find-and-replace">
1546
+ <div class="o-find-and-replace" t-on-keydown="onKeydownPanel">
1536
1547
  <Section title.translate="Search">
1537
1548
  <div class="o-input-search-container">
1538
1549
  <input
@@ -1608,33 +1619,35 @@
1608
1619
  <ValidationMessages msgType="'info'" messages="searchInfo" singleBox="true"/>
1609
1620
  </div>
1610
1621
  </Section>
1611
- <Section class="'pt-0'" t-if="!env.model.getters.isReadonly()" title.translate="Replace">
1612
- <div class="o-input-search-container">
1613
- <input
1614
- type="text"
1615
- class="o-input o-input-without-count o-replace"
1616
- t-on-keydown="onKeydownReplace"
1617
- t-model="store.toReplace"
1618
- placeholder="e.g. 'replace me'"
1619
- />
1620
- </div>
1621
- </Section>
1622
- <Section>
1623
- <div class="o-sidePanelButtons" t-if="!env.model.getters.isReadonly()">
1624
- <button
1625
- t-att-disabled="store.selectedMatchIndex === null"
1626
- t-on-click="() => store.replace()"
1627
- class="o-button o-replace">
1628
- Replace
1629
- </button>
1630
- <button
1631
- t-att-disabled="store.selectedMatchIndex === null"
1632
- t-on-click="() => store.replaceAll()"
1633
- class="o-button o-replace-all">
1634
- Replace all
1635
- </button>
1636
- </div>
1637
- </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>
1638
1651
  </div>
1639
1652
  </t>
1640
1653
 
@@ -1706,7 +1719,7 @@
1706
1719
 
1707
1720
  <Section>
1708
1721
  <div class="o-sidePanelButtons">
1709
- <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>
1710
1723
  <button t-on-click="onSave" class="o-dv-save o-button primary">Save</button>
1711
1724
  </div>
1712
1725
  </Section>
@@ -1746,26 +1759,26 @@
1746
1759
  </t>
1747
1760
 
1748
1761
  <t t-name="o-spreadsheet-ListCriterionForm">
1749
- <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">
1750
1763
  <div class="o-dv-list-values d-flex align-items-center">
1751
1764
  <div class="me-1">
1752
1765
  <RoundColorPicker
1753
- currentColor="props.criterion.colors?.[value] || '#E7E9ED'"
1754
- onColorPicked="(c) => this.onColorChanged(c, value)"
1766
+ currentColor="item.color || '#E7E9ED'"
1767
+ onColorPicked="(c) => this.onColorChanged(item_index, c)"
1755
1768
  />
1756
1769
  </div>
1757
1770
  <CriterionInput
1758
- value="props.criterion.values[value_index]"
1759
- onValueChanged="(v) => this.onValueChanged(v, value_index)"
1771
+ value="item.value"
1772
+ onValueChanged="(v) => this.onValueChanged(item_index, v)"
1760
1773
  criterionType="props.criterion.type"
1761
- onKeyDown="(ev) => this.onKeyDown(ev, value_index)"
1762
- focused="value_index === state.focusedValueIndex"
1774
+ onKeyDown="(ev) => this.onKeyDown(ev, item_index)"
1775
+ focused="item_index === this.state.focusedValueIndex"
1763
1776
  onBlur.bind="onBlurInput"
1764
1777
  disableFormulas="props.disableFormulas"
1765
1778
  />
1766
1779
  <div
1767
1780
  class="o-dv-list-item-delete ms-2 o-button-icon"
1768
- t-on-click="() => this.removeItem(value_index)">
1781
+ t-on-click="() => this.removeItem(item_index)">
1769
1782
  <t t-call="o-spreadsheet-Icon.TRASH_FILLED"/>
1770
1783
  </div>
1771
1784
  </div>
@@ -2210,7 +2223,7 @@
2210
2223
  </Section>
2211
2224
  <Section class="'pt-1'">
2212
2225
  <div class="o-sidePanelButtons">
2213
- <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>
2214
2227
  <button
2215
2228
  t-on-click="onSave"
2216
2229
  class="o-button primary o-cf-save"
@@ -2920,27 +2933,31 @@
2920
2933
  </div>
2921
2934
 
2922
2935
  <t t-set="definition" t-value="getChartDefinition(this.chartId)"/>
2923
- <div class="o-panel-content" t-ref="panelContent">
2924
- <div t-att-class="store.panel !== 'configuration' ? 'd-none' : ''">
2925
- <ChartTypePicker chartId="chartId" chartPanelStore="store"/>
2926
- <t
2927
- t-component="chartPanel.configuration"
2928
- definition="definition"
2929
- chartId="chartId"
2930
- updateChart.bind="updateChart"
2931
- canUpdateChart.bind="canUpdateChart"
2932
- t-key="chartId + definition.type"
2933
- />
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>
2934
2949
  </div>
2935
- <div t-att-class="store.panel !== 'design' ? 'd-none' : ''">
2936
- <t
2937
- t-component="chartPanel.design"
2938
- definition="definition"
2939
- chartId="chartId"
2940
- updateChart.bind="updateChart"
2941
- canUpdateChart.bind="canUpdateChart"
2942
- t-key="chartId + definition.type"
2943
- />
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>
2944
2961
  </div>
2945
2962
  </div>
2946
2963
  </div>
@@ -4509,7 +4526,7 @@
4509
4526
  spellcheck="false"
4510
4527
  placeholder="e.g. A1:A2"
4511
4528
  t-on-input="(ev) => this.onInputChanged(range.id, ev)"
4512
- t-on-focus="() => this.focus(range.id)"
4529
+ t-on-click="() => this.focus(range.id)"
4513
4530
  t-on-keydown="onKeydown"
4514
4531
  t-att-value="range.xc"
4515
4532
  t-att-style="getColor(range)"
@@ -4518,7 +4535,7 @@
4518
4535
  'o-disabled-ranges' : range.disabled and !range.isFocused,
4519
4536
  'o-focused' : range.isFocused,
4520
4537
  'o-invalid border-danger position-relative': isInvalid || !range.isValidRange,
4521
- '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'
4522
4539
  }"
4523
4540
  t-ref="{{range.isFocused ? 'focusedInput' : 'unfocusedInput' + range_index}}"
4524
4541
  />
@@ -4546,13 +4563,12 @@
4546
4563
  <button class="o-button o-add-selection" t-if="canAddRange" t-on-click="addEmptyInput">
4547
4564
  Add range
4548
4565
  </button>
4549
- <div class="ms-auto" t-if="store.hasFocus">
4566
+ <div class="ms-auto" t-if="store.hasFocus or isResettable">
4550
4567
  <button class="o-button o-selection-ko" t-if="isResettable" t-on-click="reset">
4551
4568
  Reset
4552
4569
  </button>
4553
4570
  <button
4554
4571
  class="o-button primary ms-2 o-selection-ok"
4555
- t-if="store.hasFocus"
4556
4572
  t-att-disabled="!isConfirmable"
4557
4573
  t-on-click="confirm">
4558
4574
  Confirm
@@ -4675,7 +4691,7 @@
4675
4691
  <span
4676
4692
  class="o-menu-item-button"
4677
4693
  title="Paint Format"
4678
- t-att-class="{active: isActive}"
4694
+ t-att-class="{'active': isActive, 'o-disabled': env.model.getters.isCurrentSheetLocked()}"
4679
4695
  t-attf-class="{{props.class}}"
4680
4696
  t-on-click="togglePaintFormat"
4681
4697
  t-on-dblclick="onDblClick">
@@ -4740,7 +4756,7 @@
4740
4756
  </t>
4741
4757
 
4742
4758
  <t t-name="o-spreadsheet-Menu-Popover">
4743
- <Popover t-if="props.menuItems" t-props="popoverProps">
4759
+ <Popover t-if="menuItems.length" t-props="popoverProps">
4744
4760
  <div
4745
4761
  t-ref="menu"
4746
4762
  class="o-menu-wrapper bg-white"
@@ -4773,7 +4789,7 @@
4773
4789
  t-on-pointerdown.prevent=""
4774
4790
  t-on-click.stop=""
4775
4791
  t-on-contextmenu.prevent="">
4776
- <t t-foreach="menuItemsAndSeparators" t-as="menuItem" t-key="menuItem_index">
4792
+ <t t-foreach="props.menuItems" t-as="menuItem" t-key="menuItem_index">
4777
4793
  <div t-if="menuItem === 'separator'" class="o-separator border-bottom"/>
4778
4794
  <t t-else="">
4779
4795
  <t t-set="isMenuRoot" t-value="isRoot(menuItem)"/>
@@ -4797,7 +4813,7 @@
4797
4813
  <t t-if="getIconName(menuItem)" t-call="{{getIconName(menuItem)}}"/>
4798
4814
  </div>
4799
4815
  <div class="o-menu-item-name align-middle text-truncate" t-esc="getName(menuItem)"/>
4800
- <t t-set="description" t-value="menuItem.description(env)"/>
4816
+ <t t-set="description" t-value="menuItem.description(env) or menuItem.shortcut"/>
4801
4817
  <div
4802
4818
  t-if="description"
4803
4819
  class="o-menu-item-description ms-auto text-truncate"
@@ -4879,7 +4895,7 @@
4879
4895
  onClose="() => this.menu.isOpen=false"
4880
4896
  />
4881
4897
  <div class="o-buttons">
4882
- <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>
4883
4899
  <button t-on-click="save" class="o-button primary o-save" t-att-disabled="!link.url">
4884
4900
  Confirm
4885
4901
  </button>
@@ -5637,6 +5653,11 @@
5637
5653
  <circle fill="currentColor" cx="14" cy="9" r="4"/>
5638
5654
  </svg>
5639
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>
5640
5661
  <t t-name="o-spreadsheet-Icon.SORT_RANGE">
5641
5662
  <svg class="o-icon">
5642
5663
  <path
@@ -5865,10 +5886,10 @@
5865
5886
  </svg>
5866
5887
  </t>
5867
5888
  <t t-name="o-spreadsheet-Icon.PIVOT">
5868
- <svg class="o-icon" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg">
5889
+ <svg class="o-icon">
5869
5890
  <path
5870
5891
  fill="currentColor"
5871
- 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"
5872
5893
  />
5873
5894
  </svg>
5874
5895
  </t>
@@ -5898,7 +5919,7 @@
5898
5919
  </defs>
5899
5920
  <path
5900
5921
  fill="currentColor"
5901
- 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"
5902
5923
  mask="url(#a)"
5903
5924
  />
5904
5925
  <path
@@ -5949,7 +5970,7 @@
5949
5970
  <svg class="o-icon" viewBox="0 0 122.88 99.75">
5950
5971
  <path
5951
5972
  fill="currentColor"
5952
- 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"
5953
5974
  />
5954
5975
  </svg>
5955
5976
  </t>
@@ -5991,47 +6012,42 @@
5991
6012
  </div>
5992
6013
  </t>
5993
6014
  <t t-name="o-spreadsheet-Icon.ROTATION-0">
5994
- <svg
5995
- width="18"
5996
- height="18"
5997
- viewBox="0 0 18 18"
5998
- transform="rotate(270)"
5999
- xmlns="http://www.w3.org/2000/svg">
6015
+ <svg class="o-icon" viewBox="0 0 18 18">
6000
6016
  <path
6001
- 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"
6002
6018
  fill="currentColor"
6003
6019
  />
6004
6020
  </svg>
6005
6021
  </t>
6006
6022
  <t t-name="o-spreadsheet-Icon.ROTATION-45">
6007
- <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">
6008
6024
  <path
6009
- 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"
6010
6026
  fill="currentColor"
6011
6027
  />
6012
6028
  </svg>
6013
6029
  </t>
6014
6030
  <t t-name="o-spreadsheet-Icon.ROTATION-90">
6015
- <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">
6016
6032
  <path
6017
- 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"
6018
6034
  fill="currentColor"
6019
6035
  />
6020
6036
  </svg>
6021
6037
  </t>
6022
6038
 
6023
6039
  <t t-name="o-spreadsheet-Icon.ROTATION-270">
6024
- <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">
6025
6041
  <path
6026
- 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"
6027
6043
  fill="currentColor"
6028
6044
  />
6029
6045
  </svg>
6030
6046
  </t>
6031
6047
  <t t-name="o-spreadsheet-Icon.ROTATION-315">
6032
- <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">
6033
6049
  <path
6034
- 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"
6035
6051
  fill="currentColor"
6036
6052
  />
6037
6053
  </svg>
@@ -6041,6 +6057,16 @@
6041
6057
  <i class="fa fa-thumb-tack"/>
6042
6058
  </div>
6043
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>
6044
6070
 
6045
6071
  <t t-name="o-spreadsheet-IconPicker">
6046
6072
  <div class="o-icon-picker bg-white">
@@ -6095,7 +6121,7 @@
6095
6121
  <t t-name="o-spreadsheet-Corner">
6096
6122
  <div
6097
6123
  class="o-corner d-flex justify-content-center align-items-center"
6098
- t-on-pointerdown.prevent="onMouseDown"
6124
+ t-on-pointerdown.prevent.stop="onMouseDown"
6099
6125
  t-on-touchstart.prevent.stop=""
6100
6126
  t-att-style="handlerStyle">
6101
6127
  <div
@@ -6506,7 +6532,11 @@
6506
6532
  anchorRect="menuState.anchorRect"
6507
6533
  onClose="() => this.closeMenu()"
6508
6534
  />
6509
- <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">
6510
6540
  <TableResizer table="table"/>
6511
6541
  </t>
6512
6542
  <VerticalScrollBar topOffset="HEADER_HEIGHT"/>
@@ -6563,8 +6593,8 @@
6563
6593
 
6564
6594
  <t t-name="o-spreadsheet-FilterMenuValueList">
6565
6595
  <div class="o-filter-menu-actions d-flex">
6566
- <div class="o-button-link me-4" t-on-click="selectAll">Select all</div>
6567
- <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>
6568
6598
  </div>
6569
6599
  <div class="position-relative">
6570
6600
  <input
@@ -6692,7 +6722,7 @@
6692
6722
  </SidePanelCollapsible>
6693
6723
 
6694
6724
  <div class="o-filter-menu-buttons d-flex justify-content-end">
6695
- <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>
6696
6726
  <button class="o-button primary o-filter-menu-confirm" t-on-click="confirm">
6697
6727
  Confirm
6698
6728
  </button>
@@ -6859,7 +6889,7 @@
6859
6889
  t-att-style="props.style"
6860
6890
  t-att-data-id="props.figureUI.id"
6861
6891
  tabindex="0"
6862
- t-on-keydown="(ev) => this.onKeyDown(ev)"
6892
+ t-on-keydown.stop="(ev) => this.onKeyDown(ev)"
6863
6893
  t-on-keyup.stop="">
6864
6894
  <t
6865
6895
  t-component="figureRegistry.get(props.figureUI.tag).Component"
@@ -6907,7 +6937,7 @@
6907
6937
  />
6908
6938
  </div>
6909
6939
  </div>
6910
- <t t-if="isSelected and !env.isMobile()">
6940
+ <t t-if="isFigureResizable">
6911
6941
  <div
6912
6942
  class="o-fig-anchor o-top pe-auto"
6913
6943
  t-att-style="this.getResizerPosition('top')"
@@ -6992,7 +7022,7 @@
6992
7022
  </t>
6993
7023
 
6994
7024
  <t t-name="o-spreadsheet-ChartJsComponent">
6995
- <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"/>
6996
7026
  </t>
6997
7027
 
6998
7028
  <t t-name="o-spreadsheet-ZoomableChartJsComponent">
@@ -7009,7 +7039,7 @@
7009
7039
  t-ref="masterChartCanvas"
7010
7040
  t-on-dblclick="onMasterChartDoubleClick"
7011
7041
  t-on-pointerdown="onMasterChartPointerDown"
7012
- t-on-pointermove="onMasterChartPointerMove"
7042
+ t-on-pointermove="updateMasterChartCursor"
7013
7043
  t-on-mouseleave="onMasterChartMouseLeave"
7014
7044
  />
7015
7045
  </div>
@@ -7043,7 +7073,12 @@
7043
7073
  </t>
7044
7074
 
7045
7075
  <t t-name="o-spreadsheet-SpreadsheetDashboard">
7046
- <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">
7047
7082
  <div class="mx-auto h-100 position-relative" t-ref="grid" t-att-style="gridContainer">
7048
7083
  <GridOverlay
7049
7084
  onGridResized.bind="onGridResized"
@@ -7083,9 +7118,9 @@
7083
7118
  <div class="w-100 h-100 d-flex flex-column align-items-end" t-att-class="verticalJustifyClass">
7084
7119
  <span
7085
7120
  t-if="props.sortDirection === 'none'"
7086
- class="o-icon sorting-icon pb-1"
7121
+ class="o-icon sorting-icon mb-1"
7087
7122
  t-att-style="style">
7088
- <i class="fa fa-small fa-sort"/>
7123
+ <t t-call="o-spreadsheet-Icon.CARET_SORT"/>
7089
7124
  </span>
7090
7125
  </div>
7091
7126
  </t>
@@ -7591,6 +7626,9 @@
7591
7626
  t-att-title="sheetName"
7592
7627
  t-att-data-id="props.sheetId"
7593
7628
  t-att-class="{active: isSheetActive}">
7629
+ <span t-if="isSheetLocked" class="me-1">
7630
+ <t t-call="o-spreadsheet-Icon.LOCK"/>
7631
+ </span>
7594
7632
  <span
7595
7633
  class="o-sheet-name"
7596
7634
  t-att-class="{'o-sheet-name-editable': state.isEditing }"
@@ -7604,8 +7642,9 @@
7604
7642
  t-att-contenteditable="state.isEditing ? 'plaintext-only': 'false'"
7605
7643
  />
7606
7644
  <span
7607
- class="o-sheet-icon ms-1"
7645
+ class="o-sheet-icon ms-1 rounded"
7608
7646
  tabindex="-1"
7647
+ t-ref="icon"
7609
7648
  t-on-click.stop="(ev) => this.onIconClick(ev)">
7610
7649
  <t t-call="o-spreadsheet-Icon.CARET_DOWN"/>
7611
7650
  </span>
@@ -7625,7 +7664,10 @@
7625
7664
  </t>
7626
7665
 
7627
7666
  <t t-name="o-spreadsheet-BorderEditorWidget">
7628
- <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">
7629
7671
  <span
7630
7672
  t-ref="borderEditorButton"
7631
7673
  t-on-click.stop="toggleBorderEditor"