`morphdom` integration for Turbo Streams

Overview

Turbo Morph

turbo-morph is a morphdom integration for Turbo Streams. It provides a new Turbo Stream morph action.

Note: Requires Turbo 7.2+

Getting Started

yarn add turbo-morph
// application.js
import * as Turbo from '@hotwired/turbo'

+import { registerMorph } from 'turbo-morph'
+registerMorph()

Example

<turbo-stream action="morph" target="body">
  <template>
    <body data-updated="true">
      <h1>This is the new body</h1>
    </body>
  </template>
</turbo-stream>

children-only option

morphdom exposes a childrenOnly option that can be passed to a morph call.

With Turbo Streams you can apply this option by adding the [children-only] attribute to your <turbo-stream> element.

<turbo-stream action="morph" target="body" children-only>
  ...
</turbo-stream>

Usage with Rails

TBD

Acknowledgments

tubro-morph is MIT-licensed open-source software from Marco Roth.

Turbo is MIT-licensed open-source software from Basecamp.

morphdom is MIT-licensed open-source software from Patrick Steele-Idem

Comments
  • See hotwired/turbo#684

    See hotwired/turbo#684

    I've opened https://github.com/hotwired/turbo/pull/684, which changes the way that custom actions are defined from directly accessing StreamActions to defining a turbo:before-stream-render event listener.

    If that change ships before a 7.2.0 release, this package will need to change how its morphdom integration is defined.

    opened by seanpdoyle 4
  • Struggle with use case

    Struggle with use case

    Hey!

    Thanks for the lib. I'm reading https://docs.stimulusreflex.com/rtfm/morph-modes to get better understanding of a real world benefits that morphdom brings comparing to typical Turbo.StreamActions (replace/update). Shame on me but I cannot find a good example where morphdom is better than typical stream update/replace action.

    Would you be so kind to provide more info on this?

    opened by pySilver 3
  • Fix morph() to morph instead of replace

    Fix morph() to morph instead of replace

    What?

    morph() replaces instead of morphs.

    Why?

    Due to templateContent() returning a DocumentFragment and morphdom not working right with a DocumentFragment. It replaces instead of diffing and morphing like it should.

    How?

    Pass in a ternary for the new element that if childrenOnly is true passes this.templateContent, else passes this.templateElement.innerHTML. This keeps childrenOnly working as it has no problem with a DocumentFragment but does not work with this.templateElement.innerHTML. And for regular morphs they diff and morph correctly with this.templateElement.innerHTML where templateElement() gets the the template element and innerHTML for the html.

    This had come up in the CableReady repo: #58 #67

    opened by connorwbey 1
  • Fix for use with turbo-rails

    Fix for use with turbo-rails

    Came up with a simple file to use @hotwired/turbo-rails so @hotwired/turbo doesn't need imported when using rails. Tested using local turbo-morph.js file with import maps and works great. Took out all initializers and exports as I found they weren't needed. Got a good way to implement in the lib? Also should give ideas to help turbo_power-rails.

    turbo-morph.js

    import morphdom from "morphdom"
    import { Turbo } from "@hotwired/turbo-rails"
    
    Turbo.StreamActions.morph = function() {
      const options = {
        childrenOnly: this.hasAttribute("children-only")
      }
    
      this.targetElements.forEach(element => {
        morphdom(element, options.childrenOnly ? this.templateContent : this.templateElement.innerHTML, options)
      })
    }
    

    application.js

    import "@hotwired/turbo-rails"
    import "turbo-morph"
    
    opened by connorwbey 0
  • Morph Plugins

    Morph Plugins

    A bunch of morphdom alternatives emerged over the past few months. Some of them are very similar to morphdom but each of them has their own nuance.

    For now morphdom will stay the default, but there should be an option to replace morphdom with another library fairly easily, so you can chose the best suiting morphing library for your project.

    In order to be fully compatible with all approaches turbo-morph is going to support the following morphing libraries:

    Current import/initialize

    Currently the import and initialize of the library looks like:

    import * as Turbo from '@hotwired/turbo'
    
    import TurboMorph from 'turbo-morph'
    TurboMorph.initialize(Turbo.StreamActions)
    

    With this Pull Request

    If you want to be explicit about what you use (if for some reason turbo-morph is going to change the default in the future) you can rewrite the import-statement like so and everything will continue to work as it did before:

    import * as Turbo from '@hotwired/turbo'
    
    import * as TurboMorph from 'turbo-morph/morphdom'
    TurboMorph.initialize(Turbo.StreamActions)
    

    But with that you can also easily swap out morphdom with the Alpine plugin if you like:

    import * as Turbo from '@hotwired/turbo'
    
    import * as TurboMorph from 'turbo-morph/alpine'
    TurboMorph.initialize(Turbo.StreamActions)
    

    Using multiple plugins at the same time:

    There might be reasons why you would want to use multiple plugins at the same time. For example if one library is better suited in one place whereas another library is better suited in another place in your application.

    import * as Turbo from '@hotwired/turbo'
    
    import { morph as morphdom } from 'turbo-morph/morphdom'
    import { morph as nanomorph } from 'turbo-morph/nanomorph'
    import { morph as micromorph } from 'turbo-morph/micromorph'
    
     // Set the one you want to use for `morph` if you still have `morph` Stream Action calls in your application 
    Turbo.StreamActions.morph = morphdom
    
    // And register the other ones as their own action
    Turbo.StreamActions.morphdom = morphdom
    Turbo.StreamActions.nanomorph = nanomorph
    Turbo.StreamActions.micromorph = micromorph
    
    opened by marcoroth 0
  • Cannot morph node with turbo-frame within

    Cannot morph node with turbo-frame within

    Not sure if it's a bug or expected behaviour but having this markup initially on page:

    <turbo-frame id="product_list" src="_partials/product/list.html" loading="lazy" target="_top">This is the ORIGINAL product list that is expensive to load on initial page load</turbo-frame>
    

    We cannot issue the following morph:

    <turbo-stream action="morph" target="product_list">
      <template
        ><turbo-frame id="product_list"> This is the NEW product list</turbo-frame>
      </template>
    </turbo-stream>
    

    The following happens:

    1. Morph modifies content of a turbo frame but for some reason it triggers frame reload that in turn reverts original content.
    opened by pySilver 2
Releases(v0.1.0)
  • v0.1.0(Sep 25, 2022)

    Changed

    • First non-beta release
    • Upgraded @hotwired/turbo to 7.2.0

    Commits

    Full Changelog: https://github.com/marcoroth/turbo-morph/compare/85fabc90af865f1d04163ab1b17a1cb4e249bf23...v0.1.0

    Source code(tar.gz)
    Source code(zip)
  • v0.1.0-beta.2(Jul 20, 2022)

    What's Changed

    • Fix morph() to morph instead of replace with children-only by @connorwbey in https://github.com/marcoroth/turbo-morph/pull/2

    New Contributors

    • @connorwbey made their first contribution in https://github.com/marcoroth/turbo-morph/pull/2

    Full Changelog: https://github.com/marcoroth/turbo-morph/compare/v0.1.0-beta.1...v0.1.0-beta.2

    Source code(tar.gz)
    Source code(zip)
  • v0.1.0-beta.1(Jul 19, 2022)

Owner
Marco Roth
Rubyist, part-time developer and part-time computer science student.
Marco Roth
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Connect Web Integration illustrates the integration of Connect-Web in various JS frameworks and tooling

Connect Web Integration Connect Web Integration is a repository of example projects using Connect-Web with various JS frameworks and tooling. It provi

Buf 43 Dec 29, 2022
This Is a Whatsapp Bot Made By Turbo Do Not Recode

This Is a Whatsapp Bot Made By Turbo Do Not Recode

