[MIG] web_responsive: Migration to 16.0

Fix for optional dropdown checkbox in list view

Made Changes for compatibility with web_chatter_position module

Fix for attachment delete dialog
pull/2405/head
anjeel.haria 2023-03-01 15:35:45 +01:00
parent da62162a0c
commit beda62914b
42 changed files with 930 additions and 1868 deletions

View File

@ -14,13 +14,13 @@ Web Responsive
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
:target: https://github.com/OCA/web/tree/15.0/web_responsive
:target: https://github.com/OCA/web/tree/16.0/web_responsive
:alt: OCA/web
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_responsive
:target: https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_responsive
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/162/15.0
:target: https://runbot.odoo-community.org/runbot/162/16.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
@ -31,83 +31,83 @@ This module adds responsiveness to web backend.
* New navigation with the fullscreen app menu
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/appmenu.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/appmenu.gif
* Quick menu search inside the app menu
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/appsearch.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/appsearch.gif
* Sticky header & footer in list view
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/listview.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/listview.gif
* Sticky statusbar in form view
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/formview.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/formview.gif
* Bigger checkboxes in list view
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/listview.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/listview.gif
* Increase the size of the labels in extra large screens
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/label_size_small.png
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/label_size_small.png
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/label_size_large.png
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/label_size_large.png
**Features for mobile**:
* App-specific submenus are shown on full screen when toggling them from the
"hamburger" menu
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/hamburger.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/hamburger.gif
* User-specific submenus are shown on full screen when toggling them from the
"avatar" menu
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/usermenu.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/usermenu.gif
* View type picker dropdown displays comfortably
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/viewtype.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/viewtype.gif
* Top app bar is always visible, but the control panel is hidden when
scrolling down, to save some valuable vertical space
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/navbar.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/navbar.gif
* Form status bar action and status buttons are collapsed in dropdowns.
Other control panel buttons use icons to save space.
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/form_buttons.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/form_buttons.gif
* Breadcrumbs navigation is collapsed with a "back arrow" button.
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/breadcrumbs.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/breadcrumbs.gif
* Search panel is collapsed to mobile version on small screens.
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/search_panel.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/search_panel.gif
* Followers and send button is displayed on mobile. Avatar is hidden.
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/chatter.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/chatter.gif
* Scrollable dropdowns
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/dropdown_scroll.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/dropdown_scroll.gif
* Kanban interface adopted to mobile
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/kanban.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/kanban.gif
* Calendar interface adopted to mobile
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/calendar.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/calendar.gif
* Interface is adapted dynamically on device rotation
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/device_rotation.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/device_rotation.gif
* Big inputs on form in edit mode
@ -120,29 +120,29 @@ This module adds responsiveness to web backend.
accessible by fingers of one hand.
F.x. `Alt + S` for `Save`
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/shortcuts.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/shortcuts.gif
* Autofocus on search menu box when opening the app menu
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/appsearch.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/appsearch.gif
* Full width form sheets
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/formview.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/formview.gif
* Set chatter on the side of the screen, optional per user
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/chatter_sided.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/chatter_sided.gif
* Sticky chatter topbar
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/chatter_topbar.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/chatter_topbar.gif
* When the chatter is configured on the side part, the document viewer fills that
part for side-by-side reading instead of full screen. You can still put it on full
width preview clicking on the new maximize button.
.. image:: https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/document_viewer.gif
.. image:: https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/document_viewer.gif
**Table of contents**
@ -170,7 +170,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_responsive%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_responsive%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
@ -223,6 +223,6 @@ Current `maintainers <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-Yajo| |maintainer-Tardo| |maintainer-SplashS|
This module is part of the `OCA/web <https://github.com/OCA/web/tree/15.0/web_responsive>`_ project on GitHub.
This module is part of the `OCA/web <https://github.com/OCA/web/tree/16.0/web_responsive>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -1 +0,0 @@
from . import models

View File

@ -2,52 +2,47 @@
# Copyright 2017-2018 Tecnativa - Jairo Llopis
# Copyright 2018-2019 Tecnativa - Alexandre Díaz
# Copyright 2021 ITerra - Sergey Shebanin
# Copyright 2023 Onestein - Anjeel Haria
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{
"name": "Web Responsive",
"summary": "Responsive web client, community-supported",
"version": "15.0.1.1.6",
"version": "16.0.1.0.0",
"category": "Website",
"website": "https://github.com/OCA/web",
"author": "LasLabs, Tecnativa, ITerra, " "Odoo Community Association (OCA)",
"author": "LasLabs, Tecnativa, ITerra, Onestein, "
"Odoo Community Association (OCA)",
"license": "LGPL-3",
"installable": True,
"depends": ["web", "mail"],
"development_status": "Production/Stable",
"maintainers": ["Yajo", "Tardo", "SplashS"],
"excludes": ["web_enterprise"],
"data": ["views/res_users.xml", "views/web.xml"],
"data": ["views/web.xml"],
"assets": {
"web.assets_frontend": [
"/web_responsive/static/src/legacy/js/website_apps_menu.js",
"/web_responsive/static/src/legacy/scss/website_apps_menu.scss",
],
"web.assets_backend": [
"/web_responsive/static/src/views/form/form_controller.esm.js",
"/web_responsive/static/src/legacy/scss/web_responsive.scss",
"/web_responsive/static/src/legacy/js/web_responsive.js",
"/web_responsive/static/src/legacy/scss/kanban_view_mobile.scss",
"/web_responsive/static/src/legacy/js/kanban_renderer_mobile.js",
"/web_responsive/static/src/components/ui_context.esm.js",
"/web_responsive/static/src/components/apps_menu/apps_menu.scss",
"/web_responsive/static/src/components/apps_menu/apps_menu.esm.js",
"/web_responsive/static/src/components/navbar/main_navbar.scss",
"/web_responsive/static/src/components/control_panel/control_panel.scss",
"/web_responsive/static/src/components/control_panel/control_panel.esm.js",
"/web_responsive/static/src/components/search_panel/search_panel.scss",
"/web_responsive/static/src/components/search_panel/search_panel.esm.js",
"/web_responsive/static/src/components/attachment_viewer/attachment_viewer.scss",
"/web_responsive/static/src/components/attachment_viewer/attachment_viewer.esm.js",
"/web_responsive/static/src/components/hotkey/hotkey.scss",
],
"web.assets_qweb": [
"/web_responsive/static/src/legacy/xml/form_buttons.xml",
"/web_responsive/static/src/components/apps_menu/apps_menu.xml",
"/web_responsive/static/src/components/control_panel/control_panel.xml",
"/web_responsive/static/src/components/navbar/main_navbar.xml",
"/web_responsive/static/src/components/search_panel/search_panel.xml",
"/web_responsive/static/src/components/attachment_viewer/attachment_viewer.xml",
"/web_responsive/static/src/components/hotkey/hotkey.xml",
"/web_responsive/static/src/components/chatter_topbar/chatter_topbar.esm.js",
"/web_responsive/static/src/components/chatter_topbar/chatter_topbar.xml",
"/web_responsive/static/src/components/attachment_viewer/attachment_viewer.scss",
"/web_responsive/static/src/components/attachment_viewer/attachment_viewer.esm.js",
"/web_responsive/static/src/components/attachment_viewer/attachment_viewer.xml",
],
"web.assets_tests": [
"/web_responsive/static/tests/test_patch.js",

View File

@ -1 +0,0 @@
from . import res_users

View File

@ -1,26 +0,0 @@
# Copyright 2018-2019 Alexandre Díaz
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ResUsers(models.Model):
_inherit = "res.users"
chatter_position = fields.Selection(
[("normal", "Normal"), ("sided", "Sided")],
default="sided",
)
"""Override to add access rights.
Access rights are disabled by default, but allowed on some specific
fields defined in self.SELF_{READ/WRITE}ABLE_FIELDS.
"""
@property
def SELF_READABLE_FIELDS(self):
return super().SELF_READABLE_FIELDS + ["chatter_position"]
@property
def SELF_WRITEABLE_FIELDS(self):
return super().SELF_WRITEABLE_FIELDS + ["chatter_position"]

View File

@ -1,6 +1,8 @@
* Dave Lasley <dave@laslabs.com>
* Jairo Llopis <jairo.llopis@tecnativa.com>
* Dennis Sluijk <d.sluijk@onestein.nl>
* `Onestein <https://www.onestein.nl>`_:
* Dennis Sluijk <d.sluijk@onestein.nl>
* Anjeel Haria
* Sergio Teruel <sergio.teruel@tecnativa.com>
* Alexandre Díaz <dev@redneboa.es>
* Mathias Markl <mathias.markl@mukit.at>

View File

@ -22,42 +22,16 @@ This module adds responsiveness to web backend.
.. image:: ../static/img/listview.gif
* Increase the size of the labels in extra large screens
.. image:: ../static/img/label_size_small.png
.. image:: ../static/img/label_size_large.png
**Features for mobile**:
* App-specific submenus are shown on full screen when toggling them from the
"hamburger" menu
.. image:: ../static/img/hamburger.gif
* User-specific submenus are shown on full screen when toggling them from the
"avatar" menu
.. image:: ../static/img/usermenu.gif
* View type picker dropdown displays comfortably
.. image:: ../static/img/viewtype.gif
* Top app bar is always visible, but the control panel is hidden when
scrolling down, to save some valuable vertical space
.. image:: ../static/img/navbar.gif
* Form status bar action and status buttons are collapsed in dropdowns.
Other control panel buttons use icons to save space.
* Control panel buttons use icons to save space.
.. image:: ../static/img/form_buttons.gif
* Breadcrumbs navigation is collapsed with a "back arrow" button.
.. image:: ../static/img/breadcrumbs.gif
* Search panel is collapsed to mobile version on small screens.
.. image:: ../static/img/search_panel.gif
@ -66,22 +40,6 @@ This module adds responsiveness to web backend.
.. image:: ../static/img/chatter.gif
* Scrollable dropdowns
.. image:: ../static/img/dropdown_scroll.gif
* Kanban interface adopted to mobile
.. image:: ../static/img/kanban.gif
* Calendar interface adopted to mobile
.. image:: ../static/img/calendar.gif
* Interface is adapted dynamically on device rotation
.. image:: ../static/img/device_rotation.gif
* Big inputs on form in edit mode
**Features for desktop computers**:
@ -103,15 +61,7 @@ This module adds responsiveness to web backend.
.. image:: ../static/img/formview.gif
* Set chatter on the side of the screen, optional per user
.. image:: ../static/img/chatter_sided.gif
* Sticky chatter topbar
.. image:: ../static/img/chatter_topbar.gif
* When the chatter is configured on the side part, the document viewer fills that
* When the chatter is on the side part, the document viewer fills that
part for side-by-side reading instead of full screen. You can still put it on full
width preview clicking on the new maximize button.

View File

