@odoo/o-spreadsheet 18.4.0-alpha.1 → 18.4.0-alpha.3

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 18.4.0-alpha.1
5
- @date 2025-05-02T12:35:37.038Z
6
- @hash 50d42e1
4
+ @version 18.4.0-alpha.3
5
+ @date 2025-05-13T17:55:42.200Z
6
+ @hash 70ad365
7
7
  -->
8
8
  <odoo>
9
9
  <t t-name="o-spreadsheet-ValidationMessages">
@@ -849,7 +849,7 @@
849
849
  fields="props.unusedGroupableFields"
850
850
  />
851
851
  </div>
852
- <t t-foreach="props.definition.columns" t-as="col" t-key="col.nameWithGranularity">
852
+ <t t-foreach="props.definition.columns" t-as="col" t-key="col_index">
853
853
  <div
854
854
  t-on-pointerdown="(ev) => this.startDragAndDrop(col, ev)"
855
855
  t-att-style="dragAndDrop.itemsStyle[col.nameWithGranularity]"
@@ -875,7 +875,7 @@
875
875
  fields="props.unusedGroupableFields"
876
876
  />
877
877
  </div>
878
- <t t-foreach="props.definition.rows" t-as="row" t-key="row.nameWithGranularity">
878
+ <t t-foreach="props.definition.rows" t-as="row" t-key="row_index">
879
879
  <div
880
880
  t-on-pointerdown="(ev) => this.startDragAndDrop(row, ev)"
881
881
  t-att-style="dragAndDrop.itemsStyle[row.nameWithGranularity]"
@@ -1017,7 +1017,7 @@
1017
1017
  <t t-name="o-spreadsheet-PivotDimensionGranularity">
1018
1018
  <div class="d-flex flex-row">
1019
1019
  <div class="d-flex flex-row py-1 px-2 w-100 small">
1020
- <t t-set="granularity" t-value="props.dimension.granularity"/>
1020
+ <t t-set="granularityProps" t-value="props.dimension.granularity || 'month'"/>
1021
1021
  <div class="pivot-dim-operator-label">Granularity</div>
1022
1022
  <select
1023
1023
  class="o-input flex-grow-1"
@@ -1026,10 +1026,10 @@
1026
1026
  t-foreach="props.allGranularities"
1027
1027
  t-as="granularity"
1028
1028
  t-key="granularity"
1029
- t-if="props.availableGranularities.has(granularity) || granularity === props.dimension.granularity"
1029
+ t-if="props.availableGranularities.has(granularity) || granularity === granularityProps"
1030
1030
  t-att-value="granularity"
1031
1031
  t-esc="periods[granularity]"
1032
- t-att-selected="granularity === props.dimension.granularity or (granularity === 'month' and !props.dimension.granularity)"
1032
+ t-att-selected="granularity === granularityProps or (granularity === 'month' and !granularityProps)"
1033
1033
  />
1034
1034
  </select>
1035
1035
  </div>
@@ -1324,7 +1324,88 @@
1324
1324
  </div>
1325
1325
  </t>
1326
1326
 
1327
- <t t-name="o-spreadsheet-DataValidationValueInRangeCriterionForm">
1327
+ <t t-name="o-spreadsheet-CustomCurrencyPanel">
1328
+ <div class="o-custom-currency">
1329
+ <Section t-if="availableCurrencies.length > 1" title.translate="Currency">
1330
+ <select
1331
+ class="o-input o-available-currencies"
1332
+ t-on-change="(ev) => this.updateSelectCurrency(ev)">
1333
+ <t t-foreach="availableCurrencies" t-as="currency" t-key="currency_index">
1334
+ <option
1335
+ t-att-value="currency_index"
1336
+ t-esc="currencyDisplayName(currency)"
1337
+ t-att-selected="currency_index === state.selectedCurrencyIndex"
1338
+ />
1339
+ </t>
1340
+ </select>
1341
+ </Section>
1342
+ <Section>
1343
+ <div class="o-subsection-left">
1344
+ <div class="o-section-title">Code</div>
1345
+ <input
1346
+ type="text"
1347
+ class="o-input"
1348
+ t-model="state.currencyCode"
1349
+ placeholder="code"
1350
+ t-on-input="(ev) => this.updateCode(ev)"
1351
+ />
1352
+ </div>
1353
+ <div class="o-subsection-right">
1354
+ <div class="o-section-title">Symbol</div>
1355
+ <input
1356
+ type="text"
1357
+ class="o-input"
1358
+ placeholder="symbol"
1359
+ t-model="state.currencySymbol"
1360
+ t-on-input="(ev) => this.updateSymbol(ev)"
1361
+ />
1362
+ </div>
1363
+ </Section>
1364
+ <Section title.translate="Format">
1365
+ <select
1366
+ class="o-input o-format-proposals mb-1"
1367
+ t-on-change="(ev) => this.updateSelectFormat(ev)"
1368
+ t-att-disabled="!formatProposals.length">
1369
+ <t t-foreach="formatProposals" t-as="proposal" t-key="proposal_index">
1370
+ <option
1371
+ t-att-value="proposal_index"
1372
+ t-esc="proposal.example"
1373
+ t-att-selected="proposal_index === state.selectedFormatIndex"
1374
+ />
1375
+ </t>
1376
+ </select>
1377
+ <t t-set="accounting_format_label">Accounting format</t>
1378
+ <Checkbox
1379
+ name="'accountingFormat'"
1380
+ label="accounting_format_label"
1381
+ value="state.isAccountingFormat"
1382
+ onChange.bind="toggleAccountingFormat"
1383
+ />
1384
+ <div class="o-format-examples mt-4" t-if="selectedFormat">
1385
+ <table class="w-100">
1386
+ <t t-foreach="getFormatExamples()" t-as="example" t-key="example_index">
1387
+ <tr>
1388
+ <td class="w-25 pe-3 o-fw-bold" t-esc="example.label"/>
1389
+ <td class="w-75 text-truncate" t-esc="example.value"/>
1390
+ </tr>
1391
+ </t>
1392
+ </table>
1393
+ </div>
1394
+ </Section>
1395
+ <Section>
1396
+ <div class="o-sidePanelButtons">
1397
+ <button
1398
+ class="o-button primary"
1399
+ t-on-click="() => this.apply()"
1400
+ t-att-disabled="!formatProposals.length || isSameFormat">
1401
+ Apply
1402
+ </button>
1403
+ </div>
1404
+ </Section>
1405
+ </div>
1406
+ </t>
1407
+
1408
+ <t t-name="o-spreadsheet-ValueInRangeCriterionForm">
1328
1409
  <SelectionInput
