[IMP] spreadsheet: update o_spreadsheet to latest version

### Contains the following commits:

https://github.com/odoo/o-spreadsheet/commit/c46de914 [REL] 16.5.0-alpha.9
https://github.com/odoo/o-spreadsheet/commit/1438e657 [FIX] side panels: fix link button style Task: /
https://github.com/odoo/o-spreadsheet/commit/f66c9ab4 [FIX] components: fix some button styles Task: /
https://github.com/odoo/o-spreadsheet/commit/62718580 [IMP] CF: reorder preview list with drag and drop Task: 3504521
https://github.com/odoo/o-spreadsheet/commit/d214df23 [REF] CF: split side panel into multiple components Task: 3504521
https://github.com/odoo/o-spreadsheet/commit/f954de50 [IMP] PieChart: percentage visualization for tooltip
https://github.com/odoo/o-spreadsheet/commit/6bc519e0 [IMP] SidePanel: remove unused css rule
https://github.com/odoo/o-spreadsheet/commit/6b9927b6 [MOV] side panel: rename `SidePanelErrors` to `ValidationMessages`
https://github.com/odoo/o-spreadsheet/commit/adcda849 [IMP] grid: add specific # of rows at the end of sheet
https://github.com/odoo/o-spreadsheet/commit/c8289169 [FIX] sheetview: performance issue for too many rows/cols Task: /
https://github.com/odoo/o-spreadsheet/commit/36d3a47e [IMP] spreadsheet: Add Data Validation feature Task: 3422042
https://github.com/odoo/o-spreadsheet/commit/d701460b [REF] filter: create GridCellIcon component Task: 3422042
https://github.com/odoo/o-spreadsheet/commit/953ad1d2 [MOV] error_tooltip: add dedicated test file Task: 3422042
https://github.com/odoo/o-spreadsheet/commit/d61cbee2 [IMP] edition: add CANCEL_EDITION command Task: 3422042
https://github.com/odoo/o-spreadsheet/commit/aad55661 [FIX] Find & Replace: Clear search debounce at unmount Task: /
https://github.com/odoo/o-spreadsheet/commit/f91bf8b6 [IMP] SelectionInput: remove useless ternary expression Task: /
https://github.com/odoo/o-spreadsheet/commit/a5be6827 [IMP] SelectionInput: add missing release on selectionProcessor Task: /
https://github.com/odoo/o-spreadsheet/commit/f94eed58 [FIX] filters: hide sort buttons in readonly mode Task: 3475613
https://github.com/odoo/o-spreadsheet/commit/02436b16 [FIX] locale: string dates in CF aren't (un)localized Task: 3481670
https://github.com/odoo/o-spreadsheet/commit/f7b9b9a5 [FIX] clipboard: cut/paste filter table in another sheet Task: 3498592
https://github.com/odoo/o-spreadsheet/commit/0c8782bd [IMP] EventStream: Observers register only once Task: 3142006
https://github.com/odoo/o-spreadsheet/commit/e008fe2b [FIX] test: undeterministic test in spreadsheet.test.ts
https://github.com/odoo/o-spreadsheet/commit/f2cd5abf [REV] find & replace: shorten the debounce on find search Task: 3510813
https://github.com/odoo/o-spreadsheet/commit/549142d4 [FIX] Find&Replace: purge duplicated handler Task: /

closes odoo/odoo#137440