@ -367,71 +367,40 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_responsive"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-15-0/web-15-0-web_responsive"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/162/15.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_responsive"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_responsive"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/162/16.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module adds responsiveness to web backend.</p>
<p><strong>Features for all devices</strong>:</p>
<ul>
<li><p class="first">New navigation with the fullscreen app menu</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/appmenu.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/appmenu.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/appmenu.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/appmenu.gif" />
</li>
<li><p class="first">Quick menu search inside the app menu</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/appsearch.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/appsearch.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/appsearch.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/appsearch.gif" />
</li>
<li><p class="first">Sticky header &amp; footer in list view</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/listview.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/listview.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/listview.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/listview.gif" />
</li>
<li><p class="first">Sticky statusbar in form view</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/formview.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/formview.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/formview.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/formview.gif" />
</li>
<li><p class="first">Bigger checkboxes in list view</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/listview.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/listview.gif" />
</li>
<li><p class="first">Increase the size of the labels in extra large screens</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/label_size_small.png" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/label_size_small.png" />
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/label_size_large.png" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/label_size_large.png" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/listview.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/listview.gif" />
</li>
</ul>
<p><strong>Features for mobile</strong>:</p>
<ul>
<li><p class="first">App-specific submenus are shown on full screen when toggling them from the
“hamburger” menu</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/hamburger.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/hamburger.gif" />
</li>
<li><p class="first">User-specific submenus are shown on full screen when toggling them from the
“avatar” menu</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/usermenu.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/usermenu.gif" />
</li>
<li><p class="first">View type picker dropdown displays comfortably</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/viewtype.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/viewtype.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/viewtype.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/viewtype.gif" />
</li>
<li><p class="first">Top app bar is always visible, but the control panel is hidden when
scrolling down, to save some valuable vertical space</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/navbar.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/navbar.gif" />
</li>
<li><p class="first">Form status bar action and status buttons are collapsed in dropdowns.
Other control panel buttons use icons to save space.</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/form_buttons.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/form_buttons.gif" />
</li>
<li><p class="first">Breadcrumbs navigation is collapsed with a “back arrow” button.</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/breadcrumbs.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/breadcrumbs.gif" />
<li><p class="first">Control panel buttons use icons to save space.</p>
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/form_buttons.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/form_buttons.gif" />
</li>
<li><p class="first">Search panel is collapsed to mobile version on small screens.</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/search_panel.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/search_panel.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/search_panel.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/search_panel.gif" />
</li>
<li><p class="first">Followers and send button is displayed on mobile. Avatar is hidden.</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/chatter.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/chatter.gif" />
</li>
<li><p class="first">Scrollable dropdowns</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/dropdown_scroll.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/dropdown_scroll.gif" />
</li>
<li><p class="first">Kanban interface adopted to mobile</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/kanban.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/kanban.gif" />
</li>
<li><p class="first">Calendar interface adopted to mobile</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/calendar.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/calendar.gif" />
</li>
<li><p class="first">Interface is adapted dynamically on device rotation</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/device_rotation.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/device_rotation.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/chatter.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/chatter.gif" />
</li>
<li><p class="first">Big inputs on form in edit mode</p>
</li>
@ -444,21 +413,18 @@ just <cite>Alt + [NUM]</cite> to avoid conflict with Firefox Tab switching.
Standard Odoo keyboard hotkeys changed to be more intuitive or
accessible by fingers of one hand.
F.x. <cite>Alt + S</cite> for <cite>Save</cite></p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/shortcuts.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/shortcuts.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/shortcuts.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/shortcuts.gif" />
</li>
<li><p class="first">Autofocus on search menu box when opening the app menu</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/appsearch.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/appsearch.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/appsearch.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/appsearch.gif" />
</li>
<li><p class="first">Full width form sheets</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/formview.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/formview.gif" />
</li>
<li><p class="first">Set chatter on the side of the screen, optional per user</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/chatter_sided.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/chatter_sided.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/formview.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/formview.gif" />
</li>
<li><p class="first">Sticky chatter topbar</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/chatter_topbar.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/chatter_topbar.gif" />
<img alt="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/chatter_topbar.gif" src="https://raw.githubusercontent.com/OCA/web/16.0/web_responsive/static/img/chatter_topbar.gif" />
</li>
<li><p class="first">When the chatter is configured on the side part, the document viewer fills that
<li><p class="first">When the chatter is on the side part, the document viewer fills that
part for side-by-side reading instead of full screen. You can still put it on full
width preview clicking on the new maximize button.</p>
<img alt="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/document_viewer.gif" src="https://raw.githubusercontent.com/OCA/web/15.0/web_responsive/static/img/document_viewer.gif" />
@ -499,7 +465,7 @@ width preview clicking on the new maximize button.</p>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_responsive%0Aversion:%2015.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_responsive%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
@ -518,6 +484,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<li>Dave Lasley &lt;<a class="reference external" href="mailto:dave&#64;laslabs.com">dave&#64;laslabs.com</a>&gt;</li>
<li>Jairo Llopis &lt;<a class="reference external" href="mailto:jairo.llopis&#64;tecnativa.com">jairo.llopis&#64;tecnativa.com</a>&gt;</li>
<li>Dennis Sluijk &lt;<a class="reference external" href="mailto:d.sluijk&#64;onestein.nl">d.sluijk&#64;onestein.nl</a>&gt;</li>
<li>Anjeel Haria &lt;<a class="reference external" href="mailto:anjeel_bv&#64;onestein.nl">anjeel_bv&#64;onestein.nl</a>&gt;</li>
<li>Sergio Teruel &lt;<a class="reference external" href="mailto:sergio.teruel&#64;tecnativa.com">sergio.teruel&#64;tecnativa.com</a>&gt;</li>
<li>Alexandre Díaz &lt;<a class="reference external" href="mailto:dev&#64;redneboa.es">dev&#64;redneboa.es</a>&gt;</li>
<li>Mathias Markl &lt;<a class="reference external" href="mailto:mathias.markl&#64;mukit.at">mathias.markl&#64;mukit.at</a>&gt;</li>
@ -534,7 +501,7 @@ mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainers</a>:</p>
<p><a class="reference external" href="https://github.com/Yajo"><img alt="Yajo" src="https://github.com/Yajo.png?size=40px" /></a> <a class="reference external" href="https://github.com/Tardo"><img alt="Tardo" src="https://github.com/Tardo.png?size=40px" /></a> <a class="reference external" href="https://github.com/SplashS"><img alt="SplashS" src="https://github.com/SplashS.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/15.0/web_responsive">OCA/web</a> project on GitHub.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_responsive">OCA/web</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -1,6 +1,7 @@
/** @odoo-module **/
/* Copyright 2018 Tecnativa - Jairo Llopis
* Copyright 2021 ITerra - Sergey Shebanin
* Copyright 2023 Onestein - Anjeel Haria
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
import {NavBar} from "@web/webclient/navbar/navbar";
@ -11,16 +12,16 @@ import {debounce} from "@web/core/utils/timing";
import {fuzzyLookup} from "@web/core/utils/search";
import {WebClient} from "@web/webclient/webclient";
import {patch} from "web.utils";
import {escapeRegExp} from "@web/core/utils/strings";
const {Component} = owl;
const {useState, useRef} = owl.hooks;
const {Component, useState, onPatched, onWillPatch} = owl;
// Patch WebClient to show AppsMenu instead of default app
patch(WebClient.prototype, "web_responsive.DefaultAppsMenu", {
setup() {
this._super();
useBus(this.env.bus, "APPS_MENU:STATE_CHANGED", (payload) => {
this.el.classList.toggle("o_apps_menu_opened", payload);
useBus(this.env.bus, "APPS_MENU:STATE_CHANGED", ({detail: state}) => {
document.body.classList.toggle("o_apps_menu_opened", state);
});
},
});
@ -32,16 +33,96 @@ export class AppsMenu extends Component {
setup() {
super.setup();
this.state = useState({open: false});
this.menuService = useService("menu");
useBus(this.env.bus, "ACTION_MANAGER:UI-UPDATED", () => {
this.setState(false);
this.setOpenState(false, false);
});
useBus(this.env.bus, "APPS_MENU:CLOSE", () => {
this.setState(false);
this._setupKeyNavigation();
}
setOpenState(open_state, from_home_menu_click) {
this.state.open = open_state;
// Load home page with proper systray when opening it from website
if (from_home_menu_click) {
var currentapp = this.menuService.getCurrentApp();
if (currentapp && currentapp.name == "Website") {
if (window.location.pathname != "/web") {
const icon = $(
document.querySelector(".o_navbar_apps_menu button > i")
);
icon.removeClass("fa fa-th-large").append(
$("<span/>", {class: "fa fa-spin fa-spinner"})
);
}
window.location.href = "/web#home";
} else {
this.env.bus.trigger("APPS_MENU:STATE_CHANGED", open_state);
}
} else {
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");
});
}
setState(state) {
this.state.open = state;
this.env.bus.trigger("APPS_MENU:STATE_CHANGED", state);
_onWindowKeydown(direction) {
const focusableInputElements = document.querySelectorAll(`.o_app`);
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();
}
}
}
@ -86,6 +167,16 @@ export class AppsMenu extends Component {
*/
function findNames(memo, menu) {
if (menu.actionID) {
var result = "";
if (menu.webIconData) {
const prefix = menu.webIconData.startsWith("P")
? "data:image/svg+xml;base64,"
: "data:image/png;base64,";
result = menu.webIconData.startsWith("data:image")
? menu.webIconData
: prefix + menu.webIconData.replace(/\s/g, "");
}
menu.webIconData = result;
memo[menu.name.trim()] = menu;
}
if (menu.childrenTree) {
@ -108,8 +199,7 @@ export class AppsMenuSearchBar extends Component {
offset: 0,
hasResults: false,
});
useAutofocus({selector: "input"});
this.searchBarInput = useRef("SearchBarInput");
this.searchBarInput = useAutofocus({refName: "SearchBarInput"});
this._searchMenus = debounce(this._searchMenus, 100);
// Store menu data in a format searchable by fuzzy.js
this._searchableMenus = [];
@ -122,26 +212,24 @@ export class AppsMenuSearchBar extends Component {
}
// Set up key navigation
this._setupKeyNavigation();
}
willPatch() {
// Allow looping on results
if (this.state.offset < 0) {
this.state.offset = this.state.results.length + this.state.offset;
} else if (this.state.offset >= this.state.results.length) {
this.state.offset -= this.state.results.length;
}
}
patched() {
// Scroll to selected element on keyboard navigation
if (this.state.results.length) {
const listElement = this.el.querySelector(".search-results");
const activeElement = this.el.querySelector(".highlight");
if (activeElement) {
scrollTo(activeElement, listElement);
onWillPatch(() => {
// Allow looping on results
if (this.state.offset < 0) {
this.state.offset = this.state.results.length + this.state.offset;
} else if (this.state.offset >= this.state.results.length) {
this.state.offset -= this.state.results.length;
}
}
});
onPatched(() => {
// Scroll to selected element on keyboard navigation
if (this.state.results.length) {
const listElement = document.querySelector(".search-results");
const activeElement = listElement.querySelector(".highlight");
if (activeElement) {
scrollTo(activeElement, listElement);
}
}
});
}
/**
@ -168,48 +256,12 @@ export class AppsMenuSearchBar extends Component {
* Setup navigation among search results
*/
_setupKeyNavigation() {
const repeatable = {
allowRepeat: true,
};
useHotkey(
"ArrowDown",
() => {
this.state.offset++;
},
repeatable
);
useHotkey(
"ArrowUp",
() => {
this.state.offset--;
},
repeatable
);
useHotkey(
"Tab",
() => {
this.state.offset++;
},
repeatable
);
useHotkey(
"Shift+Tab",
() => {
this.state.offset--;
},
repeatable
);
useHotkey("Home", () => {
this.state.offset = 0;
});
useHotkey("End", () => {
this.state.offset = this.state.results.length - 1;
});
useHotkey("Enter", () => {
if (this.state.results.length) {
this.el.querySelector(".highlight").click();
}
});
}
_onKeyDown(ev) {
@ -224,9 +276,60 @@ export class AppsMenuSearchBar extends Component {
} else {
this.env.bus.trigger("ACTION_MANAGER:UI-UPDATED");
}
} else if (ev.code === "Tab") {
if (document.querySelector(".search-results")) {
ev.preventDefault();
if (event.shiftKey) {
this.state.offset--;
} else {
this.state.offset++;
}
}
} else if (ev.code === "ArrowUp") {
if (document.querySelector(".search-results")) {
ev.preventDefault();
this.state.offset--;
}
} else if (ev.code === "ArrowDown") {
if (document.querySelector(".search-results")) {
ev.preventDefault();
this.state.offset++;
}
} else if (ev.code === "Enter") {
if (this.state.results.length) {
ev.preventDefault();
document.querySelector(".search-results .highlight").click();
}
}
}
_splitName(name) {
const searchValue = this.searchBarInput.el.value;
if (name) {
const splitName = name.split(
new RegExp(`(${escapeRegExp(searchValue)})`, "ig")
);
return searchValue.length && splitName.length > 1 ? splitName : [name];
}
return [];
}
}
// Patch Navbar to add proper icon for apps
patch(NavBar.prototype, "web_responsive.navbar", {
getWebIconData(menu) {
var result = "/web_responsive/static/img/default_icon_app.png";
if (menu.webIconData) {
const prefix = menu.webIconData.startsWith("P")
? "data:image/svg+xml;base64,"
: "data:image/png;base64,";
result = menu.webIconData.startsWith("data:image")
? menu.webIconData
: prefix + menu.webIconData.replace(/\s/g, "");
}
return result;
},
});
AppsMenu.template = "web_responsive.AppsMenu";
AppsMenuSearchBar.template = "web_responsive.AppsMenuSearchResults";
Object.assign(NavBar.components, {AppsMenu, AppsMenuSearchBar});

View File

@ -12,13 +12,12 @@
width: 100vw;
z-index: 200;
left: 0 !important;
top: $o-navbar-height !important;
}
.o_apps_menu_opened .o_main_navbar {
.o_menu_brand,
.o_menu_sections {
display: none;
display: none !important;
}
}
@ -32,7 +31,7 @@
.fade-leave-to {
opacity: 0;
}
.dropdown-menu {
.dropdown-menu-custom {
@include full-screen-dropdown();
cursor: pointer;
background: url("../../img/home-menu-bg-overlay.svg"),
@ -62,7 +61,24 @@
}
.o_app {
background: none;
outline: 0;
height: 100%;
display: flex;
align-items: center;
text-align: center;
flex-direction: column;
justify-content: flex-start;
white-space: normal;
color: $white !important;
padding: 15px 0 10px;
font-size: 1.25rem;
text-shadow: 1px 1px 1px rgba($black, 0.4);
border-radius: 4px;
transition: 300ms ease;
transition-property: background-color;
&:focus {
background-color: rgba($white, 0.05) !important;
}
img {
box-shadow: none;
margin-bottom: 5px;
@ -70,31 +86,10 @@
transition-property: box-shadow, transform;
}
a {
outline: 0;
height: 100%;
display: flex;
align-items: center;
text-align: center;
flex-direction: column;
justify-content: flex-start;
white-space: normal;
color: gray("white") !important;
padding: 15px 0 10px;
font-size: 1.25rem;
text-shadow: 1px 1px 1px rgba(gray("black"), 0.4);
border-radius: 4px;
transition: 300ms ease;
transition-property: background-color;
background: none;
&:focus {
background-color: rgba(gray("white"), 0.05);
}
}
&:hover img,
a:focus img {
transform: translateY(-3px);
box-shadow: 0 9px 12px -4px rgba(gray("black"), 0.3);
box-shadow: 0 9px 12px -4px rgba($black, 0.3);
}
// Size depends on screen
@ -129,21 +124,20 @@
.search-input {
display: flex;
justify-items: middle;
box-shadow: inset 0 1px 0 rgba(gray("white"), 0.1),
0 1px 0 rgba(gray("black"), 0.1);
text-shadow: 0 1px 0 rgba(gray("black"), 0.5);
justify-items: center;
box-shadow: inset 0 1px 0 rgba($white, 0.1), 0 1px 0 rgba($black, 0.1);
text-shadow: 0 1px 0 rgba($black, 0.5);
border-radius: 4px;
padding: 0.4rem 0.8rem;
margin-bottom: 1rem;
background-color: rgba(gray("white"), 0.1);
background-color: rgba($white, 0.1);
@include media-breakpoint-up(md) {
padding: 0.8rem 1.2rem;
}
.search-icon {
color: gray("white");
color: $white;
font-size: 1.5rem;
margin-right: 1rem;
padding-top: 1px;
@ -153,19 +147,27 @@
height: 2rem;
background: none;
border: none;
color: gray("white");
color: $white;
display: block;
padding: 1px 2px 2px 2px;
box-shadow: none;
&::placeholder {
color: gray("white");
color: $white;
opacity: 0.5;
}
}
}
// Allow to scroll only on results, keeping static search box above
.search-results {
.text-ellipsis {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.text-primary {
color: red !important;
}
margin-top: 1rem;
max-height: calc(100vh - #{$o-navbar-height} - 8rem) !important;
overflow: auto;
@ -177,7 +179,7 @@
background-position: left;
background-repeat: no-repeat;
background-size: contain;
color: gray("white");
color: $white;
cursor: pointer;
line-height: 2.5rem;
padding-left: 3.5rem;
@ -185,7 +187,7 @@
font-weight: 100;
&.highlight,
&:hover {
background-color: rgba(gray("black"), 0.11);
background-color: rgba($black, 0.11);
}
b {
font-weight: 700;
@ -194,3 +196,10 @@
}
}
}
.dropdown-menu-custom {
max-height: 70vh;
overflow: auto;
background-clip: border-box;
box-shadow: $o-dropdown-box-shadow;
}

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Copyright 2018 Tecnativa - Jairo Llopis
Copyright 2021 ITerra - Sergey Shebanin
Copyright 2023 Onestein - Anjeel Haria
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<templates>
<t t-inherit="web.NavBar.AppsMenu" t-inherit-mode="extension" owl="1">
@ -8,23 +9,22 @@
<!-- Same hotkey as in EE -->
<AppsMenu>
<AppsMenuSearchBar />
<MenuItem
<DropdownItem
t-foreach="apps"
t-as="app"
t-key="app.id"
class="o_app"
t-att-class="{ o_dropdown_active: menuService.getCurrentApp() === app }"
payload="app"
class="'o_app'"
dataset="{ menuXmlid: app.xmlid, section: app.id }"
href="getMenuItemHref(app)"
onSelected="() => this.onNavBarDropdownItemSelection(app)"
>
<a t-att-href="getMenuItemHref(app)">
<img
class="o-app-icon"
draggable="false"
t-attf-src="data:image/png;base64,{{app.webIconData}}"
/>
class="o-app-icon"
draggable="false"
t-att-src="getWebIconData(app)"
/>
<div t-esc="app.name" />
</a>
</MenuItem>
</DropdownItem>
</AppsMenu>
</xpath>
</t>
@ -35,16 +35,11 @@
class="dropdown-toggle"
title="Home Menu"
data-hotkey="a"
t-on-click.stop="setState(!state.open)"
t-on-click.stop="() => this.setOpenState(!state.open,true)"
>
<i class="fa fa-th-large" />
</button>
<div
t-if="state.open"
class="dropdown-menu"
style="top: 46px; left: 0px;"
t-transition="fade"
>
<div t-if="state.open" class="dropdown-menu-custom">
<t t-slot="default" />
</div>
</div>
@ -70,17 +65,31 @@
/>
</div>
<div t-if="state.results.length" class="search-results">
<t t-foreach="state.results" t-as="result">
<t t-foreach="state.results" t-as="result" t-key="result">
<t t-set="menu" t-value="_menuInfo(result)" />
<a
t-attf-class="search-result {{result_index == state.offset ? 'highlight' : ''}}"
t-att-style="menu.webIconData ? &quot;background-image:url('data:image/png;base64,&quot; + menu.webIconData + &quot;')&quot; : ''"
t-att-style="menu.webIconData ? &quot;background-image:url(&quot; + menu.webIconData + &quot;);background-size:4%&quot; : ''"
t-attf-href="#menu_id={{menu.id}}&amp;action={{menu.actionID}}"
t-att-data-menu-id="menu.id"
t-att-data-action-id="menu.actionID"
draggable="false"
t-esc="result"
/>
>
<span class="text-ellipsis" t-att-title="result.name">
<t
t-foreach="_splitName(result)"
t-as="name"
t-key="name_index"
>
<b
t-if="name_index % 2"
t-out="name"
style="text-primary"
/>
<t t-else="" t-out="name" />
</t>
</span>
</a>
</t>
</div>
</div>

View File

@ -1,11 +1,12 @@
/** @odoo-module **/
/* Copyright 2021 ITerra - Sergey Shebanin
* Copyright 2023 Onestein - Anjeel Haria
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
import {AttachmentViewer} from "@mail/components/attachment_viewer/attachment_viewer";
import {patch} from "web.utils";
const {useState} = owl.hooks;
import {registerPatch} from "@mail/model/model_core";
const {useState} = owl;
// Patch attachment viewer to add min/max buttons capability
patch(AttachmentViewer.prototype, "web_responsive.AttachmentViewer", {
@ -15,8 +16,22 @@ patch(AttachmentViewer.prototype, "web_responsive.AttachmentViewer", {
maximized: false,
});
},
// Disable auto-close to allow to use form in edit mode.
isCloseable() {
return false;
});
registerPatch({
name: "Dialog",
fields: {
isCloseable: {
compute() {
if (this.attachmentViewer) {
/**
* Prevent closing the dialog when clicking on the mask when the user is
* currently dragging the image.
*/
return false;
}
return this._super();
},
},
},
});

View File