1329
1410
  ranges="[props.criterion.values[0] || '']"
1330
1411
  onSelectionChanged="(ranges) => this.onRangeChanged(ranges[0])"
@@ -1341,16 +1422,17 @@
1341
1422
  </select>
1342
1423
  </t>
1343
1424
 
1344
- <t t-name="o-spreadsheet-DataValidationListCriterionForm">
1425
+ <t t-name="o-spreadsheet-ListCriterionForm">
1345
1426
  <t t-foreach="displayedValues" t-as="value" t-key="value_index">
1346
1427
  <div class="o-dv-list-values d-flex align-items-center">
1347
- <DataValidationInput
1428
+ <CriterionInput
1348
1429
  value="props.criterion.values[value_index]"
1349
1430
  onValueChanged="(v) => this.onValueChanged(v, value_index)"
1350
1431
  criterionType="props.criterion.type"
1351
1432
  onKeyDown="(ev) => this.onKeyDown(ev, value_index)"
1352
1433
  focused="value_index === state.focusedValueIndex"
1353
1434
  onBlur.bind="onBlurInput"
1435
+ disableFormulas="props.disableFormulas"
1354
1436
  />
1355
1437
  <div
1356
1438
  class="o-dv-list-item-delete ms-2 o-button-icon"
@@ -1373,56 +1455,27 @@
1373
1455
  </select>
1374
1456
  </t>
1375
1457
 
1376
- <t t-name="o-spreadsheet-DataValidationSingleInput">
1377
- <DataValidationInput
1458
+ <t t-name="o-spreadsheet-SingleInputCriterionForm">
1459
+ <CriterionInput
1378
1460
  value="props.criterion.values[0]"
1379
1461
  onValueChanged.bind="onValueChanged"
1380
1462
  criterionType="props.criterion.type"
1463
+ disableFormulas="props.disableFormulas"
1381
1464
  />
1382
1465
  </t>
1383
1466
 
1384
- <t t-name="o-spreadsheet-DataValidationInput">
1385
- <div class="o-dv-input position-relative w-100 p-1">
1386
- <t t-if="allowedValues === 'onlyLiterals'">
1387
- <input
1388
- type="text"
1389
- t-ref="input"
1390
- t-on-input="onInputValueChanged"
1391
- t-att-value="props.value"
1392
- class="o-input"
1393
- t-att-class="{
1394
- 'o-invalid border-danger position-relative': errorMessage,
1395
- }"
1396
- t-att-title="errorMessage"
1397
- t-att-placeholder="placeholder"
1398
- t-on-keydown="props.onKeyDown"
1399
- t-on-blur="props.onBlur"
1400
- />
1401
- </t>
1402
- <t t-else="">
1403
- <StandaloneComposer t-props="getDataValidationRuleInputComposerProps()"/>
1404
- </t>
1405
- <span
1406
- t-if="errorMessage"
1407
- class="error-icon text-danger position-absolute d-flex align-items-center"
1408
- t-att-title="errorMessage">
1409
- <t t-call="o-spreadsheet-Icon.ERROR"/>
1410
- </span>
1411
- </div>
1412
- </t>
1413
-
1414
- <t t-name="o-spreadsheet-DataValidationDoubleInput">
1415
- <DataValidationInput
1467
+ <t t-name="o-spreadsheet-DoubleInputCriterionForm">
1468
+ <CriterionInput
1416
1469
  value="props.criterion.values[0]"
1417
1470
  onValueChanged.bind="onFirstValueChanged"
1418
1471
  criterionType="props.criterion.type"
1472
+ disableFormulas="props.disableFormulas"
1419
1473
  />
1420
-
1421
- <div class="o-section-subtitle ms-1 my-2">and</div>
1422
- <DataValidationInput
1474
+ <CriterionInput
1423
1475
  value="props.criterion.values[1]"
1424
1476
  onValueChanged.bind="onSecondValueChanged"
