inline_forms 7.2.11 → 7.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +176 -0
  3. data/README.rdoc +4 -2
  4. data/app/assets/javascripts/inline_forms/inline_forms.js +23 -0
  5. data/app/assets/stylesheets/inline_forms/inline_forms.scss +32 -16
  6. data/app/controllers/concerns/versions_concern.rb +2 -1
  7. data/app/controllers/inline_forms_application_controller.rb +5 -1
  8. data/app/controllers/inline_forms_controller.rb +120 -24
  9. data/app/helpers/form_elements/ckeditor.rb +4 -30
  10. data/app/helpers/form_elements/plain_text.rb +23 -0
  11. data/app/helpers/form_elements/plain_text_area.rb +7 -3
  12. data/app/helpers/form_elements/text_area.rb +4 -44
  13. data/app/helpers/form_elements/text_area_without_ckeditor.rb +5 -4
  14. data/app/helpers/form_elements/text_field.rb +2 -2
  15. data/app/helpers/inline_forms_helper.rb +127 -71
  16. data/app/views/devise/sessions/_form.html.erb +4 -1
  17. data/app/views/inline_forms/_close.html.erb +6 -4
  18. data/app/views/inline_forms/_edit.html.erb +7 -40
  19. data/app/views/inline_forms/_list.html.erb +52 -39
  20. data/app/views/inline_forms/_new.html.erb +23 -11
  21. data/app/views/inline_forms/_show.html.erb +13 -11
  22. data/app/views/inline_forms/_versions_list.html.erb +4 -8
  23. data/app/views/inline_forms/create_list_frame.html.erb +3 -0
  24. data/app/views/inline_forms/field_edit.html.erb +3 -0
  25. data/app/views/inline_forms/field_show.html.erb +3 -0
  26. data/app/views/inline_forms/new_record.html.erb +3 -0
  27. data/app/views/inline_forms/row_close.html.erb +5 -0
  28. data/app/views/inline_forms/row_destroyed.html.erb +9 -0
  29. data/app/views/inline_forms/row_show.html.erb +3 -0
  30. data/app/views/inline_forms/versions_list_panel.html.erb +3 -0
  31. data/app/views/inline_forms/versions_panel.html.erb +3 -0
  32. data/app/views/layouts/application.html.erb +0 -1
  33. data/app/views/layouts/inline_forms.html.erb +10 -1
  34. data/bin/inline_forms +22 -1
  35. data/bin/inline_forms_installer_core.rb +38 -3
  36. data/docs/ujs-to-turbo.md +193 -0
  37. data/lib/generators/USAGE +2 -2
  38. data/lib/generators/assets/stylesheets/inline_forms.scss +32 -16
  39. data/lib/inline_forms/version.rb +1 -1
  40. data/lib/inline_forms.rb +58 -2
  41. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_field_turbo_test.rb +74 -0
  42. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_name_list_test.rb +73 -0
  43. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_photos_pagination_test.rb +199 -15
  44. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_row_turbo_test.rb +94 -0
  45. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_top_level_new_test.rb +100 -0
  46. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_versions_turbo_test.rb +27 -0
  47. data/lib/installer_templates/example_app_tests/test/models/example_app_plain_text_rich_text_edge_cases_test.rb +46 -0
  48. data/lib/installer_templates/example_app_views/apartments/name_list.html.erb +26 -0
  49. data/lib/installer_templates/example_app_views/inline_forms/_header.html.erb +45 -0
  50. data/test/inline_forms_generator_test.rb +10 -0
  51. data/test/plain_text_configuration_test.rb +90 -0
  52. metadata +21 -5
  53. data/app/views/inline_forms/edit.js.erb +0 -1
  54. data/app/views/inline_forms/show_element.js.erb +0 -1
  55. data/app/views/inline_forms/update.js.erb +0 -1
  56. data/lib/generators/assets/javascripts/ckeditor/config.js +0 -72
@@ -0,0 +1,23 @@
1
+ # -*- encoding : utf-8 -*-
2
+ InlineForms::SPECIAL_COLUMN_TYPES[:plain_text]=:text
3
+
4
+ def plain_text_show(object, attribute)
5
+ InlineForms.assert_plain_text_column!(object: object, attribute: attribute, form_element: :plain_text)
6
+ value = object[attribute]
7
+ link_to_inline_edit object, attribute, (value.nil? || value.empty?) ? "<i class='fi-plus'></i>".html_safe : value, from_callee: __callee__
8
+ end
9
+
10
+ def plain_text_edit(object, attribute)
11
+ InlineForms.assert_plain_text_column!(object: object, attribute: attribute, form_element: :plain_text)
12
+ text_area_tag attribute, object[attribute], :class => 'attribute_text_area'
13
+ end
14
+
15
+ def plain_text_update(object, attribute)
16
+ InlineForms.assert_plain_text_column!(object: object, attribute: attribute, form_element: :plain_text)
17
+ object[attribute.to_sym] = params[attribute.to_sym]
18
+ end
19
+
20
+ def plain_text_info(object, attribute)
21
+ InlineForms.assert_plain_text_column!(object: object, attribute: attribute, form_element: :plain_text)
22
+ object[attribute]
23
+ end
@@ -2,13 +2,17 @@
2
2
  InlineForms::SPECIAL_COLUMN_TYPES[:plain_text_area]=:text
3
3
 
4
4
  def plain_text_area_show(object, attribute)
5
- link_to_inline_edit object, attribute, (object[attribute].nil? || object[attribute].empty?) ? "<i class='fi-plus'></i>".html_safe : object[attribute], from_callee: __callee__
5
+ plain_text_show(object, attribute)
6
6
  end
7
7
 
8
8
  def plain_text_area_edit(object, attribute)
9
- text_area_tag attribute, object[attribute], :class => 'attribute_text_area'
9
+ plain_text_edit(object, attribute)
10
10
  end
11
11
 
12
12
  def plain_text_area_update(object, attribute)
13
- object[attribute.to_sym] = params[attribute.to_sym]
13
+ plain_text_update(object, attribute)
14
+ end
15
+
16
+ def plain_text_area_info(object, attribute)
17
+ plain_text_info(object, attribute)
14
18
  end
@@ -2,57 +2,17 @@
2
2
  InlineForms::SPECIAL_COLUMN_TYPES[:text_area]=:text
3
3
 
4
4
  def text_area_show(object, attribute)
5
- if object.send(attribute).blank?
6
- link_to_inline_edit object, attribute, "<i class='fi-plus'></i>".html_safe, from_callee: __callee__
7
- else
8
- if defined? Ckeditor
9
- link_to_inline_edit object,
10
- attribute,
11
- '<div class="ckeditor_area">'.html_safe +
12
- cktext_area_tag(
13
- attribute,
14
- object[attribute],
15
- :id => "textarea_#{object.class.name.underscore}_#{object.id}_#{attribute.to_s}",
16
- :ckeditor => { :width => '100%',
17
- :height => '200px',
18
- :toolbar => "None",
19
- :readOnly => "true",
20
- :resize_enabled => "false",
21
- :toolbarCanCollapse => "false"
22
- }
23
- ) +
24
- image_tag( 'inline_forms/glass_plate.gif',
25
- :class => "glass_plate",
26
- :title => '' ) +
27
- "<script>delete CKEDITOR.instances['textarea_#{object.class.name.underscore}_#{object.id}_#{attribute.to_s}']</script>".html_safe +
28
- '</div>'.html_safe,
29
- from_callee: __callee__
30
- else
31
- link_to_inline_edit object, attribute, object[attribute], from_callee: __callee__
32
- end
33
- end
5
+ rich_text_show(object, attribute)
34
6
  end
35
7
 
36
8
  def text_area_edit(object, attribute)
37
- if defined? Ckeditor
38
- cktext_area_tag(
39
- attribute,
40
- object[attribute],
41
- :id => "textarea_#{object.class.name.underscore}_#{object.id}_#{attribute.to_s}",
42
- :ckeditor => { :width => '100%',
43
- :height => '200px'
44
- }
45
- ) +
46
- "<script>delete CKEDITOR.instances['textarea_#{object.class.name.underscore}_#{object.id}_#{attribute.to_s}']</script>".html_safe
47
- else
48
- text_area_tag attribute, object[attribute], :class => 'attribute_text_area'
49
- end
9
+ rich_text_edit(object, attribute)
50
10
  end
51
11
 
52
12
  def text_area_update(object, attribute)
53
- object[attribute.to_sym] = params[attribute.to_sym]
13
+ rich_text_update(object, attribute)
54
14
  end
55
15
 
56
16
  def text_area_info(object, attribute)
57
- object[attribute]
17
+ rich_text_info(object, attribute)
58
18
  end
@@ -1,18 +1,19 @@
1
1
  # -*- encoding : utf-8 -*-
2
+ # Legacy alias for plain :text_area (CKEditor removed).
2
3
  InlineForms::SPECIAL_COLUMN_TYPES[:text_area_without_ckeditor]=:text
3
4
 
4
5
  def text_area_without_ckeditor_show(object, attribute)
5
- link_to_inline_edit object, attribute, (object[attribute].nil? || object[attribute].empty?) ? "<i class='fi-plus'></i>".html_safe : object[attribute], from_callee: __callee__
6
+ plain_text_show(object, attribute)
6
7
  end
7
8
 
8
9
  def text_area_without_ckeditor_edit(object, attribute)
9
- text_area_tag attribute, object[attribute], :class => 'attribute_text_area'
10
+ plain_text_edit(object, attribute)
10
11
  end
11
12
 
12
13
  def text_area_without_ckeditor_update(object, attribute)
13
- object[attribute.to_sym] = params[attribute.to_sym]
14
+ plain_text_update(object, attribute)
14
15
  end
15
16
 
16
17
  def text_area_without_ckeditor_info(object, attribute)
17
- object[attribute]
18
+ plain_text_info(object, attribute)
18
19
  end
@@ -1,8 +1,8 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  InlineForms::SPECIAL_COLUMN_TYPES[:text_field]=:string
3
3
 
4
- def text_field_show(object, attribute)
5
- link_to_inline_edit object, attribute, object[attribute].blank? ? "<i class='fi-plus'></i>".html_safe : object[attribute], from_callee: __callee__
4
+ def text_field_show(object, attribute, turbo_frame: false)
5
+ link_to_inline_edit object, attribute, object[attribute].blank? ? "<i class='fi-plus'></i>".html_safe : object[attribute], from_callee: __callee__, turbo_frame: turbo_frame
6
6
  end
7
7
 
8
8
  def text_field_edit(object, attribute)
@@ -52,6 +52,14 @@ module InlineFormsHelper
52
52
  entries.sort_by { |entry| entry[:version].created_at }
53
53
  end
54
54
 
55
+ # Turbo Frames navigation for row toolbar, versions, and nested +new+ (Step 3).
56
+ # +update_span+ must match the target +<turbo-frame id="…">+.
57
+ def inline_forms_turbo_link_data(update_span, method: :get)
58
+ data = { turbo: true, turbo_frame: update_span }
59
+ data[:turbo_method] = method.to_s.downcase unless method == :get
60
+ { data: data }
61
+ end
62
+
55
63
  private
56
64
 
57
65
  def validation_hints_as_list_for(object, attribute)
@@ -59,69 +67,74 @@ module InlineFormsHelper
59
67
  end
60
68
 
61
69
  # close link
62
- def close_link( object, update_span, html_class = 'button close_button' )
63
- link_to "<i class='fi-x'></i>".html_safe,
64
- polymorphic_path(
65
- object,
66
- :update => update_span,
67
- :close => true ),
68
- :remote => true,
69
- :class => html_class,
70
- :title => t('inline_forms.view.close')
70
+ def close_link(object, update_span, html_class = "button close_button", turbo_row: false)
71
+ path = polymorphic_path(
72
+ object,
73
+ update: update_span,
74
+ close: true
75
+ )
76
+ opts = { class: html_class, title: t("inline_forms.view.close") }
77
+ if turbo_row
78
+ opts[:data] = { turbo: true, turbo_frame: "_self" }
79
+ link_to "<i class='fi-x'></i>".html_safe, path, opts
80
+ else
81
+ link_to "<i class='fi-x'></i>".html_safe, path, opts.merge(remote: true)
82
+ end
71
83
  end
72
84
 
73
85
  # delete link. Mind the difference between delete and destroy.
74
- def link_to_soft_delete( object, update_span )
75
- soft=''
86
+ def link_to_soft_delete(object, update_span, turbo_row: true)
87
+ soft = ""
76
88
  if (object.soft_deletable? rescue false)
77
- if object.deleted? && (cancan_disabled? || ( can? :soft_restore, object ))
78
- soft = link_to "<i class='fi-refresh'></i>".html_safe,
79
- send( 'soft_restore_' + object.class.to_s.underscore + '_path',
80
- object,
81
- :update => update_span ),
82
- :method => :post,
83
- :remote => true,
84
- :title => t('inline_forms.view.undelete')
85
- elsif !object.deleted? && (cancan_disabled? || ( can? :soft_delete, object ))
86
- soft = link_to "<i class='fi-trash'></i>".html_safe,
87
- send( 'soft_delete_' + object.class.to_s.underscore + '_path',
88
- object,
89
- :update => update_span ),
90
- :method => :post,
91
- :remote => true,
92
- :title => t('inline_forms.view.trash')
89
+ if object.deleted? && (cancan_disabled? || (can? :soft_restore, object))
90
+ path = send("soft_restore_#{object.class.to_s.underscore}_path", object, update: update_span)
91
+ opts = { title: t("inline_forms.view.undelete") }
92
+ opts.merge!(turbo_row ? inline_forms_turbo_link_data(update_span, method: :post) : { method: :post, remote: true })
93
+ soft = link_to "<i class='fi-refresh'></i>".html_safe, path, opts
94
+ elsif !object.deleted? && (cancan_disabled? || (can? :soft_delete, object))
95
+ path = send("soft_delete_#{object.class.to_s.underscore}_path", object, update: update_span)
96
+ opts = { title: t("inline_forms.view.trash") }
97
+ opts.merge!(turbo_row ? inline_forms_turbo_link_data(update_span, method: :post) : { method: :post, remote: true })
98
+ soft = link_to "<i class='fi-trash'></i>".html_safe, path, opts
93
99
  end
94
100
  end
95
101
  soft.html_safe
96
102
  end
97
103
 
98
104
  # destroy link. Mind the difference between delete and destroy.
99
- def link_to_destroy( object, update_span )
100
- hard=''
101
- if cancan_disabled? || ( can? :destroy, object )
102
- hard = link_to "&nbsp;&nbsp;<font color='FF0000'><i class='fi-x'></i></font>".html_safe,
103
- polymorphic_path(
104
- object,
105
- :update => update_span ),
106
- :method => :delete,
107
- :remote => true,
108
- :title => t('inline_forms.view.trash')
105
+ def link_to_destroy(object, update_span, turbo_row: true)
106
+ hard = ""
107
+ if cancan_disabled? || (can? :destroy, object)
108
+ path = polymorphic_path(object, update: update_span)
109
+ opts = { title: t("inline_forms.view.trash") }
110
+ opts.merge!(turbo_row ? inline_forms_turbo_link_data(update_span, method: :delete) : { method: :delete, remote: true })
111
+ hard = link_to "&nbsp;&nbsp;<font color='FF0000'><i class='fi-x'></i></font>".html_safe, path, opts
109
112
  end
110
113
  hard.html_safe
111
114
  end
112
115
 
113
116
  # new link
114
- def link_to_new_record(model, path_to_new, update_span, parent_class = nil, parent_id = nil, html_class = 'button new_button')
115
- out = (link_to "<i class='fi-plus'></i>".html_safe,
116
- send(path_to_new,
117
- :update => update_span,
118
- :parent_class => parent_class,
119
- :parent_id => parent_id,
120
- ),
121
- :remote => true,
122
- :class => html_class,
123
- :title => t('inline_forms.view.add_new', :model => model.model_name.human )
124
- )
117
+ #
118
+ # +turbo_row:+ controls Turbo opt-in. Default +true+ matches the nested has_many
119
+ # contract (parent +<turbo-frame>+ swap). The top-level callsite (+_inline_forms_tabs+)
120
+ # passes no +parent_class+; for that case we fall back to UJS (+remote: true+) so
121
+ # +new.js.erb+ / +list.js.erb+ swap +#apartments_list+ contents instead of trying
122
+ # to navigate a Turbo frame the page does not have. Without this the Turbo data
123
+ # attributes either logged "Content missing" on cancel/create or fell back to a
124
+ # full-page navigation that broke the inline-edit experience (7.5.1 regression).
125
+ def link_to_new_record(model, path_to_new, update_span, parent_class = nil, parent_id = nil, html_class = "button new_button", turbo_row: true)
126
+ path = send(
127
+ path_to_new,
128
+ update: update_span,
129
+ parent_class: parent_class,
130
+ parent_id: parent_id
131
+ )
132
+ opts = {
133
+ class: html_class,
134
+ title: t("inline_forms.view.add_new", model: model.model_name.human)
135
+ }
136
+ opts.merge!(turbo_row && parent_class.present? ? inline_forms_turbo_link_data(update_span) : { remote: true })
137
+ out = link_to "<i class='fi-plus'></i>".html_safe, path, opts
125
138
  if cancan_enabled?
126
139
  if can? :create, model
127
140
  if parent_class.nil?
@@ -136,54 +149,97 @@ module InlineFormsHelper
136
149
  end
137
150
 
138
151
  # link to versions list
139
- def link_to_versions_list(path_to_versions_list, object, update_span, html_class = 'button new_button')
152
+ def link_to_versions_list(path_to_versions_list, object, update_span, html_class = "button new_button", turbo_row: true)
140
153
  if can? :list_versions, object
141
154
  if defined?(PaperTrail) && object.respond_to?(:versions)
142
- out = (link_to "<i class='fi-list'></i>".html_safe,
143
- send(path_to_versions_list,
144
- object,
145
- :update => update_span,
146
- ),
147
- :remote => true,
148
- :class => html_class,
149
- :title => t('inline_forms.view.list_versions')
150
- )
151
- raw out
155
+ path = send(path_to_versions_list, object, update: update_span)
156
+ opts = { class: html_class, title: t("inline_forms.view.list_versions") }
157
+ opts.merge!(turbo_row ? inline_forms_turbo_link_data(update_span) : { remote: true })
158
+ raw link_to("<i class='fi-list'></i>".html_safe, path, opts)
152
159
  end
153
160
  end
154
161
  end
155
162
 
156
163
  # close versions list link
157
- def close_versions_list_link(object, update_span, html_class = 'button close_button' )
158
- link_to "<i class='fi-x'></i>".html_safe,
159
- send('list_versions_' + @object.class.to_s.underscore + "_path",
160
- object,
161
- :update => update_span,
162
- :close => true
163
- ),
164
- :remote => true,
165
- :class => html_class,
166
- :title => t('inline_forms.view.close_versions_list')
164
+ def close_versions_list_link(object, update_span, html_class = "button close_button", turbo_row: true)
165
+ path = send(
166
+ "list_versions_#{object.class.to_s.underscore}_path",
167
+ object,
168
+ update: update_span,
169
+ close: true
170
+ )
171
+ opts = { class: html_class, title: t("inline_forms.view.close_versions_list") }
172
+ opts.merge!(turbo_row ? inline_forms_turbo_link_data(update_span) : { remote: true })
173
+ link_to "<i class='fi-x'></i>".html_safe, path, opts
174
+ end
175
+
176
+ # Renders the +*_show+ helper for +form_element+, passing +turbo_frame:+ when supported.
177
+ def inline_forms_field_show(object, attribute, form_element, turbo_frame: false)
178
+ show_method = "#{form_element}_show"
179
+ if turbo_frame
180
+ send(show_method, object, attribute, turbo_frame: true)
181
+ else
182
+ send(show_method, object, attribute)
183
+ end
184
+ rescue ArgumentError
185
+ send(show_method, object, attribute)
186
+ end
187
+
188
+ # Cancel control for single-field +_edit+ forms. Navigation attrs live on the outer
189
+ # +link_to+; the visible control is +input[type=button]+ so it matches the +ok+ submit
190
+ # height (Foundation sizes +a.button+ taller than +input.button+ in collapse rows).
191
+ def inline_forms_field_cancel_link(object, attribute, form_element, update_span, sub_id: nil, turbo_frame: false)
192
+ path = polymorphic_path(
193
+ object,
194
+ update: update_span,
195
+ attribute: attribute,
196
+ form_element: form_element,
197
+ sub_id: sub_id
198
+ )
199
+ opts = { class: "inline_forms-field-cancel" }
200
+ if turbo_frame
201
+ # Inside a <turbo-frame>: plain GET link; no data-method (jquery-ujs fights Turbo).
202
+ opts[:data] = { turbo: true, turbo_frame: "_self" }
203
+ else
204
+ opts[:remote] = true
205
+ opts[:method] = :get
206
+ end
207
+ link_to path, opts do
208
+ tag.input(
209
+ type: "button",
210
+ name: "cancel",
211
+ value: "cancel",
212
+ class: "postfix button alert",
213
+ tabindex: "-1"
214
+ )
215
+ end
167
216
  end
