Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer renderElement during Renderer construction #1028

Merged

Conversation

seanpdoyle
Copy link
Contributor

@seanpdoyle seanpdoyle commented Oct 9, 2023

Follow-up to #1192
Closes #1303

Remove the argument from all Renderer subclass constructor call sites.
In its place, define the default value for the Renderer.renderElement
property based on the static renderElement(currentElement, newElement)
defined by the inheriting class.

By automatically determining which renderElement implementation based
on class, this commit enables a simpler Frame morphing strategy. Instead
of attaching one-time event listeners, this commit maintains a private
#shouldMorphFrame property that is set, then un-set during the
rendering lifecycle of a frame.

Similarly, this commit implements the
MorphingFrameRenderer.preservingPermanentElements method in the same
style as the MorphingPageRenderer.preservingPermanentElements. Since
instances of MorphingFrameRenderer are now being used instead of the
static MorphingFrameRenderer.renderElement, we're able to utilize an
instance method instead of passing around an additional anonymous
function (which is the implementation that #1303 proposed).

Co-authored-by: Micah Geisel micah@botandrose.com

seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Oct 9, 2023
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Oct 9, 2023
@seanpdoyle seanpdoyle force-pushed the renderer-render-element-constructor branch from dbca3a0 to 6afdcf4 Compare October 26, 2023 16:09
@seanpdoyle
Copy link
Contributor Author

@afcapel this aims to simplify the Renderer constructor and interface.

@seanpdoyle seanpdoyle force-pushed the renderer-render-element-constructor branch 2 times, most recently from 2d63f41 to 9d8a361 Compare November 16, 2023 17:05
@seanpdoyle seanpdoyle force-pushed the renderer-render-element-constructor branch 2 times, most recently from 1cde83d to 8e6de8f Compare March 29, 2024 23:24
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 30, 2024
Follow-up to [][]
Related to [hotwired#1192][]

The `morphElements` function
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement, delegate)` function
to be invoked across the various morphing contexts. Next, move the logic
from the `MorphRenderer` into a module-private `Morph` class. The
`Morph` class (like its `MorphRenderer` predecessor) wraps a call to
`Idiomorph` based on its own set of callbacks. The bulk of the logic
remains in the `Morph` class, including checks for
`[data-turbo-permanent]`. To serve as a seam for integration, the class
retains a reference to a delegate responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `PageMorphRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`FrameMorphRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`PageMorphRenderer` to set a new precedent that communicates the level
of the document the morphing is scoped to. With that change in place,
define the static `PageMorphRenderer.renderElement` to mirror the other
existing renderer static functions (like [PageRenderer.renderElement][],
[ErrorRenderer.renderElement][], and [FrameRenderer.renderElement][]).
This integrates with the changes proposed in [hotwired#1028][].

Next, modify the rest of the `PageMorphRenderer` to integrate with its
`PageRenderer` ancestor in a way that invokes the static `renderElement`
function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `FrameMorphRenderer` class
to define the `FrameMorphRenderer.renderElement` function that invokes
the `morphElements` function with `newElement.children` and `morphStyle:
"innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = (currentElement, newElement) => {
      window.Turbo.morphElements(currentElement, newElement, {
        ...
      })
    }
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 30, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morphElements` function
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement, delegate)` function
to be invoked across the various morphing contexts. Next, move the logic
from the `MorphRenderer` into a module-private `Morph` class. The
`Morph` class (like its `MorphRenderer` predecessor) wraps a call to
`Idiomorph` based on its own set of callbacks. The bulk of the logic
remains in the `Morph` class, including checks for
`[data-turbo-permanent]`. To serve as a seam for integration, the class
retains a reference to a delegate responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `PageMorphRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`FrameMorphRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`PageMorphRenderer` to set a new precedent that communicates the level
of the document the morphing is scoped to. With that change in place,
define the static `PageMorphRenderer.renderElement` to mirror the other
existing renderer static functions (like [PageRenderer.renderElement][],
[ErrorRenderer.renderElement][], and [FrameRenderer.renderElement][]).
This integrates with the changes proposed in [hotwired#1028][].

Next, modify the rest of the `PageMorphRenderer` to integrate with its
`PageRenderer` ancestor in a way that invokes the static `renderElement`
function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `FrameMorphRenderer` class
to define the `FrameMorphRenderer.renderElement` function that invokes
the `morphElements` function with `newElement.children` and `morphStyle:
"innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = (currentElement, newElement) => {
      window.Turbo.morphElements(currentElement, newElement, {
        ...
      })
    }
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 30, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morphElements` function
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement, delegate)` function
to be invoked across the various morphing contexts. Next, move the logic
from the `MorphRenderer` into a module-private `IdomorphDelegate` class.
The `IdomorphDelegate` class (like its `MorphRenderer` predecessor)
wraps a call to `Idiomorph` based on its own set of callbacks. The bulk
of the logic remains in the `IdomorphDelegate` class, including checks
for `[data-turbo-permanent]`. To serve as a seam for integration, the
class retains a reference to a delegate responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `PageMorphRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`FrameMorphRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`PageMorphRenderer` to set a new precedent that communicates the level
of the document the morphing is scoped to. With that change in place,
define the static `PageMorphRenderer.renderElement` to mirror the other
existing renderer static functions (like [PageRenderer.renderElement][],
[ErrorRenderer.renderElement][], and [FrameRenderer.renderElement][]).
This integrates with the changes proposed in [hotwired#1028][].

Next, modify the rest of the `PageMorphRenderer` to integrate with its
`PageRenderer` ancestor in a way that invokes the static `renderElement`
function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `FrameMorphRenderer` class
to define the `FrameMorphRenderer.renderElement` function that invokes
the `morphElements` function with `newElement.children` and `morphStyle:
"innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = (currentElement, newElement) => {
      window.Turbo.morphElements(currentElement, newElement, {
        ...
      })
    }
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 30, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morphElements` function
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement, delegate)` function
to be invoked across the various morphing contexts. Next, move the logic
from the `MorphRenderer` into a module-private `IdomorphDelegate` class.
The `IdomorphDelegate` class (like its `MorphRenderer` predecessor)
wraps a call to `Idiomorph` based on its own set of callbacks. The bulk
of the logic remains in the `IdomorphDelegate` class, including checks
for `[data-turbo-permanent]`. To serve as a seam for integration, the
class retains a reference to a delegate responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `PageMorphRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`FrameMorphRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`PageMorphRenderer` to set a new precedent that communicates the level
of the document the morphing is scoped to. With that change in place,
define the static `PageMorphRenderer.renderElement` to mirror the other
existing renderer static functions (like [PageRenderer.renderElement][],
[ErrorRenderer.renderElement][], and [FrameRenderer.renderElement][]).
This integrates with the changes proposed in [hotwired#1028][].

Next, modify the rest of the `PageMorphRenderer` to integrate with its
`PageRenderer` ancestor in a way that invokes the static `renderElement`
function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `FrameMorphRenderer` class
to define the `FrameMorphRenderer.renderElement` function that invokes
the `morphElements` function with `newElement.children` and `morphStyle:
"innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = (currentElement, newElement) => {
      window.Turbo.morphElements(currentElement, newElement, {
        ...
      })
    }
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Mar 30, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morphElements` function
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement, delegate)` function
to be invoked across the various morphing contexts. Next, move the logic
from the `MorphRenderer` into a module-private `IdomorphDelegate` class.
The `IdomorphDelegate` class (like its `MorphRenderer` predecessor)
wraps a call to `Idiomorph` based on its own set of callbacks. The bulk
of the logic remains in the `IdomorphDelegate` class, including checks
for `[data-turbo-permanent]`. To serve as a seam for integration, the
class retains a reference to a delegate responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `PageMorphRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`FrameMorphRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`PageMorphRenderer` to set a new precedent that communicates the level
of the document the morphing is scoped to. With that change in place,
define the static `PageMorphRenderer.renderElement` to mirror the other
existing renderer static functions (like [PageRenderer.renderElement][],
[ErrorRenderer.renderElement][], and [FrameRenderer.renderElement][]).
This integrates with the changes proposed in [hotwired#1028][].

Next, modify the rest of the `PageMorphRenderer` to integrate with its
`PageRenderer` ancestor in a way that invokes the static `renderElement`
function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `FrameMorphRenderer` class
to define the `FrameMorphRenderer.renderElement` function that invokes
the `morphElements` function with `newElement.children` and `morphStyle:
"innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = (currentElement, newElement) => {
      window.Turbo.morphElements(currentElement, newElement, {
        ...
      })
    }
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Apr 3, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morphElements` function
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement, delegate)` function
to be invoked across the various morphing contexts. Next, move the logic
from the `MorphRenderer` into a module-private `IdomorphDelegate` class.
The `IdomorphDelegate` class (like its `MorphRenderer` predecessor)
wraps a call to `Idiomorph` based on its own set of callbacks. The bulk
of the logic remains in the `IdomorphDelegate` class, including checks
for `[data-turbo-permanent]`. To serve as a seam for integration, the
class retains a reference to a delegate responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `PageMorphRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`FrameMorphRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`PageMorphRenderer` to set a new precedent that communicates the level
of the document the morphing is scoped to. With that change in place,
define the static `PageMorphRenderer.renderElement` to mirror the other
existing renderer static functions (like [PageRenderer.renderElement][],
[ErrorRenderer.renderElement][], and [FrameRenderer.renderElement][]).
This integrates with the changes proposed in [hotwired#1028][].

Next, modify the rest of the `PageMorphRenderer` to integrate with its
`PageRenderer` ancestor in a way that invokes the static `renderElement`
function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `FrameMorphRenderer` class
to define the `FrameMorphRenderer.renderElement` function that invokes
the `morphElements` function with `newElement.children` and `morphStyle:
"innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = (currentElement, newElement) => {
      window.Turbo.morphElements(currentElement, newElement, {
        ...
      })
    }
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Apr 8, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morphElements` function
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement, delegate)` function
to be invoked across the various morphing contexts. Next, move the logic
from the `MorphRenderer` into a module-private `IdomorphDelegate` class.
The `IdomorphDelegate` class (like its `MorphRenderer` predecessor)
wraps a call to `Idiomorph` based on its own set of callbacks. The bulk
of the logic remains in the `IdomorphDelegate` class, including checks
for `[data-turbo-permanent]`. To serve as a seam for integration, the
class retains a reference to a delegate responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `PageMorphRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`FrameMorphRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`PageMorphRenderer` to set a new precedent that communicates the level
of the document the morphing is scoped to. With that change in place,
define the static `PageMorphRenderer.renderElement` to mirror the other
existing renderer static functions (like [PageRenderer.renderElement][],
[ErrorRenderer.renderElement][], and [FrameRenderer.renderElement][]).
This integrates with the changes proposed in [hotwired#1028][].

Next, modify the rest of the `PageMorphRenderer` to integrate with its
`PageRenderer` ancestor in a way that invokes the static `renderElement`
function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `FrameMorphRenderer` class
to define the `FrameMorphRenderer.renderElement` function that invokes
the `morphElements` function with `newElement.children` and `morphStyle:
"innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = (currentElement, newElement) => {
      window.Turbo.morphElements(currentElement, newElement, {
        ...
      })
    }
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Apr 8, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morphElements` function
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement, delegate)` function
to be invoked across the various morphing contexts. Next, move the logic
from the `MorphRenderer` into a module-private `IdomorphDelegate` class.
The `IdomorphDelegate` class (like its `MorphRenderer` predecessor)
wraps a call to `Idiomorph` based on its own set of callbacks. The bulk
of the logic remains in the `IdomorphDelegate` class, including checks
for `[data-turbo-permanent]`. To serve as a seam for integration, the
class retains a reference to a delegate responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `PageMorphRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`FrameMorphRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`PageMorphRenderer` to set a new precedent that communicates the level
of the document the morphing is scoped to. With that change in place,
define the static `PageMorphRenderer.renderElement` to mirror the other
existing renderer static functions (like [PageRenderer.renderElement][],
[ErrorRenderer.renderElement][], and [FrameRenderer.renderElement][]).
This integrates with the changes proposed in [hotwired#1028][].

Next, modify the rest of the `PageMorphRenderer` to integrate with its
`PageRenderer` ancestor in a way that invokes the static `renderElement`
function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `FrameMorphRenderer` class
to define the `FrameMorphRenderer.renderElement` function that invokes
the `morphElements` function with `newElement.children` and `morphStyle:
"innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = (currentElement, newElement) => {
      window.Turbo.morphElements(currentElement, newElement, {
        ...
      })
    }
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Apr 13, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morph{Page,Frames,Elements}` functions
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement)` function to be
invoked across the various morphing contexts. Next, move the logic from
the `MorphRenderer` into a module-private `IdomorphCallbacks` class. The
`IdomorphCallbacks` class (like its `MorphRenderer` predecessor) wraps a
call to `Idiomorph` based on its own set of callbacks. The bulk of the
logic remains in the `IdomorphCallbacks` class, including checks for
`[data-turbo-permanent]`. To serve as a seam for integration, the class
retains a reference to a callback responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `MorphingPageRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`MorphingFrameRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`MorphingPageRenderer` to set a new precedent that communicates the
level of the document the morphing is scoped to. With that change in
place, define the static `MorphingPageRenderer.renderElement` to mirror
the other existing renderer static functions (like
[PageRenderer.renderElement][], [ErrorRenderer.renderElement][], and
[FrameRenderer.renderElement][]). This integrates with the changes
proposed in [hotwired#1028][].

Next, modify the rest of the `MorphingPageRenderer` to integrate with
its `PageRenderer` ancestor in a way that invokes the static
`renderElement` function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `MorphingFrameRenderer`
class to define the `MorphingFrameRenderer.renderElement` function that
invokes the `morphElements` function with `newElement.children` and
`morphStyle: "innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = Turbo.morphPage
  }
})