1425
1477
  criterionType="props.criterion.type"
1478
+ disableFormulas="props.disableFormulas"
1426
1479
  />
1427
1480
  </t>
1428
1481
 
@@ -1438,92 +1491,42 @@
1438
1491
  />
1439
1492
  </select>
1440
1493
 
1441
- <DataValidationInput
1494
+ <CriterionInput
1442
1495
  t-if="props.criterion.dateValue === 'exactDate'"
1443
1496
  value="props.criterion.values[0]"
1444
1497
  onValueChanged.bind="onValueChanged"
1445
1498
  criterionType="props.criterion.type"
1499
+ disableFormulas="props.disableFormulas"
1446
1500
  />
1447
1501
  </t>
1448
1502
 
1449
- <t t-name="o-spreadsheet-CustomCurrencyPanel">
1450
- <div class="o-custom-currency">
1451
- <Section t-if="availableCurrencies.length > 1" title.translate="Currency">
1452
- <select
1453
- class="o-input o-available-currencies"
1454
- t-on-change="(ev) => this.updateSelectCurrency(ev)">
1455
- <t t-foreach="availableCurrencies" t-as="currency" t-key="currency_index">
1456
- <option
1457
- t-att-value="currency_index"
1458
- t-esc="currencyDisplayName(currency)"
1459
- t-att-selected="currency_index === state.selectedCurrencyIndex"
1460
- />
1461
- </t>
1462
- </select>
1463
- </Section>
1464
- <Section>
1465
- <div class="o-subsection-left">
1466
- <div class="o-section-title">Code</div>
1467
- <input
1468
- type="text"
1469
- class="o-input"
1470
- t-model="state.currencyCode"
1471
- placeholder="code"
1472
- t-on-input="(ev) => this.updateCode(ev)"
1473
- />
1474
- </div>
1475
- <div class="o-subsection-right">
1476
- <div class="o-section-title">Symbol</div>
1477
- <input
1478
- type="text"
1479
- class="o-input"
1480
- placeholder="symbol"
1481
- t-model="state.currencySymbol"
1482
- t-on-input="(ev) => this.updateSymbol(ev)"
1483
- />
1484
- </div>
1485
- </Section>
1486
- <Section title.translate="Format">
1487
- <select
1488
- class="o-input o-format-proposals mb-1"
1489
- t-on-change="(ev) => this.updateSelectFormat(ev)"
1490
- t-att-disabled="!formatProposals.length">
1491
- <t t-foreach="formatProposals" t-as="proposal" t-key="proposal_index">
1492
- <option
1493
- t-att-value="proposal_index"
1494
- t-esc="proposal.example"
1495
- t-att-selected="proposal_index === state.selectedFormatIndex"
1496
- />
1497
- </t>
1498
- </select>
1499
- <t t-set="accounting_format_label">Accounting format</t>
1500
- <Checkbox
1501
- name="'accountingFormat'"
1502
- label="accounting_format_label"
1503
- value="state.isAccountingFormat"
1504
- onChange.bind="toggleAccountingFormat"
1503
+ <t t-name="o-spreadsheet-CriterionInput">
1504
+ <div class="o-dv-input position-relative w-100 p-1">
1505
+ <t t-if="allowedValues === 'onlyLiterals'">
1506
+ <input
1507
+ type="text"
1508
+ t-ref="input"
1509
+ t-on-input="onInputValueChanged"
1510
+ t-att-value="props.value"
1511
+ class="o-input"
1512
+ t-att-class="{
1513
+ 'o-invalid border-danger position-relative': errorMessage,
1514
+ }"
1515
+ t-att-title="errorMessage"
1516
+ t-att-placeholder="placeholder"
1517
+ t-on-keydown="props.onKeyDown"
1518
+ t-on-blur="props.onBlur"
1505
1519
  />
1506
- <div class="o-format-examples mt-4" t-if="selectedFormat">
1507
- <table class="w-100">
1508
- <t t-foreach="getFormatExamples()" t-as="example" t-key="example_index">
1509
- <tr>
1510
- <td class="w-25 pe-3 o-fw-bold" t-esc="example.label"/>
1511
- <td class="w-75 text-truncate" t-esc="example.value"/>
1512
- </tr>
1513
- </t>
1514
- </table>
1515
- </div>
1516
- </Section>
1517
- <Section>
1518
- <div class="o-sidePanelButtons">
1519
- <button
1520
- class="o-button primary"
1521
- t-on-click="() => this.apply()"
1522
- t-att-disabled="!formatProposals.length || isSameFormat">
1523
- Apply
1524
- </button>
1525
- </div>
1526
- </Section>
1520
+ </t>
1521
+ <t t-else="">
1522
+ <StandaloneComposer t-props="getDataValidationRuleInputComposerProps()"/>
1523
+ </t>
1524
+ <span
1525
+ t-if="errorMessage"
1526
+ class="error-icon text-danger position-absolute d-flex align-items-center"
1527
+ t-att-title="errorMessage">
1528
+ <t t-call="o-spreadsheet-Icon.ERROR"/>
1529
+ </span>
1527
1530
  </div>
1528
1531
  </t>
1529
1532
 
@@ -1937,27 +1940,20 @@
1937
1940
  <t t-set="text_color">Text Color</t>
1938
1941
  <div class="o-cf-cell-is-rule">