@ -3,10 +3,12 @@
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
// Attachment Viewer
.o_web_client.o_chatter_position_sided .o_DialogManager_dialog {
.o_web_client .o_DialogManager_dialog {
/* Show sided viewer on large screens */
@include media-breakpoint-up(lg) {
position: static;
@media (min-width: 1533px) {
&:not(:has(.o_AttachmentDeleteConfirm)) {
position: static;
}
.o_AttachmentViewer_main {
padding-bottom: 20px;
}
@ -22,7 +24,7 @@
width: $chatter_zone_width;
&.o_AttachmentViewer_maximized {
width: 100%;
width: 100% !important;
}
/* Show/Hide control buttons (next, prev, etc..) */
@ -39,18 +41,21 @@
}
}
}
@include media-breakpoint-down(md) {
@media (max-width: 1533px) {
.o_AttachmentViewer_headerItemButtonMinimize,
.o_AttachmentViewer_headerItemButtonMaximize {
display: none;
display: none !important;
}
}
}
/* Attachment Viewer Max/Min buttons only are useful in sided mode */
.o_web_client:not(.o_chatter_position_sided) {
.o_FormRenderer_chatterContainer:not(.o-aside) {
.o_AttachmentViewer_headerItemButtonMinimize,
.o_AttachmentViewer_headerItemButtonMaximize {
display: none;
display: none !important;
}
}
.o_apps_menu_opened .o_AttachmentViewer {
display: none !important;
}

View File

@ -16,8 +16,8 @@
>
<div
t-if="!state.maximized"
class="o_AttachmentViewer_headerItem o_AttachmentViewer_headerItemButton o_AttachmentViewer_headerItemButtonMaximize"
t-on-click="state.maximized=true"
class="o_AttachmentViewer_headerItem o_AttachmentViewer_headerItemButton o_AttachmentViewer_headerItemButtonMaximize d-flex align-items-center mb-0 px-3 h4 text-reset cursor-pointer"
t-on-click="() => { state.maximized = true }"
role="button"
title="Maximize"
aria-label="Maximize"
@ -26,8 +26,8 @@
</div>
<div
t-if="state.maximized"
class="o_AttachmentViewer_headerItem o_AttachmentViewer_headerItemButton o_AttachmentViewer_headerItemButtonMinimize"
t-on-click="state.maximized=false"
class="o_AttachmentViewer_headerItem o_AttachmentViewer_headerItemButton o_AttachmentViewer_headerItemButtonMinimize d-flex align-items-center mb-0 px-3 h4 text-reset cursor-pointer"
t-on-click="() => { state.maximized = false }"
role="button"
title="Minimize"
aria-label="Minimize"

View File

@ -0,0 +1,15 @@
/** @odoo-module **/
/* Copyright 2023 Onestein - Anjeel Haria
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
import {ChatterTopbar} from "@mail/components/chatter_topbar/chatter_topbar";
import {deviceContext} from "@web_responsive/components/ui_context.esm";
import {patch} from "web.utils";
// Patch chatter topbar to add ui device context
patch(ChatterTopbar.prototype, "web_responsive.ChatterTopbar", {
setup() {
this._super();
this.ui = deviceContext;
},
});

View File

@ -0,0 +1,223 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright 2023 Onestein - Anjeel Haria
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-->
<templates xml:space="preserve">
<!-- Modifying the ChatterTopBar for Mobile View -->
<t
t-name="web.Responsivemail.ChatterTopbar"
t-inherit="mail.ChatterTopbar"
owl="1"
t-inherit-mode="extension"
>
<xpath expr="//div[contains(@class, 'o_ChatterTopbar')]" position="replace">
<t t-if="ui.isSmall">
<div
class="o_ChatterTopbar_rightSection d-flex border-bottom"
style="max-height:45%"
>
<button
t-if="chatterTopbar.chatter.thread.allAttachments.length === 0"
class="o_ChatterTopbar_button o_ChatterTopbar_buttonAddAttachments btn btn-light btn-primary"
type="button"
t-att-disabled="!chatterTopbar.chatter.isTemporary and !chatterTopbar.chatter.hasWriteAccess"
t-on-click="chatterTopbar.chatter.onClickButtonAddAttachments"
style="width:41%"
>
<i
class="fa fa-paperclip fa-lg me-1"
role="img"
aria-label="Attachments"
/>
<t t-if="chatterTopbar.chatter.isShowingAttachmentsLoading">
<i
class="o_ChatterTopbar_buttonAttachmentsCountLoader fa fa-circle-o-notch fa-spin"
aria-label="Attachment counter loading..."
/>
</t>
</button>
<button
t-if="chatterTopbar.chatter.thread.allAttachments.length > 0"
class="o_ChatterTopbar_button o_ChatterTopbar_buttonToggleAttachments btn btn-light btn-primary"
type="button"
t-att-disabled="!chatterTopbar.chatter.isTemporary and !chatterTopbar.chatter.hasReadAccess"
t-att-aria-expanded="chatterTopbar.chatter.attachmentBoxView ? 'true' : 'false'"
t-on-click="chatterTopbar.chatter.onClickButtonToggleAttachments"
style="width:41%"
>
<i
class="fa fa-paperclip fa-lg me-1"
role="img"
aria-label="Attachments"
/>
<t t-if="!chatterTopbar.chatter.isShowingAttachmentsLoading">
<span
class="o_ChatterTopbar_buttonCount o_ChatterTopbar_buttonAttachmentsCount"
t-esc="chatterTopbar.attachmentButtonText"
/>
</t>
<t t-if="chatterTopbar.chatter.isShowingAttachmentsLoading">
<i
class="o_ChatterTopbar_buttonAttachmentsCountLoader fa fa-circle-o-notch fa-spin"
aria-label="Attachment counter loading..."
/>
</t>
</button>
<t
t-if="chatterTopbar.chatter.hasFollowers and chatterTopbar.chatter.thread"
>
<FollowerListMenu
className="'o_ChatterTopbar_followerListMenu w-26'"
record="chatterTopbar.chatter.followerListMenuView"
/>
<t t-if="chatterTopbar.chatter.followButtonView">
<FollowButton
className="'o_ChatterTopbar_followButton'"
record="chatterTopbar.chatter.followButtonView"
/>
</t>
</t>
</div>
</t>
<div
class="o_ChatterTopbar justify-content-between d-flex"
t-attf-class="{{ className }}"
t-ref="root"
>
<div
class="o_ChatterTopbar_actions flex-fill d-flex border-transparent"
>
<div
class="o_ChatterTopbar_controllers d-flex pe-2"
t-if="chatterTopbar.chatter.threadView"
>
<button
class="o_ChatterTopbar_button o_ChatterTopbar_buttonSendMessage btn text-nowrap me-2"
type="button"
t-att-class="{
'o-active btn-odoo': chatterTopbar.chatter.composerView and !chatterTopbar.chatter.composerView.composer.isLog,
'btn-odoo': !chatterTopbar.chatter.composerView,
'btn-light': chatterTopbar.chatter.composerView and chatterTopbar.chatter.composerView.composer.isLog,
}"
t-att-disabled="!chatterTopbar.chatter.isTemporary and !chatterTopbar.chatter.hasWriteAccess"
data-hotkey="m"
t-on-click="chatterTopbar.chatter.onClickSendMessage"
>
Send message
</button>
<button
class="o_ChatterTopbar_button o_ChatterTopbar_buttonLogNote btn text-nowrap"
type="button"
t-att-class="{
'o-active btn-odoo': chatterTopbar.chatter.composerView and chatterTopbar.chatter.composerView.composer.isLog,
'btn-light': chatterTopbar.chatter.composerView and !chatterTopbar.chatter.composerView.composer.isLog or !chatterTopbar.chatter.composerView,
}"
t-att-disabled="!chatterTopbar.chatter.isTemporary and !chatterTopbar.chatter.hasWriteAccess"
t-on-click="chatterTopbar.chatter.onClickLogNote"
data-hotkey="shift+m"
>
Log note
</button>
</div>
<div
class="o_ChatterTopbar_tools position-relative d-flex flex-grow-1 border-bottom"
t-att-class="{
'border-start ps-2': chatterTopbar.chatter.hasActivities,
}"
>
<t t-if="chatterTopbar.chatter.hasActivities">
<button
class="o_ChatterTopbar_button o_ChatterTopbar_buttonScheduleActivity btn btn-light text-nowrap"
type="button"
t-att-disabled="!chatterTopbar.chatter.isTemporary and !chatterTopbar.chatter.hasWriteAccess"
t-on-click="chatterTopbar.chatter.onClickScheduleActivity"
data-hotkey="shift+a"
>
<i class="fa fa-clock-o me-1" />
<span>Activities</span>
</button>
</t>
<div
class="flex-grow-1 border-start pe-2"
t-att-class="{
'ms-2': chatterTopbar.chatter.hasActivities,
}"
/>
<t t-if="!ui.isSmall">
<div
class="o_ChatterTopbar_rightSection flex-grow-1 flex-shrink-0 justify-content-end d-flex"
>
<button
t-if="chatterTopbar.chatter.thread.allAttachments.length === 0"
class="o_ChatterTopbar_button o_ChatterTopbar_buttonAddAttachments btn btn-light btn-primary"
type="button"
t-att-disabled="!chatterTopbar.chatter.isTemporary and !chatterTopbar.chatter.hasWriteAccess"
t-on-click="chatterTopbar.chatter.onClickButtonAddAttachments"
>
<i
class="fa fa-paperclip fa-lg me-1"
role="img"
aria-label="Attachments"
/>
<t
t-if="chatterTopbar.chatter.isShowingAttachmentsLoading"
>
<i
class="o_ChatterTopbar_buttonAttachmentsCountLoader fa fa-circle-o-notch fa-spin"
aria-label="Attachment counter loading..."
/>
</t>
</button>
<button
t-if="chatterTopbar.chatter.thread.allAttachments.length > 0"
class="o_ChatterTopbar_button o_ChatterTopbar_buttonToggleAttachments btn btn-light btn-primary"
type="button"
t-att-disabled="!chatterTopbar.chatter.isTemporary and !chatterTopbar.chatter.hasReadAccess"
t-att-aria-expanded="chatterTopbar.chatter.attachmentBoxView ? 'true' : 'false'"
t-on-click="chatterTopbar.chatter.onClickButtonToggleAttachments"
>
<i
class="fa fa-paperclip fa-lg me-1"
role="img"
aria-label="Attachments"
/>
<t
t-if="!chatterTopbar.chatter.isShowingAttachmentsLoading"
>
<span
class="o_ChatterTopbar_buttonCount o_ChatterTopbar_buttonAttachmentsCount"
t-esc="chatterTopbar.attachmentButtonText"
/>
</t>
<t
t-if="chatterTopbar.chatter.isShowingAttachmentsLoading"
>
<i
class="o_ChatterTopbar_buttonAttachmentsCountLoader fa fa-circle-o-notch fa-spin"
aria-label="Attachment counter loading..."
/>
</t>
</button>
<t
t-if="chatterTopbar.chatter.hasFollowers and chatterTopbar.chatter.thread"
>
<FollowerListMenu
className="'o_ChatterTopbar_followerListMenu'"
record="chatterTopbar.chatter.followerListMenuView"
/>
<t t-if="chatterTopbar.chatter.followButtonView">
<FollowButton
className="'o_ChatterTopbar_followButton'"
record="chatterTopbar.chatter.followButtonView"
/>
</t>
</t>
</div>
</t>
</div>
</div>
</div>
</xpath>
</t>
</templates>

View File

@ -1,14 +1,15 @@
/** @odoo-module **/
/* Copyright 2021 ITerra - Sergey Shebanin
* Copyright 2023 Onestein - Anjeel Haria
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
import LegacyControlPanel from "web.ControlPanel";
import {ControlPanel} from "@web/search/control_panel/control_panel";
import {SearchBar} from "@web/search/search_bar/search_bar";
import {deviceContext} from "@web_responsive/components/ui_context.esm";
import {patch} from "web.utils";
import {Dropdown} from "@web/core/dropdown/dropdown";
const {useState, useContext} = owl.hooks;
const {useState} = owl;
// In v15.0 there are two ControlPanel's. They are mostly the same and are used in legacy and new owl views.
// We extend them two mostly the same way.
@ -20,7 +21,7 @@ patch(LegacyControlPanel.prototype, "web_responsive.LegacyControlPanelMobile", {
this.state = useState({
mobileSearchMode: this.props.withBreadcrumbs ? "" : "quick",
});
this.ui = useContext(deviceContext);
this.ui = deviceContext;
},
setMobileSearchMode(ev) {
this.state.mobileSearchMode = ev.detail;
@ -34,12 +35,11 @@ patch(ControlPanel.prototype, "web_responsive.ControlPanelMobile", {
this.state = useState({
mobileSearchMode: "",
});
this.ui = useContext(deviceContext);
this.ui = deviceContext;
},
setMobileSearchMode(ev) {
this.state.mobileSearchMode = ev.detail;
},
});
patch(SearchBar, "web_responsive.SearchBarMobile", {
template: "web_responsive.SearchBar",
});
Object.assign(LegacyControlPanel.components, {Dropdown});

View File

@ -19,12 +19,12 @@
}
// For FULL HD devices
@media (min-width: 1900px) {
.o_action_manager & .o_cp_top_left,
.o_action_manager & .o_cp_bottom_left {
.o_cp_top_left,
.o_cp_bottom_left {
width: 60%;
}
.o_action_manager & .o_cp_top_right,
.o_action_manager & .o_cp_bottom_right {
.o_cp_top_right,
.o_cp_bottom_right {
width: 40%;
}
}
@ -42,9 +42,6 @@
// It doesn't work on iOS Safari, but it looks similar as
// without this patch. With this patch it looks better for
// other browsers.
position: sticky;
left: 0;
z-index: 3;
// Arrange buttons to use space better
.o_cp_top_left,
@ -53,24 +50,24 @@
}
.o_cp_top_left {
flex-basis: 80%;
max-width: 80%;
flex-basis: 89%;
max-width: 89%;
}
.o_cp_top_right {
flex-basis: 20%;
flex-basis: 11%;
}
.o_cp_bottom {
position: relative; // Necessary for dropdown menu positioning
display: block;
margin: 0;
min-height: 30px !important;
}
.o_cp_bottom_left {
float: left;
margin: 5px 0;
max-width: 80%;
}
.o_cp_bottom_right {
@ -102,7 +99,6 @@
.dropdown-toggle {
margin: 0px 2px;
height: 100%;
padding-right: 0.5rem !important;
}
.dropdown {
height: 100%;
@ -230,16 +226,18 @@
z-index: $zindex-modal;
overflow: auto;
.o_mobile_search_header {
height: 46px;
margin-bottom: 10px;
background-color: var(--mobileSearch__header-bg, #{$o-brand-odoo});
display: flex;
min-height: $o-navbar-height;
justify-content: space-between;
width: 100%;
background-color: $o-brand-odoo;
color: white;
span:active {
background-color: darken($o-brand-primary, 10%);
}
span {
cursor: pointer;
.o_mobile_search_button {
color: white;
&:active {
background-color: darken($o-brand-primary, 10%);
}
}
}
.o_searchview_input_container {
@ -266,10 +264,10 @@
line-height: 2rem;
width: 100%;
margin: 15px 5px 0px 5px;
border: solid 1px darken(gray("200"), 20%);
border: solid 1px darken($gray-200, 20%);
}
.dropdown.show > .dropdown-toggle {
background-color: gray("200");
background-color: $gray-200;
}
.dropdown-toggle {
width: 100%;
@ -294,7 +292,7 @@
max-height: 100%;
box-shadow: none;
border: none;
color: gray("600");
color: $gray-600;
.divider {
margin: 0px;
}

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Copyright 2021 Sergey Shebanin
Copyright 2023 Onestein - Anjeel Haria
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<templates>
<!-- Legacy control panel templates -->
@ -43,7 +44,7 @@
t-att-class="state.mobileSearchMode == 'quick' ? 'o_searchview_quick' : 'o_searchview_mobile'"
role="search"
aria-autocomplete="list"
t-on-click.self="state.mobileSearchMode = ui.isSmall ? 'quick' : ''"
t-on-click.self="() => { state.mobileSearchMode = ui.isSmall ? 'quick' : '' }"
>
<t t-if="!ui.isSmall">
<i
@ -59,12 +60,12 @@
<button
t-if="props.withBreadcrumbs"
class="btn btn-link fa fa-arrow-left"
t-on-click.stop="state.mobileSearchMode = ''"
t-on-click.stop="() => { state.mobileSearchMode = '' }"
/>
<SearchBar fields="fields" />
<button
class="btn fa fa-filter"
t-on-click.stop="state.mobileSearchMode = 'full'"
t-on-click.stop="() => { state.mobileSearchMode = 'full' }"
/>
</t>
<t
@ -74,7 +75,7 @@
<t t-if="state.mobileSearchMode == ''">
<button
class="btn btn-link fa fa-search"
t-on-click.stop="state.mobileSearchMode = 'quick'"
t-on-click.stop="() => { state.mobileSearchMode = 'quick' }"
/>
</t>
</t>
@ -97,19 +98,21 @@
<t t-name="web_responsive.LegacyMobileSearchView" owl="1">
<div class="o_cp_mobile_search">
<div class="o_mobile_search_header">
<span
class="o_mobile_search_close float-left mt16 mb16 mr8 ml16"
t-on-click.stop="state.mobileSearchMode = 'quick'"
<button
type="button"
class="o_mobile_search_button btn"
t-on-click="() => state.mobileSearchMode = false"
>
<i class="fa fa-arrow-left" />
<strong class="float-right ml8">FILTER</strong>
</span>
<span
class="float-right o_mobile_search_clear_facets mt16 mr16"
t-on-click.stop="model.dispatch('clearQuery')"
<strong class="ms-2">FILTER</strong>
</button>
<button
type="button"
class="o_mobile_search_button btn"
t-on-click="() => this.model.dispatch('clearQuery')"
>
<t>CLEAR</t>
</span>
CLEAR
</button>
</div>
<SearchBar fields="fields" />
<div class="o_mobile_search_filter o_search_options mb8 mt8 ml16 mr16">
@ -134,91 +137,13 @@
</div>
<div
class="btn btn-primary o_mobile_search_show_result fixed-bottom"
t-on-click.stop="state.mobileSearchMode = (props.withBreadcrumbs ? '' : 'quick')"
t-on-click="() => { state.mobileSearchMode = (props.withBreadcrumbs ? '' : 'quick') }"
>
<t>SEE RESULT</t>
</div>
</div>
</t>
<!-- Wowl control panel templates -->
<t t-inherit="web.ControlPanel" t-inherit-mode="extension" owl="1">
<xpath expr="//nav[hasclass('o_cp_switch_buttons')]" position="replace">
<t t-if="ui.size lt= ui.SIZES.LG">
<t
t-set="view"
t-value="env.config.viewSwitcherEntries.find((v) => v.active)"
/>
<Dropdown
position="'bottom-end'"
menuClass="'d-inline-flex o_cp_switch_buttons'"
togglerClass="'btn btn-link'"
>
<t t-set-slot="toggler">
<i
class="fa fa-lg o_switch_view"
t-attf-class="o_{{view.type}} {{view.icon}} active"
/>
</t>
<t
t-foreach="env.config.viewSwitcherEntries"
t-as="view"
t-key="view.type"
>
<button
class="btn btn-light fa o_switch_view"
t-attf-class="o_{{view.type}} {{view.icon}} {{view.active ? 'active' : ''}}"
t-att-data-tooltip="view.name"
t-on-click="onViewClicked(view.type)"
/>
</t>
</Dropdown>
</t>
<t t-else="">
<nav class="btn-group o_cp_switch_buttons">
<t
t-foreach="env.config.viewSwitcherEntries"
t-as="view"
t-key="view.type"
>
<button
class="btn btn-light fa fa-lg o_switch_view "
t-attf-class="o_{{view.type}} {{view.icon}} {{view.active ? 'active' : ''}}"
t-att-data-tooltip="view.name"
t-on-click="onViewClicked(view.type)"
/>
</t>
</nav>
</t>
</xpath>
<xpath expr="//SearchBar" position="replace">
<!-- This duplication is hack because owl has a bug https://github.com/odoo/owl/issues/949 -->
<SearchBar
t-if="state.mobileSearchMode == 'quick'"
mobileSearchMode="state.mobileSearchMode"
searchMenus="searchMenus"
t-on-set-mobile-view.stop="setMobileSearchMode"
/>
<SearchBar
t-else=""
mobileSearchMode="state.mobileSearchMode"
searchMenus="searchMenus"
t-on-set-mobile-view.stop="setMobileSearchMode"
/>
</xpath>
<xpath expr="//div[hasclass('o_cp_top_left')]" position="attributes">
<attribute
name="t-att-class"
t-translation="off"
>ui.isSmall and state.mobileSearchMode == 'quick' ? 'o_hidden' : ''</attribute>
</xpath>
<xpath expr="//div[hasclass('o_search_options')]" position="attributes">
<attribute name="t-if" t-translation="off">!ui.isSmall</attribute>
<attribute
name="t-att-class"
t-translation="off"
>ui.size == ui.SIZES.MD ? 'o_search_options_hide_labels' : ''</attribute>
</xpath>
</t>
<t t-name="web_responsive.SearchBar" owl="1">
<div>
<t t-if="!env.isSmall" t-call="web.SearchBar" />
@ -228,7 +153,7 @@
<button
t-if="props.withBreadcrumbs"
class="btn btn-link fa fa-arrow-left"
t-on-click.stop="trigger('set-mobile-view', '')"
t-on-click.stop="() => this.trigger('set-mobile-view', '')"
/>
<div class="o_searchview_input_container">
<t t-call="web.SearchBar.Facets" />
@ -239,7 +164,7 @@
</div>
<button
class="btn fa fa-filter"
t-on-click.stop="trigger('set-mobile-view', 'full')"
t-on-click.stop="() => this.trigger('set-mobile-view', 'full')"
/>
</div>
</t>
@ -252,7 +177,7 @@
class="o_searchview o_searchview_mobile"
role="search"
aria-autocomplete="list"
t-on-click.stop="trigger('set-mobile-view', 'quick')"
t-on-click.stop="() => this.trigger('set-mobile-view', 'quick')"
>
<button class="btn btn-link fa fa-search" />
</div>
@ -266,14 +191,14 @@
<div class="o_mobile_search_header">
<span
class="o_mobile_search_close float-left mt16 mb16 mr8 ml16"
t-on-click.stop="trigger('set-mobile-view', 'quick')"
t-on-click.stop="() => this.trigger('set-mobile-view', 'quick')"
>
<i class="fa fa-arrow-left" />
<strong class="float-right ml8">FILTER</strong>
</span>
<span
class="float-right o_mobile_search_clear_facets mt16 mr16"
t-on-click.stop="env.searchModel.clearQuery()"
t-on-click.stop="() => env.searchModel.clearQuery()"
>
<t>CLEAR</t>
</span>
@ -292,7 +217,7 @@
</div>
<div
class="btn btn-primary o_mobile_search_show_result fixed-bottom"
t-on-click.stop="trigger('set-mobile-view', '')"
t-on-click.stop="() => this.trigger('set-mobile-view', '')"
>
<t>SEE RESULT</t>
</div>

View File

@ -36,36 +36,4 @@
>props.withAccessKey ? 'x' : false</attribute>
</xpath>
</t>
<t t-inherit="web.UserMenu.shortcutsTable" t-inherit-mode="extension" owl="1">
<xpath expr="//div[hasclass('row')]" position="attributes">
<attribute name="class" separator=" " add="justify-content-center" />
</xpath>
<xpath expr="//div[hasclass('row')]/div" position="attributes">
<attribute name="class" />
</xpath>
<xpath expr="//span[text()='a']" position="replace">
<span class="o_key">e</span>
</xpath>
<xpath expr="//span[text()='a']" position="replace">
<span class="o_key">e</span>
</xpath>
<xpath expr="//span[text()='j']" position="replace">
<span class="o_key">d</span>
</xpath>
<xpath expr="//span[text()='j']" position="replace">
<span class="o_key">d</span>
</xpath>
<xpath expr="//span[text()='p']" position="replace">
<span class="o_key">z</span>
</xpath>
<xpath expr="//span[text()='p']" position="replace">
<span class="o_key">z</span>
</xpath>
<xpath expr="//span[text()='n']" position="replace">
<span class="o_key">x</span>
</xpath>
<xpath expr="//span[text()='n']" position="replace">
<span class="o_key">x</span>
</xpath>
</t>
</templates>

View File

@ -1,53 +0,0 @@
/* Copyright 2018 Tecnativa - Jairo Llopis
* Copyright 2021 ITerra - Sergey Shebanin
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
// Main navbar (with systray items: user menu, debug menu...)
@include media-breakpoint-down(sm) {
.o_main_navbar {
// Hide big things
.o_menu_brand,
.oe_topbar_name {
display: none;
}
// Collapse sections menu to hamburger
.o_menu_sections {
width: 46px;
}
.o_menu_sections_more {
.dropdown-toggle {
font-size: 17px;
}
.fa-plus:before {
content: "\f0c9";
}
}
// User menu paddings
.o_usr_menu {
margin: 0;
}
// Custom fullscreen layout when showing submenus
.dropdown-menu {
@include full-screen-dropdown();
background-color: $dropdown-bg;
overflow: auto;
// Higher height for dropdown items, for those with sausage fingers
.dropdown-item {
padding: {
bottom: 0.5rem;
top: 1rem;
}
font-size: 16px;
a {
display: block;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
}

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Copyright 2021 ITerra - Sergey Shebanin
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<templates>
<t t-inherit="web.NavBar" t-inherit-mode="extension" owl="1">
<xpath expr="//t[@t-call='web.NavBar.SectionsMenu']" position="attributes">
<attribute
name="t-if"
t-translation="off"
>currentAppSections.length</attribute>
</xpath>
</t>
</templates>

View File

@ -6,14 +6,12 @@ import SearchPanel from "@web/legacy/js/views/search_panel";
import {deviceContext} from "@web_responsive/components/ui_context.esm";
import {patch} from "web.utils";
const {useContext} = owl.hooks;
// Patch search panel to add functionality for mobile view
patch(SearchPanel.prototype, "web_responsive.SearchPanelMobile", {
setup() {
this._super();
this.state.mobileSearch = false;
this.ui = useContext(deviceContext);
this.ui = deviceContext;
},
getActiveSummary() {
const selection = [];

View File

@ -54,7 +54,7 @@
.o_dropdown {
width: 100%;
margin: 15px 5px 0px 5px;
border: solid 1px darken(gray("200"), 20%);
border: solid 1px darken($gray-200, 20%);
}
.o_dropdown_toggler_btn {
width: 100%;
@ -78,7 +78,7 @@
transform: translate3d(0, 0, 0) !important;
box-shadow: none;
border: none;
color: gray("600");
color: $gray-600;
.divider {
margin: 0px;
}

View File

@ -7,7 +7,7 @@
<div
t-if="ui.isSmall"
class="o_search_panel_summary"
t-on-click.stop="state.mobileSearch = true"
t-on-click.stop="() => this.state.mobileSearch = true"
>
<div class="d-flex flex-wrap align-items-center">
<i class="fa fa-fw fa-filter mr-1" />

View File

@ -1,5 +1,6 @@
/** @odoo-module **/
/* Copyright 2021 ITerra - Sergey Shebanin
* Copyright 2023 Onestein - Anjeel Haria
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
import {registry} from "@web/core/registry";
@ -7,7 +8,7 @@ import {debounce} from "@web/core/utils/timing";
import config from "web.config";
import core from "web.core";
const {Context} = owl;
import Context from "web.Context";
// Legacy variant
// TODO: remove when legacy code will dropped from odoo
@ -16,7 +17,7 @@ export const deviceContext = new Context({
isSmall: config.device.isMobile,
size: config.device.size_class,
SIZES: config.device.SIZES,
});
}).eval();
// New wowl variant
// TODO: use default odoo device context when it will be realized
@ -26,7 +27,7 @@ const uiContextService = {
window.addEventListener(
"resize",
debounce(() => {
const state = deviceContext.state;
const state = deviceContext;
if (state.size !== ui.size) {
state.size = ui.size;
}

View File

@ -1,569 +0,0 @@
/* Copyright 2019 Odoo S.A.
* Copyright 2021 ITerra - Sergey Shebanin
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
odoo.define("web_responsive.KanbanRendererMobile", function (require) {
"use strict";
/**
* The purpose of this file is to improve the UX of grouped kanban views in
* mobile. It includes the KanbanRenderer (in mobile only) to only display one
* column full width, and enables the swipe to browse to the other columns.
* Moreover, records in columns are lazy-loaded.
*/
const config = require("web.config");
const core = require("web.core");
const KanbanRenderer = require("web.KanbanRenderer");
const KanbanView = require("web.KanbanView");
const KanbanQuickCreate = require("web.kanban_column_quick_create");
const _t = core._t;
const qweb = core.qweb;
KanbanQuickCreate.include({
init() {
this._super.apply(this, arguments);
this.isMobile = config.device.isMobile;
},
/**
* KanbanRenderer will decide can we close quick create or not
* @private
* @override
*/
_cancel: function () {
if (config.device.isMobile) {
this.trigger_up("close_quick_create");
}
},
/**
* Clear input when showed
* @override
*/
toggleFold: function () {
this._super.apply(this, arguments);
if (config.device.isMobile && !this.folded) {
this.$input.val("");
}
},
});
KanbanView.include({
init() {
this._super.apply(this, arguments);
this.jsLibs.push("/web/static/lib/jquery.touchSwipe/jquery.touchSwipe.js");
},
});
KanbanRenderer.include({
custom_events: _.extend({}, KanbanRenderer.prototype.custom_events || {}, {
quick_create_column_created: "_onColumnAdded",
}),
events: _.extend({}, KanbanRenderer.prototype.events, {
"click .o_kanban_mobile_tab": "_onMobileTabClicked",
"click .o_kanban_mobile_add_column": "_onMobileQuickCreateClicked",
}),
ANIMATE: true, // Allows to disable animations for the tests
/**
* @override
*/
init: function () {
this._super.apply(this, arguments);
this.activeColumnIndex = 0; // Index of the currently displayed column
this._scrollPosition = null;
},
/**
* As this renderer defines its own scrolling area (the column in grouped
* mode), we override this hook to restore the scroll position like it was
* when the renderer has been last detached.
*
* @override
*/
on_attach_callback: function () {
if (config.device.isMobile) {
if (
this._scrollPosition &&
this.state.groupedBy.length &&
this.widgets.length
) {
const $column = this.widgets[this.activeColumnIndex].$el;
$column.scrollLeft(this._scrollPosition.left);
$column.scrollTop(this._scrollPosition.top);
}
this._computeTabPosition();
}
this._super.apply(this, arguments);
core.bus.on("UI_CONTEXT:IS_SMALL_CHANGED", this, () => {
this.widgets = [];
this.columnOptions.recordsDraggable =
!config.device.isMobile &&
this.columnOptions.originRecordsDraggable;
this._renderView();
});
},
/**
* As this renderer defines its own scrolling area (the column in grouped
* mode), we override this hook to store the scroll position, so that we can
* restore it if the renderer is re-attached to the DOM later.
*
* @override
*/
on_detach_callback: function () {
if (this.state.groupedBy.length && this.widgets.length) {
const $column = this.widgets[this.activeColumnIndex].$el;
this._scrollPosition = {
left: $column.scrollLeft(),
top: $column.scrollTop(),
};
} else {
this._scrollPosition = null;
}
core.bus.off("UI_CONTEXT:IS_SMALL_CHANGED", this);
this._super.apply(this, arguments);
},
// --------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
/**
* Displays the quick create record in the active column
* override to open quick create record in current active column
*
* @override
* @returns {Promise}
*/
addQuickCreate: function () {
if (config.device.isMobile) {
if (
this._canCreateColumn() &&
this.quickCreate &&
!this.quickCreate.folded
) {
this._onMobileQuickCreateClicked();
}
return this.widgets[this.activeColumnIndex].addQuickCreate();
}
return this._super.apply(this, arguments);
},
/**
* Overrides to restore the left property and the scrollTop on the updated
* column, and to enable the swipe handlers
*
* @override
*/
updateColumn: function (localID) {
if (config.device.isMobile) {
const index = _.findIndex(this.widgets, {db_id: localID});
const $column = this.widgets[index].$el;
const scrollTop = $column.scrollTop();
return (
this._super
.apply(this, arguments)
.then(() => this._layoutUpdate(false))
// Required when clicking on 'Load More'
.then(() => $column.scrollTop(scrollTop))
.then(() => this._enableSwipe())
);
}
return this._super.apply(this, arguments);
},
// --------------------------------------------------------------------------
// Private
// --------------------------------------------------------------------------
/**
* Avoid drag'n'drop of kanban records on mobile and let the way to swipe
* @private
*/
_setState: function () {
const res = this._super.apply(this, arguments);
this.columnOptions.originRecordsDraggable =
this.columnOptions.recordsDraggable;
this.columnOptions.recordsDraggable =
!config.device.isMobile && this.columnOptions.recordsDraggable;
return res;
},
/**
* Check if we use the quick create on mobile
* @returns {Boolean}
* @private
*/
_canCreateColumn: function () {
return this.quickCreateEnabled && this.quickCreate && this.widgets.length;
},
/**
* Update the columns positions
*
* @private
* @param {Boolean} [animate=false] set to true to animate
*/
_computeColumnPosition: function (animate) {
if (this.widgets.length) {
// Check rtl to compute correct css value
const rtl = _t.database.parameters.direction === "rtl";
// Display all o_kanban_group
this.$(".o_kanban_group").show();
const $columnAfter = this._toNode(
this.widgets.filter(
(widget, index) => index > this.activeColumnIndex
)
);
const promiseAfter = this._updateColumnCss(
$columnAfter,
rtl ? {right: "100%"} : {left: "100%"},
animate
);
const $columnBefore = this._toNode(
this.widgets.filter(
(widget, index) => index < this.activeColumnIndex
)
);
const promiseBefore = this._updateColumnCss(
$columnBefore,
rtl ? {right: "-100%"} : {left: "-100%"},
animate
);
const $columnCurrent = this._toNode(
this.widgets.filter(
(widget, index) => index === this.activeColumnIndex
)
);
const promiseCurrent = this._updateColumnCss(
$columnCurrent,
rtl ? {right: "0%"} : {left: "0%"},
animate
);
promiseAfter
.then(promiseBefore)
.then(promiseCurrent)
.then(() => {
$columnAfter.hide();
$columnBefore.hide();
});
}
},
/**
* Define the o_current class to the current selected kanban (column & tab)
*
* @private
*/
_computeCurrentColumn: function () {
if (this.widgets.length) {
const column = this.widgets[this.activeColumnIndex];
if (!column) {
return;
}
const columnID = column.id || column.db_id;
this.$(
".o_kanban_mobile_tab.o_current, .o_kanban_group.o_current"
).removeClass("o_current");
this.$(
'.o_kanban_group[data-id="' +
columnID +
'"], ' +
'.o_kanban_mobile_tab[data-id="' +
columnID +
'"]'
).addClass("o_current");
}
},
/**
* Update the tabs positions
*
* @private
*/
_computeTabPosition: function () {
this._computeTabJustification();
this._computeTabScrollPosition();
},
/**
* Update the tabs positions
*
* @private
*/
_computeTabScrollPosition: function () {
if (this.widgets.length) {
const lastItemIndex = this.widgets.length - 1;
const moveToIndex = this.activeColumnIndex;
let scrollToLeft = 0;
for (let i = 0; i < moveToIndex; i++) {
const columnWidth = this._getTabWidth(this.widgets[i]);
// Apply
if (moveToIndex !== lastItemIndex && i === moveToIndex - 1) {
const partialWidth = 0.75;
scrollToLeft += columnWidth * partialWidth;
} else {
scrollToLeft += columnWidth;
}
}
// Apply the scroll x on the tabs
// XXX in case of RTL, should we use scrollRight?
this.$(".o_kanban_mobile_tabs").scrollLeft(scrollToLeft);
}
},
/**
* Compute the justify content of the kanban tab headers
*
* @private
*/
_computeTabJustification: function () {
if (this.widgets.length) {
// Use to compute the sum of the width of all tab
const widthChilds = this.widgets.reduce((total, column) => {
return total + this._getTabWidth(column);
}, 0);
// Apply a space around between child if the parent length is higher then the sum of the child width
const $tabs = this.$(".o_kanban_mobile_tabs");
$tabs.toggleClass(
"justify-content-between",
$tabs.outerWidth() >= widthChilds
);
}
},
/**
* Enables swipe event on the current column
*
* @private
*/
_enableSwipe: function () {
const step = _t.database.parameters.direction === "rtl" ? -1 : 1;
this.$el.swipe({
excludedElements: ".o_kanban_mobile_tabs",
swipeLeft: () => {
if (!config.device.isMobile) {
return;
}
const moveToIndex = this.activeColumnIndex + step;
if (moveToIndex < this.widgets.length) {
this._moveToGroup(moveToIndex, this.ANIMATE);
}
},
swipeRight: () => {
if (!config.device.isMobile) {
return;
}
const moveToIndex = this.activeColumnIndex - step;
if (moveToIndex > -1) {
this._moveToGroup(moveToIndex, this.ANIMATE);
}
},
});
},
/**
* Retrieve the outerWidth of a given widget column
*
* @param {KanbanColumn} column
* @returns {integer} outerWidth of the found column
* @private
*/
_getTabWidth: function (column) {
const columnID = column.id || column.db_id;
return this.$(
'.o_kanban_mobile_tab[data-id="' + columnID + '"]'
).outerWidth();
},
/**
* Update the kanban layout
*
* @private
* @param {Boolean} [animate=false] set to true to animate
*/
_layoutUpdate: function (animate) {
this._computeCurrentColumn();
this._computeTabPosition();
this._computeColumnPosition(animate);
this._enableSwipe();
},
/**
* Moves to the given kanban column
*
* @private
* @param {integer} moveToIndex index of the column to move to
* @param {Boolean} [animate=false] set to true to animate
* @returns {Promise} resolved when the new current group has been loaded
* and displayed
*/
_moveToGroup: function (moveToIndex, animate) {
if (this.widgets.length === 0) {
return Promise.resolve();
}
if (moveToIndex >= 0 && moveToIndex < this.widgets.length) {
this.activeColumnIndex = moveToIndex;
}
const column = this.widgets[this.activeColumnIndex];
this._enableSwipe();
if (!column.data.isOpen) {
this.trigger_up("column_toggle_fold", {
db_id: column.db_id,
onSuccess: () => this._layoutUpdate(animate),
});
} else {
this._layoutUpdate(animate);
}
return Promise.resolve();
},
/**
* @override
* @private
*/
_renderExampleBackground: function () {
// Override to avoid display of example background
if (!config.device.isMobile) {
this._super.apply(this, arguments);
}
},
/**
* @override
* @private
*/
_renderGrouped: function (fragment) {
if (config.device.isMobile) {
const newFragment = document.createDocumentFragment();
this._super.apply(this, [newFragment]);
this.defs.push(
Promise.all(this.defs).then(() => {
const data = [];
_.each(this.state.data, function (group) {
if (!group.value) {
group = _.extend({}, group, {value: _t("Undefined")});
data.unshift(group);
} else {
data.push(group);
}
});
const kanbanColumnContainer = document.createElement("div");
kanbanColumnContainer.classList.add("o_kanban_columns_content");
kanbanColumnContainer.appendChild(newFragment);
fragment.appendChild(kanbanColumnContainer);
$(
qweb.render("KanbanView.MobileTabs", {
data: data,
quickCreateEnabled: this._canCreateColumn(),
})
).prependTo(fragment);
})
);
} else {
this._super.apply(this, arguments);
}
},
/**
* @override
* @private
*/
_renderView: function () {
const def = this._super.apply(this, arguments);
if (!config.device.isMobile) {
return def;
}
return def.then(() => {
if (this.state.groupedBy.length) {
// Force first column for kanban view, because the groupedBy can be changed
return this._moveToGroup(0);
}
if (this._canCreateColumn()) {
this._onMobileQuickCreateClicked();
}
return Promise.resolve();
});
},
/**
* Retrieve the Jquery node (.o_kanban_group) for a list of a given widgets
*
* @private
* @param widgets
* @returns {jQuery} the matching .o_kanban_group widgets
*/
_toNode: function (widgets) {
const selectorCss = widgets
.map(
(widget) =>
'.o_kanban_group[data-id="' + (widget.id || widget.db_id) + '"]'
)
.join(", ");
return this.$(selectorCss);
},
/**
* Update the given column to the updated positions
*
* @private
* @param $column The jquery column
* @param cssProperties Use to update column
* @param {Boolean} [animate=false] set to true to animate
* @returns {Promise}
*/
_updateColumnCss: function ($column, cssProperties, animate) {
if (animate) {
return new Promise((resolve) =>
$column.animate(cssProperties, "fast", resolve)
);
}
$column.css(cssProperties);
return Promise.resolve();
},
// --------------------------------------------------------------------------
// Handlers
// --------------------------------------------------------------------------
/**
* @private
*/
_onColumnAdded: function () {
this._computeTabPosition();
if (this._canCreateColumn() && !this.quickCreate.folded) {
this.quickCreate.toggleFold();
}
},
/**
* @private
*/
_onMobileQuickCreateClicked: function (event) {
if (event) {
event.stopPropagation();
}
this.quickCreate.toggleFold();
this.$(".o_kanban_group").toggle(this.quickCreate.folded);
},
/**
* @private
* @param {MouseEvent} event
*/
_onMobileTabClicked: function (event) {
if (this._canCreateColumn() && !this.quickCreate.folded) {
this.quickCreate.toggleFold();
}
this._moveToGroup($(event.currentTarget).index(), true);
},
/**
* @private
* @override
*/
_onCloseQuickCreate: function () {
if (this.widgets.length && this.quickCreate && !this.quickCreate.folded) {
this.$(".o_kanban_group").toggle(true);
this.quickCreate.toggleFold();
}
},
});
});

