1import { Controller } from "@hotwired/stimulus" 2 3export default class extends Controller { 4 static targets = [ "scroller", "slide", "status", "prev", "next" ] 5 static values = { index: { type: Number, default: 0 } } 6 7 connect() { 8 this.onScroll = this.onScroll.bind(this) 9 this.onSlideClick = this.selectSlide.bind(this) 10 this.scrollerTarget.addEventListener("scroll", this.onScroll, { passive: true }) 11 this.slideTargets.forEach((slide) => slide.addEventListener("click", this.onSlideClick)) 12 this.updatePager() 13 } 14 15 disconnect() { 16 this.scrollerTarget.removeEventListener("scroll", this.onScroll) 17 this.slideTargets.forEach((slide) => slide.removeEventListener("click", this.onSlideClick)) 18 } 19 20 prev() { 21 if (this.indexValue <= 0) return 22 this.goTo(this.indexValue - 1) 23 } 24 25 next() { 26 if (this.indexValue >= this.slideTargets.length - 1) return 27 this.goTo(this.indexValue + 1) 28 } 29 30 selectSlide(event) { 31 const index = this.slideTargets.indexOf(event.currentTarget) 32 if (index === -1 || index === this.indexValue) return 33 34 event.preventDefault() 35 event.stopPropagation() 36 this.goTo(index) 37 } 38 39 goTo(index) { 40 const origin = this.slideTargets[0].offsetLeft 41 this.scrollerTarget.scrollLeft = this.slideTargets[index].offsetLeft - origin 42 this.indexValue = index 43 this.updatePager() 44 } 45 46 onScroll() { 47 const index = this.nearestIndex() 48 if (index === this.indexValue) return 49 50 this.indexValue = index 51 this.updatePager() 52 } 53 54 nearestIndex() { 55 const origin = this.slideTargets[0].offsetLeft 56 const left = this.scrollerTarget.scrollLeft 57 let best = 0 58 let bestDist = Infinity 59 60 this.slideTargets.forEach((slide, index) => { 61 const dist = Math.abs(slide.offsetLeft - origin - left) 62 if (dist < bestDist) { 63 bestDist = dist 64 best = index 65 } 66 }) 67 68 return best 69 } 70 71 updatePager() { 72 if (this.hasStatusTarget) { 73 this.statusTarget.textContent = `${this.indexValue + 1} / ${this.slideTargets.length}` 74 } 75 76 if (this.hasPrevTarget) this.prevTarget.disabled = this.indexValue <= 0 77 if (this.hasNextTarget) this.nextTarget.disabled = this.indexValue >= this.slideTargets.length - 1 78 79 this.slideTargets.forEach((slide, index) => { 80 const current = index === this.indexValue 81 slide.classList.toggle("cursor-zoom-in", current) 82 slide.classList.toggle("cursor-pointer", !current) 83 }) 84 } 85}
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.