1import { Controller } from "@hotwired/stimulus" 2 3// Journey Variant Controller 4// One choice for a journey that has more than one way through it, e.g. the 5// flour of the sandwich bread: wheat and spelt bake one way, rye, einkorn, 6// emmer and gluten-free flours another. Every step renders all of its 7// versions; this shows the chosen one and hides the rest. 8// 9// The choice lives in the URL as ?variant= (a shared link opens on the same 10// flour) and in localStorage per journey (a baker who picked rye yesterday 11// still has rye today). The URL wins over the stored choice. 12export default class extends Controller { 13 static targets = ["option"] 14 15 static values = { 16 journeySlug: String, 17 current: String 18 } 19 20 connect() { 21 this.storageKey = `loafy_journey_${this.journeySlugValue}_variant` 22 const requested = new URL(window.location).searchParams.get("variant") 23 const chosen = [requested, this.stored()].find(id => this.isOption(id)) 24 if (chosen) this.apply(chosen) 25 } 26 27 select(event) { 28 event.preventDefault() 29 this.apply(event.currentTarget.dataset.variantId) 30 } 31 32 apply(id) { 33 if (!this.isOption(id)) return 34 35 this.currentValue = id 36 this.optionTargets.forEach(option => { 37 option.setAttribute("aria-pressed", String(option.dataset.variantId === id)) 38 }) 39 this.element.querySelectorAll("[data-journey-variant]").forEach(version => { 40 version.hidden = version.dataset.journeyVariant !== id 41 }) 42 43 this.store(id) 44 const url = new URL(window.location) 45 if (url.searchParams.get("variant") !== id) { 46 url.searchParams.set("variant", id) 47 window.history.replaceState(window.history.state, "", url) 48 } 49 } 50 51 isOption(id) { 52 return Boolean(id) && this.optionTargets.some(option => option.dataset.variantId === id) 53 } 54 55 // Storage can be missing or throw (private mode, blocked site data); the 56 // switch then simply forgets, and the page stays right. 57 stored() { 58 try { 59 return localStorage.getItem(this.storageKey) 60 } catch { 61 return null 62 } 63 } 64 65 store(id) { 66 try { 67 localStorage.setItem(this.storageKey, id) 68 } catch { 69 // nothing to remember it in 70 } 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.