1939
1942
  <div class="o-section-subtitle">Format cells if...</div>
1940
- <select
1941
- class="o-input o-cell-is-operator mb-3"
1942
- t-on-change="(ev) => this.editOperator(ev.target.value)">
1943
- <t t-foreach="cellIsOperators" t-as="op" t-key="op_index">
1944
- <option
1945
- t-att-value="op"
1946
- t-esc="cellIsOperators[op]"
1947
- t-att-selected="rule.operator === op"
1948
- />
1949
- </t>
1950
- </select>
1951
- <t t-if="rule.operator !== 'IsEmpty' and rule.operator !== 'IsNotEmpty'">
1952
- <div class="o-cell-is-value mb-3">
1953
- <StandaloneComposer t-props="getCellIsRuleComposerProps(0)"/>
1954
- </div>
1955
- <t t-if="rule.operator === 'Between' || rule.operator === 'NotBetween'">
1956
- <div class="o-cell-is-value o-secondary-value mb-3">
1957
- <StandaloneComposer t-props="getCellIsRuleComposerProps(1)"/>
1958
- </div>
1959
- </t>
1960
- </t>
1943
+ <SelectMenu
1944
+ class="'o-cell-is-operator o-input mb-2'"
1945
+ menuItems="cfCriterionMenuItems"
1946
+ selectedValue="selectedCriterionName"
1947
+ />
1948
+
1949
+ <t
1950
+ t-if="criterionComponent"
1951
+ t-component="criterionComponent"
1952
+ t-key="state.rules.cellIs.operator"
1953
+ criterion="genericCriterion"
1954
+ onCriterionChanged.bind="onRuleValuesChanged"
1955
+ />
1956
+
1961
1957
  <div class="o-section-subtitle pt-3">Formatting style</div>
1962
1958
 
1963
1959
  <t t-call="o-spreadsheet-CellIsRuleEditorPreview">
@@ -2426,6 +2422,10 @@
2426
2422
  defaultStyle="defaults.valuesDesign"
2427
2423
  />
2428
2424
  </Section>
2425
+ <PieHoleSize
2426
+ value="props.definition.pieHolePercentage ?? 25"
2427
+ onValueChange.bind="onPieHoleSizeChange"
2428
+ />
2429
2429
  </t>
2430
2430
  </SidePanelCollapsible>
2431
2431
  </t>
@@ -2527,6 +2527,9 @@
2527
2527
  onSelectionConfirmed.bind="onDataSeriesConfirmed"
2528
2528
  onSelectionReordered.bind="onDataSeriesReordered"
2529
2529
  onSelectionRemoved.bind="onDataSeriesRemoved"
2530
+ canChangeDatasetOrientation="canChangeDatasetOrientation"
2531
+ datasetOrientation="datasetOrientation"
2532
+ onFlipAxis.bind="setDatasetOrientation"
2530
2533
  />
2531
2534
  <ChartLabelRange
2532
2535
  range="this.getLabelRange()"
@@ -2583,6 +2586,11 @@
2583
2586
  onChange="(showValues) => props.updateChart(this.props.figureId, { showValues })"
2584
2587
  />
2585
2588
  </Section>
2589
+ <PieHoleSize
2590
+ t-if="props.definition.isDoughnut"
2591
+ value="props.definition.pieHolePercentage ?? 50"
2592
+ onValueChange.bind="onPieHoleSizeChange"
2593
+ />
2586
2594
  </t>
2587
2595
  </GeneralDesignEditor>
2588
2596
  </t>
@@ -2654,6 +2662,9 @@
2654
2662
  onSelectionConfirmed.bind="onDataSeriesConfirmed"
2655
2663
  onSelectionReordered.bind="onDataSeriesReordered"
2656
2664
  onSelectionRemoved.bind="onDataSeriesRemoved"
2665
+ canChangeDatasetOrientation="canChangeDatasetOrientation"
2666
+ datasetOrientation="datasetOrientation"
2667
+ onFlipAxis.bind="setDatasetOrientation"
2657
2668
  />
2658
2669
  <ChartLabelRange
2659
2670
  range="this.getLabelRange()"
@@ -2787,9 +2798,14 @@
2787
2798
 
2788
2799
  <ChartDataSeries
2789
2800
  ranges="dataRanges"
2790
- onSelectionChanged="(ranges) => this.onDataSeriesRangesChanged(ranges)"
2791
- onSelectionConfirmed="() => this.onDataSeriesConfirmed()"
2792
- hasSingleRange="true"
2801
+ onSelectionChanged.bind="onDataSeriesRangesChanged"
2802
+ onSelectionConfirmed.bind="onDataSeriesConfirmed"
2803
+ onSelectionReordered.bind="onDataSeriesReordered"
2804
+ onSelectionRemoved.bind="onDataSeriesRemoved"
2805
+ maxNumberOfUsedRanges="maxNumberOfUsedRanges"
2806
+ canChangeDatasetOrientation="canChangeDatasetOrientation"
2807
+ datasetOrientation="datasetOrientation"
2808
+ onFlipAxis.bind="setDatasetOrientation"
2793
2809
  />
2794
2810
  <ChartLabelRange
2795
2811
  range="this.getLabelRange()"
@@ -3610,6 +3626,22 @@
3610
3626
  </SidePanelCollapsible>
3611
3627
  </t>
3612
3628
 
3629
+ <t t-name="o-spreadsheet.PieHoleSize">
3630
+ <Section class="'pt-0'" title.translate="Center radius">
3631
+ <div class="d-flex flex-row">
3632
+ <input
3633
+ t-att-value="props.value"
3634
+ type="number"
3635
+ class="o-input o-pie-hole-size-input"
3636
+ min="0"
3637
+ max="95"
3638
+ t-on-change="(ev) => debouncedOnChange(ev.target.value)"
3639
+ />
3640
+ %
3641
+ </div>
3642
+ </Section>
3643
+ </t>
3644
+
3613
3645
  <t t-name="o-spreadsheet-ChartLegend">
3614
3646
  <Section class="'pt-0'" title.translate="Legend position">
3615
3647
  <select
@@ -3641,6 +3673,7 @@
3641
3673
  value="option.value"
3642
3674
  onChange="option.onChange"
3643
3675
  className="'mt-2'"
3676
+ disabled="option.disabled"
3644
3677
  />
3645
3678
  </t>
3646
3679
  </Section>
@@ -3654,6 +3687,10 @@
3654
3687
  onSelectionConfirmed.bind="onDataSeriesConfirmed"
3655
3688
  onSelectionReordered.bind="onDataSeriesReordered"
3656
3689
  onSelectionRemoved.bind="onDataSeriesRemoved"
3690
+ maxNumberOfUsedRanges="maxNumberOfUsedRanges"
3691
+ datasetOrientation="datasetOrientation"
3692
+ canChangeDatasetOrientation="canChangeDatasetOrientation"
3693
+ onFlipAxis.bind="setDatasetOrientation"
3657
3694
  />
3658
3695
  <ChartLabelRange
3659
3696
  range="this.getLabelRange()"
@@ -3697,7 +3734,28 @@
3697
3734
  </t>
3698
3735
 
3699
3736
  <t t-name="o-spreadsheet.ChartDataSeries">
3700
- <Section class="'o-data-series'" title="title">
3737
+ <Section class="'o-data-series'">
3738
+ <t t-set-slot="title">
3739
+ <div class="d-flex flex-row justify-content-between">
3740
+ <t t-esc="title"/>
3741
+ <div t-if="props.onFlipAxis and props.canChangeDatasetOrientation" class="d-flex">
3742
+ <span
3743
+ t-if="props.datasetOrientation !== 'rows'"
3744
+ title="Split dataset by rows"
3745
+ t-on-click="(ev) => props.onFlipAxis('rows')"
3746
+ class="p-1 o-hoverable-button o-split-by-rows">
3747
+ <t t-call="o-spreadsheet-Icon.INSERT_ROW_BEFORE"/>
3748
+ </span>
3749
+ <span
3750
+ t-else=""
3751
+ title="Split dataset by columns"
3752
+ t-on-click="(ev) => props.onFlipAxis('columns')"
3753
+ class="p-1 o-hoverable-button o-split-by-columns">
3754
+ <t t-call="o-spreadsheet-Icon.INSERT_COL_BEFORE"/>
3755
+ </span>
3756
+ </div>
3757
+ </div>
3758
+ </t>
3701
3759
  <SelectionInput
3702
3760
  ranges="ranges"
3703
3761
  required="true"
@@ -3707,6 +3765,8 @@
3707
3765
  onSelectionReordered="props.onSelectionReordered"
3708
3766
  onSelectionRemoved="props.onSelectionRemoved"
3709
3767
  colors="colors"
3768
+ disabledRanges="disabledRanges"
3769
+ disabledRangeTitle.translate="Excluded due to chart limits. Drag to swap with another range."
3710
3770
  />
3711
3771
  </Section>
3712
3772
  </t>
@@ -3769,6 +3829,9 @@
3769
3829
  onSelectionConfirmed.bind="onDataSeriesConfirmed"
3770
3830
  onSelectionReordered.bind="onDataSeriesReordered"
3771
3831
  onSelectionRemoved.bind="onDataSeriesRemoved"
3832
+ canChangeDatasetOrientation="canChangeDatasetOrientation"
3833
+ datasetOrientation="datasetOrientation"
3834
+ onFlipAxis.bind="setDatasetOrientation"
3772
3835
  />
3773
3836
  <ChartLabelRange
3774
3837
  range="this.getLabelRange()"
@@ -3810,6 +3873,7 @@
3810
3873
  t-att-style="getColor(range)"
3811
3874
  class="o-input mb-2"
3812
3875
  t-att-class="{
3876
+ 'o-disabled-ranges' : range.disabled and !range.isFocused,
3813
3877
  'o-focused' : range.isFocused,
3814
3878
  'o-invalid border-danger position-relative': isInvalid || !range.isValidRange,
3815
3879
  'text-decoration-underline': range.xc and range.isFocused and state.mode === 'select-range'
@@ -3818,10 +3882,16 @@
3818
3882
  />
3819
3883
  <span
3820
3884
  t-if="isInvalid || !range.isValidRange"
3821
- class="error-icon text-danger position-absolute d-flex align-items-center"
3885
+ class="input-icon text-danger position-absolute d-flex align-items-center"
3822
3886
  title="This range is invalid">
