Tower: upload web_responsive 19.0.1.0.2 (was 19.0.1.0.2, via marketplace)
All checks were successful
addon-qualify / qualify (push) Successful in 13s
All checks were successful
addon-qualify / qualify (push) Successful in 13s
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
/* global document, location, window */
|
||||
|
||||
/* Copyright 2018 Tecnativa - Jairo Llopis
|
||||
* Copyright 2021 ITerra - Sergey Shebanin
|
||||
* Copyright 2023 Onestein - Anjeel Haria
|
||||
* Copyright 2023 Taras Shabaranskyi
|
||||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
|
||||
|
||||
import {Component, onWillStart, useState} from "@odoo/owl";
|
||||
import {useBus, useService} from "@web/core/utils/hooks";
|
||||
import {AppMenuItem} from "@web_responsive/components/apps_menu_item/apps_menu_item.esm";
|
||||
import {AppsMenuSearchBar} from "@web_responsive/components/menu_searchbar/searchbar.esm";
|
||||
import {NavBar} from "@web/webclient/navbar/navbar";
|
||||
import {WebClient} from "@web/webclient/webclient";
|
||||
import {browser} from "@web/core/browser/browser";
|
||||
import {patch} from "@web/core/utils/patch";
|
||||
import {router} from "@web/core/browser/router";
|
||||
import {session} from "@web/session";
|
||||
import {useHotkey} from "@web/core/hotkeys/hotkey_hook";
|
||||
import {user} from "@web/core/user";
|
||||
import {BurgerMenu} from "@web/webclient/burger_menu/burger_menu";
|
||||
|
||||
// Patch WebClient to show AppsMenu instead of default app
|
||||
patch(WebClient.prototype, {
|
||||
setup() {
|
||||
super.setup();
|
||||
useBus(this.env.bus, "APPS_MENU:STATE_CHANGED", ({detail: state}) => {
|
||||
document.body.classList.toggle("o_apps_menu_opened", state);
|
||||
});
|
||||
this.user = user;
|
||||
onWillStart(async () => {
|
||||
const is_redirect_home = await this.orm.searchRead(
|
||||
"res.users",
|
||||
[["id", "=", this.user.userId]],
|
||||
["is_redirect_home"]
|
||||
);
|
||||
user.updateContext({
|
||||
is_redirect_to_home: is_redirect_home[0]?.is_redirect_home,
|
||||
});
|
||||
});
|
||||
this.redirect = false;
|
||||
},
|
||||
_loadDefaultApp() {
|
||||
if (user.context.is_redirect_to_home) {
|
||||
this.env.bus.trigger("APPS_MENU:STATE_CHANGED", true);
|
||||
} else {
|
||||
super._loadDefaultApp();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export class AppsMenu extends Component {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.state = useState({open: false});
|
||||
this.theme = session.apps_menu.theme || "milk";
|
||||
this.menuService = useService("menu");
|
||||
browser.localStorage.setItem("redirect_menuId", "");
|
||||
if (user.context.is_redirect_to_home) {
|
||||
this.router = router;
|
||||
const menuId = Number(this.router.current.menu_id || 0);
|
||||
this.state = useState({open: menuId === 0});
|
||||
}
|
||||
useBus(this.env.bus, "ACTION_MANAGER:UI-UPDATED", () => {
|
||||
this.setOpenState(false);
|
||||
});
|
||||
this._setupKeyNavigation();
|
||||
}
|
||||
|
||||
setOpenState(open_state) {
|
||||
this.state.open = open_state;
|
||||
this.env.bus.trigger("APPS_MENU:STATE_CHANGED", open_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup navigation among app menus
|
||||
*/
|
||||
_setupKeyNavigation() {
|
||||
const repeatable = {
|
||||
allowRepeat: true,
|
||||
};
|
||||
useHotkey(
|
||||
"ArrowRight",
|
||||
() => {
|
||||
this._onWindowKeydown("next");
|
||||
},
|
||||
repeatable
|
||||
);
|
||||
useHotkey(
|
||||
"ArrowLeft",
|
||||
() => {
|
||||
this._onWindowKeydown("prev");
|
||||
},
|
||||
repeatable
|
||||
);
|
||||
useHotkey(
|
||||
"ArrowDown",
|
||||
() => {
|
||||
this._onWindowKeydown("next");
|
||||
},
|
||||
repeatable
|
||||
);
|
||||
useHotkey(
|
||||
"ArrowUp",
|
||||
() => {
|
||||
this._onWindowKeydown("prev");
|
||||
},
|
||||
repeatable
|
||||
);
|
||||
useHotkey("Escape", () => {
|
||||
this.env.bus.trigger("ACTION_MANAGER:UI-UPDATED");
|
||||
});
|
||||
}
|
||||
|
||||
_onWindowKeydown(direction) {
|
||||
const focusableInputElements = document.querySelectorAll(".o-app-menu-item");
|
||||
if (focusableInputElements.length) {
|
||||
const focusable = [...focusableInputElements];
|
||||
const index = focusable.indexOf(document.activeElement);
|
||||
let nextIndex = 0;
|
||||
if (direction === "prev" && index >= 0) {
|
||||
if (index > 0) {
|
||||
nextIndex = index - 1;
|
||||
} else {
|
||||
nextIndex = focusable.length - 1;
|
||||
}
|
||||
} else if (direction === "next") {
|
||||
if (index + 1 < focusable.length) {
|
||||
nextIndex = index + 1;
|
||||
} else {
|
||||
nextIndex = 0;
|
||||
}
|
||||
}
|
||||
focusableInputElements[nextIndex].focus();
|
||||
}
|
||||
}
|
||||
|
||||
onMenuClick() {
|
||||
if (!user.context.is_redirect_to_home) {
|
||||
this.setOpenState(!this.state.open);
|
||||
} else {
|
||||
const redirect_menuId =
|
||||
browser.localStorage.getItem("redirect_menuId") || "";
|
||||
if (!redirect_menuId) {
|
||||
this.setOpenState(true);
|
||||
} else {
|
||||
this.setOpenState(!this.state.open);
|
||||
}
|
||||
const {href, hash} = location;
|
||||
const menuId = this.router.current.menu_id;
|
||||
if (menuId && menuId !== redirect_menuId) {
|
||||
browser.localStorage.setItem(
|
||||
"redirect_menuId",
|
||||
this.router.current.menu_id
|
||||
);
|
||||
}
|
||||
|
||||
if (href.includes(hash)) {
|
||||
window.history.replaceState(null, "", href.replace(hash, ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add this patch after the WebClient patch
|
||||
patch(NavBar.prototype, {
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
useBus(this.env.bus, "APP_MENU:TOGGLE_SIDEBAR", () => {
|
||||
this._openAppMenuSidebar();
|
||||
});
|
||||
},
|
||||
|
||||
openAppMenu() {
|
||||
this.env.bus.trigger("APP_MENU:OPEN_APP_MENU");
|
||||
this._closeAppMenuSidebar();
|
||||
},
|
||||
});
|
||||
|
||||
Object.assign(AppsMenu, {
|
||||
template: "web_responsive.AppsMenu",
|
||||
props: {
|
||||
slots: {
|
||||
type: Object,
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Object.assign(NavBar.components, {AppsMenu, AppMenuItem, AppsMenuSearchBar});
|
||||
|
||||
// Add this patch after the WebClient patch
|
||||
patch(BurgerMenu.prototype, {
|
||||
setup() {
|
||||
super.setup();
|
||||
},
|
||||
|
||||
_openAppMenuSidebarMobile() {
|
||||
this.env.bus.trigger("APP_MENU:TOGGLE_SIDEBAR");
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
/* Copyright 2018 Tecnativa - Jairo Llopis
|
||||
* Copyright 2021 ITerra - Sergey Shebanin
|
||||
* Copyright 2023 Taras Shabaranskyi
|
||||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
|
||||
|
||||
:root {
|
||||
.o_grid_apps_menu[data-theme="milk"] {
|
||||
--app-menu-background:
|
||||
url("../../img/home-menu-bg-overlay.svg"),
|
||||
linear-gradient(
|
||||
to bottom,
|
||||
#{$app-menu-background-color},
|
||||
#{desaturate(lighten($app-menu-background-color, 20%), 15)}
|
||||
);
|
||||
}
|
||||
|
||||
.o_grid_apps_menu[data-theme="community"] {
|
||||
--app-menu-background:
|
||||
url("../../img/home-menu-bg-overlay.svg"),
|
||||
linear-gradient(
|
||||
to bottom,
|
||||
#{$o-brand-primary},
|
||||
#{desaturate(lighten($o-brand-primary, 20%), 15)}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin full-screen-dropdown {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
height: 100%;
|
||||
max-height: calc(var(--vh100, 100vh) - #{$o-navbar-height});
|
||||
max-height: calc(100dvh - #{$o-navbar-height});
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
width: 100vw;
|
||||
z-index: 1000;
|
||||
left: 0 !important;
|
||||
}
|
||||
|
||||
.o_apps_menu_opened .o_main_navbar {
|
||||
.o_menu_brand,
|
||||
.o_menu_sections {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
// hide and save odoo default QUnit tests
|
||||
.o_navbar_apps_menu.hide .dropdown-toggle {
|
||||
position: absolute !important;
|
||||
z-index: -100 !important;
|
||||
}
|
||||
|
||||
// Iconized full screen apps menu
|
||||
.o_grid_apps_menu {
|
||||
&__button {
|
||||
background: unset;
|
||||
border: unset;
|
||||
outline: unset;
|
||||
margin-right: 0.25rem;
|
||||
min-height: $o-navbar-height;
|
||||
height: $o-navbar-height;
|
||||
width: $o-navbar-height;
|
||||
color: $o-navbar-brand-color;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background: $o-navbar-entry-bg--hover;
|
||||
}
|
||||
}
|
||||
|
||||
.o-app-menu-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
|
||||
width: 100%;
|
||||
gap: 0.25rem;
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-menu-container {
|
||||
@include full-screen-dropdown();
|
||||
overflow: auto;
|
||||
background-clip: border-box;
|
||||
padding: 1rem 0.5rem;
|
||||
gap: 1rem;
|
||||
background: var(--app-menu-background);
|
||||
background-size: cover;
|
||||
border-radius: 0;
|
||||
// Display apps in a grid
|
||||
align-content: flex-start;
|
||||
display: flex !important;
|
||||
z-index: 1024 !important;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
|
||||
// Hide app icons when searching
|
||||
.has-results ~ .o-app-menu-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
padding: {
|
||||
left: calc((100vw - 850px) / 2);
|
||||
right: calc((100vw - 850px) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sidebar positioning
|
||||
.o_app_menu_sidebar {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!-- Copyright 2018 Tecnativa - Jairo Llopis
|
||||
Copyright 2021 ITerra - Sergey Shebanin
|
||||
Copyright 2023 Onestein - Anjeel Haria
|
||||
Copyright 2023 Taras Shabaranskyi
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
|
||||
<templates>
|
||||
<t t-inherit="web.NavBar.AppsMenu" t-inherit-mode="extension">
|
||||
<!-- odoo 18 has created a left sidebar where the button of all apps
|
||||
and the dropdown for the user where can logout
|
||||
we had to disable the lef sidebar to keep our web_resposive toggle button working
|
||||
and to keep the user dropdown in its original place -->
|
||||
<xpath expr="//t[@t-if='this.ui.isSmall']" position="attributes">
|
||||
<attribute name="t-if">false</attribute>
|
||||
</xpath>
|
||||
<!-- The kanban dropdown is replaced with the odoo default one
|
||||
as the default one took physical place in the DOM -->
|
||||
<xpath expr="//Dropdown" position="replace">
|
||||
<t t-if="this.ui.isSmall">
|
||||
<t t-call="web.NavBar.AppsMenu.Sidebar" />
|
||||
</t>
|
||||
<t t-else="" />
|
||||
<AppsMenu>
|
||||
<t t-set-slot="search_bar">
|
||||
<AppsMenuSearchBar />
|
||||
</t>
|
||||
<AppMenuItem
|
||||
t-foreach="apps"
|
||||
t-as="app"
|
||||
t-key="app.id"
|
||||
app="app"
|
||||
currentApp="currentApp"
|
||||
href="getMenuItemHref(app)"
|
||||
onClick="onNavBarDropdownItemSelection.bind(this)"
|
||||
/>
|
||||
</AppsMenu>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<!-- Apps menu -->
|
||||
<t t-name="web_responsive.AppsMenu">
|
||||
<div class="o_grid_apps_menu" t-att-data-theme="theme">
|
||||
<button
|
||||
class="o_grid_apps_menu__button"
|
||||
title="Home Menu"
|
||||
data-hotkey="h"
|
||||
t-on-click.stop="onMenuClick"
|
||||
>
|
||||
<i class="oi oi-apps fs-4" />
|
||||
</button>
|
||||
<div t-if="state.open" class="app-menu-container">
|
||||
<t t-slot="search_bar" />
|
||||
<div class="o-app-menu-list">
|
||||
<t t-slot="default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Apps Menu Sidebar -->
|
||||
<t t-inherit="web.NavBar.AppsMenu.Sidebar" t-inherit-mode="extension">
|
||||
<xpath expr="//i[hasclass('fa fa-bars')]" position="replace">
|
||||
<!-- Remove the burger menu icon -->
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div[hasclass('o_app_menu_sidebar')]" position="attributes">
|
||||
<attribute
|
||||
name="class"
|
||||
>o_app_menu_sidebar position-fixed top-0 bottom-0 start-100 d-flex flex-column flex-nowrap</attribute>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<!-- Section Menu Items -->
|
||||
<t t-inherit="web.SectionMenu" t-inherit-mode="extension">
|
||||
<!-- Add cursor pointer to menu items -->
|
||||
<xpath expr="//li[@t-on-click]" position="attributes">
|
||||
<attribute name="class">cursor-pointer</attribute>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<!-- Burger Menu -->
|
||||
<t t-inherit="web.BurgerMenu" t-inherit-mode="extension">
|
||||
<xpath expr="//button[hasclass('o_mobile_menu_toggle')]" position="after">
|
||||
<button
|
||||
class="o_mobile_menu_toggle o_nav_entry o-no-caret d-md-none border-0 pe-3"
|
||||
t-on-click.prevent="_openAppMenuSidebarMobile"
|
||||
>
|
||||
<i class="oi oi-panel-right" />
|
||||
</button>
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -0,0 +1,39 @@
|
||||
/* Copyright 2023 Taras Shabaranskyi
|
||||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
|
||||
|
||||
import {Component, xml} from "@odoo/owl";
|
||||
import {registry} from "@web/core/registry";
|
||||
import {useService} from "@web/core/utils/hooks";
|
||||
import {user} from "@web/core/user";
|
||||
|
||||
class AppsMenuPreferences extends Component {
|
||||
setup() {
|
||||
this.action = useService("action");
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
async _onClick() {
|
||||
const onClose = () => this.action.doAction("reload_context");
|
||||
const action = await this.action.loadAction(
|
||||
"web_responsive.res_users_view_form_apps_menu_preferences_action"
|
||||
);
|
||||
this.action.doAction({...action, res_id: this.user.userId}, {onClose}).then();
|
||||
}
|
||||
}
|
||||
|
||||
AppsMenuPreferences.template = xml`
|
||||
<div class="o-dropdown dropdown o-dropdown--no-caret">
|
||||
<button
|
||||
role="button"
|
||||
type="button"
|
||||
title="App Menu Preferences"
|
||||
class="dropdown-toggle o-dropdown--narrow"
|
||||
t-on-click="_onClick">
|
||||
<i class="fa fa-tint fa-lg px-1"/>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
registry
|
||||
.category("systray")
|
||||
.add("AppMenuTheme", {Component: AppsMenuPreferences}, {sequence: 100});
|
||||
Reference in New Issue
Block a user