[IMP] web: cp responsiveness

- Avoid isSmall in template and use bootstrap display classes for
  better responsiveness
- Re-order items to preserve order between size transitions as much as possible
- Change several breakpoints:
  - Viewswitcher collaspses faster (lg instead of sm)
  - Create 'always' buttons collapse in dropdown faste (lg instead of sm)

Make the searchabr appear/disappear based on sizing for better
responsiveness.

Hide the searhbar by default  once we go under the LG breakpoint,
so that it does not cause re-flows when a user selects a record's
checkbox in list view.

'always buttons' that are meant to always be included in the control
panel are collapsed into a dropdown attached to the 'new' button when
the size drops below a specific breakpoint.

This commit ensure these breakpoints are correctly mirrored between the
JS code and the XML template, and also make sure the 'btn-group' class
is removed when there is no button to display in the dropdown (otherwise
the 'new' button is styled as-if some buton is supposed to be next to
it).

Part-of: odoo/odoo#116641
This commit is contained in:
Damien Bouvy
2023-05-12 22:59:19 +02:00
committed by Pierre Paridans
parent 1e706d3069
commit 4b21d735f9
4 changed files with 60 additions and 55 deletions
@@ -74,15 +74,15 @@ export class ControlPanel extends Component {
this.mainButtons = useRef("mainButtons");
useEffect(() => {
if (!this.env.isSmall) {
return;
}
// on small screen, clean-up the dropdown elements
const dropdownButtons = this.mainButtons.el.querySelectorAll(".dropdown-menu button");
const dropdownButtons = this.mainButtons.el.querySelectorAll(".o_control_panel_collapsed_create.dropdown-menu button");
if (!dropdownButtons.length) {
this.mainButtons.el
.querySelectorAll(".dropdown-menu, .dropdown-toggle")
.querySelectorAll(".o_control_panel_collapsed_create.dropdown-menu, .o_control_panel_collapsed_create.dropdown-toggle")
.forEach((el) => el.classList.add("d-none"));
this.mainButtons.el
.querySelectorAll(".o_control_panel_collapsed_create.btn-group")
.forEach((el) => el.classList.remove("btn-group"));
return;
}
for (const button of dropdownButtons) {
@@ -3,26 +3,24 @@
<t t-name="web.ControlPanel" owl="1">
<div class="o_control_panel d-flex flex-column gap-3 gap-lg-1 px-3 pt-2 pb-3" t-ref="root">
<div class="o_control_panel_main d-flex flex-wrap flex-lg-nowrap justify-content-between align-items-lg-start gap-3">
<div class="o_control_panel_breadcrumbs d-flex align-items-center justify-content-between w-100 w-md-50 w-lg-auto">
<div class="o_control_panel_main_buttons d-flex gap-1 d-empty-none d-print-none me-md-2 order-1 order-md-0" t-ref="mainButtons">
<t t-if="env.isSmall">
<div class="btn-group">
<t t-slot="control-panel-create-button"/>
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<t t-slot="layout-buttons"/>
<t t-slot="control-panel-always-buttons"/>
</ul>
</div>
</t>
<t t-else="">
<div class="o_control_panel_main d-flex flex-wrap flex-lg-nowrap justify-content-between align-items-lg-start gap-3 flex-grow-1">
<div class="o_control_panel_breadcrumbs d-flex align-items-center gap-1 order-0 h-lg-100">
<div class="o_control_panel_main_buttons d-flex gap-1 d-empty-none d-print-none" t-ref="mainButtons">
<div class="btn-group d-xl-none o_control_panel_collapsed_create">
<t t-slot="control-panel-create-button"/>
<button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split o_control_panel_collapsed_create" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu o_control_panel_collapsed_create">
<t t-slot="layout-buttons"/>
<t t-slot="control-panel-always-buttons"/>
</ul>
</div>
<div class="d-none d-xl-inline-flex gap-1">
<t t-slot="control-panel-create-button"/>
<t t-slot="layout-buttons"/>
<t t-slot="control-panel-always-buttons"/>
</t>
</div>
</div>
<t t-if="env.config.noBreadcrumbs">
<section class="o_control_panel_breadcrumbs_actions d-contents">
@@ -36,41 +34,36 @@
<span class="d-none d-xl-block me-auto"/> <!-- Spacer -->
</div>
<div t-if="display.layoutActions" class="o_control_panel_actions d-flex align-items-center justify-content-around w-100 w-lg-auto order-3 order-lg-2">
<div t-if="display.layoutActions" class="o_control_panel_actions d-empty-none d-flex align-items-center justify-content-around order-2 order-lg-1 w-100 w-lg-auto">
<t t-slot="layout-actions"/>
</div>
<div class="o_control_panel_navigation d-flex flex-wrap flex-md-nowrap align-items-center justify-content-end w-100 w-md-auto gap-3 gap-lg-1 gap-xl-3 order-2 order-lg-3">
<span class="d-none d-xl-block ms-auto"/> <!-- Spacer -->
<div t-if="pagerProps and pagerProps.total > 0" class="o_cp_pager text-nowrap order-2 order-md-1" role="search">
<div class="o_control_panel_navigation d-flex flex-wrap flex-md-nowrap align-items-center justify-content-end gap-3 gap-lg-1 gap-xl-3 order-1 order-lg-2 flex-grow-1">
<div t-if="pagerProps and pagerProps.total > 0" class="o_cp_pager text-nowrap " role="search">
<Pager t-props="pagerProps"/>
</div>
<t t-if="env.config.viewSwitcherEntries?.length > 1">
<t t-if="env.isSmall">
<Dropdown class="'o_cp_switch_buttons order-1 order-md-2'" togglerClass="'btn btn-secondary'" showCaret="true">
<t t-set-slot="toggler">
<t t-set="activeView" t-value="env.config.viewSwitcherEntries.find((view) => view.active)"/>
<i class="oi-fw" t-att-class="activeView.icon"/>
</t>
<t t-foreach="env.config.viewSwitcherEntries" t-as="view" t-key="view.type">
<DropdownItem onSelected="() => this.onViewClicked(view.type)" class="view.active ? 'selected' : ''">
<i class="oi-fw" t-att-class="view.icon"/>
<span class="ms-1" t-out="view.name"/>
</DropdownItem>
</t>
</Dropdown>
</t>
<t t-else="">
<nav class="o_cp_switch_buttons d-print-none btn-group order-1 order-md-2">
<t t-foreach="env.config.viewSwitcherEntries" t-as="view" t-key="view.type">
<button class="btn btn-secondary o_switch_view "
t-attf-class="o_{{view.type}} {{view.icon}} {{view.active ? 'active' : ''}}"
t-att-data-tooltip="view.name"
t-on-click="() => this.onViewClicked(view.type)"
/>
</t>
</nav>
</t>
<Dropdown class="'o_cp_switch_buttons d-xl-none btn-group'" togglerClass="'btn btn-secondary'" showCaret="true">
<t t-set-slot="toggler">
<t t-set="activeView" t-value="env.config.viewSwitcherEntries.find((view) => view.active)"/>
<i class="oi-fw" t-att-class="activeView.icon"/>
</t>
<t t-foreach="env.config.viewSwitcherEntries" t-as="view" t-key="view.type">
<DropdownItem onSelected="() => this.onViewClicked(view.type)" class="view.active ? 'selected' : ''">
<i class="oi-fw" t-att-class="view.icon"/>
<span class="ms-1" t-out="view.name"/>
</DropdownItem>
</t>
</Dropdown>
<nav class="o_cp_switch_buttons d-print-none d-none d-xl-inline-flex btn-group">
<t t-foreach="env.config.viewSwitcherEntries" t-as="view" t-key="view.type">
<button class="btn btn-secondary o_switch_view "
t-attf-class="o_{{view.type}} {{view.icon}} {{view.active ? 'active' : ''}}"
t-att-data-tooltip="view.name"
t-on-click="() => this.onViewClicked(view.type)"
/>
</t>
</nav>
</t>
</div>
</div>
@@ -120,7 +113,7 @@
</div>
<div t-else="" class="d-flex gap-1">
<div class="active d-flex fs-4 min-w-0">
<div class="active d-flex fs-4 min-w-0 align-items-center">
<span class="min-w-0 text-truncate" t-call="web.Breadcrumb.Name"/>
</div>
<t t-call="web.Breadcrumb.Actions"/>
@@ -9,6 +9,9 @@ import { DomainSelectorDialog } from "@web/core/domain_selector_dialog/domain_se
import { fuzzyTest } from "@web/core/utils/search";
import { _t } from "@web/core/l10n/translation";
import { SearchBarMenu } from "../search_bar_menu/search_bar_menu";
import { useDebounced } from "@web/core/utils/timing";
import { browser } from "@web/core/browser/browser";
import { SIZES } from "@web/core/ui/ui_service";
import {
Component,
@@ -31,13 +34,14 @@ export class SearchBar extends Component {
this.fields = this.env.searchModel.searchViewFields;
this.searchItemsFields = this.env.searchModel.getSearchItems((f) => f.type === "field");
this.root = useRef("root");
this.ui = useService("ui");
// core state
this.state = useState({
expanded: [],
focusedIndex: 0,
query: "",
showSearchBar: !this.env.isSmall,
showSearchBar: this.ui.size > SIZES.SM,
});
// derived state
@@ -60,6 +64,10 @@ export class SearchBar extends Component {
useExternalListener(window, "click", this.onWindowClick);
useExternalListener(window, "keydown", this.onWindowKeydown);
this.onResize = useDebounced(this.onResize, 200);
onMounted(() => browser.addEventListener("resize", this.onResize));
onWillUnmount(() => browser.removeEventListener("resize", this.onResize));
}
/**
@@ -435,6 +443,10 @@ export class SearchBar extends Component {
this.inputRef.el.focus();
}
onResize() {
this.state.showSearchBar = this.ui.size > SIZES.SM;
}
/**
* @param {KeyboardEvent} ev
*/
@@ -97,8 +97,8 @@
</div>
<SearchBarMenu/>
</div>
<t t-if="env.isSmall" t-portal="'.o_control_panel_navigation'">
<button class="btn btn-secondary" t-on-click="onToggleSearchBar">
<t t-portal="'.o_control_panel_navigation'">
<button t-attf-class="btn btn-secondary {{state.showSearchBar?'d-md-none active':''}}" t-on-click="onToggleSearchBar">
<i class="fa fa-fw fa-search"/>
</button>
</t>