diff --git a/web_dark_mode/README.rst b/web_dark_mode/README.rst new file mode 100644 index 000000000000..673f4cbe09e3 --- /dev/null +++ b/web_dark_mode/README.rst @@ -0,0 +1,85 @@ +========= +Dark Mode +========= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:92c42b374159b10469f7a113505b270486a72f4a3bec1552c591c18146e82035 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github + :target: https://github.com/OCA/web/tree/18.0/web_dark_mode + :alt: OCA/web +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/web-18-0/web-18-0-web_dark_mode + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This modules offers the dark mode for Odoo CE. The dark mode can be +activated by every user in the user menu in the top right. + +**Table of contents** + +.. contents:: + :local: + +Known issues / Roadmap +====================== + +- Implement dark mode for PoS with a glue module + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* initOS GmbH + +Contributors +------------ + +- Florian Kantelberg +- `Pyxiris `__ + + - `Liam Noonan `__ + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/web `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/web_dark_mode/__init__.py b/web_dark_mode/__init__.py new file mode 100644 index 000000000000..1d408a8a039e --- /dev/null +++ b/web_dark_mode/__init__.py @@ -0,0 +1,4 @@ +# © 2022 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/web_dark_mode/__manifest__.py b/web_dark_mode/__manifest__.py new file mode 100644 index 000000000000..44ea0acfcf69 --- /dev/null +++ b/web_dark_mode/__manifest__.py @@ -0,0 +1,60 @@ +# © 2022 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Dark Mode", + "summary": "Enabled Dark Mode for the Odoo Backend", + "license": "AGPL-3", + "version": "18.0.1.0.0", + "website": "https://github.com/OCA/web", + "author": "initOS GmbH, Odoo Community Association (OCA)", + "depends": ["web"], + "excludes": ["web_enterprise"], + "installable": True, + "assets": { + "web.assets_backend": [ + "web_dark_mode/static/src/js/switch_item.esm.js", + ], + "web.assets_backend_lazy_dark": [ + ("include", "web.assets_variables_dark"), + ("include", "web.assets_backend_helpers_dark"), + ], + "web.assets_variables_dark": [ + ( + "before", + "web/static/src/scss/primary_variables.scss", + "web_dark_mode/static/src/scss/primary_variables.dark.scss", + ), + ( + "before", + "web/static/src/scss/secondary_variables.scss", + "web_dark_mode/static/src/scss/secondary_variables.dark.scss", + ), + ( + "before", + "web/static/src/**/*.variables.scss", + "web_dark_mode/static/src/**/*.variables.dark.scss", + ), + ], + "web.assets_backend_helpers_dark": [ + ( + "before", + "web/static/src/scss/bootstrap_overridden.scss", + "web_dark_mode/static/src/scss/bootstrap_overridden.dark.scss", + ), + ( + "after", + "web/static/lib/bootstrap/scss/_functions.scss", + "web_dark_mode/static/src/scss/bs_functions_overrides.dark.scss", + ), + ], + "web.assets_web_dark": [ + ("include", "web.assets_variables_dark"), + ("include", "web.assets_backend_helpers_dark"), + "web_dark_mode/static/src/**/*.dark.scss", + ], + }, + "data": [ + "views/res_users_views.xml", + ], +} diff --git a/web_dark_mode/i18n/es.po b/web_dark_mode/i18n/es.po new file mode 100644 index 000000000000..903638c864d5 --- /dev/null +++ b/web_dark_mode/i18n/es.po @@ -0,0 +1,40 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_dark_mode +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-09-02 20:35+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: web_dark_mode +#. odoo-javascript +#: code:addons/web_dark_mode/static/src/js/switch_item.esm.js:0 +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode +#, python-format +msgid "Dark Mode" +msgstr "Modo Oscuro" + +#. module: web_dark_mode +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode_device_dependent +msgid "Device Dependent Dark Mode" +msgstr "Modo Oscuro en Función del Dispositivo" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_ir_http +msgid "HTTP Routing" +msgstr "Enrutamiento HTTP" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_res_users +msgid "User" +msgstr "Usuario" diff --git a/web_dark_mode/i18n/es_AR.po b/web_dark_mode/i18n/es_AR.po new file mode 100644 index 000000000000..1ad2d1f79e73 --- /dev/null +++ b/web_dark_mode/i18n/es_AR.po @@ -0,0 +1,40 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_dark_mode +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-01-01 21:45+0000\n" +"Last-Translator: Ignacio Buioli \n" +"Language-Team: none\n" +"Language: es_AR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.14.1\n" + +#. module: web_dark_mode +#. odoo-javascript +#: code:addons/web_dark_mode/static/src/js/switch_item.esm.js:0 +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode +#, python-format +msgid "Dark Mode" +msgstr "Modo Oscuro" + +#. module: web_dark_mode +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode_device_dependent +msgid "Device Dependent Dark Mode" +msgstr "Dispositivo que Depende del Modo Oscuro" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_ir_http +msgid "HTTP Routing" +msgstr "Ruteo HTTP" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_res_users +msgid "User" +msgstr "Usuario" diff --git a/web_dark_mode/i18n/fr.po b/web_dark_mode/i18n/fr.po new file mode 100644 index 000000000000..3333b16a9660 --- /dev/null +++ b/web_dark_mode/i18n/fr.po @@ -0,0 +1,40 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_dark_mode +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-10-23 18:56+0000\n" +"Last-Translator: Grégory Moka Tourisme \n" +"Language-Team: none\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: web_dark_mode +#. odoo-javascript +#: code:addons/web_dark_mode/static/src/js/switch_item.esm.js:0 +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode +#, python-format +msgid "Dark Mode" +msgstr "Mode sombre" + +#. module: web_dark_mode +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode_device_dependent +msgid "Device Dependent Dark Mode" +msgstr "" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_ir_http +msgid "HTTP Routing" +msgstr "" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_res_users +msgid "User" +msgstr "Utilisateur" diff --git a/web_dark_mode/i18n/hr.po b/web_dark_mode/i18n/hr.po new file mode 100644 index 000000000000..21e8842000f7 --- /dev/null +++ b/web_dark_mode/i18n/hr.po @@ -0,0 +1,41 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_dark_mode +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-02-16 14:23+0000\n" +"Last-Translator: Bole \n" +"Language-Team: none\n" +"Language: hr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" +"X-Generator: Weblate 4.14.1\n" + +#. module: web_dark_mode +#. odoo-javascript +#: code:addons/web_dark_mode/static/src/js/switch_item.esm.js:0 +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode +#, python-format +msgid "Dark Mode" +msgstr "Tamni način" + +#. module: web_dark_mode +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode_device_dependent +msgid "Device Dependent Dark Mode" +msgstr "Tamni način zavisi od uređaja" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_ir_http +msgid "HTTP Routing" +msgstr "HTTP Routing" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_res_users +msgid "User" +msgstr "Korisnik" diff --git a/web_dark_mode/i18n/it.po b/web_dark_mode/i18n/it.po new file mode 100644 index 000000000000..e14a675b293c --- /dev/null +++ b/web_dark_mode/i18n/it.po @@ -0,0 +1,40 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_dark_mode +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-11-27 11:34+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: web_dark_mode +#. odoo-javascript +#: code:addons/web_dark_mode/static/src/js/switch_item.esm.js:0 +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode +#, python-format +msgid "Dark Mode" +msgstr "Dark mode" + +#. module: web_dark_mode +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode_device_dependent +msgid "Device Dependent Dark Mode" +msgstr "Dark mode dipendente dal dispositivo" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_ir_http +msgid "HTTP Routing" +msgstr "Instradamento HTTP" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_res_users +msgid "User" +msgstr "Utente" diff --git a/web_dark_mode/i18n/tr.po b/web_dark_mode/i18n/tr.po new file mode 100644 index 000000000000..0cd019cf46af --- /dev/null +++ b/web_dark_mode/i18n/tr.po @@ -0,0 +1,40 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_dark_mode +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-03-06 20:08+0000\n" +"Last-Translator: Ediz Duman \n" +"Language-Team: none\n" +"Language: tr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.14.1\n" + +#. module: web_dark_mode +#. odoo-javascript +#: code:addons/web_dark_mode/static/src/js/switch_item.esm.js:0 +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode +#, python-format +msgid "Dark Mode" +msgstr "Koyu Mod" + +#. module: web_dark_mode +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode_device_dependent +msgid "Device Dependent Dark Mode" +msgstr "Cihaza Bağlı Karanlık Mod" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_ir_http +msgid "HTTP Routing" +msgstr "HTTP Yönlendirme" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_res_users +msgid "User" +msgstr "Kullanıcı" diff --git a/web_dark_mode/i18n/web_dark_mode.pot b/web_dark_mode/i18n/web_dark_mode.pot new file mode 100644 index 000000000000..f3daced67eea --- /dev/null +++ b/web_dark_mode/i18n/web_dark_mode.pot @@ -0,0 +1,37 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_dark_mode +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: web_dark_mode +#. odoo-javascript +#: code:addons/web_dark_mode/static/src/js/switch_item.esm.js:0 +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode +#, python-format +msgid "Dark Mode" +msgstr "" + +#. module: web_dark_mode +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode_device_dependent +msgid "Device Dependent Dark Mode" +msgstr "" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_ir_http +msgid "HTTP Routing" +msgstr "" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_res_users +msgid "User" +msgstr "" diff --git a/web_dark_mode/i18n/zh.po b/web_dark_mode/i18n/zh.po new file mode 100644 index 000000000000..ad8461437429 --- /dev/null +++ b/web_dark_mode/i18n/zh.po @@ -0,0 +1,38 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * web_dark_mode +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: zh\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#. module: web_dark_mode +#. odoo-javascript +#: code:addons/web_dark_mode/static/src/js/switch_item.esm.js:0 +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode +#, python-format +msgid "Dark Mode" +msgstr "" + +#. module: web_dark_mode +#: model:ir.model.fields,field_description:web_dark_mode.field_res_users__dark_mode_device_dependent +msgid "Device Dependent Dark Mode" +msgstr "" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_ir_http +msgid "HTTP Routing" +msgstr "" + +#. module: web_dark_mode +#: model:ir.model,name:web_dark_mode.model_res_users +msgid "User" +msgstr "" diff --git a/web_dark_mode/models/__init__.py b/web_dark_mode/models/__init__.py new file mode 100644 index 000000000000..d565f59dcc8d --- /dev/null +++ b/web_dark_mode/models/__init__.py @@ -0,0 +1,4 @@ +# © 2022 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import ir_http, res_users diff --git a/web_dark_mode/models/ir_http.py b/web_dark_mode/models/ir_http.py new file mode 100644 index 000000000000..e2c90b53773f --- /dev/null +++ b/web_dark_mode/models/ir_http.py @@ -0,0 +1,23 @@ +# © 2022 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models +from odoo.http import request + + +class IrHttp(models.AbstractModel): + _inherit = "ir.http" + + @classmethod + def _set_color_scheme(cls, response): + scheme = request.httprequest.cookies.get("color_scheme") + user = request.env.user + user_scheme = "dark" if getattr(user, "dark_mode", None) else "light" + device_dependent = getattr(user, "dark_mode_device_dependent", None) + if (not device_dependent) and scheme != user_scheme: + response.set_cookie("color_scheme", user_scheme) + + @classmethod + def _post_dispatch(cls, response): + cls._set_color_scheme(response) + return super()._post_dispatch(response) diff --git a/web_dark_mode/models/res_users.py b/web_dark_mode/models/res_users.py new file mode 100644 index 000000000000..be6fd3ebc05a --- /dev/null +++ b/web_dark_mode/models/res_users.py @@ -0,0 +1,26 @@ +# © 2022 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import fields, models + + +class ResUsers(models.Model): + _inherit = "res.users" + + dark_mode = fields.Boolean() + dark_mode_device_dependent = fields.Boolean("Device Dependent Dark Mode") + + @property + def SELF_READABLE_FIELDS(self): + return super().SELF_READABLE_FIELDS + [ + "dark_mode_device_dependent", + "dark_mode", + ] + + @property + def SELF_WRITEABLE_FIELDS(self): + return super().SELF_WRITEABLE_FIELDS + [ + "dark_mode_device_dependent", + "dark_mode", + ] diff --git a/web_dark_mode/pyproject.toml b/web_dark_mode/pyproject.toml new file mode 100644 index 000000000000..4231d0cccb3d --- /dev/null +++ b/web_dark_mode/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/web_dark_mode/readme/CONTRIBUTORS.md b/web_dark_mode/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..38be6e4844f8 --- /dev/null +++ b/web_dark_mode/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- Florian Kantelberg \ +- [Pyxiris](https://github.com/Pyxiris) + - [Liam Noonan](https://github.com/ljmnoonan) diff --git a/web_dark_mode/readme/DESCRIPTION.md b/web_dark_mode/readme/DESCRIPTION.md new file mode 100644 index 000000000000..7d90c04edb1d --- /dev/null +++ b/web_dark_mode/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This modules offers the dark mode for Odoo CE. The dark mode can be +activated by every user in the user menu in the top right. diff --git a/web_dark_mode/readme/ROADMAP.md b/web_dark_mode/readme/ROADMAP.md new file mode 100644 index 000000000000..b1437e5f398e --- /dev/null +++ b/web_dark_mode/readme/ROADMAP.md @@ -0,0 +1 @@ +- Implement dark mode for PoS with a glue module diff --git a/web_dark_mode/static/description/icon.png b/web_dark_mode/static/description/icon.png new file mode 100644 index 000000000000..3a0328b516c4 Binary files /dev/null and b/web_dark_mode/static/description/icon.png differ diff --git a/web_dark_mode/static/description/index.html b/web_dark_mode/static/description/index.html new file mode 100644 index 000000000000..1d19272e0be5 --- /dev/null +++ b/web_dark_mode/static/description/index.html @@ -0,0 +1,435 @@ + + + + + +Dark Mode + + + +
+

