UNPKG

use-theme-toggle

Version:

A lightweight theme toggler with animated transitions and customizable strategies.

238 lines (235 loc) 6.63 kB
// src/transitions/base.ts function BaseTransition() { return (toggle, { root }, _e) => { const transition = document.startViewTransition(() => { toggle(); root.classList.add("base-transition"); }); transition.finished.finally(() => { root.classList.remove("base-transition"); }); }; } // src/transitions/diffusion.ts function ensureStaticStyleInjected(darkSelector) { const id = "diffusion-transition-style"; if (document.getElementById(id)) return; const style = document.createElement("style"); style.id = id; style.textContent = ` ::view-transition-old(*), ::view-transition-new(*) { animation: none; mix-blend-mode: normal; } ::view-transition-new(root) { z-index: 999; } ::view-transition-old(root) { z-index: 1; } ${darkSelector}::view-transition-new(root) { z-index: 1; } ${darkSelector}::view-transition-old(root) { z-index: 999; } `; document.head.appendChild(style); } function Diffusion() { return (toggle, options, e) => { const x = e.clientX; const y = e.clientY; const radius = Math.hypot( Math.max(x, window.innerWidth), Math.max(y, window.innerHeight) ); let isDark = false; ensureStaticStyleInjected(options.darkSelector); const transition = document.startViewTransition(() => { const current = toggle(); isDark = current === options.dark; options.root.classList.add("diffusion-transition"); }); transition.ready.then(() => { const clipPath = [ `circle(0px at ${x}px ${y}px)`, `circle(${radius}px at ${x}px ${y}px)` ]; options.root.animate({ clipPath: isDark ? [...clipPath].reverse() : [...clipPath] }, { duration: options.duration, easing: options.easing, pseudoElement: isDark ? "::view-transition-old(root)" : "::view-transition-new(root)" }); }); transition.finished.then(() => { options.root.classList.remove("diffusion-transition"); }); }; } // src/transitions/slide.ts function ensureStaticStyleInjected2(darkSelector) { const id = "slide-transition-style"; if (document.getElementById(id)) return; const style = document.createElement("style"); style.id = id; style.textContent = ` ::view-transition-old(*), ::view-transition-new(*) { animation: none; mix-blend-mode: normal; } ::view-transition-new(root) { z-index: 999; } ::view-transition-old(root) { z-index: 1; } ${darkSelector}::view-transition-new(root) { z-index: 1; } ${darkSelector}::view-transition-old(root) { z-index: 999; } `; document.head.appendChild(style); } function Slide() { return (toggle, options, _e) => { ensureStaticStyleInjected2(options.darkSelector); let isDark = false; const transition = document.startViewTransition(() => { const next = toggle(); isDark = next === options.dark; document.documentElement.classList.add("slide-transition"); }); transition.ready.then(() => { const duration = options.duration; const easing = options.easing; const newPseudo = isDark ? "::view-transition-new(root)" : "::view-transition-new(root)"; const oldPseudo = isDark ? "::view-transition-old(root)" : "::view-transition-old(root)"; options.root.animate([ { transform: `translateX(${isDark ? "100%" : "-100%"})` }, { transform: "translateX(0)" } ], { duration, easing, pseudoElement: newPseudo }); options.root.animate([ { transform: "translateX(0)" }, { transform: `translateX(${isDark ? "-100%" : "100%"})` } ], { duration, easing, pseudoElement: oldPseudo }); }); transition.finished.then(() => { document.documentElement.classList.remove("slide-transition"); }); }; } // src/themeToggle.ts function useThemeToggle(_arg1, _arg2) { if (typeof window === "undefined" || typeof document === "undefined" || typeof localStorage === "undefined") { return { toggle() { }, onThemeToggled() { } }; } let loader; let options; const defaultOptions = { mode: "class", key: "theme", light: "light", dark: "dark", easing: "ease-out", duration: 500 }; if (typeof _arg1 === "function") { const maybeLoader = _arg1; options = { ...defaultOptions, ..._arg2 }; if (maybeLoader.length <= 1) { loader = maybeLoader(); } else { loader = maybeLoader; } } else { loader = null; options = { ...defaultOptions, ..._arg1 }; } const { mode, light, dark, key } = options; let attributeName = "data-theme"; if (mode === "attribute") { attributeName = options.attribute || attributeName; } const root = document.documentElement; let toggledCallback = (_currentTheme) => { }; let saved = localStorage.getItem(key); if (!saved) { localStorage.setItem(key, light); saved = light; } let current = saved === dark ? dark : light; const resumeTheme = { class: () => root.classList.add(current), attribute: () => root.setAttribute(attributeName, current), both: () => { root.classList.add(current); root.setAttribute(attributeName, current); } }; resumeTheme[mode]?.(); const darkSelector = options.mode === "class" ? `.${options.dark}` : `[${attributeName}=${options.dark}]`; const toggleClassOrAttribute = () => { const next = current === dark ? light : dark; const toggleMap = { class: () => { root.classList.remove(current); root.classList.add(next); }, attribute: () => { root.setAttribute(attributeName, next); }, both: () => { root.classList.remove(current); root.classList.add(next); root.setAttribute(attributeName, next); } }; toggleMap[mode]?.(); current = next; localStorage.setItem(key, next); toggledCallback(next); return next; }; return { toggle(e) { if (!loader || typeof loader !== "function") { const loader2 = BaseTransition(); return loader2(toggleClassOrAttribute, { ...options, root, darkSelector, previousTheme: current }, e); } return loader?.(toggleClassOrAttribute, { ...options, root, darkSelector, previousTheme: current }, e); }, onThemeToggled(cb) { if (typeof cb === "function") { toggledCallback = cb; toggledCallback(current); } } }; } export { BaseTransition, Diffusion, Slide, useThemeToggle }; //# sourceMappingURL=index.js.map