View File

@ -1,19 +1,9 @@
/* Copyright 2018 Tecnativa - Jairo Llopis
* Copyright 2021 ITerra - Sergey Shebanin
* Copyright 2023 Onestein - Anjeel Haria
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
odoo.define("web_responsive", function (require) {
odoo.define("web_responsive", function () {
"use strict";
const config = require("web.config");
const core = require("web.core");
const FormRenderer = require("web.FormRenderer");
const RelationalFields = require("web.relational_fields");
const ViewDialogs = require("web.view_dialogs");
const ListRenderer = require("web.ListRenderer");
const CalendarRenderer = require("web.CalendarRenderer");
const _t = core._t;
// Fix for iOS Safari to set correct viewport height
// https://github.com/Faisal-Manzer/postcss-viewport-height-correction
function setViewportProperty(doc) {
@ -29,202 +19,4 @@ odoo.define("web_responsive", function (require) {
"resize",
_.debounce(setViewportProperty(document.documentElement), 100)
);
RelationalFields.FieldStatus.include({
/**
* Fold all on mobiles.
*
* @override
*/
_setState: function () {
this._super.apply(this, arguments);
if (config.device.isMobile) {
_.map(this.status_information, (value) => {
value.fold = true;
});
}
},
});
// Sticky Column Selector
ListRenderer.include({
_renderView: function () {
return this._super.apply(this, arguments).then(() => {
const $col_selector = this.$el.find(
".o_optional_columns_dropdown_toggle"
);
if ($col_selector.length !== 0) {
const $th = this.$el.find("thead>tr:first>th:last");
$col_selector.appendTo($th);
}
});
},
_onToggleOptionalColumnDropdown: function (ev) {
// FIXME: For some strange reason the 'stopPropagation' call
// in the main method don't work. Invoking here the same method
// does the expected behavior... O_O!
// This prevents the action of sorting the column from being
// launched.
ev.stopPropagation();
this._super.apply(this, arguments);
},
});
// Responsive view "action" buttons
FormRenderer.include({
/**
* @override
*/
on_attach_callback: function () {
this._super.apply(this, arguments);
core.bus.on("UI_CONTEXT:IS_SMALL_CHANGED", this, () => {
this._applyFormSizeClass();
this._render();
});
},
/**
* @override
*/
on_detach_callback: function () {
core.bus.off("UI_CONTEXT:IS_SMALL_CHANGED", this);
this._super.apply(this, arguments);
},
/**
* In mobiles, put all statusbar buttons in a dropdown.
*
* @override
*/
_renderHeaderButtons: function () {
const $buttons = this._super.apply(this, arguments);
if (
!config.device.isMobile ||
$buttons.children("button:not(.o_invisible_modifier)").length <= 2
) {
return $buttons;
}
// $buttons must be appended by JS because all events are bound
const $dropdown = $(
core.qweb.render("web_responsive.MenuStatusbarButtons")
);
$buttons.addClass("dropdown-menu").appendTo($dropdown);
return $dropdown;
},
});
/**
* Directly open popup dialog in mobile for search.
*/
RelationalFields.FieldMany2One.include({
start: function () {
var superRes = this._super.apply(this, arguments);
if (config.device.isMobile) {
this.$input.prop("readonly", true);
}
return superRes;
},
// --------------------------------------------------------------------------
// Private
// --------------------------------------------------------------------------
/**
* @private
* @override
*/
_bindAutoComplete: function () {
if (!config.device.isMobile) {
return this._super.apply(this, arguments);
}
},
/**
* @private
* @override
*/
_getSearchCreatePopupOptions: function () {
const options = this._super.apply(this, arguments);
_.extend(options, {
on_clear: () => this.reinitialize(false),
});
return options;
},
/**
* @private
* @override
*/
_toggleAutoComplete: function () {
if (config.device.isMobile) {
this._searchCreatePopup("search");
} else {
return this._super.apply(this, arguments);
}
},
});
/**
* Support for Clear button in search popup.
*/
ViewDialogs.SelectCreateDialog.include({
init: function () {
this._super.apply(this, arguments);
if (config.device.isMobile) {
this.viewType = "kanban";
}
this.on_clear =
this.options.on_clear ||
function () {
return undefined;
};
},
/**
* @override
*/
_prepareButtons: function () {
this._super.apply(this, arguments);
if (config.device.isMobile && this.options.disable_multiple_selection) {
this.__buttons.push({
text: _t("Clear"),
classes: "btn-secondary o_clear_button",
close: true,
click: function () {
this.on_clear();
},
});
}
},
});
CalendarRenderer.include({
/**
* @override
*/
on_attach_callback: function () {
this._super.apply(this, arguments);
core.bus.on("UI_CONTEXT:IS_SMALL_CHANGED", this, () => {
// Hack to force calendar to reload their options and rerender
this.calendar.setOption("locale", moment.locale());
});
},
/**
* @override
*/
on_detach_callback: function () {
core.bus.off("UI_CONTEXT:IS_SMALL_CHANGED", this);
this._super.apply(this, arguments);
},
/**
* @override
*/
_getFullCalendarOptions: function () {
const options = this._super.apply(this, arguments);
Object.defineProperty(options.views.dayGridMonth, "columnHeaderFormat", {
get() {
return config.device.isMobile ? "ddd" : "dddd";
},
});
return options;
},
});
});