addEventListener("turbo:before-frame-render", (event) => {
  const someCriteriaForMorphingAFrame = ...

  if (someCriteriaForMorphingAFrame) {
    event.detail.render = Turbo.morphFrames
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Apr 15, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morph{Page,Frames,Elements}` functions
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement)` function to be
invoked across the various morphing contexts. Next, move the logic from
the `MorphRenderer` into a module-private `IdomorphCallbacks` class. The
`IdomorphCallbacks` class (like its `MorphRenderer` predecessor) wraps a
call to `Idiomorph` based on its own set of callbacks. The bulk of the
logic remains in the `IdomorphCallbacks` class, including checks for
`[data-turbo-permanent]`. To serve as a seam for integration, the class
retains a reference to a callback responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `MorphingPageRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`MorphingFrameRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`MorphingPageRenderer` to set a new precedent that communicates the
level of the document the morphing is scoped to. With that change in
place, define the static `MorphingPageRenderer.renderElement` to mirror
the other existing renderer static functions (like
[PageRenderer.renderElement][], [ErrorRenderer.renderElement][], and
[FrameRenderer.renderElement][]). This integrates with the changes
proposed in [hotwired#1028][].

Next, modify the rest of the `MorphingPageRenderer` to integrate with
its `PageRenderer` ancestor in a way that invokes the static
`renderElement` function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `MorphingFrameRenderer`
class to define the `MorphingFrameRenderer.renderElement` function that
invokes the `morphElements` function with `newElement.children` and
`morphStyle: "innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = Turbo.morphPage
  }
})

addEventListener("turbo:before-frame-render", (event) => {
  const someCriteriaForMorphingAFrame = ...

  if (someCriteriaForMorphingAFrame) {
    event.detail.render = Turbo.morphFrames
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Apr 15, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morph{Page,Frames,Elements}` functions
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement)` function to be
invoked across the various morphing contexts. Next, move the logic from
the `MorphRenderer` into a module-private `IdomorphCallbacks` class. The
`IdomorphCallbacks` class (like its `MorphRenderer` predecessor) wraps a
call to `Idiomorph` based on its own set of callbacks. The bulk of the
logic remains in the `IdomorphCallbacks` class, including checks for
`[data-turbo-permanent]`. To serve as a seam for integration, the class
retains a reference to a callback responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `MorphingPageRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`MorphingFrameRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`MorphingPageRenderer` to set a new precedent that communicates the
level of the document the morphing is scoped to. With that change in
place, define the static `MorphingPageRenderer.renderElement` to mirror
the other existing renderer static functions (like
[PageRenderer.renderElement][], [ErrorRenderer.renderElement][], and
[FrameRenderer.renderElement][]). This integrates with the changes
proposed in [hotwired#1028][].

Next, modify the rest of the `MorphingPageRenderer` to integrate with
its `PageRenderer` ancestor in a way that invokes the static
`renderElement` function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `MorphingFrameRenderer`
class to define the `MorphingFrameRenderer.renderElement` function that
invokes the `morphElements` function with `newElement.children` and
`morphStyle: "innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = Turbo.morphPage
  }
})

