Last active
February 26, 2024 16:34
-
-
Save pfeiffer/54e3bf929637cad42d534c84f65b1e99 to your computer and use it in GitHub Desktop.
Turbo MorphRenderer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { morphElement } from '../../helpers' | |
import MorphableSnapshot from './morphable_snapshot' | |
// A Renderer class that will extract morphable elements tagged with [data-turbo-morph] before | |
// rendering, and morph them back into the new snapshot after rendering, similar to how permanent | |
// elements are handled by Turbo. Useful for persisting horizontal scroll etc. | |
// | |
// Note: A morphable element must have an [id] attribute. | |
class MorphRenderer { | |
constructor(existingBody, newBody) { | |
this.existingSnapshot = new MorphableSnapshot(existingBody) | |
this.newSnapshot = new MorphableSnapshot(newBody) | |
} | |
render(originalRender) { | |
const morphableElementsMap = this.existingSnapshot.getMorphableElementsMapForSnapshot(this.newSnapshot) | |
this.moveExistingElementToNewSnapshot(morphableElementsMap) | |
originalRender(this.existingSnapshot.element, this.newSnapshot.element) | |
this.morphNewElementsAfterRender(morphableElementsMap) | |
} | |
moveExistingElementToNewSnapshot(morphableElementsMap) { | |
for (const [_, { currentElement, newElement }] of Object.entries(morphableElementsMap)) { | |
const clone = currentElement.cloneNode(true) | |
currentElement.replaceWith(clone) | |
newElement.replaceWith(currentElement) | |
} | |
} | |
morphNewElementsAfterRender(morphableElementsMap) { | |
for (const [_, { currentElement, newElementHTML }] of Object.entries(morphableElementsMap)) { | |
morphElement(currentElement, newElementHTML) | |
} | |
} | |
} | |
export default MorphRenderer |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A Snapshot class that will extract morphable elements tagged with [data-turbo-morph] and [id] | |
// and allow generating a map of elements that are morphable between snapshots. | |
class MorphableSnapshot { | |
constructor(element) { | |
this.element = element | |
} | |
get morphableElements() { | |
return [...this.element.querySelectorAll("[id][data-turbo-morph]")] | |
} | |
getMorphableElementById(id) { | |
return this.element.querySelector(`#${id}[data-turbo-morph]`) | |
} | |
getMorphableElementsMapForSnapshot(snapshot) { | |
const morphableElementsMap = {} | |
for (const morphableCurrentElement of this.morphableElements) { | |
const { id } = morphableCurrentElement | |
const newMorphableElement = snapshot.getMorphableElementById(id) | |
if (newMorphableElement) { | |
morphableElementsMap[id] = { | |
currentElement: morphableCurrentElement, | |
newElement: newMorphableElement, | |
newElementHTML: newMorphableElement.outerHTML, | |
} | |
} | |
} | |
return morphableElementsMap | |
} | |
} | |
export default MorphableSnapshot |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import MorphRenderer from "../lib/turbo/morph_renderer" | |
addEventListener("turbo:before-render", ({ detail }) => { | |
const originalRender = detail.render | |
detail.render = async (existingBody, newBody) => { | |
const renderer = new MorphRenderer(existingBody, newBody) | |
renderer.render(originalRender) | |
} | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment