UNPKG

9.14 kBJavaScriptView Raw
1// @license © 2019 Google LLC. Licensed under the Apache License, Version 2.0.
2const e=window;const n=document;const t=e.localStorage;const i="(prefers-color-scheme: dark)";const a=["(prefers-color-scheme: light)","(prefers-color-scheme: no-preference)"];const o="light";const r="dark";const s="remember";const d="legend";const h="toggle";const c="switch";const l="appearance";const b="permanent";const m="mode";const p="colorschemechange";const k="permanentcolorscheme";const u="all";const g="not all";const f="dark-mode-toggle";const v="https://googlechromelabs.github.io/dark-mode-toggle/demo/";const L=(e,n,t=n)=>{Object.defineProperty(e,t,{enumerable:true,get(){const e=this.getAttribute(n);return e===null?"":e},set(e){this.setAttribute(n,e)}})};const x=(e,n,t=n)=>{Object.defineProperty(e,t,{enumerable:true,get(){return this.hasAttribute(n)},set(e){if(e){this.setAttribute(n,"")}else{this.removeAttribute(n)}}})};const y=n.createElement("template");y.innerHTML=`\n<style>\n*,\n::before,\n::after {\n box-sizing: border-box;\n}\n\n:host {\n contain: content;\n display: block;\n}\n\n:host([hidden]) {\n display: none;\n}\n\nform {\n background-color: var(--${f}-background-color, transparent);\n color: var(--${f}-color, inherit);\n padding: 0;\n}\n\nfieldset {\n border: none;\n margin: 0;\n padding-block-start: 0.25rem;\n padding-block-end: 0.25rem;\n padding-inline-start: 0.25rem;\n padding-inline-end: 0.25rem;\n}\n\nlegend {\n font: var(--${f}-legend-font, inherit);\n padding: 0;\n}\n\ninput,\nlabel {\n cursor: pointer;\n}\n\nlabel {\n padding: 0.15rem;\n white-space: nowrap;\n}\n\ninput {\n opacity: 0;\n position: absolute;\n pointer-events: none;\n}\n\ninput:focus + label {\n outline: rgb(229, 151, 0) auto 2px;\n outline: -webkit-focus-ring-color auto 5px;\n}\n\nlabel::before {\n content: "";\n display: inline-block;\n background-size: var(--${f}-icon-size, 1rem);\n background-repeat: no-repeat;\n height: var(--${f}-icon-size, 1rem);\n width: var(--${f}-icon-size, 1rem);\n vertical-align: middle;\n margin: 0 0.5rem 0 0;\n}\n\nlabel[dir="rtl"]::before {\n margin: 0 0 0 0.5rem;\n}\n\n#lightLabel::before {\n background-image: var(--${f}-light-icon, url("${v}sun.png"));\n}\n\n#darkLabel::before {\n filter: var(--${f}-icon-filter, none);\n background-image: var(--${f}-dark-icon, url("${v}moon.png"));\n}\n\n#checkboxLabel::before {\n background-image: var(--${f}-checkbox-icon, none);\n}\n\n#permanentLabel::before {\n background-image: var(--${f}-remember-icon-unchecked, url("${v}unchecked.svg"));\n}\n\n#lightLabel,\n#darkLabel,\n#checkboxLabel {\n font: var(--${f}-label-font, inherit);\n}\n\n#lightLabel:empty,\n#darkLabel:empty,\n#checkboxLabel:empty {\n font-size: 0;\n padding: 0;\n}\n\n#permanentLabel {\n font: var(--${f}-remember-font, inherit);\n}\n\ninput:checked + #permanentLabel::before {\n background-image: var(--${f}-remember-icon-checked, url("${v}checked.svg"));\n}\n\ninput:checked + #darkLabel,\ninput:checked + #lightLabel {\n background-color: var(--${f}-active-mode-background-color, transparent);\n}\n\ninput:checked + #darkLabel::before,\ninput:checked + #lightLabel::before {\n background-color: var(--${f}-active-mode-background-color, transparent);\n}\n\ninput:checked + #checkboxLabel::before {\n filter: var(--${f}-icon-filter, none);\n}\n\ninput:checked + #checkboxLabel + aside #permanentLabel::before {\n filter: var(--${f}-remember-filter, invert(100%));\n}\n\naside {\n visibility: hidden;\n margin-top: 0.15rem;\n}\n\n#lightLabel:focus-visible ~ aside,\n#darkLabel:focus-visible ~ aside,\n#checkboxLabel:focus-visible ~ aside {\n visibility: visible;\n transition: visibility 0s;\n}\n\n@media (hover: hover) {\n aside {\n transition: visibility 3s;\n }\n\n aside:hover {\n visibility: visible;\n }\n\n #lightLabel:hover ~ aside,\n #darkLabel:hover ~ aside,\n #checkboxLabel:hover ~ aside {\n visibility: visible;\n transition: visibility 0s;\n }\n\n aside #permanentLabel:empty {\n display: none;\n }\n}\n</style>\n<form>\n<fieldset>\n <legend></legend>\n\n <input id="lightRadio" name="mode" type="radio">\n <label id="lightLabel" for="lightRadio"></label>\n\n <input id="darkRadio" name="mode" type="radio">\n <label id="darkLabel" for="darkRadio"></label>\n\n <input id="darkCheckbox" type="checkbox">\n <label id="checkboxLabel" for="darkCheckbox"></label>\n\n <aside>\n <input id="permanentCheckbox" type="checkbox">\n <label id="permanentLabel" for="permanentCheckbox"></label>\n </aside>\n</fieldset>\n</form>`;export class DarkModeToggle extends HTMLElement{static get observedAttributes(){return[m,l,b,d,o,r,s]}constructor(){super();L(this,m);L(this,l);L(this,d);L(this,o);L(this,r);L(this,s);x(this,b);this._darkCSS=null;this._lightCSS=null;n.addEventListener(p,e=>{this.mode=e.detail.colorScheme;this._updateRadios();this._updateCheckbox()});n.addEventListener(k,e=>{this.permanent=e.detail.permanent;this.permanentCheckbox.checked=this.permanent});this._initializeDOM()}_initializeDOM(){const s=this.attachShadow({mode:"closed"});s.appendChild(y.content.cloneNode(true));this._darkCSS=n.querySelectorAll(`link[rel="stylesheet"][media="${i}"]`);this._lightCSS=document.querySelectorAll(a.map(e=>{return`link[rel="stylesheet"][media*="${e}"]`}).join(", "));this.lightRadio=s.querySelector("#lightRadio");this.lightLabel=s.querySelector("#lightLabel");this.darkRadio=s.querySelector("#darkRadio");this.darkLabel=s.querySelector("#darkLabel");this.darkCheckbox=s.querySelector("#darkCheckbox");this.checkboxLabel=s.querySelector("#checkboxLabel");this.legendLabel=s.querySelector("legend");this.permanentAside=s.querySelector("aside");this.permanentCheckbox=s.querySelector("#permanentCheckbox");this.permanentLabel=s.querySelector("#permanentLabel");const d=e.matchMedia("(prefers-color-scheme)").media!==g;if(d){e.matchMedia(i).addListener(({matches:e})=>{if(!this.permanent){this.mode=e?r:o;this._dispatchEvent(p,{colorScheme:this.mode})}})}const c=t.getItem(f);if(c&&[r,o].includes(c)){this.mode=c;this.permanentCheckbox.checked=true;this.permanent=true}else if(d){if(e.matchMedia(a[0]).matches||e.matchMedia(a[1]).matches){this.mode=o}else if(e.matchMedia(i).matches){this.mode=r}}if(!this.mode){this.mode=o}if(this.permanent&&!c){t.setItem(f,this.mode)}if(!this.appearance){this.appearance=h}this._updateAppearance();this._updateRadios();this._updateCheckbox();[this.lightRadio,this.darkRadio].forEach(e=>{e.addEventListener("change",()=>{this.mode=this.lightRadio.checked?o:r;this._updateCheckbox();this._dispatchEvent(p,{colorScheme:this.mode})})});this.darkCheckbox.addEventListener("change",()=>{this.mode=this.darkCheckbox.checked?r:o;this._updateRadios();this._dispatchEvent(p,{colorScheme:this.mode})});this.permanentCheckbox.addEventListener("change",()=>{this.permanent=this.permanentCheckbox.checked;this._dispatchEvent(k,{permanent:this.permanent})});this._updateMode();this._dispatchEvent(p,{colorScheme:this.mode});this._dispatchEvent(k,{permanent:this.permanent})}attributeChangedCallback(n,i,a){if(n===m){if(![o,r].includes(a)){throw new RangeError(`Allowed values: "${o}" and "${r}".`)}if(e.matchMedia("(hover: none)").matches&&this.remember){this._showPermanentAside()}if(this.permanent){t.setItem(f,this.mode)}this._updateRadios();this._updateCheckbox();this._updateMode()}else if(n===l){if(![h,c].includes(a)){throw new RangeError('Allowed values: "${TOGGLE}" and "${SWITCH}".')}this._updateAppearance()}else if(n===b){if(this.permanent){t.setItem(f,this.mode)}else{t.removeItem(f)}this.permanentCheckbox.checked=this.permanent}else if(n===d){this.legendLabel.textContent=a}else if(n===s){this.permanentLabel.textContent=a}else if(n===o){this.lightLabel.textContent=a;if(this.mode===o){this.checkboxLabel.textContent=a}}else if(n===r){this.darkLabel.textContent=a;if(this.mode===r){this.checkboxLabel.textContent=a}}}_dispatchEvent(e,n){this.dispatchEvent(new CustomEvent(e,{bubbles:true,composed:true,detail:n}))}_updateAppearance(){const e=this.appearance===h;this.lightRadio.hidden=e;this.lightLabel.hidden=e;this.darkRadio.hidden=e;this.darkLabel.hidden=e;this.darkCheckbox.hidden=!e;this.checkboxLabel.hidden=!e}_updateRadios(){if(this.mode===o){this.lightRadio.checked=true}else{this.darkRadio.checked=true}}_updateCheckbox(){if(this.mode===o){this.checkboxLabel.style.setProperty(`--${f}-checkbox-icon`,`var(--${f}-light-icon, url("${v}moon.png"))`);this.checkboxLabel.textContent=this.light;this.darkCheckbox.checked=false}else{this.checkboxLabel.style.setProperty(`--${f}-checkbox-icon`,`var(--${f}-dark-icon, url("${v}sun.png"))`);this.checkboxLabel.textContent=this.dark;this.darkCheckbox.checked=true}}_updateMode(){if(this.mode===o){this._lightCSS.forEach(e=>{e.media=u;e.disabled=false});this._darkCSS.forEach(e=>{e.media=g;e.disabled=true})}else{this._darkCSS.forEach(e=>{e.media=u;e.disabled=false});this._lightCSS.forEach(e=>{e.media=g;e.disabled=true})}}_showPermanentAside(){this.permanentAside.style.visibility="visible";e.setTimeout(()=>{this.permanentAside.style.visibility="hidden"},3e3)}}e.customElements.define(f,DarkModeToggle);
\No newline at end of file