Skip to content

Commit

Permalink
Merge pull request #5616 from WoltLab/user-color-scheme
Browse files Browse the repository at this point in the history
Overhaul the color scheme selection
  • Loading branch information
dtdesign authored Aug 10, 2023
2 parents 3037d2f + d732bab commit 84a522c
Show file tree
Hide file tree
Showing 20 changed files with 129 additions and 257 deletions.
13 changes: 3 additions & 10 deletions com.woltlab.wcf/templates/headIncludeJavaScript.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,9 @@
var COMPILER_TARGET_DEFAULT = {if !VISITOR_USE_TINY_BUILD || $__wcf->user->userID}true{else}false{/if};
{/if}
{if $__wcf->getStyleHandler()->getStyle()->hasDarkMode}
{if $__wcf->getStyleHandler()->getColorScheme() === 'system'}
{
let colorScheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
try {
const value = localStorage.getItem("wsc_colorScheme");
if (value === "light" || value === "dark") {
colorScheme = value;
}
} catch {}
const colorScheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
document.documentElement.dataset.colorScheme = colorScheme;
}
{/if}
Expand Down Expand Up @@ -82,7 +75,7 @@ window.addEventListener('pageshow', function(event) {
url: '{link controller="BackgroundQueuePerform"}{/link}',
force: {if $forceBackgroundQueuePerform|isset}true{else}false{/if}
},
colorScheme: '{@$__wcf->getStyleHandler()->getColorScheme()|encodeJS}',
dynamicColorScheme: {if $__wcf->getStyleHandler()->getColorScheme() === 'system'}true{else}false{/if},
enableUserPopover: {if $__wcf->getSession()->getPermission('user.profile.canViewUserProfile')}true{else}false{/if},
executeCronjobs: {if $executeCronjobs}'{link controller="CronjobPerform"}{/link}'{else}undefined{/if},
{if ENABLE_SHARE_BUTTONS}
Expand Down
24 changes: 5 additions & 19 deletions com.woltlab.wcf/templates/pageFooter.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,14 @@
{assign var=__showStyleChanger value=false}
{/if}

{if $__boxesFooter|count || !$boxesFooter|empty || $__showStyleChanger || $__wcf->getStyleHandler()->showColorSchemeSelector()}
{if $__boxesFooter|count || !$boxesFooter|empty || $__showStyleChanger}
<div class="boxesFooter">
<div class="layoutBoundary{if $__showStyleChanger || $__wcf->getStyleHandler()->showColorSchemeSelector()} clearfix{/if}">
{hascontent}
<div class="layoutBoundary{if $__showStyleChanger} clearfix{/if}">
{if $__showStyleChanger}
<div class="styleChanger jsOnly">
{content}
{if $__showStyleChanger}
<button type="button" class="jsButtonStyleChanger">{lang}wcf.style.changeStyle{/lang}</button>
{/if}
{if $__wcf->getStyleHandler()->showColorSchemeSelector()}
<button type="button" class="page__colorScheme jsButtonStyleColorScheme jsTooltip" title="{lang}wcf.style.setColorScheme{/lang}">
<span class="page__colorScheme--dark">
{icon name='moon' type='solid'}
</span>
<span class="page__colorScheme--light">
{icon name='sun' type='solid'}
</span>
</button>
{/if}
{/content}
<button type="button" class="jsButtonStyleChanger">{lang}wcf.style.changeStyle{/lang}</button>
</div>
{/hascontent}
{/if}
{hascontent}
<div class="boxContainer">
{content}
Expand Down
26 changes: 19 additions & 7 deletions com.woltlab.wcf/templates/settings.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@
</section>
{/if}

{if $availableStyles|count > 1}
<section class="section" id="section_style">
<h2 class="sectionTitle">{lang}wcf.user.styles{/lang}</h2>
<section class="section" id="section_style">
<h2 class="sectionTitle">{lang}wcf.user.styles{/lang}</h2>

{if $availableStyles|count > 1}
<dl>
<dt><label for="styleID">{lang}wcf.user.style{/lang}</label></dt>
<dd>
Expand All @@ -80,10 +80,22 @@
<small>{lang}wcf.user.style.description{/lang}</small>
</dd>
</dl>

{event name='styleFields'}
</section>
{/if}
{/if}

<dl>
<dt><label for="colorScheme">{lang}wcf.user.style.colorScheme{/lang}</label></dt>
<dd>
<select id="colorScheme" name="colorScheme">
<option value="system"{if $colorScheme === 'system'} selected{/if}>{lang}wcf.style.setColorScheme.system{/lang}</option>
<option value="light"{if $colorScheme === 'light'} selected{/if}>{lang}wcf.style.setColorScheme.light{/lang}</option>
<option value="dark"{if $colorScheme === 'dark'} selected{/if}>{lang}wcf.style.setColorScheme.dark{/lang}</option>
</select>
<small>{lang}wcf.user.style.colorScheme.description{/lang}</small>
</dd>
</dl>

{event name='styleFields'}
</section>

{if MODULE_TROPHY && $__wcf->getSession()->getPermission('user.profile.trophy.maxUserSpecialTrophies') > 0 && $availableTrophies|count}
<section class="section" id="section_trophy">
Expand Down
8 changes: 8 additions & 0 deletions com.woltlab.wcf/userOption.xml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@
<optiontype>boolean</optiontype>
<defaultvalue>1</defaultvalue>
</option>
<option name="colorScheme">
<categoryname>hidden</categoryname>
<optiontype>select</optiontype>
<selectoptions>system:wcf.style.setColorScheme.system
light:wcf.style.setColorScheme.light
dark:wcf.style.setColorScheme.dark</selectoptions>
<defaultvalue>system</defaultvalue>
</option>
<option name="timezone">
<categoryname>settings.general.date</categoryname>
<optiontype>timezone</optiontype>
Expand Down
1 change: 0 additions & 1 deletion ts/WoltLabSuite/Core/Acp/Bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export function setup(options: AcpBootstrapOptions): void {
options = Core.extend(
{
bootstrap: {
colorScheme: "system",
enableMobileMenu: true,
pageMenuMainProvider: new AcpUiPageMenuMainBackend(),
},
Expand Down
10 changes: 5 additions & 5 deletions ts/WoltLabSuite/Core/Bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ import { PageMenuMainProvider } from "./Ui/Page/Menu/Main/Provider";
import { whenFirstSeen } from "./LazyLoader";
import { adoptPageOverlayContainer } from "./Helper/PageOverlay";

import type { ColorScheme } from "./Controller/Style/ColorScheme";

// perfectScrollbar does not need to be bound anywhere, it just has to be loaded for WCF.js
import "perfect-scrollbar";

Expand All @@ -52,7 +50,7 @@ window.WCF.Language.addObject = Language.addObject;
window.__wcf_bc_eventHandler = EventHandler;

export interface BoostrapOptions {
colorScheme: ColorScheme;
dynamicColorScheme: boolean;
enableMobileMenu: boolean;
pageMenuMainProvider: PageMenuMainProvider;
}
Expand Down Expand Up @@ -150,8 +148,10 @@ export function setup(options: BoostrapOptions): void {

DomChangeListener.add("WoltLabSuite/Core/Bootstrap", () => initA11y);

if (options.colorScheme === "system") {
void import("./Controller/Style/ColorScheme").then(({ setup }) => setup());
if (options.dynamicColorScheme) {
void import("./Controller/Style/ColorScheme").then(({ setup }) => {
setup();
});
}

whenFirstSeen("[data-report-content]", () => {
Expand Down
6 changes: 2 additions & 4 deletions ts/WoltLabSuite/Core/BootstrapFrontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@ import UiPageMenuMainFrontend from "./Ui/Page/Menu/Main/Frontend";
import { whenFirstSeen } from "./LazyLoader";
import { prepareRequest } from "./Ajax/Backend";

import type { ColorScheme } from "./Controller/Style/ColorScheme";

interface BootstrapOptions {
backgroundQueue: {
url: string;
force: boolean;
};
colorScheme: ColorScheme;
dynamicColorScheme: boolean;
enableUserPopover: boolean;
executeCronjobs: string | undefined;
shareButtonProviders?: ShareProvider[];
Expand Down Expand Up @@ -63,7 +61,7 @@ export function setup(options: BootstrapOptions): void {
options.backgroundQueue.url = window.WSC_API_URL + options.backgroundQueue.url.substr(window.WCF_PATH.length);

Bootstrap.setup({
colorScheme: options.colorScheme,
dynamicColorScheme: options.dynamicColorScheme,
enableMobileMenu: true,
pageMenuMainProvider: new UiPageMenuMainFrontend(),
});
Expand Down
99 changes: 6 additions & 93 deletions ts/WoltLabSuite/Core/Controller/Style/ColorScheme.ts
Original file line number Diff line number Diff line change
@@ -1,105 +1,18 @@
/**
* Offer users the ability to enforce a specific color scheme.
* Dynamically updates the color scheme to match the system preference.
*
* @author Alexander Ebert
* @copyright 2001-2023 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @since 6.0
*/

import { getPhrase } from "WoltLabSuite/Core/Language";
import { attach, create } from "../../Ui/Dropdown/Builder";
import { registerCallback } from "../../Ui/Dropdown/Simple";

export type ColorScheme = "dark" | "light" | "system";

let currentScheme: ColorScheme = "system";
let mediaQuery: MediaQueryList;
let themeColor: HTMLMetaElement;

function setScheme(scheme: ColorScheme): void {
currentScheme = scheme;

if (currentScheme === "light" || currentScheme === "dark") {
document.documentElement.dataset.colorScheme = currentScheme;
updateThemeColor();
} else {
applySystemScheme();
}

try {
localStorage.setItem("wsc_colorScheme", currentScheme);
} catch {
/* Ignore any errors when accessing the `localStorage`. */
}
}

function applySystemScheme(): void {
if (currentScheme === "system") {
document.documentElement.dataset.colorScheme = mediaQuery.matches ? "dark" : "light";
updateThemeColor();
}
}

function updateThemeColor(): void {
themeColor.content = window.getComputedStyle(document.body).getPropertyValue("--wcfPageThemeColor");
}

function initializeButton(button: HTMLElement): void {
const dropdownMenu = create([
{
identifier: "light",
label: getPhrase("wcf.style.setColorScheme.light"),
callback() {
setScheme("light");
},
},
{
identifier: "dark",
label: getPhrase("wcf.style.setColorScheme.dark"),
callback() {
setScheme("dark");
},
},
"divider",
{
identifier: "system",
label: getPhrase("wcf.style.setColorScheme.system"),
callback() {
setScheme("system");
},
},
]);

attach(dropdownMenu, button);

registerCallback(button.id, (_containerId, action) => {
if (action === "open") {
dropdownMenu.querySelectorAll(".active").forEach((element) => element.classList.remove("active"));
dropdownMenu.querySelector(`[data-identifier="${currentScheme}"]`)!.classList.add("active");
}
});
}

export function setup(): void {
const button = document.querySelector<HTMLElement>(".jsButtonStyleColorScheme");
if (button) {
initializeButton(button);
}

try {
const value = localStorage.getItem("wsc_colorScheme");
if (value === "light" || value === "dark") {
currentScheme = value;
}
} catch {
/* Ignore any errors when accessing the `localStorage`. */
}

themeColor = document.querySelector('meta[name="theme-color"]')!;
const themeColor = document.querySelector<HTMLMetaElement>('meta[name="theme-color"]')!;
themeColor.content = window.getComputedStyle(document.body).getPropertyValue("--wcfPageThemeColor");

mediaQuery = matchMedia("(prefers-color-scheme: dark)");
mediaQuery.addEventListener("change", () => {
applySystemScheme();
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
document.documentElement.dataset.colorScheme = event.matches ? "dark" : "light";
themeColor.content = window.getComputedStyle(document.body).getPropertyValue("--wcfPageThemeColor");
});
}
16 changes: 6 additions & 10 deletions wcfsetup/install/files/acp/templates/header.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html
dir="{@$__wcf->getLanguage()->getPageDirection()}"
lang="{$__wcf->getLanguage()->getBcp47()}"
data-color-scheme="system"
data-color-scheme="{$__wcf->getStyleHandler()->getColorScheme()}"
>
<head>
<meta charset="utf-8">
Expand Down Expand Up @@ -50,17 +50,12 @@
{* Unlike the frontend, this option must be defined in the ACP at all times. *}
var COMPILER_TARGET_DEFAULT = true;
{if $__wcf->getStyleHandler()->getColorScheme() === 'system'}
{
let colorScheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
try {
const value = localStorage.getItem("wsc_colorScheme");
if (value === "light" || value === "dark") {
colorScheme = value;
}
} catch {}
const colorScheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
document.documentElement.dataset.colorScheme = colorScheme;
}
{/if}
</script>

<script data-eager="true" src="{$__wcf->getPath()}js/WoltLabSuite/WebComponent.js?v={@LAST_UPDATE_TIME}"></script>
Expand Down Expand Up @@ -99,7 +94,8 @@
AcpBootstrap.setup({
bootstrap: {
enableMobileMenu: {if PACKAGE_ID && $__isLogin|empty}true{else}false{/if}
dynamicColorScheme: {if $__wcf->getStyleHandler()->getColorScheme() === 'system'}true{else}false{/if},
enableMobileMenu: {if PACKAGE_ID && $__isLogin|empty}true{else}false{/if},
}
});
});
Expand Down
16 changes: 0 additions & 16 deletions wcfsetup/install/files/acp/templates/pageHeaderUser.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,6 @@
<li><a class="externalURL" href="https://www.woltlab.com/pluginstore/"{if EXTERNAL_LINK_TARGET_BLANK} target="_blank" rel="noopener"{/if}>{lang}wcf.acp.index.woltlab.pluginStore{/lang}</a></li>
</ul>
</li>

<li>
<a
href="#"
role="button"
class="page__colorScheme jsButtonStyleColorScheme jsTooltip"
title="{lang}wcf.style.setColorScheme{/lang}"
>
<span class="iconWrapper page__colorScheme--dark">
{icon size=16 name='moon' type='solid'}
</span>
<span class="iconWrapper page__colorScheme--light">
{icon size=16 name='sun' type='solid'}
</span>
</a>
</li>
{/if}

{event name='menuItems'}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions wcfsetup/install/files/js/WoltLabSuite/Core/Bootstrap.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 84a522c

Please sign in to comment.