8c8dbe888e
This patches 2 problems in Turbo/idiomorph that are causing problems when reloading the pagination frames: - No remove callback in idiomorph when removing a node that had been mapped. - Regression introduced by https://github.com/hotwired/turbo/pull/1311, where it won't prevent the removal of frames with refresh=morph I will upstream both patches, this is temporary. https://fizzy.37signals.com/5986089/cards/2291
124 lines
3.4 KiB
JavaScript
124 lines
3.4 KiB
JavaScript
import { Controller } from "@hotwired/stimulus"
|
|
import { createElement } from "helpers/html_helpers"
|
|
import { delay, nextEvent } from "helpers/timing_helpers"
|
|
import { keepingScrollPosition } from "helpers/scroll_helpers"
|
|
import { get } from "@rails/request.js"
|
|
|
|
const DELAY_BEFORE_OBSERVING = 400
|
|
|
|
export default class extends Controller {
|
|
static targets = [ "paginationLink" ]
|
|
static values = {
|
|
paginateOnIntersection: { type: Boolean, default: false },
|
|
discardFrame: Boolean,
|
|
manualActivation: Boolean
|
|
}
|
|
|
|
initialize() {
|
|
if (!this.manualActivation) {
|
|
this.activate()
|
|
}
|
|
}
|
|
|
|
disconnect() {
|
|
this.observer?.disconnect()
|
|
}
|
|
|
|
async activate() {
|
|
await delay(DELAY_BEFORE_OBSERVING)
|
|
|
|
if (this.paginateOnIntersectionValue) {
|
|
this.observer = new IntersectionObserver(this.#intersect, { rootMargin: "300px", threshold: 1 })
|
|
}
|
|
}
|
|
|
|
async paginationLinkTargetConnected(linkElement) {
|
|
if (this.paginateOnIntersectionValue) {
|
|
await delay(DELAY_BEFORE_OBSERVING)
|
|
this.observer?.observe(linkElement)
|
|
}
|
|
}
|
|
|
|
// Actions
|
|
|
|
loadPage({ target }) {
|
|
this.#loadPaginationLink(target)
|
|
}
|
|
|
|
// Private
|
|
|
|
#intersect = ([ entry ]) => {
|
|
if (entry?.isIntersecting && entry.intersectionRatio === 1) {
|
|
this.#loadPaginationLink(entry.target)
|
|
}
|
|
}
|
|
|
|
#loadPaginationLink(linkElement) {
|
|
this.observer?.unobserve(linkElement)
|
|
|
|
keepingScrollPosition(this.#closestSiblingTo(linkElement) || linkElement.parentNode, this.#expandPaginationLink(linkElement))
|
|
}
|
|
|
|
#closestSiblingTo(element) {
|
|
return element.nextElementSibling || element.previousElementSibling
|
|
}
|
|
|
|
async #expandPaginationLink(linkElement) {
|
|
linkElement.setAttribute("aria-busy", "true")
|
|
|
|
if (this.discardFrameValue) {
|
|
await this.#replacePaginationLinkWithFrameContents(linkElement)
|
|
} else {
|
|
await this.#replacePaginationLinkWithFrame(linkElement)
|
|
}
|
|
|
|
linkElement.removeAttribute("aria-busy")
|
|
}
|
|
|
|
async #replacePaginationLinkWithFrameContents(linkElement) {
|
|
linkElement.outerHTML = await this.#loadHtmlFrom(linkElement)
|
|
}
|
|
|
|
async #loadHtmlFrom(linkElement) {
|
|
const response = await get(linkElement.href, { responseKind: "html" })
|
|
const html = await response.text
|
|
const doc = new DOMParser().parseFromString(html, "text/html")
|
|
const element = doc.querySelector(`turbo-frame#${linkElement.dataset.frame}`)
|
|
return element ? element.innerHTML.trim() : ""
|
|
}
|
|
|
|
#replacePaginationLinkWithFrame(linkElement) {
|
|
const turboFrame = this.#buildTurboFrameFor(linkElement)
|
|
this.#insertTurboFrameAtPosition(linkElement, turboFrame)
|
|
}
|
|
|
|
#buildTurboFrameFor(linkElement) {
|
|
const turboFrame = createElement("turbo-frame", {
|
|
id: linkElement.dataset.frame,
|
|
src: linkElement.href,
|
|
refresh: "morph",
|
|
target: "_top"
|
|
})
|
|
|
|
this.#keepScrollPositionOnFrameRender(turboFrame, linkElement)
|
|
|
|
return turboFrame
|
|
}
|
|
|
|
async #keepScrollPositionOnFrameRender(turboFrame, linkElement) {
|
|
await nextEvent(turboFrame, "turbo:before-frame-render")
|
|
|
|
keepingScrollPosition(linkElement, nextEvent(turboFrame, "turbo:frame-render"))
|
|
}
|
|
|
|
#insertTurboFrameAtPosition(linkElement, turboFrame) {
|
|
const container = linkElement.parentNode.parentNode
|
|
|
|
if (linkElement.parentNode.firstElementChild === linkElement) {
|
|
container.prepend(turboFrame)
|
|
} else {
|
|
container.append(turboFrame)
|
|
}
|
|
}
|
|
}
|