168
217
 
169
218
  # link_to_inline_edit
170
219
  #
171
220
  # Pass +from_callee:+ +__callee__+ from the enclosing +*_show+ method so the edit route receives the correct form element name.
172
- def link_to_inline_edit(object, attribute, attribute_value='', from_callee:)
221
+ # When +turbo_frame:+ is true the link omits +remote: true+; navigation is handled by the enclosing +<turbo-frame>+.
222
+ def link_to_inline_edit(object, attribute, attribute_value='', from_callee:, turbo_frame: false)
173
223
  form_element = InlineForms.form_element_string_from_callee(from_callee)
174
224
  attribute_value = attribute_value.to_s
175
225
  spaces = attribute_value.length > 40 ? 0 : 40 - attribute_value.length
176
226
  value = h(attribute_value) + ("&nbsp;" * spaces).html_safe
177
227
  css_class_id = "#{object.class.to_s.underscore}_#{object.id}_#{attribute}"
228
+ use_turbo_frame = turbo_frame || (@inline_forms_turbo_field == true)
178
229
  if (cancan_disabled? rescue true) || ( can? :update, object, attribute )
179
230
  # some problem with concerns makes this function not available when called direct. FIXME
231
+ link_opts = if use_turbo_frame
232
+ { data: { turbo: true, turbo_frame: "_self" } }
233
+ else
234
+ { remote: true }
235
+ end
180
236
  link_to value,
181
237
  edit_polymorphic_path(
182
238
  object,
183
239
  :attribute => attribute.to_s,
184
240
  :form_element => form_element,
185
241
  :update => css_class_id ),
186
- :remote => true
242
+ link_opts
187
243
  else
188
244
  h(attribute_value)
189
245
  end
@@ -1,6 +1,9 @@
1
1
  <%= form_for(resource, as: resource_name,
2
2
  url: session_path(resource_name),
3
- html: { id: 'inline_forms_devise_form' }
3
+ html: {
4
+ id: 'inline_forms_devise_form',
5
+ data: { turbo: false }
6
+ }
4
7
  ) do |f| %>
5
8
 
6
9
  <div class="row">
@@ -1,13 +1,15 @@
1
+ <% row_turbo = @inline_forms_turbo_row %>
2
+ <% presentation_link_opts = row_turbo ? { data: { turbo: true, turbo_frame: @update_span } } : { remote: true } %>
1
3
  <% if cancan_disabled? || ( can? :soft_delete, @object ) %>
2
4
  <div class="small-1 column">
3
- <%= link_to_soft_delete(@object, @update_span) -%>
4
- <%= link_to_destroy(@object, @update_span) -%>
5
+ <%= link_to_soft_delete(@object, @update_span, turbo_row: row_turbo) -%>
6
+ <%= link_to_destroy(@object, @update_span, turbo_row: row_turbo) -%>
5
7
  </div>
6
8
  <div class="small-11 column">
7
- <%= link_to h(@object._presentation), polymorphic_path(@object, :update => @update_span), :remote => true -%>
9
+ <%= link_to h(@object._presentation), polymorphic_path(@object, :update => @update_span), presentation_link_opts -%>
8
10
  </div>
9
11
  <% else %>
10
12
  <div class="small-12 column">
11
- <%= link_to h(@object._presentation), polymorphic_path(@object, :update => @update_span), :remote => true -%>
13
+ <%= link_to h(@object._presentation), polymorphic_path(@object, :update => @update_span), presentation_link_opts -%>
12
14
  </div>
13
15
  <% end %>
@@ -1,33 +1,14 @@
1
1
  <% @BUTTONS_UNDER = [ "text_area", "kansen_slider", "rich_text" ] %>
2
+ <% edit_form_opts = { method: :put, multipart: true, class: "edit_form", abide: true } %>
3
+ <% edit_form_opts[:remote] = true unless @turbo_frame %>
2
4
  <%= form_tag polymorphic_path(@object,
3
5
  :update => @update_span,
4
6
  :attribute => @attribute,
5
7
  :form_element => @form_element,
6
8
  :sub_id => @sub_id ),
