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

Extract Morph class, then use it in FrameRenderer #1029

Conversation

seanpdoyle
Copy link
Contributor

Declaring all Idiomorph-related logic in the MorphRenderer limits
its accessibility to other parts of the system. For example,
<turbo-frame refresh="morph"> elements are unable to morph their
renders when driven by <a> navigations or <form> submissions.

This commit extracts the bulk of the morphing logic into a new Morph
class. The Morph encapsulates the call to Idiomorph, along with the
remote <turbo-frame> reloading and [data-turbo-permanent] checking.

With this extraction, the MorphRenderer is implemented in terms of
delegating to the static Morph.render method.

With that extraction in place, the FrameRenderer.renderElement method
can incorporate the element.src && element.refresh === "morph" check,
calling FrameRenderer.morph when true, then falling back to the
default FrameRenderer.replace when false.

For the sake of consistency, declare all Renderer subclasses'
renderElement methods in terms of static replace and static morph
methods to be explicit about which styles they support.

This commit includes test coverage for morphing <turbo-frame refresh="morph"> elements driven by typical navigation.

The potential for <turbo-stream action="morph">

With the Morph.render function existing separately from
MorphRenderer, there's the potential to add a new
StreamAction.morph.

The implementation would look something like:

morph() {
  this.targetElements.forEach((targetElement) => {
    Morph.render(targetElement, this.templateContent)
  })
}

I've omitted that from this commit because I'm not sure if that's an
interface we're interested in introducing, but I did want to highlight
the possibility here. It'd be an Idiomorph-powered version of the
turbo-morph package.

afcapel and others added 3 commits October 4, 2023 22:37
This commit introduces the concept of page refresh. A page refresh
happens when Turbo renders the current page again. We will offer two new
options to control behavior when a page refresh happens:

- The method used to update the page: with a new option to use morphing
  (Turbo currently replaces the body).

- The scroll strategy: with a new option to keep it (Turbo currently
  resets scroll to the top-left).

The combination of morphing and scroll-keeping results in smoother
updates that keep the screen state. For example, this will keep both
horizontal and vertical scroll, the focus, the text selection, CSS
transition states, etc.

We will also introduce a new turbo stream action that, when broadcasted,
will request a page refresh. This will offer a simplified alternative
to fine-grained broadcasted turbo-stream actions.

Co-Authored-By: Jorge Manrubia <[email protected]>
@seanpdoyle seanpdoyle changed the base branch from main to morph-refreshes October 9, 2023 15:25
@seanpdoyle seanpdoyle force-pushed the morph-refreshes-morph-renderer branch from 642c3ef to fae93de Compare October 9, 2023 15:28
@seanpdoyle seanpdoyle mentioned this pull request Oct 9, 2023
2 tasks
@seanpdoyle seanpdoyle force-pushed the morph-refreshes-morph-renderer branch from fae93de to 5196762 Compare October 9, 2023 22:46
Comment on lines 454 to 456
assert.equal(await input.count(), 1)
assert.equal(await frame.count("#permanent-form-in-frame"), 1)
assert.equal(await frame.count("#permanent-descendant-input-in-frame"), 1)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Something is off, I can't figure out a way to pass these. When the morph occurs, it duplicates the [data-turbo-permanent] elements.

Declaring all `Idiomorph`-related logic in the `MorphRenderer` limits
its accessibility to other parts of the system. For example,
`<turbo-frame refresh="morph">` elements are unable to morph their
renders when driven by `<a>` navigations or `<form>` submissions.

This commit extracts the bulk of the morphing logic into a new `Morph`
class. The `Morph` encapsulates the call to `Idiomorph`, along with the
remote `<turbo-frame>` reloading and `[data-turbo-permanent]` checking.

With this extraction, the `MorphRenderer` is implemented in terms of
delegating to the static `Morph.render` method.

With that extraction in place, the `FrameRenderer.renderElement` method
can incorporate the `element.src && element.refresh === "morph"` check,
calling `FrameRenderer.morph` when true, then falling back to the
default `FrameRenderer.replace` when false.

For the sake of consistency, declare all `Renderer` subclasses'
`renderElement` methods in terms of `static replace` and `static morph`
methods to be explicit about which styles they support.

This commit includes test coverage for morphing `<turbo-frame
refresh="morph">` elements driven by typical navigation.

The potential for `<turbo-stream action="morph">`
---

With the `Morph.render` function existing separately from
`MorphRenderer`, there's the potential to add a new
`StreamAction.morph`.

The implementation would look something like:

```js
morph() {
  this.targetElements.forEach((targetElement) => {
    Morph.render(targetElement, this.templateContent)
  })
}
```

I've omitted that from this commit because I'm not sure if that's an
interface we're interested in introducing, but I did want to highlight
the possibility here. It'd be an Idiomorph-powered version of the
[turbo-morph][] package.

[turbo-morph]: https://github.com/marcoroth/turbo-morph
@seanpdoyle seanpdoyle force-pushed the morph-refreshes-morph-renderer branch from 5196762 to 2fd95cf Compare October 12, 2023 00:20

render(morphStyle = "outerHTML") {
window.Idiomorph.morph(this.currentElement, this.newElement, {
ignoreActiveValue: true,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The ignoreActiveValue: option was introduced in 0.0.9, and feels important enough to include as a default for the sake of focus preservation and to minimize disruptions to the element with focus.

Progressing this branch forward hinges on bigskysoftware/idiomorph#12 being merged. Without it, the package needs to depend on window.Idiomorph.

@jorgemanrubia jorgemanrubia force-pushed the morph-refreshes branch 5 times, most recently from 9125652 to 00b3015 Compare October 27, 2023 07:13
@afcapel afcapel deleted the branch hotwired:morph-refreshes November 14, 2023 15:06
@afcapel afcapel closed this Nov 14, 2023
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