3823
3887
  <t t-call="o-spreadsheet-Icon.ERROR"/>
3824
3888
  </span>
3889
+ <span
3890
+ class="input-icon o-disabled-ranges position-absolute d-flex align-items-center"
3891
+ t-if="!range.isFocused and range.disabled"
3892
+ t-att-title="props.disabledRangeTitle">
3893
+ <t t-call="o-spreadsheet-Icon.CIRCLE_INFO"/>
3894
+ </span>
3825
3895
  </div>
3826
3896
  <button
3827
3897
  class="border-0 bg-transparent fw-bold o-remove-selection o-button-icon pe-0"
@@ -3830,7 +3900,6 @@
3830
3900
  <t t-call="o-spreadsheet-Icon.TRASH_FILLED"/>
3831
3901
  </button>
3832
3902
  </div>
3833
-
3834
3903
  <div class="d-flex flex-row w-100 o-selection-input">
3835
3904
  <button class="o-button o-add-selection" t-if="canAddRange" t-on-click="addEmptyInput">
3836
3905
  Add range
@@ -3866,11 +3935,62 @@
3866
3935
  t-on-wheel="props.onMouseWheel"
3867
3936
  t-att-style="popoverStyle"
3868
3937
  t-on-click.stop="">
3869
- <t t-slot="default"/>
3938
+ <div class="o-popover-content" t-ref="popoverContent">
3939
+ <t t-slot="default"/>
3940
+ </div>
3870
3941
  </div>
3871
3942
  </t>
3872
3943
  </t>
3873
3944
 
3945
+ <t t-name="o_spreadsheet.PivotHTMLRenderer">
3946
+ <div class="o_pivot_html_renderer">
3947
+ <Checkbox
3948
+ name="'missing_values'"
3949
+ label.translate="Display missing cells only"
3950
+ value="state.showMissingValuesOnly"
3951
+ onChange.bind="(value) => this.state.showMissingValuesOnly = value"
3952
+ className="'m-2'"
3953
+ />
3954
+ <t t-set="tableData" t-value="getTableData()"/>
3955
+ <table
3956
+ class="o_pivot_html_renderer"
3957
+ t-if="tableData.values.length > 0 or tableData.rows.length > 0">
3958
+ <tr t-foreach="tableData.columns" t-as="row" t-key="row_index">
3959
+ <t t-if="row_index === 0">
3960
+ <th t-att-rowspan="tableData.columns.length"/>
3961
+ </t>
3962
+ <t t-foreach="row" t-as="cell" t-key="cell_index">
3963
+ <th
3964
+ t-att-colspan="cell.span"
3965
+ t-att-style="cell.style"
3966
+ t-att-class="{ o_missing_value: cell.isMissing }"
3967
+ t-on-click="() => props.onCellClicked(cell.formula)">
3968
+ <t t-esc="cell.value"/>
3969
+ </th>
3970
+ </t>
3971
+ </tr>
3972
+ <t t-foreach="tableData.rows" t-as="row" t-key="row_index">
3973
+ <tr>
3974
+ <th
3975
+ t-att-style="row.style"
3976
+ t-att-class="{ o_missing_value: row.isMissing }"
3977
+ t-on-click="() => props.onCellClicked(row.formula)">
3978
+ <t t-esc="row.value"/>
3979
+ </th>
3980
+ <t t-foreach="tableData.values" t-as="col" t-key="col_index">
3981
+ <td
3982
+ t-att-class="{ o_missing_value: col[row_index].isMissing }"
3983
+ t-on-click="() => props.onCellClicked(col[row_index].formula)">
3984
+ <t t-esc="col[row_index].value"/>
3985
+ </td>
3986
+ </t>
3987
+ </tr>
3988
+ </t>
3989
+ </table>
3990
+ <div class="alert alert-info" t-else="1">This pivot has no cell missing on this sheet</div>
3991
+ </div>
3992
+ </t>
3993
+
3874
3994
  <t t-name="o-spreadsheet-PaintFormatButton">
3875
3995
  <span
3876
3996
  class="o-menu-item-button"
@@ -5568,6 +5688,47 @@
5568
5688
  </div>
5569
5689
  </t>
5570
5690
 
5691
+ <t t-name="o-spreadsheet-FilterMenuValueList">
5692
+ <div class="o-filter-menu-actions d-flex">
5693
+ <div class="o-button-link me-4" t-on-click="selectAll">Select all</div>
5694
+ <div class="o-button-link me-4" t-on-click="clearAll">Clear</div>
5695
+ </div>
5696
+ <div class="position-relative">
5697
+ <input
5698
+ class="w-100 o-input my-2"
5699
+ t-ref="filterMenuSearchBar"
5700
+ type="text"
5701
+ t-model="state.textFilter"
5702
+ placeholder="Search..."
5703
+ t-on-keydown="onKeyDown"
5704
+ />
5705
+ <i class="o-search-icon position-absolute">
5706
+ <t t-call="o-spreadsheet-Icon.SEARCH"/>
5707
+ </i>
5708
+ </div>
5709
+ <div
5710
+ class="o-filter-menu-list d-flex flex-column"
5711
+ t-ref="filterValueList"
5712
+ t-on-click="this.clearScrolledToValue"
5713
+ t-on-scroll="this.clearScrolledToValue">
5714
+ <t t-foreach="displayedValues" t-as="value" t-key="value.string">
5715
+ <FilterMenuValueItem
5716
+ onClick="() => this.checkValue(value)"
5717
+ onMouseMove="() => this.onMouseMove(value)"
5718
+ value="value.string"
5719
+ isChecked="value.checked"
5720
+ isSelected="value.string === state.selectedValue"
5721
+ scrolledTo="value.scrolledTo"
5722
+ />
5723
+ </t>
5724
+ <div
5725
+ t-if="displayedValues.length === 0"
5726
+ class="o-filter-menu-no-values d-flex align-items-center justify-content-center w-100 h-100 ">
5727
+ No results
5728
+ </div>
5729
+ </div>
5730
+ </t>
5731
+
5571
5732
  <t t-name="o-spreadsheet-FilterMenuValueItem">