Dark Mode

+ + +

Beta License: AGPL-3 OCA/web Translate me on Weblate Try me on Runboat

+

This modules offers the dark mode for Odoo CE. The dark mode can be +activated by every user in the user menu in the top right.

+

Table of contents

+ +
+

Known issues / Roadmap

+
    +
  • Implement dark mode for PoS with a glue module
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • initOS GmbH
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/web project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/web_dark_mode/static/src/core/colorlist/colorlist.dark.scss b/web_dark_mode/static/src/core/colorlist/colorlist.dark.scss new file mode 100644 index 000000000000..901c78c8e758 --- /dev/null +++ b/web_dark_mode/static/src/core/colorlist/colorlist.dark.scss @@ -0,0 +1,13 @@ +@for $size from 2 through length($o-colors) { + .o_colorlist_item_color_#{$size - 1} { + --background-color: #{mix( + nth($o-colors, $size), + $o-view-background-color, + 95% + )}; + // As far as I can tell this is never used to display text. The only use i found for + // It was around the border of the colorlist color square when clicked. I'm just + // setting it to contrast just in case it ever gets used for text. + --color: #{color-contrast(nth($o-colors, $size))}; + } +} diff --git a/web_dark_mode/static/src/core/notebook/notebook.dark.scss b/web_dark_mode/static/src/core/notebook/notebook.dark.scss new file mode 100644 index 000000000000..63f0dc7ac951 --- /dev/null +++ b/web_dark_mode/static/src/core/notebook/notebook.dark.scss @@ -0,0 +1,5 @@ +.o_notebook { + --notebook-link-border-color: #{$nav-tabs-border-color}; + --notebook-link-border-color-hover: #{$nav-tabs-border-color}; + --notebook-link-border-color-active-accent: #{$o-main-link-color}; +} diff --git a/web_dark_mode/static/src/core/tags_list/tags_list.dark.scss b/web_dark_mode/static/src/core/tags_list/tags_list.dark.scss new file mode 100644 index 000000000000..2b0e6c63f3b0 --- /dev/null +++ b/web_dark_mode/static/src/core/tags_list/tags_list.dark.scss @@ -0,0 +1,18 @@ +.o_tag { + @for $size from 1 through length($o-colors) { + &.o_tag_color_#{$size - 1} { + $-bg: mix(nth($o-colors, $size), $o-view-background-color, 10%); + $-color: scale-color( + nth($o-colors, $size), + $lightness: 65%, + $saturation: 100% + ); + + &, + &::after { + --background-color: #{$-bg}; + --color: #{$-color}; + } + } + } +} diff --git a/web_dark_mode/static/src/js/switch_item.esm.js b/web_dark_mode/static/src/js/switch_item.esm.js new file mode 100644 index 000000000000..63530ae974e9 --- /dev/null +++ b/web_dark_mode/static/src/js/switch_item.esm.js @@ -0,0 +1,50 @@ +/** @odoo-module **/ + +import {_t} from "@web/core/l10n/translation"; +import {browser} from "@web/core/browser/browser"; +import {cookie} from "@web/core/browser/cookie"; +import {registry} from "@web/core/registry"; +import {user} from "@web/core/user"; + +export function darkModeSwitchItem(env) { + return { + type: "switch", + id: "color_scheme.switch", + description: _t("Dark Mode"), + callback: () => { + env.services.color_scheme.switchColorScheme(); + }, + isChecked: cookie.get("color_scheme") === "dark", + sequence: 40, + }; +} + +export const colorSchemeService = { + dependencies: ["orm", "ui"], + + async start(env, {orm, ui}) { + registry.category("user_menuitems").add("darkmode", darkModeSwitchItem); + + if (!cookie.get("color_scheme")) { + const match_media = window.matchMedia("(prefers-color-scheme: dark)"); + const dark_mode = match_media.matches; + cookie.set("color_scheme", dark_mode ? "dark" : "light"); + if (dark_mode) browser.location.reload(); + } + + return { + async switchColorScheme() { + const scheme = cookie.get("color_scheme") === "dark" ? "light" : "dark"; + cookie.set("color_scheme", scheme); + await orm.write("res.users", [user.userId], { + dark_mode: scheme === "dark", + }); + + ui.block(); + browser.location.reload(); + }, + }; + }, +}; + +registry.category("services").add("color_scheme", colorSchemeService); diff --git a/web_dark_mode/static/src/scss/bootstrap_overridden.dark.scss b/web_dark_mode/static/src/scss/bootstrap_overridden.dark.scss new file mode 100644 index 000000000000..17861d9d6fa3 --- /dev/null +++ b/web_dark_mode/static/src/scss/bootstrap_overridden.dark.scss @@ -0,0 +1,37 @@ +// This overrides the overrides in web/static/src/scss/bootstrap_overridden.scss +// and also some additional overrides to web/static/lib/bootstrap/scss/_variables.scss + +// Shadows +$box-shadow: 0 0.5rem 1rem rgba($o-white, 0.4) !default; +$box-shadow-sm: 0 0.125rem 0.25rem rgba($o-white, 0.2) !default; +$box-shadow-lg: 0 1rem 3rem rgba($o-white, 0.4) !default; +$box-shadow-inset: inset 0 1px 2px rgba($o-white, 0.2) !default; + +// Dropdowns +$dropdown-bg: $o-white !default; + +// Navs +$nav-tabs-link-active-bg: transparent !default; +$nav-link-color: $o-main-text-color !default; + +// Tooltips +$tooltip-bg: $o-white !default; +$tooltip-color: $o-main-text-color !default; + +// Modals +$modal-backdrop-bg: $o-white !default; + +// User input typically entered via keyboard +$kbd-color: $o-gray-100 !default; +$kbd-bg: $o-gray-900 !default; +$kbd-box-shadow: + 0px 1px 1px rgba($o-gray-100, 0.2), + inset 0px -1px 1px 1px rgba($o-gray-700, 0.8), + inset 0px 2px 0px 0px rgba($o-black, 0.8) !default; + +// Input +$input-focus-box-shadow: 0 !default; +$input-focus-border-color: $o-enterprise-action-color !default; +$form-check-input-checked-color: $o-white !default; +$form-check-input-checked-border-color: $o-enterprise-action-color !default; +$form-check-input-checked-bg-color: $o-enterprise-action-color !default; diff --git a/web_dark_mode/static/src/scss/bs_functions_overrides.dark.scss b/web_dark_mode/static/src/scss/bs_functions_overrides.dark.scss new file mode 100644 index 000000000000..726cf9aecd20 --- /dev/null +++ b/web_dark_mode/static/src/scss/bs_functions_overrides.dark.scss @@ -0,0 +1,11 @@ +// Inverse the tint and shade functions found in web/static/lib/bootstrap/scss/_functions.scss +// It would make a lot more sense if we could use web/static/lib/bootstrap/scss/_variables-dark.scss +// but odoo has everything hardcoded to the light mode versions of the variables, so we are left +// with this rather hacky way of inverting shades and tints +@function tint-color($color, $weight) { + @return mix(#000000, $color, $weight); +} + +@function shade-color($color, $weight) { + @return mix(#ffffff, $color, $weight); +} diff --git a/web_dark_mode/static/src/scss/primary_variables.dark.scss b/web_dark_mode/static/src/scss/primary_variables.dark.scss new file mode 100644 index 000000000000..ecf7bd291f47 --- /dev/null +++ b/web_dark_mode/static/src/scss/primary_variables.dark.scss @@ -0,0 +1,131 @@ +// This file is just the things in web/static/src/scss/primary_variables.scss +// that need to be overridden for dark mode. Anything that does not need to be +// changed does not make it here. +// These variables will be loaded right before web/static/src/scss/primary_variables.scss +// so any variable defined here will override the default ones. +// The !default flag is included in case any other model needs to override these values again. + +// Color-scheme +$o-webclient-color-scheme: dark !default; + +// Colors +$o-white: #000000 !default; +$o-black: #ffffff !default; + +$o-gray-100: #181818 !default; // L: 9% +$o-gray-200: #1f1f1f !default; // L: 12% +$o-gray-300: #303030 !default; // L: 19% +$o-gray-400: #4a4a4a !default; // L: 29% +$o-gray-500: #696969 !default; // L: 41% +$o-gray-600: #8b8b8b !default; // L: 55% +$o-gray-700: #aeaeae !default; // L: 68% +$o-gray-800: #c0c0c0 !default; // L: 75% +$o-gray-900: #c8c8c8 !default; // L: 78% + +$o-community-color: $o-gray-200 !default; +$o-enterprise-color: #71639e !default; +$o-enterprise-action-color: #00c7c7 !default; + +$o-brand-odoo: $o-enterprise-color !default; +$o-brand-primary: $o-enterprise-color !default; + +// This only has 3 uses. The only one I was able to test is at +// web/static/src/views/form/form_controller.scss which changes form lables +// to this in mobile. Those are usually gray-900. +$o-brand-secondary: $o-gray-900 !default; + +$o-action: $o-enterprise-action-color !default; + +// Font colors +$o-main-link-color: $o-enterprise-action-color !default; + +// Component colors +$o-view-background-color: $o-gray-200 !default; +$o-shadow-color: $o-gray-100 !default; + +$o-form-lightsecondary: $o-gray-500 !default; + +// This is the background color. it is declared in web/static/src/scss/secondary_variables.scss +// but it really ought to be here +$o-webclient-background-color: $o-gray-100 !default; + +// List group +$o-list-group-active-bg: $o-gray-300 !default; + +// Buttons +$o-btns-bs-override: () !default; +$o-btns-bs-override: map-merge( + $o-btns-bs-override, + ( + "primary": ( + background: $o-brand-primary, + border: $o-brand-primary, + color: $o-black, + hover-background: lighten($o-brand-primary, 5%), + hover-border: lighten($o-brand-primary, 5%), + hover-color: $o-black, + + active-background: lighten($o-brand-primary, 10%), + active-border: lighten($o-brand-primary, 10%), + active-color: $o-black, + ), + "secondary": ( + background: $o-gray-300, + border: $o-gray-300, + color: $o-gray-900, + + hover-background: $o-gray-400, + hover-border: $o-gray-400, + hover-color: $o-gray-900, + + active-background: mix($o-action, $o-gray-100, 10%), + active-border: $o-action, + active-color: $o-black, + ), + "light": ( + background: transparent, + border: transparent, + color: $o-gray-800, + + hover-background: $o-gray-300, + hover-border: $o-gray-300, + hover-color: $o-gray-900, + + active-background: mix($o-action, $o-gray-100, 10%), + active-border: $o-action, + active-color: $o-black, + ), + ) +); + +$o-btns-bs-outline-override: () !default; +$o-btns-bs-outline-override: map-merge( + $o-btns-bs-outline-override, + ( + "primary": ( + background: transparent, + border: saturate(lighten($o-brand-primary, 20%), 25%), + color: saturate(lighten($o-brand-primary, 20%), 25%), + hover-background: lighten($o-brand-primary, 5%), + hover-border: lighten($o-brand-primary, 5%), + hover-color: $o-black, + + active-background: lighten($o-brand-primary, 10%), + active-border: lighten($o-brand-primary, 10%), + active-color: $o-black, + ), + "secondary": ( + background: transparent, + border: $o-gray-300, + color: $o-gray-900, + + hover-background: $o-gray-300, + hover-border: $o-gray-300, + hover-color: $o-gray-900, + + active-background: mix($o-action, $o-gray-100, 10%), + active-border: $o-action, + active-color: $o-black, + ), + ) +); diff --git a/web_dark_mode/static/src/scss/secondary_variables.dark.scss b/web_dark_mode/static/src/scss/secondary_variables.dark.scss new file mode 100644 index 000000000000..04cb45d48407 --- /dev/null +++ b/web_dark_mode/static/src/scss/secondary_variables.dark.scss @@ -0,0 +1,12 @@ +$o-colors: #a2a2a2, #ee2d2d, #dc8534, #e8bb1d, #5794dd, #9f628f, #db8865, #41a9a2, + #304be0, #ee2f8a, #61c36e, #9872e6 !default; + +// An epically lazy solution to colors that do not do well on the dark background: just delete them. +// Unfortunately this does introduce a problem where, when there are more calendar users than colors, +// the event gets a $o-webclient-background-color background and the filter check gets +// $form-check-input-checked-bg-color. This problem exists in enterprise too, +// so hopefully they will fix it. +$o-colors-secondary: #aa4b6b, #30c381, #f7cd1f, #4285f4, #d6145f, #aa3a38, #6be585, + #e9d362, #b56969, #bdc3c7, #ea00ff, #ff0026, #8bcc00, #00bfaf, #006aff, #af00bf, + #bf6300, #8cff00, #00f2ff, #ff00d0, #ffa600, #3acc00, #00b6bf, #0048ff, #bf7c00, + #04ff00, #00d0ff, #ff008c, #00bf49, #0092b3, #0004ff, #b20062 !default; diff --git a/web_dark_mode/static/src/search/search_panel/search_view.dark.scss b/web_dark_mode/static/src/search/search_panel/search_view.dark.scss new file mode 100644 index 000000000000..42581895f19e --- /dev/null +++ b/web_dark_mode/static/src/search/search_panel/search_view.dark.scss @@ -0,0 +1,13 @@ +.o_searchview { + --SearchBar-background-color: #{$o-gray-100}; + &:focus-within { + $-searchbar-border-color: rgba($input-focus-border-color, 0.5); + border-color: $-searchbar-border-color; + + .o_searchview_dropdown_toggler { + border-left-color: $-searchbar-border-color; + border-top-color: $-searchbar-border-color; + border-right-color: $-searchbar-border-color; + border-bottom-color: $-searchbar-border-color; + } + } +} diff --git a/web_dark_mode/static/src/views/calendar/calendar_renderer.dark.scss b/web_dark_mode/static/src/views/calendar/calendar_renderer.dark.scss new file mode 100644 index 000000000000..d3df07ba6203 --- /dev/null +++ b/web_dark_mode/static/src/views/calendar/calendar_renderer.dark.scss @@ -0,0 +1,63 @@ +// I really hate this whole loop and with I could just override +// $color and $color-subtle, but that doesn't seem to be possible. +@for $i from 1 through length($o-colors-complete) { + $color: nth($o-colors-complete, $i); + $color-subtle: scale-color( + mix($o-white, $color, 30%), + $lightness: -20%, + $saturation: -35% + ); + + .o_calendar_renderer { + .o_calendar_color_#{$i - 1} { + --fc-event-bg-color: #{$color-subtle}; + --o-event-bg: #{$color}; + --o-event-bg--subtle-rgb: #{to-rgb($color-subtle)}; + + &.fc-bg-event { + --fc-bg-event-color: #{$color-subtle}; + } + + &.o_event_hatched:not(.o_event_dot):not(.fc-daygrid-event):not( + .fc-timegrid-event + ), + &.o_event_hatched .fc-bg, + &.o_attendee_status_tentative:not(.o_event_dot) .fc-bg { + background: repeating-linear-gradient( + 45deg, + RGBA(to-rgb($color), 0.3), + RGBA(to-rgb($color), 0.3) 10px, + RGBA(to-rgb($color), 0.6) 10px, + RGBA(to-rgb($color), 0.6) 20px + ) !important; + } + + &.o_event_striked:not(.o_event_dot) { + background: linear-gradient( + transparent 0 45%, + $color 45% 55%, + transparent 55% 100% + ); + } + + &.fc-event:not(.o_event_dot):not(.fc-dragging) { + --fc-event-text-color: #{color-contrast($color-subtle)}; + --fc-event-border-color: #{tint-color($color, 50%)}; + } + } + } + + .o_cw_filter_color_#{$i - 1} { + &.form-check:hover .o_cw_filter_input_bg:not(.no_filter_color) { + border-color: shade-color($color, 20%); + } + + .o_cw_filter_input_bg { + border-color: $color; + + &:checked { + background-color: $color; + } + } + } +} diff --git a/web_dark_mode/static/src/views/fields/image/image_field.dark.scss b/web_dark_mode/static/src/views/fields/image/image_field.dark.scss new file mode 100644 index 000000000000..f6371764635f --- /dev/null +++ b/web_dark_mode/static/src/views/fields/image/image_field.dark.scss @@ -0,0 +1,7 @@ +// Unfortunately, images with trasparent backgrounds do +// not display well on a dark background. +// I use #ffffff instead of $o-black to ensure that they will +// always be on a white background no matter what. +.o_field_image { + --ImageField-background-color: #ffffff; +} diff --git a/web_dark_mode/static/src/views/fields/properties/properties_field.dark.scss b/web_dark_mode/static/src/views/fields/properties/properties_field.dark.scss new file mode 100644 index 000000000000..41098213b50b --- /dev/null +++ b/web_dark_mode/static/src/views/fields/properties/properties_field.dark.scss @@ -0,0 +1,27 @@ +.o_field_property_definition_type .o_field_property_dropdown img, +.o_field_property_definition_type_menu .dropdown-item img { + -webkit-filter: invert(100%); + filter: invert(100%); +} + +.o_property_field_value { + select { + option { + background-color: $dropdown-bg; + } + } +} + +// This is the < Create "tagname" > link that shows when you type a +// tag that doesn't exist. It was getting set to purple for some reason +.o_property_field_value + .ui-widget.ui-autocomplete + .ui-menu-item.o_field_property_dropdown_add + a { + color: $o-main-link-color !important; +} + +.o_field_properties div:not([columns="1"]) .o_property_drag_group { + background-color: #{mix($o-action, $o-view-background-color, 10%)}; + border: 1px solid #{rgba($o-action, 0.5)}; +} diff --git a/web_dark_mode/static/src/views/form/form_controller.dark.scss b/web_dark_mode/static/src/views/form/form_controller.dark.scss new file mode 100644 index 000000000000..5b6014e7d4a2 --- /dev/null +++ b/web_dark_mode/static/src/views/form/form_controller.dark.scss @@ -0,0 +1,3 @@ +.o_form_view .o_field_widget .o_list_renderer { + --ListRenderer-thead-bg-active-color: #{$o-gray-300}; +} diff --git a/web_dark_mode/static/src/views/kanban/kanban_controller.dark.scss b/web_dark_mode/static/src/views/kanban/kanban_controller.dark.scss new file mode 100644 index 000000000000..265dd424f30a --- /dev/null +++ b/web_dark_mode/static/src/views/kanban/kanban_controller.dark.scss @@ -0,0 +1,11 @@ +.o_kanban_renderer { + --KanbanColumn__highlight-background: #{mix($o-action, $o-gray-100, 10%)}; + --KanbanColumn__highlight-border: #{rgba($o-action, 0.5)}; + + // For some reason attachment images do not get the o_field_image class + .o_kanban_record:not(.o_legacy_kanban_record) { + .o_attachment_image > img { + background-color: #ffffff; + } + } +} diff --git a/web_dark_mode/static/src/webclient/navbar/navbar.variables.dark.scss b/web_dark_mode/static/src/webclient/navbar/navbar.variables.dark.scss new file mode 100644 index 000000000000..537cc13a64f7 --- /dev/null +++ b/web_dark_mode/static/src/webclient/navbar/navbar.variables.dark.scss @@ -0,0 +1,14 @@ +// The navbar is usually purple, as set by web/static/src/webclient/navbar/navbar.variables.scss +// We override it here for dark mode. +$o-navbar-background: $o-view-background-color !default; +// We do not really need to disable the border, but it helps on web_responsive +// when we change the color of the navbar to match the app grid background. +// If we do not disable it, it changes color with a noticeable delay after the navbar. +$o-navbar-border-bottom: 0px solid $o-navbar-background !default; + +$o-navbar-entry-color: $o-gray-900 !default; +$o-navbar-entry-color--hover: $o-black !default; + +$o-navbar-badge-bg: $o-danger !default; +$o-navbar-badge-color: $o-black !default; +$o-navbar-badge-text-shadow: 0 !default; diff --git a/web_dark_mode/tests/__init__.py b/web_dark_mode/tests/__init__.py new file mode 100644 index 000000000000..634cfc9d3dc0 --- /dev/null +++ b/web_dark_mode/tests/__init__.py @@ -0,0 +1,4 @@ +# © 2022 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_dark_mode diff --git a/web_dark_mode/tests/test_dark_mode.py b/web_dark_mode/tests/test_dark_mode.py new file mode 100644 index 000000000000..466ee7e3ac19 --- /dev/null +++ b/web_dark_mode/tests/test_dark_mode.py @@ -0,0 +1,35 @@ +# © 2022 Florian Kantelberg - initOS GmbH +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from unittest.mock import MagicMock + +import odoo.http +from odoo.tests import common + + +class TestDarkMode(common.TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.request = MagicMock(env=cls.env) + odoo.http._request_stack.push(cls.request) + + def test_dark_mode_cookie(self): + response = MagicMock() + + # Cookie is set because the color_scheme changed + self.request.httprequest.cookies = {"color_scheme": "dark"} + self.env["ir.http"]._post_dispatch(response) + response.set_cookie.assert_called_with("color_scheme", "light") + + # Cookie isn't set because the color_scheme is the same + response.reset_mock() + self.request.httprequest.cookies = {"color_scheme": "light"} + self.env["ir.http"]._post_dispatch(response) + response.set_cookie.assert_not_called() + + # Cookie isn't set because it's device dependent + self.env.user.dark_mode_device_dependent = True + self.request.httprequest.cookies = {"color_scheme": "dark"} + self.env["ir.http"]._post_dispatch(response) + response.set_cookie.assert_not_called() diff --git a/web_dark_mode/views/res_users_views.xml b/web_dark_mode/views/res_users_views.xml new file mode 100644 index 000000000000..47f02427f757 --- /dev/null +++ b/web_dark_mode/views/res_users_views.xml @@ -0,0 +1,14 @@ + + + + res.users + + + + + + + + + +