Module: StimulusGridRails::TurboStreams
- Defined in:
- lib/stimulus_grid_rails/turbo_streams_helper.rb
Overview
Custom Turbo Stream actions (RAILS.md §1) — generates <turbo-stream> tags whose ‘action=` is one of: cell, cell-attr, cell-confirm, cell-revert, cell-conflict, row-insert-sorted, row-remove, aggregate, bulk.
The matching client-side StreamActions are registered by app/assets/javascripts/stimulus_grid_rails.js (registerStreamActions).
Use directly:
render turbo_stream: StimulusGridRails::TurboStreams.cell(
grid: "athletes", row_id: athlete.id, column: :age,
value: athlete.age, optimistic_id: params[:optimistic_id],
)
Class Method Summary collapse
-
.aggregate(grid:, column:, kind:, value:) ⇒ Object
Update a footer aggregate (sum/avg/count/etc.).
-
.bulk(grid:, streams:) ⇒ Object
Atomic batched stream — wraps N inner streams so the client applies them in a single DOM reflow.
-
.cell(grid:, row_id:, column:, value:, optimistic_id: nil) ⇒ Object
Update one cell — value rendered inline as the cell’s textContent.
-
.cell_attr(grid:, row_id:, column:, attr:, value:) ⇒ Object
Set an attribute on a cell (e.g. data-dirty=“false”).
-
.cell_confirm(grid:, row_id:, column:, value:, optimistic_id:) ⇒ Object
Clear pending/optimistic state on a cell after a successful save.
-
.cell_conflict(grid:, row_id:, column:, server_value:, client_value:, optimistic_id:) ⇒ Object
Conflict — server value differs from client base value.
-
.cell_revert(grid:, row_id:, column:, value:, errors:, optimistic_id:) ⇒ Object
Restore prior server value + render inline error.
-
.presence(grid:, row_id:, column:, user_id:, user_label:, active:) ⇒ Object
Per-user editing indicator on a cell.
-
.row_insert_sorted(grid:, row_id:, payload:) ⇒ Object
Insert a row, respecting the client’s current sort order.
-
.row_remove(grid:, row_id:) ⇒ Object
Remove a row by id.
-
.tag(action, **attrs) ⇒ Object
Internal — build a <turbo-stream> element.
Class Method Details
.aggregate(grid:, column:, kind:, value:) ⇒ Object
Update a footer aggregate (sum/avg/count/etc.).
73 74 75 76 77 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 73 def aggregate(grid:, column:, kind:, value:) tag("aggregate", grid: grid, column: column, kind: kind) do ERB::Util.html_escape(value.to_s) end end |
.bulk(grid:, streams:) ⇒ Object
Atomic batched stream — wraps N inner streams so the client applies them in a single DOM reflow. Pass an array of already-built turbo-stream strings (other helpers return strings).
82 83 84 85 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 82 def bulk(grid:, streams:) inner = streams.join tag("bulk", grid: grid) { inner } end |
.cell(grid:, row_id:, column:, value:, optimistic_id: nil) ⇒ Object
Update one cell — value rendered inline as the cell’s textContent. ‘optimistic_id` is echoed back so the originating client can suppress its own broadcast.
20 21 22 23 24 25 26 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 20 def cell(grid:, row_id:, column:, value:, optimistic_id: nil) tag("cell", grid: grid, row_id: row_id, column: column, optimistic_id: optimistic_id) do # Plain HTML — the StreamAction handler reads this as the new cell content. ERB::Util.html_escape(value.to_s) end end |
.cell_attr(grid:, row_id:, column:, attr:, value:) ⇒ Object
Set an attribute on a cell (e.g. data-dirty=“false”).
29 30 31 32 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 29 def cell_attr(grid:, row_id:, column:, attr:, value:) tag("cell-attr", grid: grid, row_id: row_id, column: column, attr: attr, value: value) end |
.cell_confirm(grid:, row_id:, column:, value:, optimistic_id:) ⇒ Object
Clear pending/optimistic state on a cell after a successful save.
35 36 37 38 39 40 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 35 def cell_confirm(grid:, row_id:, column:, value:, optimistic_id:) tag("cell-confirm", grid: grid, row_id: row_id, column: column, optimistic_id: optimistic_id) do ERB::Util.html_escape(value.to_s) end end |
.cell_conflict(grid:, row_id:, column:, server_value:, client_value:, optimistic_id:) ⇒ Object
Conflict — server value differs from client base value.
52 53 54 55 56 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 52 def cell_conflict(grid:, row_id:, column:, server_value:, client_value:, optimistic_id:) tag("cell-conflict", grid: grid, row_id: row_id, column: column, optimistic_id: optimistic_id, server_value: server_value, client_value: client_value) end |
.cell_revert(grid:, row_id:, column:, value:, errors:, optimistic_id:) ⇒ Object
Restore prior server value + render inline error.
43 44 45 46 47 48 49 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 43 def cell_revert(grid:, row_id:, column:, value:, errors:, optimistic_id:) tag("cell-revert", grid: grid, row_id: row_id, column: column, optimistic_id: optimistic_id, errors: errors.to_json) do ERB::Util.html_escape(value.to_s) end end |
.presence(grid:, row_id:, column:, user_id:, user_label:, active:) ⇒ Object
Per-user editing indicator on a cell.
88 89 90 91 92 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 88 def presence(grid:, row_id:, column:, user_id:, user_label:, active:) tag("presence", grid: grid, row_id: row_id, column: column, user_id: user_id, user_label: user_label, active: active.to_s) end |
.row_insert_sorted(grid:, row_id:, payload:) ⇒ Object
Insert a row, respecting the client’s current sort order. ‘payload` is a JSON row object; it’s HTML-escaped here and decoded by the client’s textContent read, so names containing & or < stay valid JSON.
61 62 63 64 65 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 61 def row_insert_sorted(grid:, row_id:, payload:) tag("row-insert-sorted", grid: grid, row_id: row_id) do ERB::Util.html_escape(payload) end end |
.row_remove(grid:, row_id:) ⇒ Object
Remove a row by id.
68 69 70 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 68 def row_remove(grid:, row_id:) tag("row-remove", grid: grid, row_id: row_id) end |
.tag(action, **attrs) ⇒ Object
Internal — build a <turbo-stream> element. Keys with nil values are dropped. Block content becomes the <template> payload.
96 97 98 99 100 101 102 103 |
# File 'lib/stimulus_grid_rails/turbo_streams_helper.rb', line 96 def tag(action, **attrs) attrs[:action] = action kept = attrs.compact attr_str = kept.map { |k, v| %(#{k.to_s.tr("_", "-")}="#{ERB::Util.html_escape(v)}") }.join(" ") payload = block_given? ? yield : nil template = payload ? "<template>#{payload}</template>" : "" %(<turbo-stream #{attr_str}>#{template}</turbo-stream>) end |