/
morph_renderer.js
89 lines (74 loc) · 2.34 KB
/
morph_renderer.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import Idiomorph from "idiomorph"
import { dispatch } from "../../util"
import { Renderer } from "../renderer"
export class MorphRenderer extends Renderer {
async render() {
if (this.willRender) await this.#morphBody()
}
get renderMethod() {
return "morph"
}
// Private
async #morphBody() {
this.#morphElements(this.currentElement, this.newElement)
this.#reloadRemoteFrames()
dispatch("turbo:morph", {
detail: {
currentElement: this.currentElement,
newElement: this.newElement
}
})
}
#morphElements(currentElement, newElement, morphStyle = "outerHTML") {
this.isMorphingTurboFrame = this.#isFrameReloadedWithMorph(currentElement)
Idiomorph.morph(currentElement, newElement, {
morphStyle: morphStyle,
callbacks: {
beforeNodeAdded: this.#shouldAddElement,
beforeNodeMorphed: this.#shouldMorphElement,
beforeNodeRemoved: this.#shouldRemoveElement
}
})
}
#shouldAddElement = (node) => {
return !(node.id && node.hasAttribute("data-turbo-permanent") && document.getElementById(node.id))
}
#shouldMorphElement = (oldNode, newNode) => {
if (oldNode instanceof HTMLElement) {
return !oldNode.hasAttribute("data-turbo-permanent") && (this.isMorphingTurboFrame || !this.#isFrameReloadedWithMorph(oldNode))
} else {
return true
}
}
#shouldRemoveElement = (node) => {
return this.#shouldMorphElement(node)
}
#reloadRemoteFrames() {
this.#remoteFrames().forEach((frame) => {
if (this.#isFrameReloadedWithMorph(frame)) {
this.#renderFrameWithMorph(frame)
frame.reload()
}
})
}
#renderFrameWithMorph(frame) {
frame.addEventListener("turbo:before-frame-render", (event) => {
event.detail.render = this.#morphFrameUpdate
}, { once: true })
}
#morphFrameUpdate = (currentElement, newElement) => {
dispatch("turbo:before-frame-morph", {
target: currentElement,
detail: { currentElement, newElement }
})
this.#morphElements(currentElement, newElement.children, "innerHTML")
}
#isFrameReloadedWithMorph(element) {
return element.src && element.refresh === "morph"
}
#remoteFrames() {
return Array.from(document.querySelectorAll('turbo-frame[src]')).filter(frame => {
return !frame.closest('[data-turbo-permanent]')
})
}
}