diff --git a/src/core/drive/morph_renderer.js b/src/core/drive/morph_renderer.js index 70ac6b585..b694c26ec 100644 --- a/src/core/drive/morph_renderer.js +++ b/src/core/drive/morph_renderer.js @@ -57,9 +57,9 @@ export class MorphRenderer extends PageRenderer { }) return !event.defaultPrevented - } else { - return false } + + return false } } diff --git a/src/core/streams/stream_actions.js b/src/core/streams/stream_actions.js index 064e94ca4..a343e4333 100644 --- a/src/core/streams/stream_actions.js +++ b/src/core/streams/stream_actions.js @@ -1,4 +1,7 @@ import { session } from "../" +import { morph } from "idiomorph" +import { dispatch } from "../../util" + export const StreamActions = { after() { @@ -37,4 +40,38 @@ export const StreamActions = { refresh() { session.refresh(this.baseURI, this.requestId) } + + morph() { + this.targetElements.forEach((targetElement) => { + try { + const morphStyle = this.getAttribute("data-turbo-morph-style") || "outerHTML" + const ignoreActive = this.getAttribute("data-turbo-morph-ignore-active") || true + const ignoreActiveValue = this.getAttribute("data-turbo-morph-ignore-active-value") || true + const head = this.getAttribute("data-turbo-morph-head") || 'merge' + morph(targetElement, this.templateContent, { + morphStyle: morphStyle, + ignoreActive: ignoreActive, + ignoreActiveValue: ignoreActiveValue, + head: head, + callbacks: { + beforeNodeAdded: (node) => { + return !(node.id && node.hasAttribute("data-turbo-permanent") && document.getElementById(node.id)) + }, + afterNodeMorphed: (oldNode, newNode) => { + if (newNode instanceof HTMLElement) { + dispatch("turbo:morph-element", { + target: oldNode, + detail: { + newElement: newNode + } + }) + } + } + } + }) + } catch (error) { + console.error(error) + } + }) + } } diff --git a/src/tests/unit/stream_element_tests.js b/src/tests/unit/stream_element_tests.js index 21a9ca8aa..c084c8e9f 100644 --- a/src/tests/unit/stream_element_tests.js +++ b/src/tests/unit/stream_element_tests.js @@ -196,3 +196,16 @@ test("test action=refresh discarded when matching request id", async () => { assert.ok(document.body.hasAttribute("data-modified")) }) + +test("action=morph", async () => { + const templateElement = createTemplateElement(`
Hello Turbo Morphed
`) + const element = createStreamElement("morph", "hello", templateElement) + + assert.equal(subject.find("#hello")?.textContent, "Hello Turbo") + + subject.append(element) + await nextAnimationFrame() + + assert.notOk(subject.find("#hello")?.textContent, "Hello Turbo") + assert.equal(subject.find("#hello")?.textContent, "Hello Turbo Morphed") +})