forked from fixin.me/fixin.me
Rewrite stream rendering to avoid client-side expanding
* adding streams in client breaks things (e.g. autofocus) * some tasks need to be performed in one stream action to avoid flickering (e.g. table row substitution)
This commit is contained in:
parent
759a0b31b7
commit
1198add901
@ -103,8 +103,6 @@ module ApplicationHelper
|
|||||||
end
|
end
|
||||||
|
|
||||||
def render_turbo_stream(partial, locals)
|
def render_turbo_stream(partial, locals)
|
||||||
# TODO: extend with smth like "if outside of rendering, render; otherwise
|
|
||||||
# appendChild() template within current render"
|
|
||||||
"Turbo.renderStreamMessage('#{j(render partial: partial, locals: locals)}'); return false;"
|
"Turbo.renderStreamMessage('#{j(render partial: partial, locals: locals)}'); return false;"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ function beforeStreamRender(event) {
|
|||||||
document.addEventListener('turbo:before-stream-render', beforeStreamRender)
|
document.addEventListener('turbo:before-stream-render', beforeStreamRender)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
Turbo.session.streamMessageRenderer.appendFragment = async function (fragment) {
|
Turbo.session.streamMessageRenderer.appendFragment = async function (fragment) {
|
||||||
for (let child of [...fragment.children]) {
|
for (let child of [...fragment.children]) {
|
||||||
document.documentElement.appendChild(child)
|
document.documentElement.appendChild(child)
|
||||||
@ -32,6 +33,38 @@ Turbo.session.streamMessageRenderer.appendFragment = async function (fragment) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.renderTurboStream = function (message) {
|
||||||
|
// Render if not already rendering, otherwise just append fragment to DOM
|
||||||
|
if (document.documentElement.getElementsByTagName("turbo-stream").length == 0) {
|
||||||
|
Turbo.renderStreamMessage(message)
|
||||||
|
} else {
|
||||||
|
Turbo.session.streamMessageRenderer
|
||||||
|
.appendFragment(Turbo.StreamMessage.wrap(message).fragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Turbo.StreamElement.prototype.enableElement = function(element) {
|
||||||
|
element.removeAttribute("disabled")
|
||||||
|
element.removeAttribute("aria-disabled")
|
||||||
|
// 'tabindex' is not used explicitly, so removing it is safe
|
||||||
|
element.removeAttribute("tabindex")
|
||||||
|
}
|
||||||
|
|
||||||
|
Turbo.StreamElement.prototype.removePreviousForm = function(form) {
|
||||||
|
const id = form.id
|
||||||
|
const row = document.getElementById(id + "_cached")
|
||||||
|
form.remove()
|
||||||
|
if (row) {
|
||||||
|
row.id = id
|
||||||
|
row.style.display = "revert"
|
||||||
|
}
|
||||||
|
if (form.hasAttribute("data-link-id")) {
|
||||||
|
const link = document.getElementById(form.getAttribute("data-link-id"))
|
||||||
|
this.enableElement(link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Turbo.StreamActions.disable = function() {
|
Turbo.StreamActions.disable = function() {
|
||||||
this.targetElements.forEach((e) => {
|
this.targetElements.forEach((e) => {
|
||||||
@ -42,12 +75,7 @@ Turbo.StreamActions.disable = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Turbo.StreamActions.enable = function() {
|
Turbo.StreamActions.enable = function() {
|
||||||
this.targetElements.forEach((e) => {
|
this.targetElements.forEach((e) => { this.enableElement(e) })
|
||||||
e.removeAttribute("disabled")
|
|
||||||
e.removeAttribute("aria-disabled")
|
|
||||||
// 'tabindex' is not used explicitly, so removing it is safe
|
|
||||||
e.removeAttribute("tabindex")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Turbo.StreamActions.blur = function() {
|
Turbo.StreamActions.blur = function() {
|
||||||
@ -59,6 +87,43 @@ Turbo.StreamActions.focus = function() {
|
|||||||
this.targetElements[0].focus({focusVisible: true})
|
this.targetElements[0].focus({focusVisible: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Turbo.StreamActions.prepend_form = function() {
|
||||||
|
this.targetElements.forEach((e) => {
|
||||||
|
[...e.getElementsByClassName("form")].forEach((f) => {
|
||||||
|
this.removePreviousForm(f)
|
||||||
|
})
|
||||||
|
e.prepend(this.templateContent)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Turbo.StreamActions.after_form = function() {
|
||||||
|
this.targetElements.forEach((e) => {
|
||||||
|
[...e.parentElement?.getElementsByClassName("form")].forEach((f) => {
|
||||||
|
this.removePreviousForm(f)
|
||||||
|
})
|
||||||
|
e.parentElement?.insertBefore(this.templateContent, e.nextSibling)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Turbo.StreamActions.replace_form = function() {
|
||||||
|
this.targetElements.forEach((e) => {
|
||||||
|
[...e.parentElement?.getElementsByClassName("form")].forEach((f) => {
|
||||||
|
this.removePreviousForm(f)
|
||||||
|
})
|
||||||
|
e.style.display = "none"
|
||||||
|
e.id = e.id + "_cached"
|
||||||
|
e.parentElement?.insertBefore(this.templateContent, e.nextSibling)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Turbo.StreamActions.close_form = function() {
|
||||||
|
this.targetElements.forEach((e) => {
|
||||||
|
this.removePreviousForm(e.closest(".form"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
Turbo.StreamActions.click = function() {
|
Turbo.StreamActions.click = function() {
|
||||||
this.targetElements.forEach((e) => { e.click() })
|
this.targetElements.forEach((e) => { e.click() })
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<%= fields_for @unit do |form| %>
|
<%= fields_for @unit do |form| %>
|
||||||
<tr id="<%= dom_id(@unit) %>" onkeydown="processKey(event)">
|
<%= tag.tr id: dom_id(@unit), class: "form", onkeydown: "processKey(event)",
|
||||||
|
data: {link_id: link_id} do %>
|
||||||
|
|
||||||
<td class="<%= class_names({subunit: @unit.base}) %>">
|
<td class="<%= class_names({subunit: @unit.base}) %>">
|
||||||
<%= form.text_field :symbol, form: :unit_form, required: true, autofocus: true, size: 12,
|
<%= form.text_field :symbol, form: :unit_form, required: true, autofocus: true, size: 12,
|
||||||
maxlength: @unit.class.columns_hash['symbol'].limit, autocomplete: "off" %>
|
maxlength: @unit.class.columns_hash['symbol'].limit, autocomplete: "off" %>
|
||||||
@ -19,8 +21,8 @@
|
|||||||
<td class="actions">
|
<td class="actions">
|
||||||
<%= form.submit form: :unit_form %>
|
<%= form.submit form: :unit_form %>
|
||||||
<%= image_link_to t(:cancel), "close-circle-outline", units_path, class: 'dangerous',
|
<%= image_link_to t(:cancel), "close-circle-outline", units_path, class: 'dangerous',
|
||||||
name: :cancel, onclick: render_turbo_stream('form_close', {id: id}) %>
|
name: :cancel, onclick: render_turbo_stream('form_close', {link_id: link_id}) %>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
<% end %>
|
||||||
<!-- TODO: display error_messages_for unit -->
|
<!-- TODO: display error_messages_for unit -->
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
<%= @unit.persisted? ? turbo_stream.replace(@unit) : turbo_stream.remove(@unit) %>
|
<%= turbo_stream.close_form @unit %>
|
||||||
<%= turbo_stream.enable id %>
|
<%#= turbo_stream.focus link_id %>
|
||||||
<%= turbo_stream.focus id %>
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<%# TODO: make sure turbo_stream layout is used in new/edit %>
|
<%# TODO: make sure turbo_stream layout is used in new/edit %>
|
||||||
<%= turbo_stream.replace @unit, partial: 'form', locals: {id: dom_id(@unit, :edit)} %>
|
|
||||||
|
|
||||||
<%= turbo_stream.update :unit_form_frame do %>
|
<%= turbo_stream.update :unit_form_frame do %>
|
||||||
<%= form_with model: @unit, html: {id: :unit_form} do %>
|
<%= form_with model: @unit, html: {id: :unit_form} do %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end unless @unit.errors.present? %>
|
<% end %>
|
||||||
|
|
||||||
|
<%= turbo_stream.replace_form @unit, partial: 'form', locals: {link_id: dom_id(@unit, :edit)} %>
|
||||||
|
@ -1,20 +1,9 @@
|
|||||||
<% options = {partial: 'form', locals: {id: dom_id(@unit.base || @unit, :add)}} %>
|
<% link_id = dom_id(@unit.base || @unit, :add) %>
|
||||||
|
<%= turbo_stream.disable link_id -%>
|
||||||
|
|
||||||
<% if @unit.errors.present? %>
|
<%= turbo_stream.update :unit_form_frame do %>
|
||||||
<%= turbo_stream.replace @unit, **options -%>
|
|
||||||
<% else %>
|
|
||||||
<%= turbo_stream.disable options[:locals][:id] -%>
|
|
||||||
<%= turbo_stream.click_all 'tbody a[name=cancel]' -%>
|
|
||||||
<%#= turbo_stream.blur_all %>
|
|
||||||
|
|
||||||
<% if @unit.base.nil? %>
|
|
||||||
<%= turbo_stream.prepend :units, **options -%>
|
|
||||||
<% else %>
|
|
||||||
<%= turbo_stream.after @unit.base, **options -%>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<%= turbo_stream.update :unit_form_frame do %>
|
|
||||||
<%= form_with model: @unit, html: {id: :unit_form} do %>
|
<%= form_with model: @unit, html: {id: :unit_form} do %>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<%= turbo_stream.insert_form (@unit.base || :units), partial: 'form', locals: {link_id: link_id} %>
|
||||||
|
@ -23,11 +23,27 @@ ActiveSupport.on_load :turbo_streams_tag_builder do
|
|||||||
action :focus, target, allow_inferred_rendering: false
|
action :focus, target, allow_inferred_rendering: false
|
||||||
end
|
end
|
||||||
|
|
||||||
def click(target)
|
#def click(target)
|
||||||
action :click, target, allow_inferred_rendering: false
|
# action :click, target, allow_inferred_rendering: false
|
||||||
|
#end
|
||||||
|
|
||||||
|
#def click_all(targets)
|
||||||
|
# action_all :click, targets, allow_inferred_rendering: false
|
||||||
|
#end
|
||||||
|
|
||||||
|
def insert_form(target, content = nil, **rendering, &block)
|
||||||
|
if target.is_a? Symbol
|
||||||
|
action :prepend_form, target, content, **rendering, &block
|
||||||
|
else
|
||||||
|
action :after_form, target, content, **rendering, &block
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def click_all(targets)
|
def replace_form(target, content = nil, **rendering, &block)
|
||||||
action_all :click, targets, allow_inferred_rendering: false
|
action :replace_form, target, content, **rendering, &block
|
||||||
|
end
|
||||||
|
|
||||||
|
def close_form(target)
|
||||||
|
action :close_form, target, allow_inferred_rendering: false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user