7
- :method => :put, # this is going to the update method!
8
- :multipart => true,
9
- :class => "edit_form",
10
- :abide => true,
11
- :remote => true do -%>
12
- <div id="flash" class="row">
13
- <% flash.each do |key, value| %>
14
- <div class="flash <%= key %>">
15
- <ul>
16
- <% if value.is_a?(Array) %>
17
- <% value.each do |msg| %>
18
- <li>
19
- <%= msg %>
20
- </li>
21
- <% end %>
22
- <% else %>
23
- <li>
24
- <%= value %>
25
- </li>
26
- <% end %>
27
- </ul>
28
- </div>
29
- <% end %>
30
- </div>
9
+ edit_form_opts do -%>
10
+ <%# Do not render `flash` here: this partial is injected into a field span via %>
11
+ <%# UJS; session flash (e.g. Devise :notice) would otherwise appear inside the editor. %>
31
12
 
32
13
  <% if @BUTTONS_UNDER.include? @form_element %>
33
14
  <div class="row collapse">
@@ -43,14 +24,7 @@
43
24
  <%= submit_tag "ok", :class => "postfix button"-%>
44
25
  </div>
45
26
  <div class="small-2 columns">
46
- <%= link_to( polymorphic_path( @object, :update => @update_span || "field_#{@attribute}_#{@object.id.to_s}",
47
- :attribute => @attribute,
48
- :form_element => @form_element,
49
- :sub_id => @sub_id ),
50
- :method => :get, # this is going to the show method!
51
- :remote => true ) do %>
52
- <input type="button" name="cancel" value="cancel" class="button alert postfix radius" />
53
- <% end %>
27
+ <%= inline_forms_field_cancel_link(@object, @attribute, @form_element, @update_span, sub_id: @sub_id, turbo_frame: @turbo_frame) %>
54
28
  </div>
55
29
  </div>
56
30
  <% else %>
@@ -62,14 +36,7 @@
62
36
  <%= submit_tag "ok", :class => "postfix button"-%>
63
37
  </div>
64
38
  <div class="small-2 columns">
65
- <%= link_to( polymorphic_path( @object, :update => @update_span || "field_#{@attribute}_#{@object.id.to_s}",
66
- :attribute => @attribute,
67
- :form_element => @form_element,
68
- :sub_id => @sub_id ),
69
- :method => :get, # this is going to the show method!
70
- :remote => true ) do %>
71
- <input type="button" name="cancel" value="cancel" class="button alert postfix radius" />
72
- <% end %>
39
+ <%= inline_forms_field_cancel_link(@object, @attribute, @form_element, @update_span, sub_id: @sub_id, turbo_frame: @turbo_frame) %>
73
40
  </div>
74
41
  </div>
75
42
  <% end%>
@@ -45,8 +45,11 @@
45
45
  Container element. For the nested has_many case (parent_class set) we
46
46
  emit a `<turbo-frame>` so pagination clicks (and any future in-frame
47
47
  re-render of this list) swap only the matching frame instead of doing
48
- a full-page navigation. Top-level lists (parent_class.nil?) keep the
49
- classic <div> + full-page pagination they already had.
48
+ a full-page navigation. Top-level lists keep the classic `<div>` and
49
+ full-page navigation; their `+ new` link is plain (no Turbo frame
50
+ target) so cancel / create just navigate -- a top-level `<turbo-frame>`
51
+ collapses inside `position: absolute` `#outer_container` and hides the
52
+ list (regression hit during the 7.5.2 spike).
50
53
  -%>
51
54
  <% if ul_needed -%>
52
55
  <% if parent_class.nil? -%>
@@ -65,48 +68,58 @@
65
68
  <% css_class_id = parent_class.to_s.underscore + '_' + parent_id.to_s + '_' + attribute.to_s.singularize.underscore + "_" + object.id.to_s -%>
66
69
  <% path_to_object = attribute.to_s.singularize.underscore + "_path" %>
67
70
  <% end %>
71
+ <% row_open_link_opts = { data: { turbo: true, turbo_frame: css_class_id } } %>
68
72
  <%#
