standard-ui/components/dist/theme-toggle.js
2021-07-19 00:56:26 +05:30

186 lines
6.2 KiB
JavaScript

const themeToggle = document.createElement('template')
themeToggle.innerHTML = `
<style>
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
:host{
cursor: pointer;
--height: 2.5rem;
--width: 2.5rem;
}
.theme-toggle {
display: flex;
position: relative;
width: 1.4rem;
height: 1.4rem;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
.theme-toggle::after{
content: '';
position: absolute;
height: var(--height);
width: var(--width);
top: 50%;
left: 50%;
opacity: 0;
border-radius: 50%;
pointer-events: none;
transition: transform 0.3s, opacity 0.3s;
transform: translate(-50%, -50%) scale(1.2);
background-color: rgba(var(--text-color), 0.12);
}
:host(:focus-within) .theme-toggle{
outline: none;
}
:host(:focus-within) .theme-toggle::after{
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
.icon {
position: absolute;
height: 100%;
width: 100%;
fill: rgba(var(--text-color), 1);
transition: transform 0.6s;
}
.theme-switcher__checkbox {
display: none;
}
:host([checked]) .moon-icon {
transform: scale(0) rotate(90deg);
}
:host(:not([checked])) .sun-icon {
transform: scale(0) rotate(-90deg);
}
</style>
<label class="theme-toggle" title="Change theme" tabindex="0">
<slot name="light-mode-icon">
<svg class="icon moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24"
height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M10 6a8 8 0 0 0 11.955 6.956C21.474 18.03 17.2 22 12 22 6.477 22 2 17.523 2 12c0-5.2 3.97-9.474 9.044-9.955A7.963 7.963 0 0 0 10 6zm-6 6a8 8 0 0 0 8 8 8.006 8.006 0 0 0 6.957-4.045c-.316.03-.636.045-.957.045-5.523 0-10-4.477-10-10 0-.321.015-.64.045-.957A8.006 8.006 0 0 0 4 12zm14.164-9.709L19 2.5v1l-.836.209a2 2 0 0 0-1.455 1.455L16.5 6h-1l-.209-.836a2 2 0 0 0-1.455-1.455L13 3.5v-1l.836-.209A2 2 0 0 0 15.29.836L15.5 0h1l.209.836a2 2 0 0 0 1.455 1.455zm5 5L24 7.5v1l-.836.209a2 2 0 0 0-1.455 1.455L21.5 11h-1l-.209-.836a2 2 0 0 0-1.455-1.455L18 8.5v-1l.836-.209a2 2 0 0 0 1.455-1.455L20.5 5h1l.209.836a2 2 0 0 0 1.455 1.455z" />
</svg>
</slot>
<slot name="dark-mode-icon">
<svg class="icon sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z" />
</svg>
</slot>
</label>
`
class ThemeToggle extends HTMLElement {
constructor() {
super();
this.attachShadow({
mode: 'open'
}).append(themeToggle.content.cloneNode(true))
this.isChecked = false
this.hasTheme = 'light'
this.toggleState = this.toggleState.bind(this)
this.fireEvent = this.fireEvent.bind(this)
this.handleThemeChange = this.handleThemeChange.bind(this)
}
static get observedAttributes() {
return ['checked'];
}
daylight() {
this.hasTheme = 'light'
document.body.dataset.theme = 'light'
this.setAttribute('aria-checked', 'false')
}
nightlight() {
this.hasTheme = 'dark'
document.body.dataset.theme = 'dark'
this.setAttribute('aria-checked', 'true')
}
toggleState() {
this.toggleAttribute('checked')
this.fireEvent()
}
handleKeyDown(e) {
if (e.code === 'Space') {
this.toggleState()
}
}
handleThemeChange(e) {
if (e.detail.theme !== this.hasTheme) {
if (e.detail.theme === 'dark') {
this.setAttribute('checked', '')
}
else {
this.removeAttribute('checked')
}
}
}
fireEvent() {
this.dispatchEvent(
new CustomEvent('themechange', {
bubbles: true,
composed: true,
detail: {
theme: this.hasTheme
}
})
)
}
connectedCallback() {
this.setAttribute('role', 'switch')
this.setAttribute('aria-label', 'theme toggle')
if (localStorage.theme === "dark") {
this.nightlight();
this.setAttribute('checked', '')
} else if (localStorage.theme === "light") {
this.daylight();
this.removeAttribute('checked')
}
else {
if (window.matchMedia(`(prefers-color-scheme: dark)`).matches) {
this.nightlight();
this.setAttribute('checked', '')
} else {
this.daylight();
this.removeAttribute('checked')
}
}
this.addEventListener("click", this.toggleState);
this.addEventListener("keydown", this.handleKeyDown);
document.addEventListener('themechange', this.handleThemeChange)
}
disconnectedCallback() {
this.removeEventListener("click", this.toggleState);
this.removeEventListener("keydown", this.handleKeyDown);
document.removeEventListener('themechange', this.handleThemeChange)
}
attributeChangedCallback(name, oldVal, newVal) {
if (name === 'checked') {
if (this.hasAttribute('checked')) {
this.nightlight();
localStorage.setItem("theme", "dark");
} else {
this.daylight();
localStorage.setItem("theme", "light");
}
}
}
}
window.customElements.define('theme-toggle', ThemeToggle);