From 55d849d7cc14d0ec60c75e8b179472313f8d1e1c Mon Sep 17 00:00:00 2001
From: transcaffeine
Date: Thu, 15 Aug 2024 20:19:38 +0200
Subject: [PATCH] feat: allow 'auto' as a colortheme preference
---
netbox/project-static/js/setmode.js | 15 ++++-
netbox/project-static/src/colorMode.ts | 77 ++++++++++++++------------
netbox/project-static/src/global.d.ts | 2 +-
3 files changed, 57 insertions(+), 37 deletions(-)
diff --git a/netbox/project-static/js/setmode.js b/netbox/project-static/js/setmode.js
index 9418a9f39..45e9eade7 100644
--- a/netbox/project-static/js/setmode.js
+++ b/netbox/project-static/js/setmode.js
@@ -1,10 +1,15 @@
/**
* Set the color mode on the `` element and in local storage.
*
- * @param mode {"dark" | "light"} UI color mode.
+ * @param mode {"dark" | "light" | "auto"} UI color mode.
*/
function setMode(mode) {
- document.documentElement.setAttribute("data-bs-theme", mode);
+ document.documentElement.setAttribute(
+ "data-bs-theme",
+ mode === "auto"
+ ? window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
+ : mode
+ );
localStorage.setItem("netbox-color-mode", mode);
}
@@ -23,6 +28,12 @@ function initMode() {
if (clientMode !== null) {
return setMode(clientMode, false);
}
+ // If client wants to auto-switch, attach an onchange listener to the media query
+ if (clientMode === 'auto') {
+ window.matchMedia('(prefers-color-scheme: light)').onchange = event => {
+ document.documentElement.dataset.bsTheme = (e.matches) ? 'light' : 'dark';
+ }
+ }
// Fall back to the mode preferred by the browser, if specified
if (preferDark) {
diff --git a/netbox/project-static/src/colorMode.ts b/netbox/project-static/src/colorMode.ts
index 453617740..a086d2e32 100644
--- a/netbox/project-static/src/colorMode.ts
+++ b/netbox/project-static/src/colorMode.ts
@@ -1,6 +1,6 @@
import { getElements, isTruthy } from './util';
-const COLOR_MODE_KEY = 'netbox-color-mode';
+const COLOR_MODE_PREFERENCE_KEY = 'netbox-color-mode-preference';
/**
* Determine if a value is a supported color mode string value.
@@ -9,28 +9,41 @@ function isColorMode(value: unknown): value is ColorMode {
return value === 'dark' || value === 'light';
}
+function isDefinedColorModePreference(value: unknown): value is ColorModePreference {
+ return value === 'auto' || isColorMode(value);
+}
+
+
/**
* Set the color mode to light or dark.
*
- * @param mode `'light'` or `'dark'`
+ * @param mode `'light'`, `'dark'` or `'auto'`
* @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 storeColorMode(modePreference: ColorModePreference): void {
+ return localStorage.setItem(COLOR_MODE_PREFERENCE_KEY, mode);
}
-function updateElements(targetMode: ColorMode): void {
+function updateElements(targetMode: ColorModePreference): void {
const body = document.querySelector('body');
- if (body && targetMode == 'dark') {
- body.setAttribute('data-bs-theme', 'dark');
- } else if (body) {
- body.setAttribute('data-bs-theme', 'light');
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+ const theme = (targetMode === 'auto')
+ ? (mediaQuery.matches ? 'dark' : 'light'
+ : (targetMode === 'none' ? targetMode : 'light');
+
+ if (body) {
+ body.setAttribute('data-bs-theme', theme);
+ }
+ if (body && targetMode === 'auto') {
+ mediaQuery.onchange = event => {
+ body.setAttribute('data-bs-theme', event.matches ? 'dark' : 'light');
+ }
}
for (const elevation of getElements('.rack_elevation')) {
const svg = elevation.contentDocument?.querySelector('svg') ?? null;
if (svg !== null) {
- svg.setAttribute(`data-bs-theme`, targetMode);
+ svg.setAttribute(`data-bs-theme`, theme);
}
}
}
@@ -40,7 +53,7 @@ function updateElements(targetMode: ColorMode): void {
*
* @param mode Target color mode.
*/
-export function setColorMode(mode: ColorMode): void {
+export function setColorMode(mode: ColorModePreference): void {
storeColorMode(mode);
updateElements(mode);
}
@@ -49,11 +62,11 @@ export function setColorMode(mode: ColorMode): void {
* 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');
+ const prevValue = localStorage.getItem(COLOR_MODE_PREFERENCE_KEY);
+ if (isColorMode(prevValue)) {
+ setColorMode(prevValue === 'light' ? 'dark' : 'light');
+ } else if (prevValue === 'auto') {
+ console.log('Ignoring color mode toggle in auto mode');
} else {
console.warn('Unable to determine the current color mode');
}
@@ -64,33 +77,29 @@ function handleColorModeToggle(): void {
*/
function defaultColorMode(): void {
// Get the current color mode value from local storage.
- const currentValue = localStorage.getItem(COLOR_MODE_KEY) as Nullable;
-
- if (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;
- }
- }
+ const currentValue = localStorage.getItem(COLOR_MODE_PREFERENCE_KEY) as Nullable;
if (isTruthy(currentValue) && isColorMode(currentValue)) {
return setColorMode(currentValue);
}
+ let preference: ColorModePreference = 'none';
+
+ // Determine if the user prefers dark, light or auto mode.
+ if (preference !== 'auto') {
+ for (const mode of ['dark', 'light']) {
+ if (window.matchMedia(`(prefers-color-scheme: ${mode})`).matches) {
+ preference = mode as ColorModePreference;
+ break;
+ }
+ }
+ }
switch (preference) {
+ case 'auto':
case 'dark':
- return setColorMode('dark');
case 'light':
- return setColorMode('light');
+ return setColorMode(preference);
case 'none':
- return setColorMode('light');
default:
return setColorMode('light');
}
diff --git a/netbox/project-static/src/global.d.ts b/netbox/project-static/src/global.d.ts
index d7244339a..1afc4a7b2 100644
--- a/netbox/project-static/src/global.d.ts
+++ b/netbox/project-static/src/global.d.ts
@@ -78,4 +78,4 @@ declare const messages: string[];
type FormControls = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
type ColorMode = 'light' | 'dark';
-type ColorModePreference = ColorMode | 'none';
+type ColorModePreference = ColorMode | 'none' | 'auto';