69
- For NESTED rows (parent_class present) the surrounding
70
- <turbo-frame> would otherwise intercept the UJS-driven inline-edit
71
- links/forms swapped in by show.js.erb / edit.js.erb / new.js.erb
72
- -- those all do `$('#<row_id>').html(...)`, so the swapped content
73
- stays a descendant of this row. Turbo would submit the multipart
74
- `<form>` from _edit.html.erb with `Accept: text/html`, which
75
- inline_forms_controller's update / create / destroy only respond
76
- to via format.js, raising `ActionController::UnknownFormat` (HTTP
77
- 406) AFTER the DB write already happened. `data-turbo="false"`
78
- here keeps UJS handling those, unchanged.
79
-
80
- For TOP-LEVEL rows (parent_class.nil?) there is no surrounding
81
- frame, so the attribute would be a no-op for the row's own click
82
- -- BUT after UJS swaps in the inline edit (which itself contains
83
- the nested-photos `<turbo-frame>`), Turbo's `elementIsNavigatable`
84
- walks up from any link inside that inner frame looking for the
85
- closest `[data-turbo]` ancestor; it does NOT stop at the
86
- intervening `<turbo-frame>`. A `data-turbo="false"` on the
87
- top-level row therefore poisons every descendant including the
88
- pagination links inside the inner photos frame, breaking
89
- in-frame navigation. So we only emit it on nested rows.
73
+ Each list row is `<turbo-frame id="…">` with a Turbo presentation link
74
+ (GET `show` / `close` as HTML). Nested rows use the same contract as
75
+ top-level; `not_accessible_through_html?` child models (Photo) still
76
+ serve `format.html` when `params[:update]` matches a nested row id.
90
77
  -%>
91
- <div class="row <%= cycle('odd', 'even') %><%= " top-level" if parent_class.nil? %>" id="<%= css_class_id -%>"<%= raw ' data-turbo="false"' unless parent_class.nil? %>>
92
- <% if cancan_disabled? || ( can? :soft_delete, object ) %>
93
- <div class="small-1 column">
94
- <%= link_to_soft_delete(object, css_class_id) -%>
95
- <%= link_to_destroy(object, css_class_id) -%>
96
- </div>
97
- <div class="small-11 column">
98
- <%= link_to h(object._presentation),
99
- send( path_to_object, object, :update => css_class_id),
100
- :remote => true -%>
78
+ <% if parent_class.nil? -%>
79
+ <%= tag.turbo_frame(id: css_class_id) do %>
80
+ <div class="row <%= cycle('odd', 'even') %> top-level">
81
+ <% if cancan_disabled? || ( can? :soft_delete, object ) %>
82
+ <div class="small-1 column">
83
+ <%= link_to_soft_delete(object, css_class_id) -%>
84
+ <%= link_to_destroy(object, css_class_id) -%>
85
+ </div>
86
+ <div class="small-11 column">
87
+ <%= link_to h(object._presentation),
88
+ send( path_to_object, object, :update => css_class_id),
89
+ row_open_link_opts -%>
90
+ </div>
91
+ <% else %>
92
+ <div class="small-12 column">
93
+ <%= link_to h(object._presentation),
94
+ send( path_to_object, object, :update => css_class_id),
95
+ row_open_link_opts -%>
96
+ </div>
97
+ <% end %>
101
98
  </div>
102
- <% else %>
103
- <div class="small-12 column">
104
- <%= link_to h(object._presentation),
105
- send( path_to_object, object, :update => css_class_id),
106
- :remote => true -%>
99
+ <% end %>
100
+ <% else -%>
101
+ <%= tag.turbo_frame(id: css_class_id) do %>
102
+ <div class="row <%= cycle('odd', 'even') %>">
103
+ <% if cancan_disabled? || ( can? :soft_delete, object ) %>
104
+ <div class="small-1 column">
105
+ <%= link_to_soft_delete(object, css_class_id) -%>
106
+ <%= link_to_destroy(object, css_class_id) -%>
107
+ </div>
108
+ <div class="small-11 column">
109
+ <%= link_to h(object._presentation),
110
+ send( path_to_object, object, :update => css_class_id),
111
+ row_open_link_opts -%>
112
+ </div>
113
+ <% else %>
114
+ <div class="small-12 column">
115
+ <%= link_to h(object._presentation),
116
+ send( path_to_object, object, :update => css_class_id),
117
+ row_open_link_opts -%>
118
+ </div>
119
+ <% end %>
107
120
  </div>
108
121
  <% end %>
109
- </div>
122
+ <% end -%>
110
123
  <% end -%>
111
124
  <!-- # pagination -->
112
125
  <% if parent_id.nil? -%>