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

Add support to replace the turbo_frame contents #145

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/prettier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ jobs:
run: 'npm install --legacy-peer-deps'

- name: Run Prettier
run: 'npx prettier --check --fail-on-errors .'
run: 'npx prettier --check .'

2 changes: 1 addition & 1 deletion app/assets/builds/@turbo-boost/commands.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions app/assets/builds/@turbo-boost/commands.js.map

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions app/javascript/invoker.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const parseError = error => {
dispatch(lifecycle.events.clientError, document, { detail: { message, error } }, true)
}

const parseAndRenderResponse = response => {
const parseAndRenderResponse = frameId => response => {
const { strategy } = headers.tokenize(response.headers.get(headers.RESPONSE_HEADER))
response.text().then(content => render(strategy, content))
response.text().then(content => render(strategy, content, frameId))
}

const invoke = (payload = {}) => {
Expand All @@ -21,7 +21,7 @@ const invoke = (payload = {}) => {
headers: headers.prepare({}),
body: JSON.stringify(payload)
})
.then(parseAndRenderResponse)
.then(parseAndRenderResponse(payload.frameId))
.catch(parseError)
} catch (error) {
parseError(error)
Expand Down
18 changes: 17 additions & 1 deletion app/javascript/renderer.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
import elements from './elements'

const getElementByIdFromHTMLString = (htmlString, id) => {
var parser = new DOMParser()
var doc = parser.parseFromString(htmlString, 'text/html')
var element = doc.getElementById(id)
return element.outerHTML
}

const frameReplace = (content, frameId) => {
const newFrame = getElementByIdFromHTMLString(content, frameId)
const frame = document.querySelector(`#${frameId}`)
if (frame && newFrame) TurboBoost?.Streams?.morph?.method(frame, newFrame)
}

const append = content => {
document.body.insertAdjacentHTML('beforeend', content)
}
Expand All @@ -14,10 +29,11 @@ const replace = content => {
}

// TODO: dispatch events after append/replace so we can apply page state
export const render = (strategy, content) => {
export const render = (strategy, content, frameId) => {
if (strategy && content) {
if (strategy.match(/^Append$/i)) return append(content)
if (strategy.match(/^Replace$/i)) return replace(content)
if (strategy.match(/^FrameReplace$/i)) return frameReplace(content, frameId)
}
}

Expand Down
4 changes: 4 additions & 0 deletions lib/turbo_boost/commands/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ def turbo_stream_template_exists?
# Commands support the following redering strategies on the client.
# 1. Replace: The entire page (head, body) is replaced with the new content via morph
# 2. Append: The new content is appended to the body
# 3. FrameReplace: Replace the turbo frame specified in the command with the frame in the content. The content can contain more( e.g. whole content)
def client_render_strategy
# Use the replace strategy if the follow things are true:
#
Expand All @@ -244,6 +245,9 @@ def client_render_strategy
if command_params[:driver] == "window" && controller_action_allowed?
return "Replace" unless turbo_stream_template_exists?
end
if command_params[:driver] == "frame" && controller_action_allowed?
return "FrameReplace" unless turbo_stream_template_exists?
end

"Append"
end
Expand Down
2 changes: 1 addition & 1 deletion test/dummy/app/assets/builds/tailwind.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions test/dummy/app/commands/decrement_count_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
class DecrementCountCommand < ApplicationCommand
def perform
Current.user.decrement! :count
controller.instance_variable_set(:@extra_data, "decremented")
end
end
1 change: 1 addition & 0 deletions test/dummy/app/commands/increment_count_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
class IncrementCountCommand < ApplicationCommand
def perform
Current.user.increment! :count
controller.instance_variable_set(:@extra_data, "incremented")
end
end
8 changes: 8 additions & 0 deletions test/dummy/app/controllers/turbo_frame_replaces_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

class TurboFrameReplacesController < ApplicationController
# GET /turbo_frame_replace
def show
@extra_data ||= "Unchanged"
end
end
25 changes: 25 additions & 0 deletions test/dummy/app/views/basic_commands/@stylesheet.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
@layer components {
#basic_command-no-frame,
#basic_command-turbo-frame-replace,
#basic_command-turbo-frame {
@apply relative mx-auto block max-w-screen-xl rounded-lg border;
}

#basic_command-no-frame {
@apply border-sky-500 p-4 dark:border-sky-800;
}

#basic_command-turbo-frame-replace {
@apply border-blue-500 p-4 dark:border-blue-800;
}

#basic_command-turbo-frame-replace::before {
content: '<turbo-frame with replace>';
@apply absolute -top-4 left-4 inline-block rounded-t bg-blue-500 px-2 font-mono text-xs text-white dark:bg-blue-800 dark:text-blue-200;
}

#basic_command-no-frame::before {
content: '<partial>';
@apply absolute -top-4 left-4 inline-block rounded-t bg-sky-500 px-2 font-mono text-xs text-white dark:bg-sky-800 dark:text-sky-200;
Expand All @@ -17,39 +29,52 @@
content: '<turbo-frame>';
@apply absolute -top-4 left-4 inline-block rounded-t bg-rose-500 px-2 font-mono text-xs text-white dark:bg-rose-800 dark:text-rose-200;
}

#basic_command-no-frame > h3,
#basic_command-turbo-frame-replace > h3,
#basic_command-turbo-frame > h3 {
@apply text-2xl font-semibold text-gray-500 dark:text-white md:text-2xl;
}

#basic_command-no-frame > p,
#basic_command-turbo-frame-replace > p,
#basic_command-turbo-frame > p {
@apply mb-4 font-light text-gray-500 dark:text-gray-400;
}

#basic_command-no-frame > code,
#replace_this code,
#basic_command-turbo-frame > code {
@apply px-3 py-1 text-3xl font-bold text-red-500 md:text-4xl;
}
#basic_command-no-frame > div,
#basic_command-turbo-frame-replace > div,
#basic_command-turbo-frame > div {
@apply flex rounded-md shadow-sm;
}
#basic_command-no-frame > div:last-child,
#replace_this > div:last-child,
#basic_command-turbo-frame > div:last-child {
@apply mt-5;
}

#basic_command-no-frame > div > button,
#replace_this > div > button,
#basic_command-turbo-frame > div > button {
@apply inline-flex items-center border border-gray-200 bg-white px-4 py-2 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:text-blue-700 focus:ring-2 focus:ring-blue-700 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:hover:bg-gray-600 dark:hover:text-white dark:focus:text-white dark:focus:ring-blue-500;
}
#basic_command-no-frame > div > button:first-child,
#replace_this > div > button:first-child,
#basic_command-turbo-frame > div > button:first-child {
@apply rounded-l-lg;
}
#basic_command-no-frame > div > button:last-child,
#replace_this > div > button:last-child,
#basic_command-turbo-frame > div > button:last-child {
@apply rounded-r-md;
}
#basic_command-no-frame > div > button > svg,
#replace_this > div > button > svg,
#basic_command-turbo-frame > div > button > svg {
@apply mr-1 h-3 w-3;
}
Expand Down
1 change: 1 addition & 0 deletions test/dummy/app/views/sidebars/_sidebar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

<%= render "sidebars/button", value: "Demos", heroicon: :play_circle, active: demos_controller? do %>
<%= render "sidebars/link", label: "Basic Command", href: basic_command_path, icon: :command_line, active: controller_action?("basic_commands#show") %>
<%= render "sidebars/link", label: "Turbo Frame Replace", href: turbo_frame_replace_path, icon: :command_line, active: controller_action?("/turbo_frame_replace#show_more") %>
<%= render "sidebars/link", label: "Tables", href: todos_path(demo: "tables"), icon: :command_line, active: params[:demo] == "tables" %>
<%= render "sidebars/link", label: "Simple Form", href: todos_path(demo: "simple-form"), icon: :command_line, active: params[:demo] == "simple-form" %>
<%= render "sidebars/link", label: "Complex Form", href: todos_path(demo: "complex-form"), icon: :command_line, active: params[:demo] == "complex-form" %>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!-- content... -->

<%= turbo_frame_tag "basic_command-turbo-frame-replace" do %>
<h3>Counter</h3>

<p>
In this demo, the counter value or "state" is managed by the User model.
It is persisted to the database and used when rendering the page. But this data:
</p>
<code class="mb-3" role="state"><%= @extra_data %></code>
<p>
is outside of the frame and although it is updated in the returned HTML, it is not updated on the screen.
</p>

<%= turbo_frame_tag :replace_this do %>
<code role="counter"><%= padded_number Current.user.count %></code>

<% cache do %>
<div>
<button data-turbo-command="IncrementCountCommand">
<%= render "icons/heroicons/plus", solid: true %> Increment
</button>

<button data-turbo-command="DecrementCountCommand">
<%= render "icons/heroicons/minus", solid: true %> Decrement
</button>

<button data-turbo-command="ResetCountCommand" data-turbo-confirm="Are you sure?">
<%= render "icons/heroicons/arrow_path", solid: true %> Reset
</button>
</div>
<% end %>
<% end %>

<% end %>

<!-- content... -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
├── app
│ ├── commands
│ │ ├── decrement_count_command.rb
│ │ ├── increment_count_command.rb
│ │ └── reset_count_command.rb
│ │
│ ├── controllers
│ │ └── turbo_framereplaces_controller.rb
│ │
│ ├── models
│ │ └── user.rb
│ │
│ ├── views
│ │ └── basic_commands
│ │ ├── _turbo_frame_replace.html.erb
│ │ └── show.html.erb
Loading