5572
5733
  <div
5573
5734
  t-on-pointermove="this.props.onMouseMove"
@@ -5589,61 +5750,72 @@
5589
5750
  </div>
5590
5751
  </t>
5591
5752
 
5753
+ <t t-name="o-spreadsheet-FilterMenuCriterion">
5754
+ <SelectMenu
5755
+ class="'o-filter-criterion-type o-input m-1 mb-2'"
5756
+ menuItems="criterionMenuItems"
5757
+ selectedValue="selectedCriterionName"
5758
+ />
5759
+
5760
+ <t
5761
+ t-if="criterionComponent"
5762
+ t-component="criterionComponent"
5763
+ t-key="selectedCriterionName"
5764
+ criterion="state.criterion"
5765
+ onCriterionChanged.bind="onCriterionChanged"
5766
+ disableFormulas="true"
5767
+ />
5768
+ </t>
5769
+
5592
5770
  <t t-name="o-spreadsheet-FilterMenu">
5593
5771
  <div class="o-filter-menu d-flex flex-column bg-white" t-on-wheel.stop="">
5594
5772
  <t t-if="isSortable">
5595
5773
  <div>
5596
- <div class="o-filter-menu-item my-2 mb-3" t-on-click="() => this.sortFilterZone('asc')">
5774
+ <div
5775
+ class="o-filter-menu-item o-sort-item py-2 mb-1"
5776
+ t-on-click="() => this.sortFilterZone('asc')">
5597
5777
  Sort ascending (A ⟶ Z)
5598
5778
  </div>
5599
- <div class="o-filter-menu-item my-2" t-on-click="() => this.sortFilterZone('desc')">
5779
+ <div
5780
+ class="o-filter-menu-item o-sort-item py-2"
5781
+ t-on-click="() => this.sortFilterZone('desc')">
5600
5782
  Sort descending (Z ⟶ A)
5601
5783
  </div>
5602
5784
  </div>
5603
- <div class="o-separator"/>
5604
5785
  </t>
5605
- <div class="o-filter-menu-actions d-flex">
5606
- <div class="o-button-link me-4" t-on-click="selectAll">Select all</div>
5607
- <div class="o-button-link me-4" t-on-click="clearAll">Clear</div>
5608
- </div>
5609
- <div class="position-relative">
5610
- <input
5611
- class="w-100 o-input my-2"
5612
- t-ref="filterMenuSearchBar"
5613
- type="text"
5614
- t-model="state.textFilter"
5615
- placeholder="Search..."
5616
- t-on-keydown="onKeyDown"
5617
- />
5618
- <i class="o-search-icon position-absolute">
5619
- <t t-call="o-spreadsheet-Icon.SEARCH"/>
5620
- </i>
5621
- </div>
5622
- <div
5623
- class="o-filter-menu-list d-flex flex-column"
5624
- t-ref="filterValueList"
5625
- t-on-click="this.clearScrolledToValue"
5626
- t-on-scroll="this.clearScrolledToValue">
5627
- <t t-foreach="displayedValues" t-as="value" t-key="value.string">
5628
- <FilterMenuValueItem
5629
- onClick="() => this.checkValue(value)"
5630
- onMouseMove="() => this.onMouseMove(value)"
5631
- value="value.string"
5632
- isChecked="value.checked"
5633
- isSelected="value.string === state.selectedValue"
5634
- scrolledTo="value.scrolledTo"
5635
- />
5636
- </t>
5637
- <div
5638
- t-if="displayedValues.length === 0"
5639
- class="o-filter-menu-no-values d-flex align-items-center justify-content-center w-100 h-100 ">
5640
- No results
5786
+ <div class="o-filter-menu-content">
5787
+ <div class="o-separator"/>
5788
+ <SidePanelCollapsible
5789
+ isInitiallyCollapsed="filterValueType !== 'criterion'"
5790
+ title.translate="Filter by criterion">
5791
+ <t t-set-slot="content">
5792
+ <FilterMenuCriterion
5793
+ filterPosition="props.filterPosition"
5794
+ onCriterionChanged.bind="onCriterionChanged"
5795
+ criterionOperators="criterionOperators"
5796
+ />
5797
+ <div class="mb-3"/>
5798
+ </t>
5799
+ </SidePanelCollapsible>
5800
+
5801
+ <SidePanelCollapsible
5802
+ isInitiallyCollapsed="filterValueType === 'criterion'"
5803
+ title.translate="Filter by values">
5804
+ <t t-set-slot="content">
5805
+ <FilterMenuValueList
5806
+ filterPosition="props.filterPosition"
5807
+ onUpdateHiddenValues.bind="onUpdateHiddenValues"
5808
+ />
5809
+ </t>
5810
+ </SidePanelCollapsible>
5811
+
5812
+ <div class="o-filter-menu-buttons d-flex justify-content-end">
5813
+ <button class="o-button o-filter-menu-cancel me-2" t-on-click="cancel">Cancel</button>
5814
+ <button class="o-button primary o-filter-menu-confirm" t-on-click="confirm">
5815
+ Confirm
5816
+ </button>
5641
5817
  </div>
