mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-21 23:43:17 +01:00
Kdots dark mode
This commit is contained in:
parent
844eed2eee
commit
c06b1aafda
@ -1229,11 +1229,11 @@ abstract class Framework extends Framework\Extra
|
|||||||
$topmenu_info_items = [
|
$topmenu_info_items = [
|
||||||
'user_avatar' => $this->_user_avatar_menu(),
|
'user_avatar' => $this->_user_avatar_menu(),
|
||||||
'update' => ($update = Framework\Updates::notification()) ? $update : null,
|
'update' => ($update = Framework\Updates::notification()) ? $update : null,
|
||||||
'logout' => (Header\UserAgent::mobile()) ? self::_logout_menu() : null,
|
'logout' => (Header\UserAgent::mobile()) ? static::_logout_menu() : null,
|
||||||
'notifications' => ($GLOBALS['egw_info']['user']['apps']['notifications']) ? self::_get_notification_bell() : null,
|
'notifications' => ($GLOBALS['egw_info']['user']['apps']['notifications']) ? static::_get_notification_bell() : null,
|
||||||
'quick_add' => $vars['quick_add'],
|
'quick_add' => $vars['quick_add'],
|
||||||
'print_title' => $this->_print_menu(),
|
'print_title' => $this->_print_menu(),
|
||||||
'darkmode' => self::_darkmode_menu()
|
'darkmode' => static::_darkmode_menu()
|
||||||
];
|
];
|
||||||
|
|
||||||
// array of topmenu items (orders of the items matter)
|
// array of topmenu items (orders of the items matter)
|
||||||
|
@ -1,3 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* kDots main styles
|
||||||
|
*
|
||||||
|
* Note that light / dark colors should go in framework_light.less & framework_dark.less
|
||||||
|
*/
|
||||||
|
/** Theme customisations **/
|
||||||
|
html[data-darkmode="true"] body {
|
||||||
|
background-color: black;
|
||||||
|
color: var(--sl-color-neutral-700);
|
||||||
|
/*** HEADER ***/
|
||||||
|
/*** APPLICATION ***/
|
||||||
|
/*** End APPLICATION ***/
|
||||||
|
/*** WIDGETS ***/
|
||||||
|
/* This should mostly go away with webcomponents */
|
||||||
|
/** End WIDGETS **/
|
||||||
|
}
|
||||||
|
html[data-darkmode="true"] body #egw_fw_topmenu_info_items #topmenu_info_timer:before {
|
||||||
|
filter: initial;
|
||||||
|
}
|
||||||
|
html[data-darkmode="true"] body egw-app {
|
||||||
|
--application-header-text-color: var(--sl-color-neutral-700);
|
||||||
|
}
|
||||||
|
html[data-darkmode="true"] body .nextmatch_sortheader {
|
||||||
|
color: #96bcd9;
|
||||||
|
}
|
||||||
|
/** End theme customisations **/
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
@ -118,6 +144,7 @@ egw-framework#egw_fw_basecontainer .egw_fw_ui_sidemenu_entry_header {
|
|||||||
#egw_fw_topmenu_info_items #topmenu_info_timer #topmenu_timer {
|
#egw_fw_topmenu_info_items #topmenu_info_timer #topmenu_timer {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
#egw_fw_topmenu_info_items #topmenu_info_timer:hover {
|
#egw_fw_topmenu_info_items #topmenu_info_timer:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* kDots main styles
|
||||||
|
*
|
||||||
|
* Note that light / dark colors should go in framework_light.less & framework_dark.less
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/** Theme customisations **/
|
||||||
|
@import "./framework_dark.less";
|
||||||
|
/** End theme customisations **/
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
@ -6,7 +17,6 @@ html, body {
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
|
||||||
/** Messages **/
|
/** Messages **/
|
||||||
|
|
||||||
.sl-toast-stack {
|
.sl-toast-stack {
|
||||||
top: auto;
|
top: auto;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
@ -140,6 +150,7 @@ egw-framework#egw_fw_basecontainer {
|
|||||||
#topmenu_timer {
|
#topmenu_timer {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -397,5 +408,4 @@ div.et2_nextmatch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*** END WIDGETS ***/
|
/*** END WIDGETS ***/
|
28
kdots/assets/styles/framework_dark.less
Normal file
28
kdots/assets/styles/framework_dark.less
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
html[data-darkmode="true"] body {
|
||||||
|
background-color: black;
|
||||||
|
color: var(--sl-color-neutral-700);
|
||||||
|
|
||||||
|
/*** HEADER ***/
|
||||||
|
#egw_fw_topmenu_info_items {
|
||||||
|
#topmenu_info_timer:before {
|
||||||
|
filter:initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** APPLICATION ***/
|
||||||
|
|
||||||
|
egw-app {
|
||||||
|
--application-header-text-color: var(--sl-color-neutral-700);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** End APPLICATION ***/
|
||||||
|
|
||||||
|
/*** WIDGETS ***/
|
||||||
|
/* This should mostly go away with webcomponents */
|
||||||
|
|
||||||
|
.nextmatch_sortheader {
|
||||||
|
color: #96bcd9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** End WIDGETS **/
|
||||||
|
}
|
@ -34,7 +34,7 @@
|
|||||||
{include_wz_tooltip}
|
{include_wz_tooltip}
|
||||||
<!-- END head -->
|
<!-- END head -->
|
||||||
<!-- BEGIN framework -->
|
<!-- BEGIN framework -->
|
||||||
<egw-framework id="egw_fw_basecontainer" class="sl-theme-light "
|
<egw-framework id="egw_fw_basecontainer" class=" "
|
||||||
application-list="{application-list}"
|
application-list="{application-list}"
|
||||||
>
|
>
|
||||||
<a slot="logo" href="{logo_url}" target="_blank"><img src="{logo_header}" title="{logo_title}" alt="Site logo"/></a>
|
<a slot="logo" href="{logo_url}" target="_blank"><img src="{logo_header}" title="{logo_title}" alt="Site logo"/></a>
|
||||||
|
@ -76,6 +76,9 @@ class kdots_framework extends Api\Framework\Ajax
|
|||||||
case 'user_avatar':
|
case 'user_avatar':
|
||||||
$vars['topmenu_info_items'] .= "<sl-dropdown class=\"topmenu_info_item\" id=\"topmenu_info_{$id}\" aria-label='" . lang("User menu") . "' tabindex='0'><div slot='trigger'>$item</div> {$vars['topmenu_items']}</sl-dropdown>";
|
$vars['topmenu_info_items'] .= "<sl-dropdown class=\"topmenu_info_item\" id=\"topmenu_info_{$id}\" aria-label='" . lang("User menu") . "' tabindex='0'><div slot='trigger'>$item</div> {$vars['topmenu_items']}</sl-dropdown>";
|
||||||
break;
|
break;
|
||||||
|
case 'darkmode':
|
||||||
|
$vars['topmenu_info_items'] .= $item;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
$vars['topmenu_info_items'] .= '<button class="topmenu_info_item"' .
|
$vars['topmenu_info_items'] .= '<button class="topmenu_info_item"' .
|
||||||
(is_numeric($id) ? '' : ' id="topmenu_info_' . $id . '"') . '>' . $item . "</button>\n";
|
(is_numeric($id) ? '' : ' id="topmenu_info_' . $id . '"') . '>' . $item . "</button>\n";
|
||||||
@ -174,4 +177,17 @@ class kdots_framework extends Api\Framework\Ajax
|
|||||||
|
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns darkmode menu
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected static function _darkmode_menu()
|
||||||
|
{
|
||||||
|
$mode = $GLOBALS['egw_info']['user']['preferences']['common']['darkmode'] == 1 ? 'dark' : 'light';
|
||||||
|
return '<egw-darkmode-toggle title="' . lang("%1 mode", $mode) . '" class="' .
|
||||||
|
($mode == 'dark' ? 'darkmode_on' : '') . '"' . ($mode == 'dark' ? 'darkmode' : '') . '> </egw-darkmode-toggle>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
94
kdots/js/EgwDarkmodeToggle.ts
Normal file
94
kdots/js/EgwDarkmodeToggle.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import {css, html, LitElement} from "lit";
|
||||||
|
import {customElement} from "lit/decorators/custom-element.js";
|
||||||
|
import {property} from "lit/decorators/property.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary System message
|
||||||
|
*
|
||||||
|
* @dependency sl-alert
|
||||||
|
* @dependency sl-icon
|
||||||
|
*
|
||||||
|
* @slot - Content
|
||||||
|
* @slot icon - An icon to show in the message
|
||||||
|
*
|
||||||
|
* @csspart base - Wraps it all.
|
||||||
|
* @csspart icon -
|
||||||
|
*/
|
||||||
|
@customElement('egw-darkmode-toggle')
|
||||||
|
export class EgwDarkmodeToggle extends LitElement
|
||||||
|
{
|
||||||
|
static get styles()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
css`
|
||||||
|
sl-icon-button::part(base) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@property({type: Boolean})
|
||||||
|
darkmode = false;
|
||||||
|
|
||||||
|
private _initialValue = false;
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this._initialValue = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||||
|
this.handleDarkmodeChange = this.handleDarkmodeChange.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback()
|
||||||
|
{
|
||||||
|
super.connectedCallback();
|
||||||
|
this.toggleDarkmode(this.hasAttribute("darkmode") ? this.darkmode : this._initialValue);
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", this.handleDarkmodeChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback()
|
||||||
|
{
|
||||||
|
super.disconnectedCallback();
|
||||||
|
window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", this.handleDarkmodeChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleDarkmode(force = null)
|
||||||
|
{
|
||||||
|
if(force == null)
|
||||||
|
{
|
||||||
|
force = !(document.documentElement.getAttribute("data-darkmode") == "true");
|
||||||
|
}
|
||||||
|
this.darkmode = force;
|
||||||
|
if(force)
|
||||||
|
{
|
||||||
|
document.documentElement.setAttribute("data-darkmode", "true");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
document.documentElement.setAttribute("data-darkmode", "0");
|
||||||
|
}
|
||||||
|
// Set class for Shoelace
|
||||||
|
document.documentElement.classList.toggle("sl-theme-dark", this.darkmode);
|
||||||
|
this.requestUpdate("darkmode")
|
||||||
|
this.updateComplete.then(() =>
|
||||||
|
{
|
||||||
|
this.dispatchEvent(new CustomEvent("egw-darkmode-change", {bubbles: true}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleDarkmodeChange(e)
|
||||||
|
{
|
||||||
|
this.toggleDarkmode(e.matches ? "dark" : "light");
|
||||||
|
}
|
||||||
|
|
||||||
|
render() : unknown
|
||||||
|
{
|
||||||
|
return html`
|
||||||
|
<sl-icon-button name="${this.darkmode ? "sun" : "moon"}"
|
||||||
|
@click=${(e) => {this.toggleDarkmode()}}
|
||||||
|
></sl-icon-button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -111,6 +111,11 @@ export class EgwFramework extends LitElement
|
|||||||
|
|
||||||
private get tabs() : SlTabGroup { return this.shadowRoot.querySelector("sl-tab-group");}
|
private get tabs() : SlTabGroup { return this.shadowRoot.querySelector("sl-tab-group");}
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.handleDarkmodeChange = this.handleDarkmodeChange.bind(this);
|
||||||
|
}
|
||||||
connectedCallback()
|
connectedCallback()
|
||||||
{
|
{
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
@ -124,6 +129,15 @@ export class EgwFramework extends LitElement
|
|||||||
// Override framework setSidebox, use arrow function to force context
|
// Override framework setSidebox, use arrow function to force context
|
||||||
this.egw.framework.setSidebox = (applicationName, sideboxData, hash?) => this.setSidebox(applicationName, sideboxData, hash);
|
this.egw.framework.setSidebox = (applicationName, sideboxData, hash?) => this.setSidebox(applicationName, sideboxData, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.body.addEventListener("egw-darkmode-change", this.handleDarkmodeChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback()
|
||||||
|
{
|
||||||
|
super.disconnectedCallback();
|
||||||
|
|
||||||
|
document.body.removeEventListener("egw-darkmode-change", this.handleDarkmodeChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(_changedProperties : PropertyValues)
|
protected firstUpdated(_changedProperties : PropertyValues)
|
||||||
@ -572,6 +586,15 @@ export class EgwFramework extends LitElement
|
|||||||
|
|
||||||
protected getBaseUrl() {return "";}
|
protected getBaseUrl() {return "";}
|
||||||
|
|
||||||
|
protected handleDarkmodeChange(event)
|
||||||
|
{
|
||||||
|
// Update CSS classes
|
||||||
|
this.classList.toggle("sl-theme-light", !event.target.darkmode);
|
||||||
|
this.classList.toggle("sl-theme-dark", event.target.darkmode);
|
||||||
|
|
||||||
|
// Update preference
|
||||||
|
this.egw.set_preference("common", "darkmode", (event.target.darkmode ? "1" : "0"));
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* An application tab is chosen, show the app
|
* An application tab is chosen, show the app
|
||||||
*
|
*
|
||||||
|
@ -39,13 +39,13 @@ export default css`
|
|||||||
max-height: 3em;
|
max-height: 3em;
|
||||||
|
|
||||||
background-color: var(--application-color, --primary-background-color);
|
background-color: var(--application-color, --primary-background-color);
|
||||||
color: var(--application-header-text-color, white);
|
color: var(--application-header-text-color, var(--sl-color-neutral-0));
|
||||||
font-size: 1.8em;
|
font-size: 1.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.egw_fw_app__header sl-icon-button::part(base), .egw_fw_app__header et2-button-icon {
|
.egw_fw_app__header sl-icon-button::part(base), .egw_fw_app__header et2-button-icon {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
color: var(--application-header-text-color, white);
|
color: var(--application-header-text-color, var(--sl-color-neutral-0));
|
||||||
}
|
}
|
||||||
|
|
||||||
.egw_fw_app__header et2-button-icon {
|
.egw_fw_app__header et2-button-icon {
|
||||||
@ -53,7 +53,7 @@ export default css`
|
|||||||
}
|
}
|
||||||
|
|
||||||
.egw_fw_app__header sl-icon-button::part(base):hover, .egw_fw_app__header et2-button-icon::part(base):hover {
|
.egw_fw_app__header sl-icon-button::part(base):hover, .egw_fw_app__header et2-button-icon::part(base):hover {
|
||||||
color: var(--application-header-text-color, white);
|
color: var(--application-header-text-color, var(--sl-color-neutral-0));
|
||||||
filter: brightness(70%);
|
filter: brightness(70%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
import {EgwFramework} from "./EgwFramework";
|
import {EgwFramework} from "./EgwFramework";
|
||||||
import {EgwFrameworkApp} from "./EgwFrameworkApp";
|
import {EgwFrameworkApp} from "./EgwFrameworkApp";
|
||||||
|
import {EgwDarkmodeToggle} from "./EgwDarkmodeToggle";
|
||||||
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () =>
|
document.addEventListener('DOMContentLoaded', () =>
|
||||||
{
|
{
|
||||||
// Not sure what's up here
|
// Not sure what's up here, but it makes sure everything is loaded
|
||||||
if(!window.customElements.get("egw-framework"))
|
if(!window.customElements.get("egw-framework"))
|
||||||
{
|
{
|
||||||
window.customElements.define("egw-framework", EgwFramework);
|
window.customElements.define("egw-framework", EgwFramework);
|
||||||
@ -17,6 +18,10 @@ document.addEventListener('DOMContentLoaded', () =>
|
|||||||
{
|
{
|
||||||
window.customElements.define("egw-app", EgwFrameworkApp);
|
window.customElements.define("egw-app", EgwFrameworkApp);
|
||||||
}
|
}
|
||||||
|
if(!window.customElements.get("egw-darkmode-toggle"))
|
||||||
|
{
|
||||||
|
window.customElements.define("egw-darkmode-toggle", EgwDarkmodeToggle);
|
||||||
|
}
|
||||||
/* Set up listener on avatar menu */
|
/* Set up listener on avatar menu */
|
||||||
const avatarMenu = document.querySelector("#topmenu_info_user_avatar");
|
const avatarMenu = document.querySelector("#topmenu_info_user_avatar");
|
||||||
if(avatarMenu)
|
if(avatarMenu)
|
||||||
|
Loading…
Reference in New Issue
Block a user