View File

@ -1,52 +0,0 @@
/* Copyright 2021 Sergey Shebanin
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
odoo.define("web_responsive.website_apps_menu", function (require) {
"use strict";
/*
* We can't require anything from website here because `web_responsive` doesn't depend on `website`.
* In this case we will try to get WebsiteNavbar class through the PublicRoot registry.
* WebsiteNavbar can be unavailable if `website` is not installed. In this case this file do nothing.
*/
const publicRoot = require("root.widget");
const lazyloader = require("web.public.lazyloader");
const registry = publicRoot._getRegistry();
function patchNavbar() {
const navbar = registry.get("WebsiteNavbar", false);
if (!navbar) return false;
navbar.Widget.include({
/**
* We don't need to load app menus
* @override
*/
async _loadAppMenus() {
return Promise.resolve();
},
/**
* We add a spinner for the user to understand the loading
* @override
*/
_onOeApplicationsShow: function () {
const icon = $(
document.querySelector("#oe_main_menu_navbar a.full > i")
);
icon.removeClass("fa fa-th-large").append(
$("<span/>", {class: "fa fa-spin fa-spinner"})
);
window.location.href = "/web#home";
// Prevent dropdown to be showed
return false;
},
});
const menu = $("#oe_applications");
menu.addClass("o_responsive_loaded").after(
"<span class='o_menu_brand'>" + menu.find("a.full").text() + "</span>"
);
return true;
}
// Try to patch navbar. If it is not in registry - make another try after lazyload
if (!patchNavbar()) {
lazyloader.allScriptsLoaded.then(patchNavbar);
}
});

