diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/css/style.css | 44 | ||||
-rw-r--r-- | source/css/style.min.css | 2 | ||||
-rw-r--r-- | source/js/script.js | 155 | ||||
-rw-r--r-- | source/js/script.min.js | 2 | ||||
-rw-r--r-- | source/js/script.ts | 168 |
5 files changed, 319 insertions, 52 deletions
diff --git a/source/css/style.css b/source/css/style.css index 3a9430f..5a62406 100644 --- a/source/css/style.css +++ b/source/css/style.css @@ -8,19 +8,45 @@ pre { -webkit-filter: grayscale(1) !important; } +:root { + --transition-speed: unset; + --background-color: white; + --brightness: 1; + --contrast: 1; +} + @media(prefers-color-scheme:dark) { - * { - transition: backgourd-color .5s - } - .card { - box-shadow: none!important; - background-color: #121212!important - } - img:not([src*=".svg"]) { - filter: brightness(.7) contrast(1.2); + + :root:not([data-user-color-scheme]) { + --box-shadow: none!important; + --transition-speed: 0.5s; + --background-color: #121212!important; + --brightness: 0.7; + --contrast: 1.2; } } +[data-user-color-scheme='dark'] { + --box-shadow: none!important; + --transition-speed: 0.5s; + --background-color: #121212!important; + --brightness: 0.7; + --contrast: 1.2; +} + +* { + transition: backgourd-color var(--transition-speed); +} + +.card { + box-shadow: var(--box-shadow); + background-color: var(--background-color); +} + +img:not([src*=".svg"]) { + filter: brightness(--brightness) contrast(--contrast); +} + @media screen and (max-width:1023px) { .navbar-menu { box-shadow: 0 8px 16px -8px rgb(10 10 10 / 10%)!important diff --git a/source/css/style.min.css b/source/css/style.min.css index d8c3a0b..ac94b6b 100644 --- a/source/css/style.min.css +++ b/source/css/style.min.css @@ -1 +1 @@ -pre{background-color:none!important;padding:0!important}#mourn{filter:grayscale(1)!important;-webkit-filter:grayscale(1)!important}@media(prefers-color-scheme:dark){*{transition:backgourd-color .5s}.card{box-shadow:none!important;background-color:#121212!important}img:not([src*=".svg"]){filter:brightness(.7) contrast(1.2)}}@media screen and (max-width:1023px){.navbar-menu{box-shadow:0 8px 16px -8px rgb(10 10 10 / 10%)!important}.post-content{padding-top:0!important}.post-card{margin:0!important}}.post-podcast-player{padding-bottom:1.5rem}.post-card{margin:0 1rem 0 1rem}.navbar-brand .navbar-item:hover{background-color:inherit!important}.entry{margin-bottom:1.5rem!important;margin-top:1.5rem!important;transition:box-shadow .1s}.entry:hover{box-shadow:0 1em 2em -.125em rgb(10 10 10 / 10%),0 0 0 1px rgb(10 10 10 / 2%)}.pagination-bar{padding-left:.25rem;padding-right:.25rem}.footer{padding:1rem 1rem 1rem!important;background-color:transparent!important}.subnav-top{border-bottom:1px solid;padding-bottom:1rem;display:inline}.subnav-bottom{margin-top:1.6rem!important}
\ No newline at end of file +pre{background-color:none!important;padding:0!important}#mourn{filter:grayscale(1)!important;-webkit-filter:grayscale(1)!important}:root{--transition-speed:unset;--background-color:white;--brightness:1;--contrast:1}@media(prefers-color-scheme:dark){:root:not([data-user-color-scheme]){--box-shadow:none!important;--transition-speed:0.5s;--background-color:#121212!important;--brightness:0.7;--contrast:1.2}}[data-user-color-scheme=dark]{--box-shadow:none!important;--transition-speed:0.5s;--background-color:#121212!important;--brightness:0.7;--contrast:1.2}*{transition:backgourd-color var(--transition-speed)}.card{box-shadow:var(--box-shadow);background-color:var(--background-color)}img:not([src*=".svg"]){filter:brightness(--brightness) contrast(--contrast)}@media screen and (max-width:1023px){.navbar-menu{box-shadow:0 8px 16px -8px rgb(10 10 10 / 10%)!important}.post-content{padding-top:0!important}.post-card{margin:0!important}}.post-podcast-player{padding-bottom:1.5rem}.post-card{margin:0 1rem 0 1rem}.navbar-brand .navbar-item:hover{background-color:inherit!important}.entry{margin-bottom:1.5rem!important;margin-top:1.5rem!important;transition:box-shadow .1s}.entry:hover{box-shadow:0 1em 2em -.125em rgb(10 10 10 / 10%),0 0 0 1px rgb(10 10 10 / 2%)}.pagination-bar{padding-left:.25rem;padding-right:.25rem}.footer{padding:1rem 1rem 1rem!important;background-color:transparent!important}.subnav-top{border-bottom:1px solid;padding-bottom:1rem;display:inline}.subnav-bottom{margin-top:1.5rem!important}
\ No newline at end of file diff --git a/source/js/script.js b/source/js/script.js index 8a3daa5..665d379 100644 --- a/source/js/script.js +++ b/source/js/script.js @@ -1,4 +1,3 @@ -"use strict"; /* Add elements listener */ window.addEventListener("load", function () { // Menubar burgers click listenr @@ -20,15 +19,17 @@ window.addEventListener("load", function () { var clsname = ".auto-dark"; var elements = Array.prototype.slice.call(document.querySelectorAll(clsname), 0); elements.forEach(function (element) { - if (mode === SystemDarkmodePrefrence.dark) { + if (mode === SystemDarkmodePreference.dark) { element === null || element === void 0 ? void 0 : element.classList.add("is-dark"); element === null || element === void 0 ? void 0 : element.classList.remove("is-light"); } else { + // If mode == null , fallback to light mode element === null || element === void 0 ? void 0 : element.classList.add("is-light"); element === null || element === void 0 ? void 0 : element.classList.remove("is-dark"); } }); + console.debug("".concat(clsname, " class changed to ").concat(enumModeToStringMode(mode))); }; darklistener.add(colorman); // Add logo color selector @@ -38,36 +39,128 @@ window.addEventListener("load", function () { var lightsrc = logo === null || logo === void 0 ? void 0 : logo.getAttribute("data-src-lightmode"); if (!logo || !darksrc || !lightsrc) return; - var src = (mode === SystemDarkmodePrefrence.dark) ? darksrc : lightsrc; + var src = (mode === SystemDarkmodePreference.dark) ? darksrc : lightsrc; logo.setAttribute("src", src); + console.debug("logo src changed to ".concat(enumModeToStringMode(mode), "src")); }; darklistener.add(logoman); + // Add player theme handler + var playerman = function (mode) { + var shikwasa = document.body.querySelector('div[data-name="shikwasa"]'); + shikwasa === null || shikwasa === void 0 ? void 0 : shikwasa.setAttribute("data-theme", enumModeToStringMode(mode) ? enumModeToStringMode(mode) : "light"); + console.debug("shikwasa theme: ".concat(shikwasa === null || shikwasa === void 0 ? void 0 : shikwasa.getAttribute("data-theme"))); + }; + darklistener.add(playerman); + // Listen after all the setups (to get the handler work :p). + darklistener.listen(); }); /* Darkmode listener */ -var SystemDarkmodePrefrence; -(function (SystemDarkmodePrefrence) { - SystemDarkmodePrefrence[SystemDarkmodePrefrence["dark"] = 0] = "dark"; - SystemDarkmodePrefrence[SystemDarkmodePrefrence["light"] = 1] = "light"; -})(SystemDarkmodePrefrence || (SystemDarkmodePrefrence = {})); +var SystemDarkmodePreference; +(function (SystemDarkmodePreference) { + SystemDarkmodePreference[SystemDarkmodePreference["dark"] = 0] = "dark"; + SystemDarkmodePreference[SystemDarkmodePreference["light"] = 1] = "light"; +})(SystemDarkmodePreference || (SystemDarkmodePreference = {})); ; +var modeMap = { + dark: SystemDarkmodePreference.dark, + light: SystemDarkmodePreference.light +}; +var invertDarkModeObj = { + 'dark': 'light', + 'light': 'dark' +}; +var enumModeToStringMode = function (i) { + var keysArray = Object.keys(modeMap); + var result = keysArray.filter(function (key) { return isValidKey(key, modeMap) && modeMap[key] === i; }); + return result ? result[0] : undefined; +}; var DarkmodeListener = /** @class */ (function () { function DarkmodeListener() { var darking = window.matchMedia('(prefers-color-scheme: dark)').matches; - this._mode = darking ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; + this._mode = darking ? SystemDarkmodePreference.dark : SystemDarkmodePreference.light; this._handlers = []; - this._listen(); } - DarkmodeListener.prototype._listen = function () { + DarkmodeListener.prototype.listen = function () { var _this = this; - var media = window.matchMedia('(prefers-color-scheme: dark)'); - var callback = function (event) { - var mode = event.matches ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; - _this._handlers.forEach(function (handler) { - handler(mode); - }); - _this._mode = mode; + var rootElement = document.documentElement; + var darkModeStorageKey = 'user-color-scheme'; + var rootElementDarkModeAttributeName = 'data-user-color-scheme'; + var darkModeTogglebuttonElement = document.getElementById('btn-toggle-dark'); + var resetRootDarkModeAttributeAndLS = function () { + rootElement.removeAttribute(rootElementDarkModeAttributeName); + removeLS(darkModeStorageKey); + }; + // Partially taken from https://blog.skk.moe/post/hello-darkmode-my-old-friend, CC BY-NC-SA 4.0 + var applyCustomDarkModeSettings = function (mode) { + // 接受从「开关」处传来的模式,或者从 localStorage 读取 + var LSSetting = getLS(darkModeStorageKey) == 'dark' ? SystemDarkmodePreference.dark : 'light' ? SystemDarkmodePreference.light : null; + var currentSetting = mode || LSSetting; + console.debug("applyCustomDarkModeSettings: ".concat(currentSetting)); + if (currentSetting === _this._mode) { + // 当用户自定义的显示模式和 prefers-color-scheme 相同时重置、恢复到自动模式 + resetRootDarkModeAttributeAndLS(); + console.debug('Resetting to auto mode...'); + } + else if (currentSetting == SystemDarkmodePreference.dark || currentSetting == SystemDarkmodePreference.light) { + // 否则设置为用户自定义的显示模式 + rootElement.setAttribute(rootElementDarkModeAttributeName, enumModeToStringMode(currentSetting)); + console.debug('Setting prop: "data-user-color-scheme" in HTML...'); + } + else { + // 首次访问或从未使用过开关、localStorage 中没有存储的值,currentSetting 是 null + // 或者 localStorage 被篡改,currentSetting 不是合法值 + // 默认显示浅色主题 + resetRootDarkModeAttributeAndLS(); + console.debug('Initial setup, setting theme to light as default...'); + currentSetting = SystemDarkmodePreference.light; + } + var lightCSS = document.getElementById("bulma-light"); + var darkCSS = document.getElementById("bulma-dark"); + if (currentSetting == SystemDarkmodePreference.dark) { + rootElement.setAttribute(rootElementDarkModeAttributeName, 'dark'); + lightCSS.setAttribute("media", "none"); + darkCSS.setAttribute("media", "all"); + _this._handlers.forEach(function (handler) { + handler(SystemDarkmodePreference.dark); + console.debug('Invoking dark theme handler...'); + }); + console.debug('Dark theme applied.'); + } + else if (currentSetting == SystemDarkmodePreference.light) { + rootElement.setAttribute(rootElementDarkModeAttributeName, 'light'); + lightCSS.setAttribute("media", "all"); + darkCSS.setAttribute("media", "none"); + _this._handlers.forEach(function (handler) { + handler(SystemDarkmodePreference.light); + console.debug('Invoking light theme handler...'); + }); + console.debug('Light theme applied.'); + } + }; + var toggleCustomDarkMode = function () { + var currentSetting = getLS(darkModeStorageKey); + if (currentSetting === null) { + var curMode = enumModeToStringMode(_this._mode); + currentSetting = isValidKey(curMode, invertDarkModeObj) ? invertDarkModeObj[curMode] : currentSetting; + } + else if (currentSetting == 'dark' || 'light') { + // 从 localStorage 中读取模式,并取相反的模式 + currentSetting = isValidKey(currentSetting, invertDarkModeObj) ? invertDarkModeObj[currentSetting] : currentSetting; + } + else { + // 不知道出了什么幺蛾子,比如 localStorage 被篡改成非法值 + return; // 直接 return; + } + // 将相反的模式写入 localStorage + setLS(darkModeStorageKey, currentSetting); + return isValidKey(currentSetting, invertDarkModeObj) ? modeMap[currentSetting] : null; }; - media.addEventListener("change", callback); + applyCustomDarkModeSettings(null); + darkModeTogglebuttonElement === null || darkModeTogglebuttonElement === void 0 ? void 0 : darkModeTogglebuttonElement.addEventListener('click', function () { + // 当用户点击「开关」时,获得新的显示模式、写入 localStorage、并在页面上生效 + console.debug('User clicked button. Doing black magic now...'); + applyCustomDarkModeSettings(toggleCustomDarkMode()); + }); }; DarkmodeListener.prototype.add = function (callback) { callback(this._mode); @@ -80,3 +173,27 @@ var DarkmodeListener = /** @class */ (function () { }()); ; var darklistener = new DarkmodeListener(); +function setLS(k, v) { + try { + localStorage.setItem(k, v); + } + catch (e) { } +} +function removeLS(k) { + try { + localStorage.removeItem(k); + } + catch (e) { } +} +function getLS(k) { + try { + return localStorage.getItem(k); + } + catch (e) { + return null; // 与 localStorage 中没有找到对应 key 的行为一致 + } +} +// 使用isValidKey判断key是否存在对象类型中 +function isValidKey(key, obj) { + return key in obj; +} diff --git a/source/js/script.min.js b/source/js/script.min.js index 487569f..823246f 100644 --- a/source/js/script.min.js +++ b/source/js/script.min.js @@ -1 +1 @@ -"use strict"; window.addEventListener("load", function () { var a = Array.prototype.slice.call(document.querySelectorAll(".navbar-burger"), 0); if (a.length > 0) { a.forEach(function (d) { d.addEventListener("click", function () { var f = d.getAttribute("data-target"); d.classList.toggle("is-active"); if (!f) { return } var e = document.getElementById(f); e === null || e === void 0 ? void 0 : e.classList.toggle("is-active") }, false) }) } var c = function (f) { var e = ".auto-dark"; var d = Array.prototype.slice.call(document.querySelectorAll(e), 0); d.forEach(function (g) { if (f === SystemDarkmodePrefrence.dark) { g === null || g === void 0 ? void 0 : g.classList.add("is-dark"); g === null || g === void 0 ? void 0 : g.classList.remove("is-light") } else { g === null || g === void 0 ? void 0 : g.classList.add("is-light"); g === null || g === void 0 ? void 0 : g.classList.remove("is-dark") } }) }; darklistener.add(c); var b = function (h) { var e = document.getElementById("logo"); var g = e === null || e === void 0 ? void 0 : e.getAttribute("data-src-darkmode"); var d = e === null || e === void 0 ? void 0 : e.getAttribute("data-src-lightmode"); if (!e || !g || !d) { return } var f = (h === SystemDarkmodePrefrence.dark) ? g : d; e.setAttribute("src", f) }; darklistener.add(b) }); var SystemDarkmodePrefrence; (function (a) { a[a["dark"] = 0] = "dark"; a[a["light"] = 1] = "light" })(SystemDarkmodePrefrence || (SystemDarkmodePrefrence = {})); var DarkmodeListener = (function () { function a() { var b = window.matchMedia("(prefers-color-scheme: dark)").matches; this._mode = b ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; this._handlers = []; this._listen() } a.prototype._listen = function () { var d = this; var b = window.matchMedia("(prefers-color-scheme: dark)"); var c = function (e) { var f = e.matches ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; d._handlers.forEach(function (g) { g(f) }); d._mode = f }; b.addEventListener("change", c) }; a.prototype.add = function (b) { b(this._mode); this._handlers.push(b) }; a.prototype.mode = function () { return this._mode }; return a }()); var darklistener = new DarkmodeListener();
\ No newline at end of file +window.addEventListener("load",function(){var burgers=Array.prototype.slice.call(document.querySelectorAll(".navbar-burger"),0);0<burgers.length&&burgers.forEach(function(element){element.addEventListener("click",function(){var idstr=element.getAttribute("data-target");element.classList.toggle("is-active"),!idstr||null!=(idstr=document.getElementById(idstr))&&idstr.classList.toggle("is-active")},!1)}),darklistener.add(function(mode){Array.prototype.slice.call(document.querySelectorAll(".auto-dark"),0).forEach(function(element){mode===SystemDarkmodePreference.dark?(null!=element&&element.classList.add("is-dark"),null!=element&&element.classList.remove("is-light")):(null!=element&&element.classList.add("is-light"),null!=element&&element.classList.remove("is-dark"))}),console.debug("".concat(".auto-dark"," class changed to ").concat(enumModeToStringMode(mode)))}),darklistener.add(function(mode){var logo=document.getElementById("logo"),darksrc=null==logo?void 0:logo.getAttribute("data-src-darkmode"),lightsrc=null==logo?void 0:logo.getAttribute("data-src-lightmode");logo&&darksrc&&lightsrc&&(darksrc=mode===SystemDarkmodePreference.dark?darksrc:lightsrc,logo.setAttribute("src",darksrc),console.debug("logo src changed to ".concat(enumModeToStringMode(mode),"src")))});darklistener.add(function(mode){var shikwasa=document.body.querySelector('div[data-name="shikwasa"]');null!=shikwasa&&shikwasa.setAttribute("data-theme",enumModeToStringMode(mode)?enumModeToStringMode(mode):"light"),console.debug("shikwasa theme: ".concat(null==shikwasa?void 0:shikwasa.getAttribute("data-theme")))}),darklistener.listen()}),function(SystemDarkmodePreference){SystemDarkmodePreference[SystemDarkmodePreference.dark=0]="dark",SystemDarkmodePreference[SystemDarkmodePreference.light=1]="light"}(SystemDarkmodePreference=SystemDarkmodePreference||{});var SystemDarkmodePreference,modeMap={dark:SystemDarkmodePreference.dark,light:SystemDarkmodePreference.light},invertDarkModeObj={dark:"light",light:"dark"},enumModeToStringMode=function(i){var result=Object.keys(modeMap).filter(function(key){return isValidKey(key,modeMap)&&modeMap[key]===i});return result?result[0]:void 0},DarkmodeListener=function(){function DarkmodeListener(){var darking=window.matchMedia("(prefers-color-scheme: dark)").matches;this._mode=darking?SystemDarkmodePreference.dark:SystemDarkmodePreference.light,this._handlers=[]}return DarkmodeListener.prototype.listen=function(){function applyCustomDarkModeSettings(mode){var LSSetting="dark"==getLS(darkModeStorageKey)?SystemDarkmodePreference.dark:SystemDarkmodePreference.light,mode=mode||LSSetting,LSSetting=(console.debug("applyCustomDarkModeSettings: ".concat(mode)),mode===_this._mode?(resetRootDarkModeAttributeAndLS(),console.debug("Resetting to auto mode...")):mode==SystemDarkmodePreference.dark||mode==SystemDarkmodePreference.light?(rootElement.setAttribute(rootElementDarkModeAttributeName,enumModeToStringMode(mode)),console.debug('Setting prop: "data-user-color-scheme" in HTML...')):(resetRootDarkModeAttributeAndLS(),console.debug("Initial setup, setting theme to light as default..."),mode=SystemDarkmodePreference.light),document.getElementById("bulma-light")),darkCSS=document.getElementById("bulma-dark");mode==SystemDarkmodePreference.dark?(rootElement.setAttribute(rootElementDarkModeAttributeName,"dark"),LSSetting.setAttribute("media","none"),darkCSS.setAttribute("media","all"),_this._handlers.forEach(function(handler){handler(SystemDarkmodePreference.dark),console.debug("Invoking dark theme handler...")}),console.debug("Dark theme applied.")):mode==SystemDarkmodePreference.light&&(rootElement.setAttribute(rootElementDarkModeAttributeName,"light"),LSSetting.setAttribute("media","all"),darkCSS.setAttribute("media","none"),_this._handlers.forEach(function(handler){handler(SystemDarkmodePreference.light),console.debug("Invoking light theme handler...")}),console.debug("Light theme applied."))}var _this=this,rootElement=document.documentElement,darkModeStorageKey="user-color-scheme",rootElementDarkModeAttributeName="data-user-color-scheme",darkModeTogglebuttonElement=document.getElementById("btn-toggle-dark"),resetRootDarkModeAttributeAndLS=function(){rootElement.removeAttribute(rootElementDarkModeAttributeName),removeLS(darkModeStorageKey)};applyCustomDarkModeSettings(null),null!=darkModeTogglebuttonElement&&darkModeTogglebuttonElement.addEventListener("click",function(){var curMode,currentSetting;console.debug("User clicked button. Doing black magic now..."),applyCustomDarkModeSettings((currentSetting=null===(currentSetting=getLS(darkModeStorageKey))?isValidKey(curMode=enumModeToStringMode(_this._mode),invertDarkModeObj)?invertDarkModeObj[curMode]:currentSetting:isValidKey(currentSetting,invertDarkModeObj)?invertDarkModeObj[currentSetting]:currentSetting,setLS(darkModeStorageKey,currentSetting),isValidKey(currentSetting,invertDarkModeObj)?modeMap[currentSetting]:null))})},DarkmodeListener.prototype.add=function(callback){callback(this._mode),this._handlers.push(callback)},DarkmodeListener.prototype.mode=function(){return this._mode},DarkmodeListener}(),darklistener=new DarkmodeListener;function setLS(k,v){try{localStorage.setItem(k,v)}catch(e){}}function removeLS(k){try{localStorage.removeItem(k)}catch(e){}}function getLS(k){try{return localStorage.getItem(k)}catch(e){return null}}function isValidKey(key,obj){return key in obj} diff --git a/source/js/script.ts b/source/js/script.ts index 912c1d9..bfc1d64 100644 --- a/source/js/script.ts +++ b/source/js/script.ts @@ -17,73 +17,197 @@ window.addEventListener("load", () => { } // Add header hover page class changer - const colorman = (mode: SystemDarkmodePrefrence) => { + const colorman = (mode: SystemDarkmodePreference) => { let clsname: string = ".auto-dark"; const elements: Element[] = Array.prototype.slice.call( document.querySelectorAll(clsname), 0); elements.forEach((element) => { - if (mode === SystemDarkmodePrefrence.dark) { + if (mode === SystemDarkmodePreference.dark) { element?.classList.add("is-dark"); element?.classList.remove("is-light"); } else { + // If mode == null , fallback to light mode element?.classList.add("is-light"); element?.classList.remove("is-dark"); } }) + console.debug(`${clsname} class changed to ${enumModeToStringMode(mode)}`); } darklistener.add(colorman); // Add logo color selector - const logoman = (mode: SystemDarkmodePrefrence) => { + const logoman = (mode: SystemDarkmodePreference) => { const logo = document.getElementById("logo"); const darksrc = logo?.getAttribute("data-src-darkmode"); const lightsrc = logo?.getAttribute("data-src-lightmode"); if (!logo || !darksrc || !lightsrc) return; - let src = (mode === SystemDarkmodePrefrence.dark) ? darksrc : lightsrc; + let src = (mode === SystemDarkmodePreference.dark) ? darksrc : lightsrc; logo.setAttribute("src", src); + console.debug(`logo src changed to ${enumModeToStringMode(mode)}src`); } darklistener.add(logoman); - + + // Add player theme handler + const playerman = (mode: SystemDarkmodePreference) => { + const shikwasa = document.body.querySelector('div[data-name="shikwasa"]'); + shikwasa?.setAttribute("data-theme", enumModeToStringMode(mode) ? enumModeToStringMode(mode)! : "light"); + console.debug(`shikwasa theme: ${shikwasa?.getAttribute("data-theme")}`); + }; + darklistener.add(playerman); + + // Listen after all the setups (to get the handler work :p). + darklistener.listen(); }); /* Darkmode listener */ -enum SystemDarkmodePrefrence { +enum SystemDarkmodePreference { dark = 0, light = 1 }; +const modeMap = { + dark: SystemDarkmodePreference.dark, + light: SystemDarkmodePreference.light +}; + +const invertDarkModeObj: Object = { + 'dark': 'light', + 'light': 'dark' +}; + +const enumModeToStringMode = (i: SystemDarkmodePreference) => { + let keysArray = Object.keys(modeMap); + let result = keysArray.filter(key => isValidKey(key, modeMap) && modeMap[key] === i); + return result ? result[0] : undefined; +}; + class DarkmodeListener { - private _mode: SystemDarkmodePrefrence; - private _handlers: ((mode: SystemDarkmodePrefrence) => void)[]; + private _mode: SystemDarkmodePreference; + private _handlers: ((mode: SystemDarkmodePreference) => void)[]; public constructor() { let darking = window.matchMedia('(prefers-color-scheme: dark)').matches; - this._mode = darking ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; + this._mode = darking ? SystemDarkmodePreference.dark : SystemDarkmodePreference.light; this._handlers = []; - this._listen(); } - private _listen(): void { - let media = window.matchMedia('(prefers-color-scheme: dark)'); - let callback = (event: MediaQueryListEvent) => { - let mode = event.matches ? SystemDarkmodePrefrence.dark : SystemDarkmodePrefrence.light; - this._handlers.forEach(handler => { - handler(mode); - }); - this._mode = mode; - }; - media.addEventListener("change", callback); + public listen(): void { + const rootElement = document.documentElement; + const darkModeStorageKey = 'user-color-scheme'; + const rootElementDarkModeAttributeName = 'data-user-color-scheme'; + const darkModeTogglebuttonElement = document.getElementById('btn-toggle-dark'); + + const resetRootDarkModeAttributeAndLS = () => { + rootElement.removeAttribute(rootElementDarkModeAttributeName); + removeLS(darkModeStorageKey); + } + + // Partially taken from https://blog.skk.moe/post/hello-darkmode-my-old-friend, CC BY-NC-SA 4.0 + const applyCustomDarkModeSettings = (mode: any) => { + // 接受从「开关」处传来的模式,或者从 localStorage 读取 + let LSSetting = getLS(darkModeStorageKey) == 'dark' ? SystemDarkmodePreference.dark : 'light' ? SystemDarkmodePreference.light : null; + let currentSetting: SystemDarkmodePreference = mode || LSSetting; + console.debug(`applyCustomDarkModeSettings: ${currentSetting}`); + if (currentSetting === this._mode) { + // 当用户自定义的显示模式和 prefers-color-scheme 相同时重置、恢复到自动模式 + resetRootDarkModeAttributeAndLS(); + console.debug('Resetting to auto mode...'); + } else if (currentSetting == SystemDarkmodePreference.dark || currentSetting == SystemDarkmodePreference.light) { + // 否则设置为用户自定义的显示模式 + rootElement.setAttribute(rootElementDarkModeAttributeName, enumModeToStringMode(currentSetting)!); + console.debug('Setting prop: "data-user-color-scheme" in HTML...'); + } else { + // 首次访问或从未使用过开关、localStorage 中没有存储的值,currentSetting 是 null + // 或者 localStorage 被篡改,currentSetting 不是合法值 + // 默认显示浅色主题 + resetRootDarkModeAttributeAndLS(); + console.debug('Initial setup, setting theme to light as default...') + currentSetting = SystemDarkmodePreference.light; + } + let lightCSS = document.getElementById("bulma-light")! + let darkCSS = document.getElementById("bulma-dark")! + if (currentSetting == SystemDarkmodePreference.dark) { + rootElement.setAttribute(rootElementDarkModeAttributeName, 'dark'); + lightCSS.setAttribute("media", "none"); + darkCSS.setAttribute("media", "all"); + this._handlers.forEach(handler => { + handler(SystemDarkmodePreference.dark); + console.debug('Invoking dark theme handler...'); + }); + console.debug('Dark theme applied.'); + } else if (currentSetting == SystemDarkmodePreference.light) { + rootElement.setAttribute(rootElementDarkModeAttributeName, 'light'); + lightCSS.setAttribute("media", "all"); + darkCSS.setAttribute("media", "none"); + this._handlers.forEach(handler => { + handler(SystemDarkmodePreference.light); + console.debug('Invoking light theme handler...'); + }); + console.debug('Light theme applied.'); + } + + } + + const toggleCustomDarkMode = () => { + let currentSetting = getLS(darkModeStorageKey); + if (currentSetting === null) { + let curMode = enumModeToStringMode(this._mode)!; + currentSetting = isValidKey(curMode, invertDarkModeObj) ? invertDarkModeObj[curMode] : currentSetting; + } else if (currentSetting == 'dark' || 'light') { + // 从 localStorage 中读取模式,并取相反的模式 + currentSetting = isValidKey(currentSetting, invertDarkModeObj) ? invertDarkModeObj[currentSetting] : currentSetting; + } else { + // 不知道出了什么幺蛾子,比如 localStorage 被篡改成非法值 + return; // 直接 return; + } + // 将相反的模式写入 localStorage + setLS(darkModeStorageKey, currentSetting); + return isValidKey(currentSetting!, invertDarkModeObj) ? modeMap[currentSetting!] : null; + } + + applyCustomDarkModeSettings(null); + + darkModeTogglebuttonElement?.addEventListener('click', () => { + // 当用户点击「开关」时,获得新的显示模式、写入 localStorage、并在页面上生效 + console.debug('User clicked button. Doing black magic now...'); + applyCustomDarkModeSettings(toggleCustomDarkMode()); + }); } - public add(callback: (mode: SystemDarkmodePrefrence) => void): void { + public add(callback: (mode: SystemDarkmodePreference) => void): void { callback(this._mode); this._handlers.push(callback); } - public mode(): SystemDarkmodePrefrence { + public mode(): SystemDarkmodePreference { return this._mode; } }; var darklistener = new DarkmodeListener(); + +function setLS(k: string, v: any) { + try { + localStorage.setItem(k, v); + } catch (e) { } +} + +function removeLS(k: string) { + try { + localStorage.removeItem(k); + } catch (e) { } +} + +function getLS(k: string) { + try { + return localStorage.getItem(k); + } catch (e) { + return null; // 与 localStorage 中没有找到对应 key 的行为一致 + } +} + +// 使用isValidKey判断key是否存在对象类型中 +function isValidKey(key: string, obj: object): key is keyof typeof obj { + return key in obj; +}
\ No newline at end of file |