1import { Controller } from "@hotwired/stimulus" 2import { Turbo } from "@hotwired/turbo-rails" 3 4// Loads the next page of rows when the "next page" link scrolls into view. 5// The server replies with a turbo stream that appends rows to the table body 6// and replaces this element with the next page's link. 7export default class extends Controller { 8 static targets = ["link", "spinner"] 9 static values = { offset: { type: String, default: "300px" } } 10 11 connect() { 12 if (!this.hasLinkTarget) return 13 14 this.observer = new IntersectionObserver( 15 (entries) => entries.forEach((entry) => entry.isIntersecting && this.loadMore()), 16 { rootMargin: this.rootMargin } 17 ) 18 this.observer.observe(this.linkTarget) 19 } 20 21 // a bare number is taken as pixels, anything else passed through as a CSS length 22 get rootMargin() { 23 return /^-?\d+$/.test(this.offsetValue) ? `${this.offsetValue}px` : this.offsetValue 24 } 25 26 disconnect() { 27 this.observer?.disconnect() 28 } 29 30 async loadMore(event) { 31 event?.preventDefault() 32 if (this.loading || !this.hasLinkTarget) return 33 34 this.loading = true 35 this.observer?.disconnect() 36 this.showSpinner() 37 38 try { 39 const response = await fetch(this.linkTarget.href, { 40 headers: { Accept: "text/vnd.turbo-stream.html" }, 41 credentials: "same-origin" 42 }) 43 44 if (response.ok) { 45 // the stream replaces this whole element, taking the spinner with it 46 Turbo.renderStreamMessage(await response.text()) 47 } else { 48 console.warn("Infinite scroll: request failed", response.status) 49 this.reset() 50 } 51 } catch (error) { 52 console.warn("Infinite scroll: request errored", error) 53 this.reset() 54 } 55 } 56 57 showSpinner() { 58 if (this.hasLinkTarget) this.linkTarget.hidden = true 59 if (this.hasSpinnerTarget) this.spinnerTarget.hidden = false 60 } 61 62 // on failure put the link back so it can be retried by clicking 63 reset() { 64 if (this.hasSpinnerTarget) this.spinnerTarget.hidden = true 65 if (this.hasLinkTarget) this.linkTarget.hidden = false 66 this.loading = false 67 } 68}
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.