addEventListener("turbo:before-frame-render", (event) => {
  const someCriteriaForMorphingAFrame = ...

  if (someCriteriaForMorphingAFrame) {
    event.detail.render = Turbo.morphFrames
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Apr 15, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morph{Page,Frames,Elements}` functions
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement)` function to be
invoked across the various morphing contexts. Next, move the logic from
the `MorphRenderer` into a module-private `IdomorphCallbacks` class. The
`IdomorphCallbacks` class (like its `MorphRenderer` predecessor) wraps a
call to `Idiomorph` based on its own set of callbacks. The bulk of the
logic remains in the `IdomorphCallbacks` class, including checks for
`[data-turbo-permanent]`. To serve as a seam for integration, the class
retains a reference to a callback responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `MorphingPageRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`MorphingFrameRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`MorphingPageRenderer` to set a new precedent that communicates the
level of the document the morphing is scoped to. With that change in
place, define the static `MorphingPageRenderer.renderElement` to mirror
the other existing renderer static functions (like
[PageRenderer.renderElement][], [ErrorRenderer.renderElement][], and
[FrameRenderer.renderElement][]). This integrates with the changes
proposed in [hotwired#1028][].

Next, modify the rest of the `MorphingPageRenderer` to integrate with
its `PageRenderer` ancestor in a way that invokes the static
`renderElement` function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `MorphingFrameRenderer`
class to define the `MorphingFrameRenderer.renderElement` function that
invokes the `morphElements` function with `newElement.children` and
`morphStyle: "innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = Turbo.morphPage
  }
})

addEventListener("turbo:before-frame-render", (event) => {
  const someCriteriaForMorphingAFrame = ...

  if (someCriteriaForMorphingAFrame) {
    event.detail.render = Turbo.morphFrames
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
seanpdoyle added a commit to seanpdoyle/turbo that referenced this pull request Jul 3, 2024
Follow-up to [hotwired#1185][]
Related to [hotwired#1192][]

The `morph{Page,Frames,Elements}` functions
---

Introduce a new `src/core/morphing` module to expose a centralized and
re-usable `morphElements(currentElement, newElement)` function to be
invoked across the various morphing contexts. Next, move the logic from
the `MorphRenderer` into a module-private `IdomorphCallbacks` class. The
`IdomorphCallbacks` class (like its `MorphRenderer` predecessor) wraps a
call to `Idiomorph` based on its own set of callbacks. The bulk of the
logic remains in the `IdomorphCallbacks` class, including checks for
`[data-turbo-permanent]`. To serve as a seam for integration, the class
retains a reference to a callback responsible for:

* providing options for the `Idiomorph`
* determining whether or not a node should be skipped while morphing

The `MorphingPageRenderer` skips `<turbo-frame refresh="morph">` elements
so that it can override their rendering to use morphing. Similarly, the
`MorphingFrameRenderer` provides the `morphStyle: "innerHTML"` option to
morph its children.

Changes to the renderers
---

To integrate with the new module, first rename the `MorphRenderer` to
`MorphingPageRenderer` to set a new precedent that communicates the
level of the document the morphing is scoped to. With that change in
place, define the static `MorphingPageRenderer.renderElement` to mirror
the other existing renderer static functions (like
[PageRenderer.renderElement][], [ErrorRenderer.renderElement][], and
[FrameRenderer.renderElement][]). This integrates with the changes
proposed in [hotwired#1028][].

Next, modify the rest of the `MorphingPageRenderer` to integrate with
its `PageRenderer` ancestor in a way that invokes the static
`renderElement` function. This involves overriding the
`preservingPermanentElements(callback)` method. In theory, morphing has
implications on the concept of "permanence". In practice, morphing has
the `[data-turbo-permanent]` attribute receive special treatment during
morphing.

Following the new precedent, introduce a new `MorphingFrameRenderer`
class to define the `MorphingFrameRenderer.renderElement` function that
invokes the `morphElements` function with `newElement.children` and
`morphStyle: "innerHTML"`.

Changes to the StreamActions
---

The extraction of the `morphElements` function makes entirety of the
`src/core/streams/actions/morph.js` module redundant. This commit
removes that module and invokes `morphElements` directly within the
`StreamActions.morph` function.

Future possibilities
---

In the future, additional changes could be made to expose the morphing
capabilities as part of the `window.Turbo` interface.

For example, applications could experiment with supporting [Page
Refresh-style morphing for pages with different URL pathnames][hotwired#1177] by
overriding the rendering mechanism in `turbo:before-render`:

```js
addEventListener("turbo:before-render", (event) => {
  const someCriteriaForMorphing = ...

  if (someCriteriaForMorphing) {
    event.detail.render = Turbo.morphPage
  }
})

addEventListener("turbo:before-frame-render", (event) => {
  const someCriteriaForMorphingAFrame = ...

  if (someCriteriaForMorphingAFrame) {
    event.detail.render = Turbo.morphFrames
  }
})
```

[hotwired#1185]: hotwired#1185 (comment)
[hotwired#1192]: hotwired#1192
[PageRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/page_renderer.js#L5-L11
[ErrorRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/drive/error_renderer.js#L5-L9
[FrameRenderer.renderElement]: https://github.com/hotwired/turbo/blob/9fb05e3ed3ebb15fe7b13f52941f25df425e3d15/src/core/frames/frame_renderer.js#L5-L16
[hotwired#1028]: hotwired#1028
[hotwired#1177]: hotwired#1177
Follow-up to [hotwired#1192][]
Closes [hotwired#1303][]

Remove the argument from all `Renderer` subclass constructor call sites.
In its place, define the default value for the `Renderer.renderElement`
property based on the `static renderElement(currentElement, newElement)`
defined by the inheriting class.

By automatically determining which `renderElement` implementation based
on class, this commit enables a simpler Frame morphing strategy. Instead
of attaching one-time event listeners, this commit maintains a private
`#shouldMorphFrame` property that is set, then un-set during the
rendering lifecycle of a frame.

Similarly, this commit implements the
`MorphingFrameRenderer.preservingPermanentElements` method in the same
style as the `MorphingPageRenderer.preservingPermanentElements`. Since
_instances_ of `MorphingFrameRenderer` are now being used instead of the
static `MorphingFrameRenderer.renderElement`, we're able to utilize an
instance method instead of passing around an additional anonymous
function (which is the implementation that [hotwired#1303][] proposed).

[hotwired#1192]: hotwired#1192
[hotwired#1303]: hotwired#1303

Co-authored-by: Micah Geisel <micah@botandrose.com>
@seanpdoyle seanpdoyle force-pushed the renderer-render-element-constructor branch from 8e6de8f to 03549a9 Compare September 12, 2024 14:53
@seanpdoyle seanpdoyle changed the title Remove renderElement from Renderer constructor Infer renderElement during Renderer construction Sep 12, 2024
@seanpdoyle
Copy link
Contributor Author

@jorgemanrubia @brunoprietog while this is an older PR, I've re-purposed the changes to follow up #1192 and to resolve a bug documented by #1303. It's ready for review.

Copy link
Contributor

@botandrose botandrose left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seanpdoyle Nice! Much better. +1 from me.

Copy link
Member

@jorgemanrubia jorgemanrubia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice one @seanpdoyle, thanks.

@jorgemanrubia jorgemanrubia merged commit 3163a06 into hotwired:main Sep 13, 2024
1 check passed
@seanpdoyle seanpdoyle deleted the renderer-render-element-constructor branch September 13, 2024 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants