https://github.com/seanpdoyle/turbo_stream_button
Harness the power of Turbo Streams to declare click event handlers as a series of HTML mutations.
https://github.com/seanpdoyle/turbo_stream_button
hotwire html rails stimulus turbo
Last synced: about 1 month ago
JSON representation
Harness the power of Turbo Streams to declare click event handlers as a series of HTML mutations.
- Host: GitHub
- URL: https://github.com/seanpdoyle/turbo_stream_button
- Owner: seanpdoyle
- License: mit
- Created: 2022-06-27T20:41:32.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2024-10-24T18:42:17.000Z (12 months ago)
- Last Synced: 2025-01-01T00:11:28.330Z (9 months ago)
- Topics: hotwire, html, rails, stimulus, turbo
- Language: Ruby
- Homepage:
- Size: 67.4 KB
- Stars: 42
- Watchers: 3
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: MIT-LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# `` + ``
Harness the power of [Turbo Streams][] to declare `click` event handlers as a
series of HTML mutations.Combine built-in `` elements with `` elements to
declaratively drive client-side interactions with server-generated HTML.```html
Click me to insert the template's contents after this button!
You clicked the button!
```
[Try it out.](https://jsfiddle.net/toybqx89/)
[Turbo Streams]: https://turbo.hotwired.dev/reference/streams
## Usage
In your JavaScript code, import and register the `turbo-stream-button`
controller with your Stimulus application:```javascript
import "@hotwired/turbo"
import { Application } from "stimulus"
import { TurboStreamButtonController } from "@seanpdoyle/turbo_stream_button"const application = Application.start()
application.register("turbo-stream-button", TurboStreamButtonController)
```In your Rails templates, call the `turbo_stream_button_tag` helper or render the
`turbo_stream_button` view partial to create the `` element. The view
partial renders:* the block content as the `` element's content
* other keyword arguments as the `` element's attributes
* any content captured by any call to the `#turbo_streams` method invoked on the
block's single argumentWhen the button is clicked, the `turbo-stream-button` [Stimulus controller][]
invokes the `evaluate` [Action][] to insert the contents of the [``
element][mdn-template], activating any `` elements nested inside.[Stimulus controller]: https://stimulus.hotwired.dev/handbook/hello-stimulus#controllers-bring-html-to-life
[Action]: https://stimulus.hotwired.dev/handbook/building-something-real#connecting-the-action
[mdn-template]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template### Introductory: Hello, world
```html+erb
<%= turbo_stream_button_tag id: "the-button" do |button| %>
Click me to say "hello"<% button.turbo_streams do %>
<%= turbo_stream.after "the-button", "Hello, world!" %>
<% end %>
<% end %><%# =>
Click me to say "hello"
Hello, world!
%>
```### Intermediate: Compose with other Stimulus controller actions
```html+erb
import "@hotwired/turbo"
import { Application, Controller } from "stimulus"
import { TurboStreamButtonController } from "@seanpdoyle/turbo_stream_button"class ClipboardController extends Controller {
copy({ target: { value } }) {
navigator.clipboard.writeText(value)
}
}const application = Application.start()
application.register("turbo-stream-button", TurboStreamButtonController)
application.register("clipboard", ClipboardController)<%= turbo_stream_button_tag value: "invitation-code-abc123",
data: { controller: "clipboard", action: "click->clipboard#copy" } do |button| %>
Copy to clipboard<% button.turbo_streams do %>
Copied "invitation-code-abc123" to your clipboard!
<% end %>
<% end %><%# =>
Copy to clipboard
Copied "invitation-code-abc123" to your clipboard!
%>
```### Advanced: Nest buttons
```html+erb
<%= turbo_stream_button_tag do |button| %>
Append flash message<% button.turbo_streams do %>
Hello, world!<%= turbo_stream_button_tag do |button| %>
Dismiss<% button.turbo_streams do %>
<%= turbo_stream.remove "a_flash_message" %>
<% end %>
<% end %>
<% end %>
<% end %><%# =>
Append flash message
Hello, world!
Dismiss
%>
```### Advanced: Append form controls
```html+erb
import "@hotwired/turbo"
import { Application } from "stimulus"
import { TurboStreamButtonController } from "@seanpdoyle/turbo_stream_button"
import { TemplateInstance } from "https://cdn.skypack.dev/@github/template-parts"class CloneController extends Controller {
static targets = [ "template" ]
static values = { count: Number, counter: String }templateTargetConnected(target) {
const templateInstance = new TemplateInstance(target, {
[this.counterValue]: this.countValue
})target.content.replaceChildren(templateInstance)
this.countValue++
}
}const application = Application.start()
application.register("turbo-stream-button", TurboStreamButtonController)
application.register("clone", CloneController)<%= form_with scope: :applicant do |form| %>
References
<%= form.fields :reference_attributes, index: "{{counter}}" do |reference_form| %>
<%= turbo_stream_button_tag do |button| %>
Add reference<% button.turbo_streams do %>
<%= reference_form.label :referrer %>
<%= reference_form.text_field :referrer %>
<%= reference_form.label :relationship %>
<%= reference_form.text_field :relationship %>
<% end %>
<% end %>
<% end %>
<% end %>
<%# =>
Add reference
Referrer
Relationship
%>
```
## Helpers
There are two helpers declared by the engine:
### `turbo_stream_button_tag`
The `turbo_stream_button_tag` helper renders a `` element ready to
evaluate a collection of `` elements:
```erb
<%= turbo_stream_button_tag do |button| %>
Click to append "Hello!"
<% button.turbo_streams do %>
<%= turbo_stream.append_all "body" do %>
Hello!
<% end %>
<% end %>
<% end %>
```
### `turbo_stream_button`
The `turbo_stream_button` helper returns an attributes-aware HTML tag builder.
Render a `` element with the appropriate `[data-controller]` and
`[data-action]` attributes by calling `#tag`:
```erb
<%= turbo_stream_button.tag do %>
Click to append "Hello!"
<% end %>
```
The return a `Hash` of attributes containing the appropriate `[data-controller]`
and `[data-action]` attributes, call `#merge`, `#to_h` or splat them into
keyword arguments (with `**`):
```erb
<%= form_with model: Post.new do |form| %>
<%= form.button **turbo_stream_button, type: :submit do %>
Click to append "Hello!"
<%= tag.template turbo_stream_button.template.merge(data: { a_controller_target: "template" }) do %>
<%= turbo_stream.append_all "body" do %>
Hello!
<% end %>
<% end %>
<% end %>
<% end %>
```
To render the `` element nested within the ``, call
`#template_tag`:
```erb
<%= turbo_stream_button.tag do %>
Click to append "Hello!"
<% turbo_stream_button.template_tag do %>
<%= turbo_stream.append_all "body" do %>
Hello!
<% end %>
<% end %>
<% end %>
```
The return a `Hash` of attributes containing the appropriate
`[data-turbo-stream-button-target]` attribute, call `#merge`, `#to_h`, or splat
them into keyword arguments (with `**`):
```erb
<%= turbo_stream_button.tag do %>
Click to append "Hello!"
<%= tag.template turbo_stream_button.template.merge(data: { a_controller_target: "template" }) do %>
<%= turbo_stream.append_all "body" do %>
Hello!
<% end %>
<% end %>
<% end %>
```
## Exploring examples
To poke around with some working examples, start the [dummy application][]
locally:
```sh
cd test/dummy
bundle exec rails server --port 3000
```
Then, visit .
[](https://replit.com/@seanpdoyle/turbostreambutton)
You can also fork the [@seanpdoyle/turbo_stream_button][] sandbox project on
[replit.com][].
[dummy application]: ./test/dummy
[replit.com]: https://replit.com/
[@seanpdoyle/turbo_stream_button]: https://replit.com/@seanpdoyle/turbostreambutton
## Installation
Add the `turbo_stream_button` dependency to your application's Gemfile:
```ruby
gem "turbo_stream_button", github: "seanpdoyle/turbo_stream_button", branch: "main"
```
And then execute:
```bash
$ bundle
```
### Installation through [importmap-rails][]
Once the gem is installed, add the client-side dependency mapping to your
project's `config/importmap.rb` declaration:
```ruby
# config/importmap.rb
pin "stimulus", to: "stimulus.min.js", preload: true
pin "@seanpdoyle/turbo_stream_button", to: "turbo_stream_button.js"
```
[importmap-rails]: https://github.com/rails/importmap-rails
### Installation through `npm` or `yarn`
Once the gem is installed, add the client-side dependency to your project via
npm or Yarn:
```bash
yarn add https://github.com/seanpdoyle/turbo_stream_button.git
```
## Contributing
Please read [CONTRIBUTING.md](./CONTRIBUTING.md).
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).