app/javascript/controllers/user_dropdown_controller.js
import Rails from '@rails/ujs'
import { Controller } from "@hotwired/stimulus"
import { Turbo } from '@hotwired/turbo-rails'
export default class extends Controller {
static targets = ['menu', 'header', 'switchToLight', 'switchToDark']
initialize() {
this.currentlyOpen = false
this.lightStyles = document.querySelectorAll('link')[0]
this.darkStyles = document.querySelectorAll('link')[1]
}
open(e) {
e.preventDefault()
this.menuTarget.style.display = 'block'
this.currentlyOpen = true
// a bit more manual than using click@window->user-dropdown#close
// but this ensures the click handler only gets added when
// the menu has been opened, which makes my performance brain happy
e.stopImmediatePropagation()
window.addEventListener('click', this)
window.addEventListener('touchstart', this)
}
// This is a "special" method with secret implicit powers.
// We are passing an object (this) into add/remove event listener
// This is the method that will be implicitly called
// https://medium.com/@photokandy/til-you-can-pass-an-object-instead-of-a-function-to-addeventlistener-7838a3c4ec62
handleEvent(e) {
if (this.open && (this.escKeyPressed(e) || this.clickOutsideMenu(e))) {
this.menuTarget.style.display = 'none'
this.currentlyOpen = false
window.removeEventListener('click', this)
window.removeEventListener('touchstart', this)
}
}
escKeyPressed(e) {
return (e.type === 'keydown') && (e.keyCode === 27)
}
clickOutsideMenu(e) {
return (e.type !== 'keydown')
&& (!this.menuTarget.contains(e.target) // outside whole menu
|| this.headerTarget.contains(e.target)) // inside header
}
switchToLight(e) {
this.toggleTheme(e)
this.switchToDarkTarget.style.display = 'block'
this.switchToLightTarget.style.display = 'none'
this.lightStyles.disabled = false
// Buy the browser a bit of time to load the new styles
// Ideally, while loading, we ask if the stylesheet is still 'disabled'
// But that instantly returns false on toggling
// So we are choosing a perceptual threshold that "just works"
setTimeout(() => this.disableDark(), 100)
this.toggleThemableImages('dark', 'light')
}
switchToDark(e) {
this.toggleTheme(e)
this.switchToLightTarget.style.display = 'block'
this.switchToDarkTarget.style.display = 'none'
this.darkStyles.disabled = false
setTimeout(() => this.disableLight(), 100)
this.toggleThemableImages('light', 'dark')
}
disableLight() {
this.lightStyles.disabled = true
}
disableDark() {
this.darkStyles.disabled = true
}
toggleTheme(e) {
e.stopImmediatePropagation()
Rails.ajax({
url: "/toggle_theme",
type: "PUT"
})
Turbo.cache.clear()
}
toggleThemableImages(oldTheme, newTheme) {
document.querySelectorAll('img.themeable').forEach((image) => image.classList.toggle('hidden'))
}
}