1// Switches dark/light theme based upon the preference 2// 3// Set storage value to "localStorage" for saving the preference in the browser 4// Otherwise, preference is saved on User profile in database 5// 6// For localStorage, your head tag should include the following JS to apply the dark class before paint 7// document.documentElement.classList.toggle("dark", localStorage.theme === 'dark' || (localStorage.theme !== "light" && window.matchMedia('(prefers-color-scheme: dark)').matches)) 8 9import { Controller } from "@hotwired/stimulus" 10 11export default class extends Controller { 12 static values = { 13 preference: String, 14 storage: { type: String, default: "user" } 15 } 16 17 connect() { 18 if (this.storageValue == "localStorage") this.preferenceValue = localStorage.theme 19 window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", this.preferenceValueChanged.bind(this)) 20 } 21 22 disconnect() { 23 window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", this.preferenceValueChanged) 24 } 25 26 preferenceValueChanged() { 27 document.documentElement.classList.toggle('dark', this.preferenceValue === "dark" || (this.preferenceValue === "" && this.systemInDarkMode)) 28 } 29 30 get systemInDarkMode() { 31 return window.matchMedia('(prefers-color-scheme: dark)').matches 32 } 33 34 light() { 35 this.preferenceValue = "light" 36 this.save() 37 } 38 39 dark() { 40 this.preferenceValue = "dark" 41 this.save() 42 } 43 44 system() { 45 this.preferenceValue = "" 46 this.save() 47 } 48 49 // This isn't called on preferenceValueChanged callback because that is fired on load which would trigger a save every page load 50 save() { 51 this.storageValue == "localStorage" ? this.saveToLocalStorage() : this.saveToUser() 52 } 53 54 saveToLocalStorage() { 55 localStorage.theme = this.preferenceValue 56 } 57 58 saveToUser() { 59 fetch("/users", { 60 method: "PATCH", 61 body: JSON.stringify({user: {"theme": this.preferenceValue }}), 62 headers: { 63 "X-CSRF-Token": this.csrfToken, 64 "Content-Type": "application/json" 65 } 66 }) 67 } 68 69 get csrfToken() { 70 return document.querySelector("[name='csrf-token']").content 71 } 72}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.