TURBOMODS 7 Dec 6, 2022
Provides event handling and an HTMLElement mixin for Declarative Shadow DOM in Hotwire Turbo.

Turbo Shadow Provides event handling and an HTMLElement mixin for Declarative Shadow DOM support in Hotwire Turbo. Requires Turbo 7.2 or higher. Quick

Whitefusion 17 Sep 28, 2022
Nuxt.js 3 x Histoire x Vitest x VitePress x Turbo (pnpm)

Turborepo nuxt starter This is a monorepo with Nuxt, Histoire, Vitest & VitePress as a starter for any project that can be easily extended. You can al

Gurvan 19 Dec 19, 2022
This repository contains a fullstack chatbot project based on the ChatGPT `gpt-3.5-turbo` model.

This is a fullstack chatbot created with React, Nodejs, OpenAi, and ChatGPT while developing the following tutorial: How To Build A Chat Bot Applicati

NJOKU SAMSON EBERE 6 May 10, 2023
Trusted timestamps that you can physically include in photos, videos and live streams using QR codes and audible data signals.

QR Date This is the reference implementation for the first version of QR Date, a signed timestamp inside a QR code that you can use to verify the date

QR Date 36 Oct 5, 2022
Iterables, streams for typescript

Iterable for Typescript Similar to what we know from C#, Dart or any other language which supports them, we use Iterables to stream over collections.

null 3 Oct 15, 2022
Example-browserstack-reporting - This repository contains an example of running Selenium tests and reporting BrowserStack test results, including full CI pipeline integration.

BrowserStack reporting and Selenium test result example This repository contains an example of running Selenium tests and reporting BrowserStack test

Testmo 1 Jan 1, 2022
Runs various integration tests for the composable picasso parachain.

Picasso Integration Tester Picasso Integration Tester is a collection of different implementation tests for the Picasso Polkadot Parachain. Installati

Dominik Roth 0 Jan 11, 2022
Playstation integration for Homebridge / HOOBS.

Homebridge Playstation Playstation integration for Homebridge / HOOBS. Hey Siri, turn on Playstation finally possible! This integration exposes a Swit

Flavio De Stefano 54 Jan 1, 2023
🔐 Lambda Authorizer ready for integration with Serverless Framework and Auth0.

Getting started 1. Clone the repository (or generate a serverless project) sls create --name auth-service --template-url https://github.com/GustavoNor

Gustavo Noronha 2 Feb 10, 2022
A Next.js 12 integration of GraphQL Server.

This is a Next.js 12 integration of GraphQL Server. It is early work and has not been extensively tested in production. It is based on samples provide

Arthur Puyou 15 Dec 29, 2022
Example Serverless DynamoDB integration tests using Jest, TypeScript and the AWS CDK

serverless dynamodb integration tests ?? Example Serverless DynamoDB integration tests using Jest, TypeScript and the AWS CDK Introduction How to inte

Lee Gilmore 8 Nov 4, 2022
Integration of WebSlides and Markdown.

WebSlides.md 集成 WebSlides 和 Markdown。 这个项目做什么用? 简单来说当然是在线分享PPT(Slides)(手动狗头)。 具体来说就是你可以用任意一个简单的、在线能够运行 Web 代码的 Playground 环境来当做 PPT 制作工具来写你的 PPT,比如 Co

稀土 8 Oct 10, 2022
This plugin allows for basic integration with Raindrop.io, a bookmarking service and Obsidian.

Obsidian Raindrop Plugin This plugin allows for basic integration with Raindrop.io, a bookmarking service and Obsidian. Current Features Create a code

null 108 Jan 7, 2023
Node.js TypeScript project demonstrating a Prisma integration with PostgreSQL

Prisma with PostgreSQL, TypeScript, Serverless and Parameter Store This project is the outcome of following the Prisma Getting Started guide. In addit

Christian Rich 10 Aug 28, 2022