1import { Controller } from "@hotwired/stimulus" 2 3// Live preview for storefront category cards: updates text color and background 4// image as staff adjust the theme settings. 5export default class extends Controller { 6 static targets = ["textColor", "textColorLabel", "primaryColor", "primaryColorLabel", "sourceImage", "card", "title", "subtitle"] 7 static values = { 8 defaultTextColor: { type: String, default: "#ffffff" }, 9 sampleTitle: { type: String, default: "Burgers" }, 10 sampleSubtitle: { type: String, default: "Tap to browse this category" } 11 } 12 13 connect() { 14 this.update() 15 this.observeSourceImage() 16 } 17 18 disconnect() { 19 this.sourceObserver?.disconnect() 20 } 21 22 updatePrimaryLabel() { 23 if (this.hasPrimaryColorTarget && this.hasPrimaryColorLabelTarget) { 24 this.primaryColorLabelTarget.textContent = this.primaryColorTarget.value 25 } 26 } 27 28 update() { 29 const textColor = this.hasTextColorTarget 30 ? (this.textColorTarget.value || this.defaultTextColorValue) 31 : this.defaultTextColorValue 32 const primary = this.hasPrimaryColorTarget 33 ? (this.primaryColorTarget.value || "#570df8") 34 : "#570df8" 35 const imageUrl = this.currentImageUrl() 36 37 if (this.hasTextColorLabelTarget) { 38 this.textColorLabelTarget.textContent = textColor 39 } 40 41 if (this.hasCardTarget) { 42 this.cardTarget.style.color = textColor 43 if (imageUrl) { 44 this.cardTarget.style.backgroundImage = 45 `linear-gradient(to top, rgba(0,0,0,.55), rgba(0,0,0,.15)), url('${imageUrl}')` 46 this.cardTarget.style.backgroundSize = "cover" 47 this.cardTarget.style.backgroundPosition = "center" 48 this.cardTarget.style.backgroundColor = "" 49 } else { 50 this.cardTarget.style.backgroundImage = "" 51 this.cardTarget.style.backgroundColor = primary 52 } 53 } 54 55 if (this.hasTitleTarget) { 56 this.titleTarget.style.color = textColor 57 this.titleTarget.style.textShadow = imageUrl ? "0 1px 2px rgb(0 0 0 / 35%)" : "none" 58 } 59 60 if (this.hasSubtitleTarget) { 61 this.subtitleTarget.style.color = textColor 62 this.subtitleTarget.style.opacity = "0.9" 63 this.subtitleTarget.style.textShadow = imageUrl ? "0 1px 2px rgb(0 0 0 / 35%)" : "none" 64 } 65 } 66 67 currentImageUrl() { 68 if (!this.hasSourceImageTarget) return null 69 const img = this.sourceImageTarget 70 if (img.classList.contains("hidden")) return null 71 const parent = img.closest("[data-image-upload-target='preview']") 72 if (parent?.classList.contains("hidden")) return null 73 const src = img.getAttribute("src") 74 return src && src.length > 0 ? src : null 75 } 76 77 observeSourceImage() { 78 if (!this.hasSourceImageTarget) return 79 80 this.sourceObserver = new MutationObserver(() => this.update()) 81 this.sourceObserver.observe(this.sourceImageTarget, { 82 attributes: true, 83 attributeFilter: ["src", "class"] 84 }) 85 86 const preview = this.sourceImageTarget.closest("[data-image-upload-target='preview']") 87 if (preview) { 88 this.sourceObserver.observe(preview, { 89 attributes: true, 90 attributeFilter: ["class"] 91 }) 92 } 93 } 94};
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.