Class: NitroKit::AppearanceBootstrap
- Inherits:
-
Phlex::HTML
- Object
- Phlex::HTML
- NitroKit::AppearanceBootstrap
- Defined in:
- app/components/nitro_kit/appearance_bootstrap.rb
Constant Summary collapse
- PREFERENCES =
%i[light dark system].freeze
- STORAGE_KEY =
"nitro-kit-appearance"- SCRIPT =
<<~JAVASCRIPT.freeze (() => { const preferences = ["light", "dark", "system"] const storageKey = "nitro-kit-appearance" const requestEvent = "nitro-kit:appearance-request" const changeEvent = "nitro-kit:appearance-change" const runtimeKey = Symbol.for("nitro-kit.appearance") const suppliedDefault = document.currentScript?.dataset.nkAppearanceDefault const valid = (value) => preferences.includes(value) const initialDefault = valid(suppliedDefault) ? suppliedDefault : "system" const existingRuntime = window[runtimeKey] if (existingRuntime) { existingRuntime.start(initialDefault) return } const root = document.documentElement const system = window.matchMedia("(prefers-color-scheme: dark)") let defaultPreference = initialDefault let preference = initialDefault let mediaListening = false let started = false const read = () => { try { const stored = window.localStorage.getItem(storageKey) return valid(stored) ? stored : defaultPreference } catch (_error) { return defaultPreference } } const write = (value) => { try { window.localStorage.setItem(storageKey, value) } catch (_error) { // The in-document preference still works when storage is unavailable. } } const resolve = () => { if (preference !== "system") return preference return system.matches ? "dark" : "light" } const broadcast = (theme) => { window.dispatchEvent(new CustomEvent(changeEvent, { detail: { preference, theme } })) } const reflect = () => { const theme = resolve() const changed = root.dataset.themePreference !== preference || root.dataset.theme !== theme root.dataset.themePreference = preference root.dataset.theme = theme if (changed) broadcast(theme) } const handleMediaChange = () => reflect() const synchronizeMediaListener = () => { if (preference === "system" && !mediaListening) { system.addEventListener("change", handleMediaChange) mediaListening = true } else if (preference !== "system" && mediaListening) { system.removeEventListener("change", handleMediaChange) mediaListening = false } } const apply = (nextPreference, persist = false) => { if (!valid(nextPreference)) return preference = nextPreference synchronizeMediaListener() if (persist) write(preference) reflect() } const handleRequest = (event) => { apply(event.detail?.preference, true) } const handleStorage = (event) => { if (event.key !== storageKey) return apply(valid(event.newValue) ? event.newValue : defaultPreference) } const runtime = { start(nextDefault) { if (valid(nextDefault)) defaultPreference = nextDefault if (!started) { window.addEventListener(requestEvent, handleRequest) window.addEventListener("storage", handleStorage) started = true } apply(read()) } } window[runtimeKey] = runtime runtime.start(initialDefault) })() JAVASCRIPT
- CSP_HASH =
"sha256-Vcime4euWSeYtHSfjYjqz/XhRyzMcLpn6Ip2LlaHleY="
Instance Method Summary collapse
-
#initialize(default: :system, nonce: nil) ⇒ AppearanceBootstrap
constructor
A new instance of AppearanceBootstrap.
- #view_template ⇒ Object
Constructor Details
#initialize(default: :system, nonce: nil) ⇒ AppearanceBootstrap
Returns a new instance of AppearanceBootstrap.
120 121 122 123 |
# File 'app/components/nitro_kit/appearance_bootstrap.rb', line 120 def initialize(default: :system, nonce: nil) @default = validate_default!(default) @nonce = validate_nonce!(nonce) end |
Instance Method Details
#view_template ⇒ Object
125 126 127 128 129 130 |
# File 'app/components/nitro_kit/appearance_bootstrap.rb', line 125 def view_template script( nonce: @nonce, data: { nk_appearance_default: @default } ) { raw safe(SCRIPT) } end |