refactor theme toggle; add delegate-it
This commit is contained in:
parent
28511efd08
commit
47a2fc8153
|
@ -0,0 +1,90 @@
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
/** Keeps track of raw listeners added to the base elements to avoid duplication */
|
||||||
|
const ledger = new WeakMap();
|
||||||
|
function editLedger(wanted, baseElement, callback, setup) {
|
||||||
|
var _a, _b;
|
||||||
|
if (!wanted && !ledger.has(baseElement)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const elementMap = (_a = ledger.get(baseElement)) !== null && _a !== void 0 ? _a : new WeakMap();
|
||||||
|
ledger.set(baseElement, elementMap);
|
||||||
|
if (!wanted && !ledger.has(baseElement)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const setups = (_b = elementMap.get(callback)) !== null && _b !== void 0 ? _b : new Set();
|
||||||
|
elementMap.set(callback, setups);
|
||||||
|
const existed = setups.has(setup);
|
||||||
|
if (wanted) {
|
||||||
|
setups.add(setup);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setups.delete(setup);
|
||||||
|
}
|
||||||
|
return existed && wanted;
|
||||||
|
}
|
||||||
|
function isEventTarget(elements) {
|
||||||
|
return typeof elements.addEventListener === 'function';
|
||||||
|
}
|
||||||
|
function safeClosest(event, selector) {
|
||||||
|
let target = event.target;
|
||||||
|
if (target instanceof Text) {
|
||||||
|
target = target.parentElement;
|
||||||
|
}
|
||||||
|
if (target instanceof Element && event.currentTarget instanceof Element) {
|
||||||
|
// `.closest()` may match ancestors of `currentTarget` but we only need its children
|
||||||
|
const closest = target.closest(selector);
|
||||||
|
if (closest && event.currentTarget.contains(closest)) {
|
||||||
|
return closest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This type isn't exported as a declaration, so it needs to be duplicated above
|
||||||
|
function delegate(base, selector, type, callback, options) {
|
||||||
|
// Handle Selector-based usage
|
||||||
|
if (typeof base === 'string') {
|
||||||
|
base = document.querySelectorAll(base);
|
||||||
|
}
|
||||||
|
// Handle Array-like based usage
|
||||||
|
if (!isEventTarget(base)) {
|
||||||
|
const subscriptions = Array.prototype.map.call(base, (element) => delegate(element, selector, type, callback, options));
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
for (const subscription of subscriptions) {
|
||||||
|
subscription.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// `document` should never be the base, it's just an easy way to define "global event listeners"
|
||||||
|
const baseElement = base instanceof Document ? base.documentElement : base;
|
||||||
|
// Handle the regular Element usage
|
||||||
|
const capture = Boolean(typeof options === 'object' ? options.capture : options);
|
||||||
|
const listenerFn = (event) => {
|
||||||
|
const delegateTarget = safeClosest(event, selector);
|
||||||
|
if (delegateTarget) {
|
||||||
|
event.delegateTarget = delegateTarget;
|
||||||
|
callback.call(baseElement, event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Drop unsupported `once` option https://github.com/fregante/delegate-it/pull/28#discussion_r863467939
|
||||||
|
if (typeof options === 'object') {
|
||||||
|
delete options.once;
|
||||||
|
}
|
||||||
|
const setup = JSON.stringify({ selector, type, capture });
|
||||||
|
const isAlreadyListening = editLedger(true, baseElement, callback, setup);
|
||||||
|
const delegateSubscription = {
|
||||||
|
destroy() {
|
||||||
|
baseElement.removeEventListener(type, listenerFn, options);
|
||||||
|
editLedger(false, baseElement, callback, setup);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (!isAlreadyListening) {
|
||||||
|
baseElement.addEventListener(type, listenerFn, options);
|
||||||
|
}
|
||||||
|
return delegateSubscription;
|
||||||
|
}
|
||||||
|
// export default delegate;
|
||||||
|
|
||||||
|
window.delegate = delegate;
|
||||||
|
})();
|
|
@ -26,6 +26,27 @@
|
||||||
|
|
||||||
{{- partial "extend_footer.html" . }}
|
{{- partial "extend_footer.html" . }}
|
||||||
|
|
||||||
|
{{/* Load delegate script */}}
|
||||||
|
{{- $delegate := resources.Get "js/delegate-it.js" }}
|
||||||
|
<script src="{{ $delegate.RelPermalink }}" data-no-instant
|
||||||
|
{{- if site.Params.assets.disableFingerprinting }}integrity="{{ $delegate.Data.Integrity }}"{{- end }}
|
||||||
|
></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
/* theme toggle */
|
||||||
|
const disableThemeToggle = '{{ if site.Params.disableThemeToggle }}1{{ end }}' == '1';
|
||||||
|
if (disableThemeToggle) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// listen to toggle button
|
||||||
|
document.getElementById("theme-toggle").addEventListener("click", () => {
|
||||||
|
window.dispatchEvent(new CustomEvent('toggle-theme'));
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
let menu = document.getElementById('menu')
|
let menu = document.getElementById('menu')
|
||||||
|
@ -75,20 +96,6 @@
|
||||||
</script>
|
</script>
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
|
||||||
{{- if (not site.Params.disableThemeToggle) }}
|
|
||||||
<script>
|
|
||||||
document.getElementById("theme-toggle").addEventListener("click", () => {
|
|
||||||
if (document.body.className.includes("dark")) {
|
|
||||||
document.body.classList.remove('dark');
|
|
||||||
localStorage.setItem("pref-theme", 'light');
|
|
||||||
} else {
|
|
||||||
document.body.classList.add('dark');
|
|
||||||
localStorage.setItem("pref-theme", 'dark');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{- /* Base64Email */}}
|
{{- /* Base64Email */}}
|
||||||
{{- if (.Param "MaskedEmail") }}
|
{{- if (.Param "MaskedEmail") }}
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -1,43 +1,61 @@
|
||||||
{{- /* theme-toggle is enabled */}}
|
<script data-no-instant>
|
||||||
{{- if (not site.Params.disableThemeToggle) }}
|
function switchTheme(theme) {
|
||||||
{{- /* theme is light */}}
|
switch (theme) {
|
||||||
{{- if (eq site.Params.defaultTheme "light") }}
|
case 'light':
|
||||||
<script>
|
document.body.classList.remove('dark');
|
||||||
if (localStorage.getItem("pref-theme") === "dark") {
|
break;
|
||||||
|
case 'dark':
|
||||||
document.body.classList.add('dark');
|
document.body.classList.add('dark');
|
||||||
}
|
break;
|
||||||
|
// auto
|
||||||
</script>
|
default:
|
||||||
{{- /* theme is dark */}}
|
|
||||||
{{- else if (eq site.Params.defaultTheme "dark") }}
|
|
||||||
<script>
|
|
||||||
if (localStorage.getItem("pref-theme") === "light") {
|
|
||||||
document.body.classList.remove('dark')
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
{{- else }}
|
|
||||||
{{- /* theme is auto */}}
|
|
||||||
<script>
|
|
||||||
if (localStorage.getItem("pref-theme") === "dark") {
|
|
||||||
document.body.classList.add('dark');
|
|
||||||
} else if (localStorage.getItem("pref-theme") === "light") {
|
|
||||||
document.body.classList.remove('dark')
|
|
||||||
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
||||||
document.body.classList.add('dark');
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
{{- end }}
|
|
||||||
{{- /* theme-toggle is disabled and theme is auto */}}
|
|
||||||
{{- else if (and (ne site.Params.defaultTheme "light") (ne site.Params.defaultTheme "dark"))}}
|
|
||||||
<script>
|
|
||||||
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||||
document.body.classList.add('dark');
|
document.body.classList.add('dark');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isDarkTheme() {
|
||||||
|
return document.body.className.includes("dark");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPrefTheme() {
|
||||||
|
return localStorage.getItem("pref-theme");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPrefTheme(theme) {
|
||||||
|
switchTheme(theme)
|
||||||
|
localStorage.setItem("pref-theme", theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleThemeCallbacks = []
|
||||||
|
toggleThemeCallbacks.push((isDark) => {
|
||||||
|
// console.log('window toggle-theme')
|
||||||
|
if (isDark) {
|
||||||
|
setPrefTheme('light');
|
||||||
|
} else {
|
||||||
|
setPrefTheme('dark');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// listen to set-theme event,
|
||||||
|
// because window is never changed by InstantClick,
|
||||||
|
// we add the listener to window to ensure the event is always received
|
||||||
|
window.addEventListener('toggle-theme', function() {
|
||||||
|
const isDark = isDarkTheme()
|
||||||
|
toggleThemeCallbacks.forEach(callback => callback(isDark))
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
// load theme, as early as possible
|
||||||
|
(function() {
|
||||||
|
const defaultTheme = '{{ site.Params.defaultTheme | default "light" }}';
|
||||||
|
const prefTheme = getPrefTheme();
|
||||||
|
const theme = prefTheme ? prefTheme : defaultTheme;
|
||||||
|
|
||||||
|
switchTheme(theme);
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
|
|
Loading…
Reference in New Issue