5642
5818
  </div>
5643
- <div class="o-filter-menu-buttons d-flex justify-content-end">
5644
- <button class="o-button o-filter-menu-cancel me-2" t-on-click="cancel">Cancel</button>
5645
- <button class="o-button primary o-filter-menu-confirm" t-on-click="confirm">Confirm</button>
5646
- </div>
5647
5819
  </div>
5648
5820
  </t>
5649
5821
 
@@ -5716,6 +5888,9 @@
5716
5888
  t-key="this.props.figureUI.id"
5717
5889
  />
5718
5890
  </div>
5891
+ <div t-if="env.isDashboard()" class="position-absolute top-0 end-0">
5892
+ <ChartDashboardMenu figureUI="props.figureUI"/>
5893
+ </div>
5719
5894
  </t>
5720
5895
 
5721
5896
  <t t-name="o-spreadsheet-FigureComponent">
@@ -5811,6 +5986,32 @@
5811
5986
  <canvas class="o-figure-canvas o-gauge-chart w-100 h-100" t-ref="chartContainer"/>
5812
5987
  </t>
5813
5988
 
5989
+ <t t-name="spreadsheet.ChartDashboardMenu">
5990
+ <div class="o-dashboard-chart-select position-absolute top-0 end-0" t-on-click.stop="">
5991
+ <div class="d-flex flex-row px-1" t-att-style="backgroundColor">
5992
+ <t t-foreach="getAvailableTypes()" t-as="type" t-key="type.chartSubtype">
5993
+ <button
5994
+ t-attf-class=" {{type.icon}} {{type.chartType === selectedChartType ? 'active' : ''}}"
5995
+ class="o-chart-dashboard-item btn mt-1 me-1 p-1 "
5996
+ t-att-title="type.displayName"
5997
+ t-on-click="() => this.onTypeChange(type.chartSubtype)"
5998
+ t-att-data-id="type.chartSubtype"
5999
+ />
6000
+ </t>
6001
+ <button
6002
+ class="o-chart-dashboard-item btn mt-1 p-1 fa fa-ellipsis-v"
6003
+ t-on-click="openContextMenu"
6004
+ />
6005
+ </div>
6006
+ <Menu
6007
+ t-if="menuState.isOpen"
6008
+ anchorRect="menuState.anchorRect"
6009
+ menuItems="menuState.menuItems"
6010
+ onClose="() => this.menuState.isOpen=false"
6011
+ />
6012
+ </div>
6013
+ </t>
6014
+
5814
6015
  <t t-name="o-spreadsheet-ChartJsComponent">
5815
6016
  <canvas class="o-figure-canvas w-100 h-100" t-att-style="canvasStyle" t-ref="graphContainer"/>
5816
6017
  </t>
@@ -6063,12 +6264,17 @@
6063
6264
  t-on-dblclick="onDblClick"
6064
6265
  t-on-contextmenu="onContextMenu"
6065
6266
  t-on-blur="onBlur"
6267
+ t-on-wheel="onWheel"
6066
6268
  />
6067
6269
  </div>
6068
6270
  <div
6069
6271
  class="o-composer-assistant-container shadow position-absolute z-1"
6070
6272
  t-att-style="assistantContainerStyle"
6071
- t-if="props.focus !== 'inactive' and !assistant.forcedClosed and assistantIsAvailable">
6273
+ t-if="props.focus !== 'inactive' and !assistant.forcedClosed and assistantIsAvailable"
6274
+ t-on-wheel.stop=""
6275
+ t-on-pointerdown.prevent.stop=""
6276
+ t-on-pointerup.prevent.stop=""
6277
+ t-on-click.prevent.stop="">
6072
6278
  <span
6073
6279
  role="button"
6074
6280
  t-on-click="closeAssistant"
@@ -6076,13 +6282,7 @@
6076
6282
  <i class="fa fa-circle fa-stack-1x fa-inverse"/>
6077
6283
  <i class="fa fa-times-circle fa-stack-1x text-muted"/>
6078
6284
  </span>
6079
- <div
6080
- class="o-composer-assistant overflow-auto"
6081
- t-att-style="assistantStyle"
6082
- t-on-wheel.stop=""
6083
- t-on-pointerdown.prevent.stop=""
6084
- t-on-click.prevent.stop=""
6085
- t-on-pointerup.prevent.stop="">
6285
+ <div class="o-composer-assistant overflow-auto" t-att-style="assistantStyle">
6086
6286
  <FunctionDescriptionProvider
6087
6287
  t-if="functionDescriptionState.showDescription"
6088
6288
  functionDescription="functionDescriptionState.functionDescription"