View File

@ -1,113 +0,0 @@
/* Copyright 2019 Odoo S.A.
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
@include media-breakpoint-down(sm) {
.o_kanban_view {
width: 100%;
}
.o_kanban_view.o_kanban_grouped {
display: block;
position: relative;
overflow-x: hidden;
&.o_renderer_with_searchpanel {
width: 100%;
}
.o_kanban_mobile_tabs_container {
position: sticky;
display: flex;
justify-content: space-between;
width: 100%;
top: 0;
z-index: 1;
background-color: #5e5e5e;
.o_kanban_mobile_add_column {
height: $o-kanban-mobile-tabs-height;
padding: 10px;
border-left: grey 1px solid;
color: white;
font-size: 14px;
}
.o_kanban_mobile_tabs {
position: relative;
display: flex;
width: 100%;
height: $o-kanban-mobile-tabs-height;
overflow-x: auto;
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
.o_kanban_mobile_tab {
height: $o-kanban-mobile-tabs-height;
padding: 10px 20px;
font-size: 14px;
color: white;
&.o_current {
font-weight: bold;
border-bottom: 3px solid $o-brand-primary;
background-color: gray("600");
font-weight: bold;
}
.o_column_title {
white-space: nowrap;
text-transform: uppercase;
}
}
}
}
.o_kanban_columns_content {
position: relative;
}
// [class] to get same specificity as elsewhere (kanban_view.less)
&[class] .o_kanban_group:not(.o_column_folded) {
@include o-position-absolute(
$top: $o-kanban-mobile-tabs-height,
$left: 0,
$bottom: 0
);
width: 100%;
padding: 0;
margin-left: 0; // override the margin-left: -1px of the desktop mode
border: none;
&.o_current {
position: inherit;
top: 0;
&.o_kanban_no_records {
// set a default height for clarity when embedded in another view
min-height: $o-kanban-mobile-empty-height;
}
}
.o_kanban_header {
display: none;
}
.o_kanban_record,
.o_kanban_quick_create {
border: none;
border-bottom: 1px solid lightgray;
padding: 10px 16px;
margin: 0;
}
}
}
.modal {
z-index: 1052;
}
.o_kanban_view .o_column_quick_create {
.o_quick_create_folded {
display: none !important;
}
.o_quick_create_unfolded {
width: 100%;
}
}
}

View File

@ -1,8 +1,9 @@
/* Copyright 2018 Tecnativa - Jairo Llopis
* Copyright 2021 ITerra - Sergey Shebanin
* Copyright 2023 Onestein - Anjeel Haria
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
$chatter_zone_width: 35%;
$chatter_zone_width: 35% !important;
// Support for long titles
@include media-breakpoint-up(md) {
@ -19,10 +20,9 @@ html .o_web_client .o_action_manager .o_action {
overflow: auto;
.o_content {
overflow: visible;
overflow: visible !important;
}
}
max-width: 100%;
}
@ -78,43 +78,16 @@ html .o_web_client .o_action_manager .o_action {
box-shadow: inset 0 -5px #7c7bad;
}
}
}
}
// Size of labels
.o_web_client {
&.o_chatter_position_sided {
.o_action_manager {
.o_content,
.modal-content {
@include media-breakpoint-up(xl, $o-extra-grid-breakpoints) {
.o_inner_group {
.o_td_label {
min-width: 260px !important;
}
}
}
@include media-breakpoint-between(lg, xl, $o-extra-grid-breakpoints) {
.o_group_col_6 {
width: 100% !important;
}
}
}
.settings > .app_settings_block .o_settings_container {
padding-left: 0;
padding-right: 0;
}
}
&:not(.o_chatter_position_sided) {
@include media-breakpoint-up(lg, $o-extra-grid-breakpoints) {
.o_action_manager {
.o_content,
.modal-content {
.o_inner_group {
.o_td_label {
min-width: 260px !important;
}
}
}
}
}
.o_kanban_view .o_control_panel .o_cp_bottom_right .o_cp_pager .btn-group {
top: -1px;
}
.o_kanban_renderer {
width: 100%;
}
}
@ -124,13 +97,13 @@ html .o_web_client .o_action_manager .o_action {
max-width: 100%;
// Form views
.o_form_view {
.o_form_editable {
.o_form_sheet {
max-width: calc(100% - 32px);
max-width: calc(100% - 32px) !important;
overflow-x: auto;
}
.o_td_label .o_form_label:not(.o_status):not(.o_calendar_invitation) {
.o_cell .o_form_label:not(.o_status):not(.o_calendar_invitation) {
min-height: 23px;
@include media-breakpoint-up(md) {
margin-bottom: 10px;
@ -139,10 +112,14 @@ html .o_web_client .o_action_manager .o_action {
.o_horizontal_separator {
font-size: 14px;
}
// Some UX improvements for form in edit mode
@include media-breakpoint-down(sm) {
.o_field_widget {
vertical-align: middle;
.nav-item {
border-bottom: 1px solid #dee2e6;
}
.nav-tabs {
border-bottom: none;
}
&.o_form_editable .o_field_widget {
&:not(.o_stat_info):not(.o_readonly_modifier):not(.oe_form_field_html):not(.o_field_image) {
@ -153,13 +130,13 @@ html .o_web_client .o_action_manager .o_action {
}
&.o_field_float_percentage,
&.o_field_monetary,
&.o_field_many2manytags,
&.o_field_many2many_selection,
.o_field_many2one_selection {
align-items: center;
}
.o_field_many2one_selection .o_input_dropdown,
&.o_datepicker,
&.o_field_partner_autocomplete {
&.o_partner_autocomplete_info {
input {
min-height: 35px;
}
@ -173,53 +150,34 @@ html .o_web_client .o_action_manager .o_action {
right: 6px;
bottom: auto;
}
}
}
.o_FormRenderer_chatterContainer {
padding-top: 0;
.o_Activity_info {
flex-wrap: wrap;
}
.o_ActivityBox_title {
margin-bottom: 0;
}
.o_MessageList_separatorDate {
padding-bottom: 0;
}
}
// Sided chatter scrolling behavior
.o_Chatter {
height: fit-content;
.o_Chatter_fixedPanel {
position: sticky;
top: 0;
z-index: 1;
background-color: white;
padding-bottom: 10px;
}
.o_Chatter_scrollPanel {
overflow: initial;
.o_field_many2many_selection .o_dropdown_button {
top: 0 !important;
}
}
}
// Sticky statusbar
.o_form_statusbar {
position: sticky;
position: sticky !important;
top: 0;
z-index: 2;
}
// Support for long title (with ellipsis)
.oe_title {
span.o_field_widget:not(.oe_inline) {
max-width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
width: initial;
&:active {
white-space: normal;
.o_field_widget:not(.oe_inline) {
display: block;
span {
display: block;
max-width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
width: initial;
&:active {
white-space: normal;
}
}
}
}
@ -231,16 +189,11 @@ html .o_web_client .o_action_manager .o_action {
.oe_button_box {
.o_dropdown_more {
button:last-child {
border-right: 1px solid gray("400");
border-right: 1px solid $gray-400 !important;
}
}
}
// Avoid overflow on forms with title and/or button box
.oe_title {
max-width: 100%;
}
.oe_button_box + .oe_title,
.oe_button_box + .oe_avatar + .oe_title {
width: 100%;
@ -257,53 +210,11 @@ html .o_web_client .o_action_manager .o_action {
width: auto !important;
}
// Make all input groups vertical
.o_group_col_6,
.o_group_col_8 {
width: 100%;
}
// Statusbar buttons dropdown for mobiles
.o_statusbar_buttons_dropdown {
border: {
bottom: 0;
radius: 0;
top: 0;
}
height: 100%;
}
.o_statusbar_buttons.dropdown-menu {
.btn {
border-radius: 0;
border: 0;
width: 100%;
margin-bottom: 0.2rem;
white-space: nowrap;
@include media-breakpoint-down(xs) {
max-width: 80vw;
overflow: hidden;
text-overflow: ellipsis;
}
&:last-child {
margin-bottom: 0;
}
}
}
.o_statusbar_status {
// Arrow from rightmost button exceeds allowed width
.o_arrow_button:first-child::before {
content: none;
display: none;
}
}
// Full width in form sheets
.o_form_sheet,
.o_FormRenderer_chatterContainer {
min-width: auto;
max-width: 98%;
max-width: 98% !important;
}
// Settings pages
@ -320,7 +231,7 @@ html .o_web_client .o_action_manager .o_action {
.o_Chatter_composer {
&.o-has-current-partner-avatar {
grid-template-columns: 0px 1fr;
padding: 1rem 1rem 1.5rem 1rem;
padding: 1rem 1rem 1.5rem 1rem !important;
}
.o_Composer_sidebarMain {
@ -343,71 +254,26 @@ html .o_web_client .o_action_manager .o_action {
display: none;
}
}
// Sided chatter, if user wants
.o_chatter_position_sided & {
@include media-breakpoint-up(lg) {
.o_form_view:not(.o_form_nosheet) {
display: flex;
flex-flow: row nowrap;
height: 100%;
.o_form_sheet_bg {
flex: 1 1 auto;
overflow: auto;
> .o_form_sheet {
min-width: unset;
}
}
.o_FormRenderer_chatterContainer {
border-left: 1px solid gray("400");
flex: 0 0 $chatter_zone_width;
max-width: initial;
min-width: initial;
overflow: auto;
.o_chatter_header_container {
padding-top: $grid-gutter-width * 0.5;
top: 0;
position: sticky;
background-color: $o-view-background-color;
z-index: 1;
// Scrollable input text to avoid hide conversation & buttons
.o_composer_text_field {
max-height: 120px;
overflow-y: auto !important; /* Forced because Odoo uses inline style */
}
.o_attachments_list {
overflow: auto;
max-height: $o-mail-attachment-image-size * 3;
margin-top: 0.4em;
}
.o_attachments_previews {
overflow: auto;
max-height: $o-mail-attachment-image-size * 6;
}
}
}
}
}
}
}
// Sticky Header & Footer in List View
.o_list_view {
.table-responsive {
overflow: visible;
.o_list_table {
// th & td are here for compatibility with chrome
thead tr:nth-child(1) th {
position: sticky;
position: sticky !important;
top: 0;
z-index: 1;
z-index: 999;
}
thead tr:nth-child(1) th {
background-color: $o-list-footer-bg-color;
background-color: var(--ListRenderer-thead-bg-color);
border-top: none !important;
border-bottom: none !important;
border-left: transparent;
box-shadow: inset 0 0 0 $o-community-color,
inset 0 -1px 0 $o-community-color;
}
tfoot,
tfoot tr:nth-child(1) td {
@ -416,6 +282,15 @@ html .o_web_client .o_action_manager .o_action {
}
tfoot tr:nth-child(1) td {
background-color: $o-list-footer-bg-color;
border-top: none !important;
border-bottom: none !important;
box-shadow: inset 0 1px 0 $o-community-color,
inset 0 0 0 $o-community-color;
}
}
.table {
thead tr:nth-child(1) th {
z-index: 1;
}
}
}
@ -423,14 +298,39 @@ html .o_web_client .o_action_manager .o_action {
// Big checkboxes
.o_list_view,
.o_settings_container .o_setting_box {
.o_setting_container .o_setting_box {
.o_setting_right_pane {
margin-left: 34px;
}
.custom-checkbox:not(.o_boolean_toggle) {
.o-checkbox:not(.o_boolean_toggle) {
margin-right: 10px;
margin-top: -6px;
&.d-inline-block {
display: block !important;
}
.form-check-input {
height: 24px;
width: 24px;
}
}
.o_optional_columns_dropdown {
.o-checkbox {
margin-top: 0;
}
.form-check-input {
height: 1em !important;
width: 1em !important;
}
}
}
.custom-control-label {
.o_setting_container .o_setting_box {
.o_setting_right_pane {
margin-left: 34px;
}
.o-checkbox:not(.o_boolean_toggle) {
margin-right: 10px;
.form-check-label {
&::after {
width: 24px;
height: 24px;
@ -444,18 +344,41 @@ html .o_web_client .o_action_manager .o_action {
}
}
}
.o_list_view {
.custom-checkbox:not(.o_boolean_toggle) {
top: -6px;
.o_chatter_header_container {
padding-top: $grid-gutter-width * 0.5;
top: 0;
position: sticky;
background-color: $o-view-background-color;
z-index: 1;
}
.o_FormRenderer_chatterContainer {
&.o-isInFormSheetBg:not(.o-aside) {
background-color: $white;
&:not(.o-aside) {
width: auto;
border-top: 1px solid $border-color;
}
}
&.o-aside {
flex: 0 0 $chatter_zone_width;
max-width: initial;
min-width: initial;
overflow: auto;
}
}
@include media-breakpoint-down(sm) {
.o_base_settings
.o_setting_container
.settings
> .app_settings_block
.o_settings_container {
padding-left: 0;
padding-right: 0;
body:not(.o_statusbar_buttons) {
.oe-toolbar {
z-index: 0 !important;
}
}
.o_inner_group > .mb-sm-0 {
margin-bottom: 0 !important;
}
.w-26 {
width: 26%;
}

View File

@ -1,35 +0,0 @@
/* Copyright 2021 ITerra - Sergey Shebanin
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
// Website main navbar and his AppsMenu button
#oe_main_menu_navbar {
#oe_applications .dropdown-toggle::after {
display: none;
}
a.full {
> i {
padding: 0 10px 0 0;
font-size: 17px;
}
font-size: 20px;
line-height: 46px;
@include media-breakpoint-down(sm) {
width: 46px;
overflow: hidden;
}
}
#oe_applications.o_responsive_loaded {
a.full {
width: 46px;
overflow: hidden;
}
}
.o_menu_brand {
white-space: nowrap;
padding: 0 16px 0 4px;
text-transform: capitalize;
@include media-breakpoint-down(sm) {
display: none;
}
}
}

View File

@ -4,108 +4,165 @@
Copyright 2018 Alexandre Díaz
Copyright 2018 Tecnativa - Jairo Llopis
Copyright 2021 ITerra - Sergey Shebanin
Copyright 2023 Onestein - Anjeel Haria
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-->
<templates id="form_view" xml:space="preserve">
<!-- Template for buttons that display only the icon in xs -->
<t t-name="web_responsive.icon_button">
<t t-name="web_responsive.icon_button" owl="1">
<i t-attf-class="fa fa-#{icon}" t-att-title="label" />
<span class="d-none d-sm-inline" t-esc="label" />
</t>
<t t-name="web_responsive.MenuStatusbarButtons">
<div class="dropdown">
<t
t-name="web.ResponsiveFormView.Buttons"
t-inherit="web.FormView.Buttons"
owl="1"
t-inherit-mode="extension"
>
<!-- Change "Discard" button hotkey to "D" -->
<xpath
expr="//button[contains(@class, 'o_form_button_cancel')]"
position="attributes"
>
<attribute name="data-hotkey">d</attribute>
</xpath>
<xpath
expr="//button[contains(@class, 'o_form_button_create')]"
position="replace"
>
<button
class="o_statusbar_buttons_dropdown btn btn-secondary dropdown-toggle"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
class="btn btn-secondary o_form_button_create"
data-hotkey="c"
t-on-click.stop="create"
>
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'cogs'" />
<t t-set="label">Quick actions</t>
<t t-set="icon" t-value="'plus'" />
<t t-set="label"> Create</t>
</t>
</button>
<!-- A div.o_statusbar_buttons.dropdown-menu
is appended here from JS -->
</div>
</xpath>
</t>
<t t-extend="FormView.buttons">
<!-- Change "Edit" button hotkey to "E" -->
<t t-jquery=".o_form_button_edit" t-operation="attributes">
<attribute name="accesskey">e</attribute>
</t>
<t
t-name="web.ResponsiveFormView"
t-inherit="web.FormView"
owl="1"
t-inherit-mode="extension"
>
<xpath
expr="//button[contains(@class, 'o_form_button_create')]"
position="replace"
>
<button
type="button"
class="btn btn-outline-primary o_form_button_create"
data-hotkey="c"
t-on-click.stop="create"
t-att-disabled="state.isDisabled"
>
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'plus'" />
<t t-set="label"> Create</t>
</t>
</button>
</xpath>
</t>
<t
t-name="web.ResponsiveFormStatusIndicator"
t-inherit="web.FormStatusIndicator"
owl="1"
>
<!-- Change "Discard" button hotkey to "D" -->
<t t-jquery=".o_form_button_cancel" t-operation="attributes">
<attribute name="accesskey">d</attribute>
</t>
<!-- Add responsive icons to buttons -->
<t t-jquery=".o_form_button_edit" t-operation="inner">
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'pencil'" />
<t t-set="label">Edit</t>
</t>
</t>
<t t-jquery=".o_form_button_create" t-operation="inner">
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'plus'" />
<t t-set="label">Create</t>
</t>
</t>
<t t-jquery=".o_form_button_save" t-operation="inner">
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'check'" />
<t t-set="label">Save</t>
</t>
</t>
<t t-jquery=".o_form_button_cancel" t-operation="inner">
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'times'" />
<t t-set="label">Discard</t>
</t>
</t>
<xpath
expr="//button[contains(@class, 'o_form_button_cancel')]"
position="attributes"
>
<attribute name="data-hotkey">d</attribute>
</xpath>
</t>
<t t-extend="KanbanView.buttons">
<t
t-name="web.ResponsiveKanbanView.Buttons"
t-inherit="web.KanbanView.Buttons"
owl="1"
t-inherit-mode="extension"
>
<!-- Add responsive icons to buttons -->
<t t-jquery="button" t-operation="inner">
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'plus'" />
<t t-set="label" t-value="create_text || _t('Create')" />
</t>
</t>
<xpath
expr="//button[contains(@class, 'o-kanban-button-new')]"
position="replace"
>
<button
type="button"
class="btn btn-primary o-kanban-button-new"
accesskey="c"
t-on-click="() => this.createRecord(null)"
data-bounce-button=""
>
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'plus'" />
<t t-set="label"> Create</t>
</t>
</button>
</xpath>
</t>
<t t-extend="ListView.buttons">
<!-- Change "Discard" button hotkey to "D" -->
<t t-jquery=".o_list_button_discard" t-operation="attributes">
<attribute name="accesskey">d</attribute>
</t>
<t
t-name="web.ResponsiveListView.Buttons"
t-inherit="web.ListView.Buttons"
owl="1"
t-inherit-mode="extension"
>
<!-- Add responsive icons to buttons -->
<t t-jquery=".o_list_button_add" t-operation="inner">
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'plus'" />
<t t-set="label">Create</t>
</t>
</t>
<t t-jquery=".o_list_button_save" t-operation="inner">
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'check'" />
<t t-set="label">Save</t>
</t>
</t>
<t t-jquery=".o_list_button_discard" t-operation="inner">
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'times'" />
<t t-set="label">Discard</t>
</t>
</t>
</t>
<t t-extend="CalendarView.navigation_buttons">
<!-- Add responsive icons to buttons -->
<t t-jquery=".o_calendar_button_today" t-operation="inner">
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'calendar-check-o'" />
<t t-set="label">Today</t>
</t>
</t>
<xpath
expr="//button[contains(@class, 'o_list_button_add')]"
position="replace"
>
<button
type="button"
class="btn btn-primary o_list_button_add"
data-hotkey="c"
t-on-click="onClickCreate"
data-bounce-button=""
>
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'plus'" />
<t t-set="label"> Create</t>
</t>
</button>
</xpath>
<xpath
expr="//button[contains(@class, 'o_list_button_save')]"
position="replace"
>
<button
type="button"
class="btn btn-primary o_list_button_save"
data-hotkey="s"
t-on-click.stop="onClickSave"
>
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'check'" />
<t t-set="label"> Save</t>
</t>
</button>
</xpath>
<xpath
expr="//button[contains(@class, 'o_list_button_discard')]"
position="replace"
>
<button
type="button"
class="btn btn-secondary o_list_button_discard"
data-hotkey="d"
t-on-click="onClickDiscard"
t-on-mousedown="onMouseDownDiscard"
>
<t t-call="web_responsive.icon_button">
<t t-set="icon" t-value="'times'" />
<t t-set="label"> Discard</t>
</t>
</button>
</xpath>
</t>
</templates>

View File

@ -0,0 +1,14 @@
/** @odoo-module */
/* Copyright 2023 Onestein - Anjeel Haria
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
import {patch} from "@web/core/utils/patch";
import {FormController} from "@web/views/form/form_controller";
// Patch FormController to always load attachment alongwith the chatter on the side bar
patch(FormController.prototype, "web_responsive.FormController", {
setup() {
this._super();
this.hasAttachmentViewerInArch = false;
},
});

View File

@ -1 +0,0 @@
from . import test_res_users

View File

@ -1,14 +0,0 @@
# Copyright 2018 Alexandre Díaz
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo.tests import common
class TestResUsers(common.TransactionCase):
def test_chatter_position_wr(self):
user_public = self.env.ref("base.public_user")
user_public = user_public.with_user(user_public)
self.assertEqual(user_public.chatter_position, "sided")
user_public.write({"chatter_position": "normal"})
self.assertEqual(user_public.chatter_position, "normal")

View File

@ -1,17 +0,0 @@
<?xml version="1.0" ?>
<!--
Copyright 2018
@author Alexanre Díaz <dev@redneboa.es>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-->
<odoo>
<record id="view_users_form_simple_modif" model="ir.ui.view">
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form_simple_modif" />
<field name="arch" type="xml">
<xpath expr="//field[@name='email']" position="after">
<field name="chatter_position" readonly="0" />
</xpath>
</field>
</record>
</odoo>

View File

@ -2,22 +2,10 @@
<!--
Copyright 2018 Alexandre Díaz
Copyright 2021 ITerra - Sergey Shebanin
Copyright 2023 Onestein - Anjeel Haria
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-->
<odoo>
<template
id="webclient_bootstrap"
inherit_id="web.webclient_bootstrap"
name="App Drawer - Web Client"
>
<xpath expr="//t[@t-set='body_classname']" position="attributes">
<attribute
name="t-value"
add="+ ' o_chatter_position_' + (request.env.user.chatter_position or 'normal')"
separator=" "
/>
</xpath>
</template>
<template
id="responsive_web_layout"
inherit_id="web.layout"