simplify color mode selection

This commit is contained in:
Abhimanyu Saharan 2023-04-24 14:09:40 +05:30
parent aba00ca01d
commit 01d1e7adbc
16 changed files with 33 additions and 205 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,72 +1,42 @@
/**
* Set the color mode on the `<html/>` element and in local storage.
* Set the color mode on the `<html/>` element.
*
* @param mode {"dark" | "light"} NetBox Color Mode.
* @param inferred {boolean} Value is inferred from browser/system preference.
* @param mode {"dark" | "light" | "system"} NetBox Color Mode.
*/
function setMode(mode, inferred) {
document.documentElement.setAttribute("data-netbox-color-mode", mode);
localStorage.setItem("netbox-color-mode", mode);
localStorage.setItem("netbox-color-mode-inferred", inferred);
function setMode(mode) {
if (mode === 'system') {
mode = getSystemColorMode();
}
document.documentElement.setAttribute('data-netbox-color-mode', mode);
}
/**
* Determine the system color mode (light or dark).
*
* @return {string} "dark" or "light" based on system preference.
*/
function getSystemColorMode() {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
/**
* Determine the best initial color mode to use prior to rendering.
*/
function initMode() {
try {
// Browser prefers dark color scheme.
var preferDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
// Browser prefers light color scheme.
var preferLight = window.matchMedia("(prefers-color-scheme: light)").matches;
// Client NetBox color-mode override.
var clientMode = localStorage.getItem("netbox-color-mode");
// NetBox server-rendered value.
var serverMode = document.documentElement.getAttribute("data-netbox-color-mode");
// Color mode is inferred from browser/system preference and not deterministically set by
// the client or server.
var inferred = JSON.parse(localStorage.getItem("netbox-color-mode-inferred"));
try {
// NetBox server-rendered value.
const serverMode = document.documentElement.getAttribute('data-netbox-color-mode');
if (inferred === true && (serverMode === "light" || serverMode === "dark")) {
// The color mode was previously inferred from browser/system preference, but
// the server now has a value, so we should use the server's value.
return setMode(serverMode, false);
}
if (clientMode === null && (serverMode === "light" || serverMode === "dark")) {
// If the client mode is not set but the server mode is, use the server mode.
return setMode(serverMode, false);
}
if (clientMode !== null && serverMode === "unset") {
// The color mode has been set, deterministically or otherwise, and the server
// has no preference or has not been set. Use the client mode, but allow it to
/// be overridden by the server if/when a server value exists.
return setMode(clientMode, true);
}
if (
clientMode !== null &&
(serverMode === "light" || serverMode === "dark") &&
clientMode !== serverMode
) {
// If the client mode is set and is different than the server mode (which is also set),
// use the client mode over the server mode, as it should be more recent.
return setMode(clientMode, false);
}
if (clientMode === serverMode) {
// If the client and server modes match, use that value.
return setMode(clientMode, false);
}
if (preferDark && serverMode === "unset") {
// If the server mode is not set but the browser prefers dark mode, use dark mode, but
// allow it to be overridden by an explicit preference.
return setMode("dark", true);
}
if (preferLight && serverMode === "unset") {
// If the server mode is not set but the browser prefers light mode, use light mode,
// but allow it to be overridden by an explicit preference.
return setMode("light", true);
}
} catch (error) {
// In the event of an error, log it to the console and set the mode to light mode.
console.error(error);
if (serverMode === 'light' || serverMode === 'dark' || serverMode === 'system') {
// If the server mode is set (light, dark, or system), use the server mode.
return setMode(serverMode);
}
return setMode("light", true);
};
// If server mode is not set, use the system color mode.
return setMode('system');
} catch (error) {
// In the event of an error, log it to the console and set the mode to system mode.
console.error(error);
return setMode('system');
}
}

View File

@ -1,134 +0,0 @@
import { getElements, isTruthy } from './util';
const COLOR_MODE_KEY = 'netbox-color-mode';
const TEXT_WHEN_DARK = 'Light Mode';
const TEXT_WHEN_LIGHT = 'Dark Mode';
const ICON_WHEN_DARK = 'mdi-lightbulb-on';
const ICON_WHEN_LIGHT = 'mdi-lightbulb';
/**
* Determine if a value is a supported color mode string value.
*/
function isColorMode(value: unknown): value is ColorMode {
return value === 'dark' || value === 'light';
}
/**
* Set the color mode to light or dark.
*
* @param mode `'light'` or `'dark'`
* @returns `true` if the color mode was successfully set, `false` if not.
*/
function storeColorMode(mode: ColorMode): void {
return localStorage.setItem(COLOR_MODE_KEY, mode);
}
function updateElements(targetMode: ColorMode): void {
document.documentElement.setAttribute(`data-${COLOR_MODE_KEY}`, targetMode);
for (const text of getElements<HTMLSpanElement>('span.color-mode-text')) {
if (targetMode === 'light') {
text.innerText = TEXT_WHEN_LIGHT;
} else if (targetMode === 'dark') {
text.innerText = TEXT_WHEN_DARK;
}
}
for (const icon of getElements<HTMLSpanElement>('i.color-mode-icon', 'span.color-mode-icon')) {
if (targetMode === 'light') {
icon.classList.remove(ICON_WHEN_DARK);
icon.classList.add(ICON_WHEN_LIGHT);
} else if (targetMode === 'dark') {
icon.classList.remove(ICON_WHEN_LIGHT);
icon.classList.add(ICON_WHEN_DARK);
}
}
for (const elevation of getElements<HTMLObjectElement>('.rack_elevation')) {
const svg = elevation.contentDocument?.querySelector('svg') ?? null;
if (svg !== null) {
svg.setAttribute(`data-${COLOR_MODE_KEY}`, targetMode);
}
}
}
/**
* Call all functions necessary to update the color mode across the UI.
*
* @param mode Target color mode.
*/
export function setColorMode(mode: ColorMode): void {
for (const func of [storeColorMode, updateElements]) {
func(mode);
}
}
/**
* Toggle the color mode when a color mode toggle is clicked.
*/
function handleColorModeToggle(): void {
const currentValue = localStorage.getItem(COLOR_MODE_KEY);
if (currentValue === 'light') {
setColorMode('dark');
} else if (currentValue === 'dark') {
setColorMode('light');
} else {
console.warn('Unable to determine the current color mode');
}
}
/**
* Determine the user's preference and set it as the color mode.
*/
function defaultColorMode(): void {
// Get the current color mode value from local storage.
const currentValue = localStorage.getItem(COLOR_MODE_KEY) as Nullable<ColorMode>;
const serverValue = document.documentElement.getAttribute(`data-${COLOR_MODE_KEY}`);
if (isTruthy(serverValue) && isTruthy(currentValue)) {
return setColorMode(currentValue);
}
let preference: ColorModePreference = 'none';
// Determine if the user prefers dark or light mode.
for (const mode of ['dark', 'light']) {
if (window.matchMedia(`(prefers-color-scheme: ${mode})`).matches) {
preference = mode as ColorModePreference;
break;
}
}
if (isTruthy(currentValue) && !isTruthy(serverValue) && isColorMode(currentValue)) {
return setColorMode(currentValue);
}
switch (preference) {
case 'dark':
return setColorMode('dark');
case 'light':
return setColorMode('light');
case 'none':
return setColorMode('light');
default:
return setColorMode('light');
}
}
/**
* Initialize color mode toggle buttons and set the default color mode.
*/
function initColorModeToggle(): void {
for (const element of getElements<HTMLButtonElement>('button.color-mode-toggle')) {
element.addEventListener('click', handleColorModeToggle);
}
}
/**
* Initialize all color mode elements.
*/
export function initColorMode(): void {
window.addEventListener('load', defaultColorMode);
for (const func of [initColorModeToggle]) {
func();
}
}

View File

@ -3,7 +3,6 @@ import { initBootstrap } from './bs';
import { initQuickSearch } from './search';
import { initSelect } from './select';
import { initButtons } from './buttons';
import { initColorMode } from './colorMode';
import { initMessages } from './messages';
import { initClipboard } from './clipboard';
import { initDateSelector } from './dateSelector';
@ -18,7 +17,6 @@ import { initHtmx } from './htmx';
function initDocument(): void {
for (const init of [
initBootstrap,
initColorMode,
initMessages,
initForms,
initQuickSearch,

View File

@ -7,13 +7,7 @@
data-netbox-url-name="{{ request.resolver_match.url_name }}"
data-netbox-base-path="{{ settings.BASE_PATH }}"
{% with preferences|get_key:'ui.colormode' as color_mode %}
{% if color_mode == 'dark'%}
data-netbox-color-mode="dark"
{% elif color_mode == 'light' %}
data-netbox-color-mode="light"
{% else %}
data-netbox-color-mode="unset"
{% endif %}
data-netbox-color-mode={{ color_mode }}
{% endwith %}
>
<head>