Signed-off-by: Rémi Rahir (rar) <rar@odoo.com>
This commit is contained in:
Lucas Lefèvre (lul)
2023-10-05 08:08:59 +00:00
parent ae62a08cbc
commit b86ab422a1
2 changed files with 4493 additions and 1959 deletions
File diff suppressed because it is too large Load Diff
@@ -1,9 +1,9 @@
<!--
This file is generated by o-spreadsheet build tools. Do not edit it.
@see https://github.com/odoo/o-spreadsheet
@version 16.5.0-alpha.8
@date 2023-09-22T07:50:26.512Z
@hash 0b7527d5
@version 16.5.0-alpha.9
@date 2023-10-03T13:36:42.361Z
@hash c46de914
-->
<odoo>
<t t-name="o-spreadsheet-ActionButton">
@@ -193,7 +193,7 @@
t-ref="sheetList"
t-on-wheel="onWheel"
t-on-scroll="onScroll">
<t t-foreach="getSheets()" t-as="sheet" t-key="sheet.id">
<t t-foreach="getVisibleSheets()" t-as="sheet" t-key="sheet.id">
<BottomBarSheet
style="getSheetStyle(sheet.id)"
sheetId="sheet.id"
@@ -428,10 +428,10 @@
'shadow':props.values.length}">
<t t-foreach="props.values" t-as="v" t-key="v.text">
<div
class="d-flex flex-column"
class="d-flex flex-column text-start"
t-att-class="{'o-autocomplete-value-focus': props.selectedIndex === v_index}"
t-on-click="() => this.props.onValueSelected(v.text)">
<div class="o-autocomplete-value" t-esc="v.text"/>
<div class="o-autocomplete-value text-truncate" t-esc="v.text"/>
<div
class="o-autocomplete-description text-truncate"
t-esc="v.description"
@@ -615,9 +615,48 @@
</div>
</t>
<t t-name="o-spreadsheet-DataValidationOverlay">
<t t-foreach="checkBoxCellPositions" t-as="position" t-key="'checkbox'+position_index">
<GridCellIcon cellPosition="position">
<DataValidationCheckbox cellPosition="position"/>
</GridCellIcon>
</t>
<t t-foreach="listIconsCellPositions" t-as="position" t-key="'list'+position_index">
<GridCellIcon cellPosition="position" horizontalAlign="'right'">
<DataValidationListIcon cellPosition="position"/>
</GridCellIcon>
</t>
</t>
<t t-name="o-spreadsheet-DataValidationCheckbox">
<input
type="checkbox"
class="o-dv-checkbox"
t-att-class="{'pe-none': isDisabled}"
t-on-change="onCheckboxChange"
t-att-checked="checkBoxValue"
/>
</t>
<t t-name="o-spreadsheet-DataValidationListIcon">
<div
class="o-dv-list-icon d-flex align-items-center justify-content-center"
t-on-click="onClick">
<t t-call="o-spreadsheet-Icon.CARET_DOWN"/>
</div>
</t>
<t t-name="o-spreadsheet-ErrorToolTip">
<div class="o-error-tooltip">
<t t-esc="props.text"/>
<t t-foreach="props.errors" t-as="error" t-key="error_index">
<div class="o-error-tooltip-title fw-bold text-danger">
<t t-esc="error.title"/>
</div>
<div class="o-error-tooltip-message">
<t t-esc="error.message"/>
</div>
</t>
</div>
</t>
@@ -792,29 +831,26 @@
</t>
<t t-name="o-spreadsheet-FilterIcon">
<div class="o-filter-icon" t-att-style="style" t-on-click.stop="props.onClick">
<t t-if="props.isActive" t-call="o-spreadsheet-Icon.FILTER_ICON_ACTIVE"/>
<div class="o-filter-icon" t-on-click.stop="onClick">
<t t-if="isFilterActive" t-call="o-spreadsheet-Icon.FILTER_ICON_ACTIVE"/>
<t t-else="" t-call="o-spreadsheet-Icon.FILTER_ICON"/>
</div>
</t>
<t t-name="o-spreadsheet-FilterIconsOverlay">
<t
t-foreach="getVisibleFilterHeaders()"
t-as="header"
t-key="'filter'+header.col + '_' + header.row">
<FilterIcon
position="getFilterHeaderPosition(header)"
isActive="isFilterActive(header)"
t-key="'icon_'+header.col + '_' + header.row"
onClick="() => this.toggleFilterMenu(header)"
/>
t-foreach="getFilterHeadersPositions()"
t-as="position"
t-key="'filter'+position.col + '_' + position.row">
<GridCellIcon cellPosition="position" offset="props.gridPosition" horizontalAlign="'right'">
<FilterIcon cellPosition="position"/>
</GridCellIcon>
</t>
</t>
<t t-name="o-spreadsheet-FilterMenu">
<div class="o-filter-menu d-flex flex-column bg-white" t-on-wheel.stop="">
<div>
<div t-if="!isReadonly">
<div class="o-filter-menu-item" t-on-click="() => this.sortFilterZone('ascending')">
Sort ascending (A ⟶ Z)
</div>
@@ -822,7 +858,7 @@
Sort descending (Z ⟶ A)
</div>
</div>
<div class="o-separator"/>
<div class="o-separator" t-if="!isReadonly"/>
<div class="o-filter-menu-actions">
<div class="o-filter-menu-action-text" t-on-click="selectAll">Select all</div>
<div class="o-filter-menu-action-text" t-on-click="clearAll">Clear</div>
@@ -1008,6 +1044,36 @@
</div>
</t>
<t t-name="o-spreadsheet-GridAddRowsFooter">
<div
class="o-grid-add-rows mt-2 ms-2 w-100 d-flex position-relative align-items-center"
t-att-style="addRowsPosition"
t-on-mousedown.stop.prevent="">
<button t-on-click="onConfirm" t-att-disabled="state.errorFlag" class="o-button">Add</button>
<input
type="text"
class="o-grid-add-rows-input o-input mt-0 me-2"
t-ref="inputRef"
value="100"
t-on-click.stop=""
t-on-keydown.stop="onKeydown"
t-on-mousedown.stop=""
t-on-input.stop="onInput"
/>
<span>more rows at the bottom</span>
<ValidationMessages t-if="state.errorFlag" messages="errorMessages" msgType="'error'"/>
</div>
</t>
<t t-name="o-spreadsheet-GridCellIcon">
<div
class="o-grid-cell-icon position-absolute overflow-hidden"
t-if="isPositionVisible(this.props.cellPosition)"
t-att-style="iconStyle">
<t t-slot="default"/>
</div>
</t>
<t t-name="o-spreadsheet-GridOverlay">
<div
t-ref="gridOverlay"
@@ -1018,6 +1084,12 @@
t-on-dblclick.self="onDoubleClick"
t-on-contextmenu="onContextMenu">
<FiguresContainer onFigureDeleted="props.onFigureDeleted"/>
<DataValidationOverlay/>
<GridAddRowsFooter
t-if="!env.model.getters.isReadonly()"
t-key="env.model.getters.getActiveSheetId()"
focusGrid="props.onFigureDeleted"
/>
</div>
</t>
@@ -2247,6 +2319,27 @@
/>
</svg>
</t>
<t t-name="o-spreadsheet-Icon.DATA_VALIDATION">
<svg class="o-icon">
<path
fill="currentColor"
d="M.5 2A1.5 1.5 0 0 1 2 .5h14A1.5 1.5 0 0 1 17.5 2v7H11V7H7v4s-.5 0-1.5 1h-1v5h-3a1.5 1.5 0 0 1-1-1M6 6V2H2v4m9 0V2H7v4m9 0V2h-4v4m-6 5V7H2v4m14-2V7h-4v2m-7.5 6.5V12H2v3.5"
/>
<path
stroke="currentColor"
style="fill:none; stroke-linecap:round; stroke-width:1.5"
d="M8,13 l3 3 l6 -5"
/>
</svg>
</t>
<t t-name="o-spreadsheet-Icon.THIN_DRAG_HANDLE">
<svg class="o-icon" viewBox="0 0 4 16" fill="currentColor">
<circle cx="2" cy="3.5" r="1"/>
<circle cx="2" cy="6.5" r="1"/>
<circle cx="2" cy="9.5" r="1"/>
<circle cx="2" cy="12.5" r="1"/>
</svg>
</t>
<t t-name="o-spreadsheet-LinkDisplay">
<div class="o-link-tool d-flex align-items-center">
@@ -2551,7 +2644,7 @@
</div>
<div class="o-section" t-if="errorMessages.length">
<SidePanelErrors messages="errorMessages" msgType="'error'"/>
<ValidationMessages messages="errorMessages" msgType="'error'"/>
</div>
</div>
</t>
@@ -2624,7 +2717,7 @@
</div>
<div class="o-section" t-if="configurationErrorMessages.length">
<SidePanelErrors messages="configurationErrorMessages" msgType="'error'"/>
<ValidationMessages messages="configurationErrorMessages" msgType="'error'"/>
</div>
</div>
</t>
@@ -2690,7 +2783,7 @@
</div>
<div class="o-section" t-if="designErrorMessages.length">
<SidePanelErrors messages="designErrorMessages" msgType="'error'"/>
<ValidationMessages messages="designErrorMessages" msgType="'error'"/>
</div>
</div>
</t>
@@ -2824,7 +2917,7 @@
</div>
<div class="o-section" t-if="errorMessages.length">
<SidePanelErrors messages="errorMessages" msgType="'error'"/>
<ValidationMessages messages="errorMessages" msgType="'error'"/>
</div>
</div>
</t>
@@ -2951,7 +3044,7 @@
</div>
<div class="o-section" t-if="errorMessages.length">
<SidePanelErrors messages="errorMessages" msgType="'error'"/>
<ValidationMessages messages="errorMessages" msgType="'error'"/>
</div>
</div>
</t>
@@ -3103,7 +3196,7 @@
</div>
<div class="o-section" t-if="errorMessages.length">
<SidePanelErrors messages="errorMessages" msgType="'error'"/>
<ValidationMessages messages="errorMessages" msgType="'error'"/>
</div>
</div>
</t>
@@ -3276,6 +3369,90 @@
</div>
</t>
<t t-name="o-spreadsheet-ConditionalFormattingEditor">
<div class="o-cf-ruleEditor">
<div class="o-section o-cf-range">
<div class="o-section-title">Apply to range</div>
<div class="o-selection-cf">
<SelectionInput
ranges="() => state.currentCF.ranges"
class="'o-range'"
isInvalid="isRangeValid"
onSelectionChanged="(ranges) => this.onRangesChanged(ranges)"
required="true"
/>
</div>
<div class="o-section-title">Format rules</div>
<div class="o_field_radio o_horizontal o_field_widget o-cf-type-selector">
<div
class="custom-control form-check o_cf_radio_item"
t-on-click="() => this.changeRuleType('CellIsRule')">
<input
class="form-check-input o_radio_input"
t-att-checked="state.currentCFType === 'CellIsRule'"
type="radio"
id="cellIsRule"
name="ruleType"
value="CellIsRule"
/>
<label for="cellIsRule" class="form-check-label o_form_label">Single color</label>
</div>
<div
class="custom-control form-check o_cf_radio_item"
t-on-click="() => this.changeRuleType('ColorScaleRule')">
<input
class="form-check-input o_radio_input"
t-att-checked="state.currentCFType === 'ColorScaleRule'"
type="radio"
id="colorScaleRule"
name="ruleType"
value="ColorScaleRule"
/>
<label for="colorScaleRule" class="form-check-label o_form_label">Color scale</label>
</div>
<div
class="custom-control form-check o_cf_radio_item"
t-on-click="() => this.changeRuleType('IconSetRule')">
<input
class="form-check-input o_radio_input"
t-att-checked="state.currentCFType === 'IconSetRule'"
type="radio"
id="iconSetRule"
name="ruleType"
value="IconSetRule"
/>
<label for="iconSetRule" class="form-check-label o_form_label">Icon set</label>
</div>
</div>
</div>
<div class="o-section o-cf-editor">
<t t-if="state.currentCFType === 'CellIsRule'" t-call="o-spreadsheet-CellIsRuleEditor">
<t t-set="rule" t-value="state.rules.cellIs"/>
</t>
<t
t-if="state.currentCFType === 'ColorScaleRule'"
t-call="o-spreadsheet-ColorScaleRuleEditor">
<t t-set="rule" t-value="state.rules.colorScale"/>
</t>
<t t-if="state.currentCFType === 'IconSetRule'" t-call="o-spreadsheet-IconSetEditor">
<t t-set="rule" t-value="state.rules.iconSet"/>
</t>
<div class="o-sidePanelButtons">
<button t-on-click="props.onExitEdition" class="o-button o-cf-cancel">Cancel</button>
<button t-on-click="saveConditionalFormat" class="o-button primary o-cf-save">
Save
</button>
</div>
</div>
<div class="o-section">
<div class="o-cf-error" t-foreach="state.errors || []" t-as="error" t-key="error_index">
<t t-esc="errorMessage(error)"/>
</div>
</div>
</div>
</t>
<t t-name="o-spreadsheet-ColorScaleRuleEditorPreview">
<div class="o-cf-preview-gradient" t-attf-style="{{getPreviewGradient()}}">Preview text</div>
</t>
@@ -3348,177 +3525,6 @@
</div>
</t>
<t t-name="o-spreadsheet-ConditionalFormattingPanel">
<div class="o-cf">
<t t-if="state.mode === 'list' || state.mode === 'reorder'">
<div class="o-cf-preview-list">
<div
t-on-click="(ev) => this.editConditionalFormat(cf, ev)"
t-foreach="conditionalFormats"
t-as="cf"
t-key="cf.id">
<t t-call="o-spreadsheet-ConditionalFormattingPanelPreview"/>
</div>
</div>
<t t-if="state.mode === 'list'">
<div
class="btn btn-link o-cf-btn-link o-cf-add"
t-on-click.prevent.stop="addConditionalFormat">
+ Add another rule
</div>
<div
class="btn btn-link o-cf-btn-link o-cf-reorder"
t-on-click="reorderConditionalFormats">
Reorder rules
</div>
</t>
<t t-if="state.mode === 'reorder'">
<div class="btn btn-link o-cf-btn-link o-cf-exit-reorder" t-on-click="switchToList">
Stop reordering rules
</div>
</t>
</t>
<t t-if="state.mode === 'edit' || state.mode === 'add'" t-key="state.currentCF.id">
<div class="o-cf-ruleEditor">
<div class="o-section o-cf-range">
<div class="o-section-title">Apply to range</div>
<div class="o-selection-cf">
<SelectionInput
ranges="() => state.currentCF.ranges"
class="'o-range'"
isInvalid="isRangeValid"
onSelectionChanged="(ranges) => this.onRangesChanged(ranges)"
required="true"
/>
</div>
<div class="o-section-title">Format rules</div>
<div class="o_field_radio o_horizontal o_field_widget o-cf-type-selector">
<div
class="custom-control form-check o_cf_radio_item"
t-on-click="() => this.changeRuleType('CellIsRule')">
<input
class="form-check-input o_radio_input"
t-att-checked="state.currentCFType === 'CellIsRule'"
type="radio"
id="cellIsRule"
name="ruleType"
value="CellIsRule"
/>
<label for="cellIsRule" class="form-check-label o_form_label">Single color</label>
</div>
<div
class="custom-control form-check o_cf_radio_item"
t-on-click="() => this.changeRuleType('ColorScaleRule')">
<input
class="form-check-input o_radio_input"
t-att-checked="state.currentCFType === 'ColorScaleRule'"
type="radio"
id="colorScaleRule"
name="ruleType"
value="ColorScaleRule"
/>
<label for="colorScaleRule" class="form-check-label o_form_label">
Color scale
</label>
</div>
<div
class="custom-control form-check o_cf_radio_item"
t-on-click="() => this.changeRuleType('IconSetRule')">
<input
class="form-check-input o_radio_input"
t-att-checked="state.currentCFType === 'IconSetRule'"
type="radio"
id="iconSetRule"
name="ruleType"
value="IconSetRule"
/>
<label for="iconSetRule" class="form-check-label o_form_label">Icon set</label>
</div>
</div>
</div>
<div class="o-section o-cf-editor">
<t t-if="state.currentCFType === 'CellIsRule'" t-call="o-spreadsheet-CellIsRuleEditor">
<t t-set="rule" t-value="state.rules.cellIs"/>
</t>
<t
t-if="state.currentCFType === 'ColorScaleRule'"
t-call="o-spreadsheet-ColorScaleRuleEditor">
<t t-set="rule" t-value="state.rules.colorScale"/>
</t>
<t t-if="state.currentCFType === 'IconSetRule'" t-call="o-spreadsheet-IconSetEditor">
<t t-set="rule" t-value="state.rules.iconSet"/>
</t>
<div class="o-sidePanelButtons">
<button t-on-click="switchToList" class="o-sidePanelButton o-cf-cancel">
Cancel
</button>
<button t-on-click="saveConditionalFormat" class="o-sidePanelButton o-cf-save">
Save
</button>
</div>
</div>
<div class="o-section">
<div class="o-cf-error" t-foreach="state.errors || []" t-as="error" t-key="error_index">
<t t-esc="errorMessage(error)"/>
</div>
</div>
</div>
</t>
</div>
</t>
<t t-name="o-spreadsheet-ConditionalFormattingPanelPreview">
<div class="o-cf-preview" t-att-class="{ 'o-cf-cursor-ptr': state.mode !== 'reorder' }">
<t t-if="cf.rule.type==='IconSetRule'">
<div class="o-cf-preview-icon">
<t t-call="o-spreadsheet-Icon.{{icons[cf.rule.icons.upper].template}}"/>
<t t-call="o-spreadsheet-Icon.{{icons[cf.rule.icons.middle].template}}"/>
<t t-call="o-spreadsheet-Icon.{{icons[cf.rule.icons.lower].template}}"/>
</div>
</t>
<t t-else="">
<div t-att-style="getStyle(cf.rule)" class="o-cf-preview-image">123</div>
</t>
<div class="o-cf-preview-description">
<div class="o-cf-preview-ruletype">
<div class="o-cf-preview-description-rule">
<t t-esc="getDescription(cf)"/>
</div>
</div>
<div class="o-cf-preview-range" t-esc="cf.ranges"/>
</div>
<t t-if="state.mode === 'reorder'">
<div class="o-cf-reorder">
<t t-if="!cf_first">
<div
class="o-cf-reorder-button-up o-cf-reorder-button"
t-on-click="(ev) => this.reorderRule(cf, 'up', ev)">
<t t-call="o-spreadsheet-Icon.CARET_UP"/>
</div>
</t>
<t t-if="!cf_last">
<div
class="o-cf-reorder-button-down o-cf-reorder-button"
t-on-click="(ev) => this.reorderRule(cf, 'down', ev)">
<t t-call="o-spreadsheet-Icon.CARET_DOWN"/>
</div>
</t>
</div>
</t>
<t t-else="">
<div class="o-cf-delete">
<div
class="o-cf-delete-button"
t-on-click.stop="(ev) => this.deleteConditionalFormat(cf, ev)"
aria-label="Remove rule">
<t t-call="o-spreadsheet-Icon.TRASH"/>
</div>
</div>
</t>
</div>
</t>
<t t-name="o-spreadsheet-IconSets">
<div>
<div class="o-section-subtitle">Icons</div>
@@ -3641,6 +3647,80 @@
</div>
</t>
<t t-name="o-spreadsheet-ConditionalFormatPreviewList">
<div class="o-cf-preview-list h-100 overflow-auto" t-ref="cfList">
<t t-foreach="props.conditionalFormats" t-as="cf" t-key="cf.id">
<t t-call="o-spreadsheet-ConditionalFormattingPanelPreview"/>
</t>
<div
class="btn btn-link o-sidePanel-btn-link o-cf-add float-end"
t-on-click.prevent.stop="props.onAddConditionalFormat">
+ Add another rule
</div>
</div>
</t>
<t t-name="o-spreadsheet-ConditionalFormattingPanelPreview">
<div
class="o-cf-preview d-flex position-relative"
t-att-class="{ 'o-cf-dragging': dragAndDrop.draggedItemId === cf.id }"
t-att-style="getPreviewDivStyle(cf)"
t-att-data-id="cf.id"
t-on-click="(ev) => props.onPreviewClick(cf)"
t-on-mousedown="(ev) => this.onMouseDown(cf, ev)">
<div class="position-relative h-100 w-100 d-flex align-items-center">
<div class="o-cf-drag-handle h-100 position-absolute d-flex align-items-center text-muted">
<t t-call="o-spreadsheet-Icon.THIN_DRAG_HANDLE"/>
</div>
<t t-if="cf.rule.type==='IconSetRule'">
<div class="o-cf-preview-icon d-flex justify-content-around align-items-center me-2">
<t t-call="o-spreadsheet-Icon.{{icons[cf.rule.icons.upper].template}}"/>
<t t-call="o-spreadsheet-Icon.{{icons[cf.rule.icons.middle].template}}"/>
<t t-call="o-spreadsheet-Icon.{{icons[cf.rule.icons.lower].template}}"/>
</div>
</t>
<t t-else="">
<div
t-att-style="getPreviewImageStyle(cf.rule)"
class="o-cf-preview-icon d-flex justify-content-around align-items-center me-2">
123
</div>
</t>
<div class="o-cf-preview-description">
<div class="o-cf-preview-ruletype">
<div class="o-cf-preview-description-rule text-truncate">
<t t-esc="getDescription(cf)"/>
</div>
</div>
<div class="o-cf-preview-range text-truncate" t-esc="cf.ranges"/>
</div>
<div class="o-cf-delete">
<div
class="o-cf-delete-button text-muted"
t-on-click.stop="(ev) => this.deleteConditionalFormat(cf, ev)"
title="Remove rule">
<t t-call="o-spreadsheet-Icon.TRASH"/>
</div>
</div>
</div>
</div>
</t>
<t t-name="o-spreadsheet-ConditionalFormattingPanel">
<div class="o-cf h-100">
<t t-if="state.mode === 'list'">
<ConditionalFormatPreviewList
conditionalFormats="conditionalFormats"
onPreviewClick.bind="editConditionalFormat"
onAddConditionalFormat.bind="addConditionalFormat"
/>
</t>
<t t-if="state.mode === 'edit'">
<ConditionalFormattingEditor editedCf="state.editedCf" onExitEdition.bind="switchToList"/>
</t>
</div>
</t>
<t t-name="o-spreadsheet-CustomCurrencyPanel">
<div class="o-custom-currency">
<div class="o-section" t-if="availableCurrencies.length > 1">
@@ -3694,7 +3774,7 @@
</div>
<div class="o-sidePanelButtons">
<button
class="o-sidePanelButton"
class="o-button"
t-on-click="() => this.apply()"
t-att-disabled="!formatProposals.length || isSameFormat">
Apply
@@ -3703,6 +3783,210 @@
</div>
</t>
<t t-name="o-spreadsheet-DataValidationPanel">
<div class="o-data-validation">
<t t-if="state.mode === 'list'">
<div class="o-dv-preview-list">
<t t-foreach="validationRules" t-as="rule" t-key="rule.id">
<DataValidationPreview
rule="localizeDVRule(rule)"
onClick="() => this.onPreviewClick(rule.id)"
/>
</t>
</div>
<div
class="o-dv-add btn btn-link o-sidePanel-btn-link float-end"
t-on-click="addDataValidationRule">
+ Add another rule
</div>
</t>
<t t-else="">
<DataValidationEditor rule="localizeDVRule(state.activeRule)" onExit.bind="onExitEditMode"/>
</t>
</div>
</t>
<t t-name="o-spreadsheet-DataValidationDateCriterion">
<select class="o-dv-date-value o-input mb-4" t-on-change="onDateValueChanged">
<option
t-foreach="dateValues"
t-as="dateValue"
t-key="dateValue.value"
t-att-value="dateValue.value"
t-esc="dateValue.title"
t-att-selected="dateValue.value === props.criterion.dateValue"
/>
</select>
<DataValidationInput
t-if="props.criterion.dateValue === 'exactDate'"
value="props.criterion.values[0]"
onValueChanged.bind="onValueChanged"
criterionType="props.criterion.type"
/>
</t>
<t t-name="o-spreadsheet-DataValidationDoubleInput">
<DataValidationInput
value="props.criterion.values[0]"
onValueChanged.bind="onFirstValueChanged"
criterionType="props.criterion.type"
/>
<div class="o-section-subtitle ms-1 my-2">and</div>
<DataValidationInput
value="props.criterion.values[1]"
onValueChanged.bind="onSecondValueChanged"
criterionType="props.criterion.type"
/>
</t>
<t t-name="o-spreadsheet-DataValidationInput">
<div class="o-dv-input position-relative w-100">
<input
type="text"
t-ref="input"
t-on-input="onValueChanged"
t-att-value="props.value"
class="o-input"
t-att-class="{
'o-invalid border-danger position-relative': errorMessage,
}"
t-att-title="errorMessage"
t-att-placeholder="placeholder"
t-on-keydown="props.onKeyDown"
t-on-blur="props.onBlur"
/>
<span
t-if="errorMessage"
class="error-icon text-danger position-absolute d-flex align-items-center"
t-att-title="errorMessage">
<t t-call="o-spreadsheet-Icon.ERROR"/>
</span>
</div>
</t>
<t t-name="o-spreadsheet-DataValidationSingleInput">
<DataValidationInput
value="props.criterion.values[0]"
onValueChanged.bind="onValueChanged"
criterionType="props.criterion.type"
/>
</t>
<t t-name="o-spreadsheet-DataValidationListCriterionForm">
<t t-foreach="displayedValues" t-as="value" t-key="value_index">
<div class="o-dv-list-values d-flex align-items-center">
<DataValidationInput
value="props.criterion.values[value_index]"
onValueChanged="(v) => this.onValueChanged(v, value_index)"
criterionType="props.criterion.type"
onKeyDown="(ev) => this.onKeyDown(ev, value_index)"
focused="value_index === state.focusedValueIndex"
onBlur.bind="onBlurInput"
/>
<div
class="o-dv-list-item-delete ms-2 me-1"
t-on-click="() => this.removeItem(value_index)">
<t t-call="o-spreadsheet-Icon.TRASH"/>
</div>
</div>
<div class="mb-2"/>
</t>
<button class="o-dv-list-add-value o-button mb-3" t-on-click="onAddAnotherValue">
Add another item
</button>
<div class="o-section-subtitle">Display style</div>
<select class="o-dv-display-style o-input" t-on-change="onChangedDisplayStyle">
<option t-att-selected="props.criterion.displayStyle === 'arrow'" value="arrow">Arrow</option>
<option t-att-selected="props.criterion.displayStyle === 'plainText'" value="plainText">
Plain text
</option>
</select>
</t>
<t t-name="o-spreadsheet-DataValidationValueInRangeCriterionForm">
<SelectionInput
ranges="() => [props.criterion.values[0] || '']"
onSelectionChanged="(ranges) => this.onRangeChanged(ranges[0])"
required="true"
hasSingleRange="true"
/>
<div class="o-section-subtitle">Display style</div>
<select class="o-dv-display-style o-input" t-on-change="onChangedDisplayStyle">
<option t-att-selected="props.criterion.displayStyle === 'arrow'" value="arrow">Arrow</option>
<option t-att-selected="props.criterion.displayStyle === 'plainText'" value="plainText">
Plain text
</option>
</select>
</t>
<t t-name="o-spreadsheet-DataValidationEditor">
<div class="o-dv-form w-100 h-100">
<div class="o-section o-dv-range">
<div class="o-section-title">Apply to range</div>
<SelectionInput
ranges="() => state.rule.ranges"
onSelectionChanged="(ranges) => this.onRangesChanged(ranges)"
required="true"
/>
<div class="o-subsection o-dv-settings">
<div class="o-section-title">Criteria</div>
<SelectMenu
class="'o-dv-type o-input mb-4'"
menuItems="dvCriterionMenuItems"
selectedValue="selectedCriterionName"
/>
<t
t-if="criterionComponent"
t-component="criterionComponent"
t-key="state.rule.criterion.type"
criterion="state.rule.criterion"
onCriterionChanged.bind="onCriterionChanged"
/>
</div>
</div>
<div class="o-section o-dv-invalid-option">
<div class="o-section-title">If the data is invalid</div>
<select class="o-dv-reject-input o-input" t-on-change="changeRuleIsBlocking">
<option t-att-selected="!state.rule.isBlocking" value="false">Show a warning</option>
<option t-att-selected="state.rule.isBlocking" value="true">Reject the input</option>
</select>
</div>
<div class="o-sidePanelButtons">
<button t-on-click="props.onExit" class="o-dv-cancel o-button">Cancel</button>
<button
t-on-click="onSave"
class="o-dv-save o-button primary"
t-att-class="{'o-disabled': !canSave }">
Save
</button>
</div>
</div>
</t>
<t t-name="o-spreadsheet-DataValidationPreview">
<div class="o-dv-preview p-3" t-on-click="props.onClick">
<div class="d-flex justify-content-between">
<div class="o-dv-container d-flex flex-column">
<div class="o-dv-preview-description fw-bold text-truncate" t-esc="descriptionString"/>
<div class="o-dv-preview-ranges text-truncate" t-esc="rangesString"/>
</div>
<div
class="o-dv-preview-delete d-flex align-items-center text-muted px-3"
t-on-click.stop="deleteDataValidation">
<t t-call="o-spreadsheet-Icon.TRASH"/>
</div>
</div>
</div>
</t>
<t t-name="o-spreadsheet-FindAndReplacePanel">
<div class="o-find-and-replace">
<div class="o-section">
@@ -3754,13 +4038,10 @@
<button
t-att-disabled="!hasSearchResult"
t-on-click="onSelectPreviousCell"
class="o-sidePanelButton">
class="o-button">
Previous
</button>
<button
t-att-disabled="!hasSearchResult"
t-on-click="onSelectNextCell"
class="o-sidePanelButton">
<button t-att-disabled="!hasSearchResult" t-on-click="onSelectNextCell" class="o-button">
Next
</button>
</div>
@@ -3780,13 +4061,13 @@
<button
t-att-disabled="env.model.getters.getCurrentSelectedMatchIndex() === null"
t-on-click="replace"
class="o-sidePanelButton">
class="o-button">
Replace
</button>
<button
t-att-disabled="env.model.getters.getCurrentSelectedMatchIndex() === null"
t-on-click="replaceAll"
class="o-sidePanelButton">
class="o-button">
Replace all
</button>
</div>
@@ -3830,7 +4111,7 @@
<div class="o-sidePanelButtons">
<button
class="o-sidePanelButton"
class="o-button"
t-att-class="{'o-disabled': !canConfirm}"
t-on-click="onRemoveDuplicates">
Remove duplicates
@@ -3838,11 +4119,27 @@
</div>
<div class="o-section" t-if="errorMessages.length">
<SidePanelErrors messages="errorMessages" msgType="'error'"/>
<ValidationMessages messages="errorMessages" msgType="'error'"/>
</div>
</div>
</t>
<t t-name="o-spreadsheet-SelectMenu">
<select
t-att-class="props.class"
t-ref="select"
t-on-mousedown.stop.prevent=""
t-on-click="onClick">
<option selected="true" t-esc="props.selectedValue"/>
</select>
<Menu
t-if="state.isMenuOpen"
menuItems="props.menuItems"
position="menuPosition"
onClose.bind="onMenuClosed"
/>
</t>
<t t-name="o-spreadsheet-SettingsPanel">
<div class="o-settings-panel">
<div class="o-section">
@@ -3901,15 +4198,6 @@
</div>
</t>
<t t-name="o-spreadsheet-SidePanelErrors">
<t t-foreach="props.messages" t-as="msg" t-key="msg">
<div class="d-flex align-items-center o-side-panel-error" t-att-class="divClasses">
<t t-call="o-spreadsheet-Icon.TRIANGLE_EXCLAMATION"/>
<span t-esc="msg"/>
</div>
</t>
</t>
<t t-name="o-spreadsheet-SplitIntoColumnsPanel">
<div class="o-split-to-cols-panel">
<div class="o-section">
@@ -3948,15 +4236,15 @@
<div class="o-sidePanelButtons">
<button
class="o-sidePanelButton"
class="o-button"
t-att-class="{'o-disabled': isConfirmDisabled}"
t-on-click="confirm">
Confirm
</button>
</div>
<SidePanelErrors messages="errorMessages" msgType="'error'"/>
<SidePanelErrors messages="warningMessages" msgType="'warning'"/>
<ValidationMessages messages="errorMessages" msgType="'error'"/>
<ValidationMessages messages="warningMessages" msgType="'warning'"/>
</div>
</div>
</t>
@@ -4191,4 +4479,13 @@
onMenuClicked="() => this.props.onClick()"
/>
</t>
<t t-name="o-spreadsheet-ValidationMessages">
<t t-foreach="props.messages" t-as="msg" t-key="msg">
<div class="d-flex align-items-center o-side-panel-error" t-att-class="divClasses">
<t t-call="o-spreadsheet-Icon.TRIANGLE_EXCLAMATION"/>
<span t-esc="msg"/>
</div>
</t>
</t>
</odoo>