[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 dialogpull/2405/head
|
@ -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.
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
from . import models
|
|
@ -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",
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
from . import res_users
|
|
@ -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"]
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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 & 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 <<a class="reference external" href="mailto:dave@laslabs.com">dave@laslabs.com</a>></li>
|
||||
<li>Jairo Llopis <<a class="reference external" href="mailto:jairo.llopis@tecnativa.com">jairo.llopis@tecnativa.com</a>></li>
|
||||
<li>Dennis Sluijk <<a class="reference external" href="mailto:d.sluijk@onestein.nl">d.sluijk@onestein.nl</a>></li>
|
||||
<li>Anjeel Haria <<a class="reference external" href="mailto:anjeel_bv@onestein.nl">anjeel_bv@onestein.nl</a>></li>
|
||||
<li>Sergio Teruel <<a class="reference external" href="mailto:sergio.teruel@tecnativa.com">sergio.teruel@tecnativa.com</a>></li>
|
||||
<li>Alexandre Díaz <<a class="reference external" href="mailto:dev@redneboa.es">dev@redneboa.es</a>></li>
|
||||
<li>Mathias Markl <<a class="reference external" href="mailto:mathias.markl@mukit.at">mathias.markl@mukit.at</a>></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>
|
||||
|
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 187 KiB |
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 42 KiB |
|
@ -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});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 ? "background-image:url('data:image/png;base64," + menu.webIconData + "')" : ''"
|
||||
t-att-style="menu.webIconData ? "background-image:url(" + menu.webIconData + ");background-size:4%" : ''"
|
||||
t-attf-href="#menu_id={{menu.id}}&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>
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
});
|
|
@ -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>
|
|
@ -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});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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 = [];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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%;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
});
|
|
@ -1 +0,0 @@
|
|||
from . import test_res_users
|
|
@ -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")
|
|
@ -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>
|
|
@ -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"
|
||||
|
|