inline_forms 7.5.2 → 7.9.1

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +110 -0
  3. data/README.rdoc +10 -0
  4. data/app/assets/javascripts/inline_forms/inline_forms.js +3 -10
  5. data/app/assets/stylesheets/inline_forms/inline_forms.scss +5 -4
  6. data/app/controllers/concerns/versions_concern.rb +0 -2
  7. data/app/controllers/inline_forms_controller.rb +76 -21
  8. data/app/helpers/inline_forms_helper.rb +42 -28
  9. data/app/views/inline_forms/_close.html.erb +9 -7
  10. data/app/views/inline_forms/_edit.html.erb +1 -1
  11. data/app/views/inline_forms/_list.html.erb +6 -21
  12. data/app/views/inline_forms/_new.html.erb +3 -5
  13. data/app/views/inline_forms/_show.html.erb +0 -26
  14. data/app/views/inline_forms/_versions.html.erb +5 -4
  15. data/app/views/inline_forms/_versions_list.html.erb +8 -8
  16. data/app/views/inline_forms/row_close.html.erb +10 -2
  17. data/app/views/inline_forms/versions_panel.html.erb +5 -2
  18. data/app/views/layouts/application.html.erb +2 -4
  19. data/app/views/layouts/inline_forms.html.erb +2 -5
  20. data/archived/README.md +47 -0
  21. data/archived/form_elements/README.md +27 -0
  22. data/archived/form_elements/chicas/README.md +31 -0
  23. data/archived/form_elements/geo_code_curacao/README.md +62 -0
  24. data/{app → archived/form_elements/geo_code_curacao/app}/helpers/form_elements/geo_code_curacao.rb +0 -1
  25. data/archived/form_elements/geo_code_curacao/app/views/geo_code_curacao/list_streets.html.erb +1 -0
  26. data/archived/form_elements/geo_code_curacao/app/views/geo_code_curacao/list_streets.js.erb +1 -0
  27. data/archived/form_elements/kansen_slider/README.md +31 -0
  28. data/archived/form_elements/tree/README.md +47 -0
  29. data/archived/form_elements/tree/app/views/inline_forms/_show_tree.html.erb +30 -0
  30. data/{app → archived/form_elements/tree/app}/views/inline_forms/_tree.html.erb +18 -5
  31. data/bin/inline_forms_installer_core.rb +70 -5
  32. data/docs/ujs-to-turbo.md +36 -37
  33. data/lib/generators/assets/stylesheets/inline_forms.scss +5 -4
  34. data/lib/inline_forms/archived_form_elements.rb +70 -0
  35. data/lib/inline_forms/version.rb +1 -1
  36. data/lib/inline_forms.rb +3 -1
  37. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_field_turbo_test.rb +6 -7
  38. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_name_required_test.rb +21 -0
  39. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_photos_pagination_test.rb +28 -0
  40. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_row_turbo_test.rb +14 -5
  41. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_top_level_new_test.rb +30 -60
  42. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_top_level_pagination_test.rb +40 -0
  43. data/lib/installer_templates/example_app_tests/test/integration/example_app_apartment_versions_turbo_test.rb +93 -0
  44. data/lib/installer_templates/example_app_tests/test/integration/example_app_photo_revert_test.rb +94 -0
  45. data/lib/installer_templates/example_app_tests/test/integration/example_app_turbo_layout_test.rb +6 -9
  46. data/lib/installer_templates/example_app_tests/test/models/example_app_apartment_name_validation_test.rb +16 -0
  47. data/test/archived_form_elements_test.rb +41 -0
  48. data/test/form_element_from_callee_test.rb +2 -2
  49. metadata +25 -20
  50. data/app/views/geo_code_curacao/list_streets.html.erb +0 -1
  51. data/app/views/geo_code_curacao/list_streets.js.erb +0 -1
  52. data/app/views/inline_forms/close.js.erb +0 -4
  53. data/app/views/inline_forms/list.js.erb +0 -1
  54. data/app/views/inline_forms/new.js.erb +0 -1
  55. data/app/views/inline_forms/record_destroyed.js.erb +0 -1
  56. data/app/views/inline_forms/show.js.erb +0 -1
  57. data/app/views/inline_forms/show_undo.js.erb +0 -1
  58. data/app/views/inline_forms/versions.js.erb +0 -4
  59. data/app/views/inline_forms/versions_list.js.erb +0 -1
  60. /data/{app → archived/form_elements/chicas/app}/helpers/form_elements/chicas_dropdown_with_family_members.rb +0 -0
  61. /data/{app → archived/form_elements/chicas/app}/helpers/form_elements/chicas_family_photo_list.rb +0 -0
  62. /data/{app → archived/form_elements/chicas/app}/helpers/form_elements/chicas_photo_list.rb +0 -0
  63. /data/{app → archived/form_elements/geo_code_curacao/app}/controllers/geo_code_curacao_controller.rb +0 -0
  64. /data/{app → archived/form_elements/geo_code_curacao/app}/models/geo_code_curacao.rb +0 -0
  65. /data/{app → archived/form_elements/kansen_slider/app}/helpers/form_elements/kansen_slider.rb +0 -0
  66. /data/{app → archived/form_elements/tree/app}/helpers/form_elements/move.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bbf211e9092af732b645bf10e9f9c338ced10c55e3ebf0a533146537669d1a4d
4
- data.tar.gz: 90bc8cf55083ca23bbc1d3122a49e835620bf2e4800c8c83a93b871230d54718
3
+ metadata.gz: 9cce3cdf65d2a40a3481fba51c95e57bfa6a65cd4a73359ad974fc327252dd7a
4
+ data.tar.gz: 7f57a6489ef1d88328474330fe70492f1266e5e394163260b2cc773c414571b8
5
5
  SHA512:
6
- metadata.gz: 5f66995db3089c59e1654f92f1a7c78b9701ccad3ea0a12fb4cf7729ea5ed1bff70003847af80ebcae6347c05e29f7e6fc9e8a9317252ea120a7a09d790c2284
7
- data.tar.gz: 2e04d9869aff6e13a1eb6556e9865e640725dee6d9ad5a56887ed23f125bd9378ab679c773fd84cbc72b8df408335dd7026998e113254366e4cc358155c70785
6
+ metadata.gz: fb6275f4b67706bee0e3b263e3b54b5a5a8be8a7f20676fd0ea0504be0e98edc3e3f41c98a807f1f28cdc4f5367e9a0f9a8c895bfa052978483f09148e1904e7
7
+ data.tar.gz: f168431ca3b73f07cfd2190b21ba211be91ab529861751d588ff33ed2e832e985411ec414ae2c5f958a9dd867e84083b5841e79424d2662bc558f35c10caac15
data/CHANGELOG.md CHANGED
@@ -4,6 +4,116 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [7.9.1] - 2026-05-17
8
+
9
+ ### Added
10
+
11
+ - **Example app — apartment name required:** the `--example` installer injects `validates :name, presence: true` on `Apartment`. Top-level create without a name re-renders the new form instead of persisting.
12
+ - **Regression tests:** `example_app_apartment_name_validation_test.rb` (model); `example_app_apartment_name_required_test.rb` (Turbo create).
13
+
14
+ ### Verified
15
+
16
+ - **`bundle exec rails test`** (new validation tests) in `--example` MyApp — **3 runs, 10 assertions, 0 failures**.
17
+
18
+ ## [7.9.0] - 2026-05-16
19
+
20
+ ### Added
21
+
22
+ - **Restore rich_text (ActionText) versions from the versions panel:** the Restore link now renders for `:rich_text` entries too. `InlineFormsController#revert` reifies the `ActionText::RichText` row, saves it, and `touch`es the parent so any timestamp display refreshes.
23
+ - **CarrierWave history for `image_field` / `multi_image_field`:** the installer ships a `config/initializers/carrierwave.rb` with `remove_previously_stored_files_after_update = false` and patches the generated `app/uploaders/image_uploader.rb` with a no-op `remove!` plus a per-upload UUID `filename` prefix. PaperTrail reverts on an image column now restore the previous bytes (not just the previous filename). Source: <https://stackoverflow.com/questions/9423279/papertrail-and-carrierwave> (Answers 2, 4, 5).
24
+ - **Regression tests:** `example_app_photo_revert_test.rb` (image-column revert restores bytes); rich_text revert assertions in `example_app_apartment_versions_turbo_test.rb`.
25
+
26
+ ### Changed
27
+
28
+ - **`render_revert_turbo_streams`:** no longer mutates `@update_span` mid-method. The row and versions frames are rendered with explicit `locals: { update_span:, object:, inline_forms_turbo_row: }`. The `row_close` / `versions_panel` templates and their `_close` / `_versions` partials prefer `local_assigns[:…]` and fall back to the matching ivar.
29
+ - **Frame ids in revert:** `row_id` and `versions_id` now derive from `@parent` (the rich_text branch sets `@parent = @rich_text_record.record`), so revert works identically for primary and rich_text versions.
30
+
31
+ ### Removed
32
+
33
+ - **`format.html` fallback in `revert`:** the action only responds with `turbo_stream` now. The "Turbo POST on row frame" regression test was updated to send `Accept: text/vnd.turbo-stream.html`.
34
+
35
+ ### Trade-offs
36
+
37
+ - The `ImageUploader` no longer deletes previous files on update or destroy. Files accumulate; sweep tooling is out of scope (would have to reconcile with `PaperTrail::Version#object` snapshots across the whole table).
38
+
39
+ ### Verified
40
+
41
+ - **`bundle exec rails test`** in `--example` MyApp — **68 runs, 358 assertions, 0 failures, 0 errors, 0 skips**.
42
+
43
+ ## [7.8.1] - 2026-05-16
44
+
45
+ ### Fixed
46
+
47
+ - **Restore from versions panel (Turbo 2 + Drive):** POSTs from inside `<turbo-frame id="…_versions">` sent `Turbo-Frame: …_versions` while the server returned only the **row** frame, so Turbo showed **Content missing**. Restore links now request **`turbo_stream`**; **`revert`** responds with **`turbo_stream.replace`** for both the row and the versions panel.
48
+
49
+ ### Added
50
+
51
+ - **Regression tests:** versions list restore link carries `data-turbo-stream`; revert with versions-frame header returns stream (`example_app_apartment_versions_turbo_test.rb`).
52
+
53
+ ### Verified
54
+
55
+ - **`bundle exec rails test`** in `--example` MyApp — **67 runs, 342 assertions, 0 failures**.
56
+
57
+ ## [7.8.0] - 2026-05-16
58
+
59
+ ### Changed
60
+
61
+ - **Step 5 (UJS → Turbo):** **Turbo Drive** left at the default (**enabled**); removed **`Turbo.session.drive = false`** from **`inline_forms`** and **`application`** layouts.
62
+ - **`app/assets/javascripts/inline_forms/inline_forms.js`:** dropped **`jquery_ujs`** and **`jquery.remotipart`**; inline navigation uses Turbo only.
63
+ - **Installer Gemfile** (`bin/inline_forms_installer_core.rb`): removed **`remotipart`**; updated **`turbo-rails`** comment.
64
+ - **Helpers / partials:** removed **`remote: true`** fallbacks (`close_link`, toolbar, new/versions links, field cancel, `link_to_inline_edit` legacy branch, **`_close`**, **`_new`**); all use **`data-turbo`** + frame targets.
65
+
66
+ ### Verified
67
+
68
+ - **`bundle exec rails test`** in `--example` MyApp — **65 runs, 330 assertions, 0 failures**.
69
+
70
+ ## [7.7.3] - 2026-05-16
71
+
72
+ ### Changed
73
+
74
+ - **Step 4 (UJS → Turbo) — versions panel:** `VersionsConcern#list_versions` is **HTML-only** (`versions_panel` / `versions_list_panel` inside matching `<turbo-frame>`). Removed **`versions.js.erb`** and **`versions_list.js.erb`**.
75
+
76
+ ### Fixed
77
+
78
+ - **Nested Photo (and other `not_accessible_through_html?` children) versions restore:** `_versions_list` revert links targeted `photo_<id>` but nested list rows use `apartment_<aid>_photo_<id>`. Turbo could not find that frame, fell back to `photo_<id>_versions`, and `revert` returned **406** (`row_html_turbo_allowed?` false for bare `photo_<id>`). **`inline_forms_row_turbo_frame_id`** now matches `_list.html.erb` row ids.
79
+
80
+ ### Added
81
+
82
+ - **Regression tests:** expanded-row versions open link (Turbo, not `data-remote`); revert from versions list via Turbo POST (`example_app_apartment_versions_turbo_test.rb`); nested Photo versions list restore + Turbo POST revert (`example_app_apartment_photos_pagination_test.rb`).
83
+
84
+ ### Verified
85
+
86
+ - **`bundle exec rails test`** in `--example` MyApp — **65 runs, 330 assertions, 0 failures**.
87
+
88
+ ## [7.7.0] - 2026-05-16
89
+
90
+ ### Changed
91
+
92
+ - **Step 4 (UJS → Turbo) — top-level list + create:** `/apartments` index list root is **`<turbo-frame id="apartments_list">`** with in-frame pagination (`update=apartments_list`); **`+ new` / cancel / create** use Turbo HTML (`new_record` / `create_list_frame`) instead of **`new.js.erb`** / **`list.js.erb`**. **`#outer_container > turbo-frame.list_container { width: 100% }`** fixes the 7.5.2 layout collapse.
93
+ - **`:tree` / `:move` archived:** hierarchical child list (**`_tree.html.erb`**) and reparent helper (**`move.rb`**) moved to **`archived/form_elements/tree/`** — they expect host-app tree APIs (`#children`, `#hash_tree_to_collection`, `#add_child`), not defined in the gem; no example-app model. Registry blocks `:tree` / `:move` in `inline_forms_attribute_list`.
94
+ - **Removed row/list UJS templates:** **`show.js.erb`**, **`close.js.erb`**, **`record_destroyed.js.erb`**, **`show_undo.js.erb`**, **`new.js.erb`**, **`list.js.erb`**; row actions and list flows use **`format.html`** only.
95
+
96
+ ### Added
97
+
98
+ - **Regression tests:** `example_app_apartment_top_level_new_test.rb` (Turbo new/cancel/create), `example_app_apartment_top_level_pagination_test.rb`.
99
+
100
+ ### Verified
101
+
102
+ - **`bundle exec rails test`** in `--example` MyApp — **62 runs, 312 assertions, 0 failures**.
103
+
104
+ ## [7.6.0] - 2026-05-16
105
+
106
+ ### Changed
107
+
108
+ - **Project-specific form elements archived (not loaded by default):** `:geo_code_curacao` (helper + model + controller + views), **`chicas_*`** (`:chicas_photo_list`, `:chicas_family_photo_list`, `:chicas_dropdown_with_family_members`), and **`:kansen_slider`**. Sources live under **`archived/form_elements/`** with per-folder README restore steps.
109
+ - **Registry:** **`InlineForms::ARCHIVED_FORM_ELEMENTS`**; boot raises **`ArchivedFormElementError`** if a model still declares an archived symbol (including **`:absence_list`**, removed in 6.3.0 without source in tree).
110
+ - **Versioned archive:** **`archived/README.md`** catalogs all retired features.
111
+ - **`_edit.html.erb`:** removed **`kansen_slider`** from `@BUTTONS_UNDER` (element archived).
112
+
113
+ ### Upgrade notes
114
+
115
+ - **Breaking:** Remove or vendor archived symbols from `inline_forms_attribute_list`: **`:geo_code_curacao`**, **`:chicas_*`**, **`:kansen_slider`**. Copy from the matching **`archived/form_elements/<name>/`** README (geo also needs host route `get "geo_code_curacao", to: "geo_code_curacao#list_streets"`).
116
+
7
117
  ## [7.5.2] - 2026-05-16
8
118
 
9
119
  ### Fixed
data/README.rdoc CHANGED
@@ -48,6 +48,16 @@ The +:image_field+ form element uses CarrierWave. Generated apps depend on +carr
48
48
 
49
49
  To switch to S3, add +carrierwave-aws+ (or use the bundled fog backend) and configure a +CarrierWave.configure+ block in +config/initializers/carrierwave.rb+; nothing in inline_forms hard-codes local storage.
50
50
 
51
+ === PaperTrail-driven restore keeps previous image bytes
52
+
53
+ PaperTrail snapshots the column scalar (a CarrierWave filename) on update; CarrierWave's defaults overwrite the previous file on disk and reuse the same filename, so a vanilla +version.reify; save!+ ends up restoring a filename whose bytes are gone. The generated +ImageUploader+ ships three knobs that fix this:
54
+
55
+ * +CarrierWave.configure { |c| c.remove_previously_stored_files_after_update = false }+ in +config/initializers/carrierwave.rb+ — covers +:multi_image_field+ uploaders too.
56
+ * +remove!+ overridden to a no-op, so hard-destroyed records keep their bytes and revert-after-destroy can still find them.
57
+ * +filename+ prefixed with a per-upload UUID, so successive uploads never collide on disk.
58
+
59
+ Trade-off: files accumulate on disk; periodic sweeping is out of scope of the gem. Source: https://stackoverflow.com/questions/9423279/papertrail-and-carrierwave (Answers 2, 4 and 5).
60
+
51
61
  For long text fields, use +:plain_text+ for a plain textarea backed by a DB +text+ column, or +:rich_text+ for ActionText/Trix content. +:plain_text+ requires an actual column on the model table; if the column is missing, inline_forms now raises +InlineForms::PlainTextColumnMissingError+ during controller boot/runtime checks.
52
62
 
53
63
  Note: generated apps also depend on ActiveStorage transitively because the +:rich_text+ form element uses ActionText (+active_storage:install+ runs during +inline_forms create+). Image uploads still go through CarrierWave; ActiveStorage is only there to back ActionText embeds.
@@ -1,24 +1,17 @@
1
1
  //= require jquery
2
- //= require jquery_ujs
3
2
  //= require jquery.ui.all
4
3
  //= require jquery.timepicker.js
5
4
  //= require foundation
6
- //= require jquery.remotipart
7
5
  //= require autocomplete-rails
8
6
 
9
7
  // Turbo / Hotwire is intentionally NOT required into this Sprockets bundle.
10
8
  // `turbo-rails` ships only an ES-module build of turbo (`app/assets/javascripts/turbo.js`
11
9
  // ends with `export { Turbo, cable }`). Concatenating that into a regular
12
- // `<script>` bundle produces a parse error at the top-level `export`, which
13
- // silently kills jquery-ujs initialization (forms then submit as plain
14
- // HTML POST and inline_forms controllers respond with `format.js` only,
15
- // raising `ActionController::UnknownFormat`).
10
+ // `<script>` bundle produces a parse error at the top-level `export`.
16
11
  //
17
12
  // Turbo is loaded separately by `app/views/layouts/inline_forms.html.erb`
18
- // (and `application.html.erb`) as `<script type="module">`, with
19
- // `Turbo.session.drive = false` so existing UJS-driven links/forms keep
20
- // working unchanged. `<turbo-frame>` and `format.turbo_stream` are
21
- // available for the per-view conversions that follow.
13
+ // (and `application.html.erb`) as `<script type="module">`. Inline flows use
14
+ // `<turbo-frame>` + HTML responses; jquery-ujs / remotipart were removed in 7.8.0.
22
15
 
23
16
  $(function(){ $(document).foundation(); });
24
17
  // initialize datepickers
@@ -224,13 +224,14 @@ select:hover, select:focus {
224
224
  font-size: 110%;
225
225
  }
226
226
  // Custom elements default to `display: inline`, which collapses the row layout
227
- // of an empty/just-mounted top-level `<turbo-frame id="apartments_list">` and
228
- // hides its rows under the fixed top bar. Force block layout so frames behave
229
- // like the legacy `<div class="list_container">` and `<div class="row">` they
230
- // replaced (see app/views/inline_forms/_list.html.erb).
227
+ // of a top-level `<turbo-frame id="apartments_list">` inside `#outer_container`.
231
228
  turbo-frame {
232
229
  display: block;
233
230
  }
231
+ #outer_container > turbo-frame.list_container {
232
+ display: block;
233
+ width: 100%;
234
+ }
234
235
  .list_container {
235
236
  .row {
236
237
  font-size: 1.2rem;
@@ -12,12 +12,10 @@ module VersionsConcern
12
12
  if close
13
13
  respond_to do |format|
14
14
  format.html { render "inline_forms/versions_panel", layout: "turbo_rails/frame" }
15
- format.js { render :versions }
16
15
  end
17
16
  else
18
17
  respond_to do |format|
19
18
  format.html { render "inline_forms/versions_list_panel", layout: "turbo_rails/frame" }
20
- format.js { render :versions_list }
21
19
  end
22
20
  end
23
21
  end
@@ -44,7 +44,12 @@ class InlineFormsController < ApplicationController
44
44
  foreign_key = @Klass.reflect_on_association(@parent_class.underscore.to_sym).options[:foreign_key] || @parent_class.foreign_key
45
45
  conditions = [ "#{foreign_key} = ?", @parent_id ]
46
46
  end
47
- # if we are using cancan, then make sure to select only accessible records
47
+ # CanCan's load_and_authorize_resource sets @apartments (etc.); keep @objects in sync.
48
+ collection_ivar = :"@#{controller_name}"
49
+ if instance_variable_defined?(collection_ivar)
50
+ loaded = instance_variable_get(collection_ivar)
51
+ @objects = loaded unless loaded.nil?
52
+ end
48
53
  @objects ||= @Klass.accessible_by(current_ability) if cancan_enabled?
49
54
  @objects ||= @Klass
50
55
  @objects = @objects.order(@Klass.table_name + "." + @Klass.order_by_clause) if @Klass.respond_to?(:order_by_clause) && ! @Klass.order_by_clause.nil?
@@ -79,12 +84,14 @@ class InlineFormsController < ApplicationController
79
84
  format.html do
80
85
  if @parent_class.present?
81
86
  render_nested_associated_list_html
87
+ elsif turbo_frame_request? && list_frame_id?(params[:update])
88
+ @ul_needed = true
89
+ render "inline_forms/_list", layout: "turbo_rails/frame"
82
90
  else
83
91
  render "inline_forms/_list", layout: "inline_forms"
84
92
  end
85
93
  end
86
94
  end
87
- format.js { render :list }
88
95
  end
89
96
  end
90
97
 
@@ -103,8 +110,7 @@ class InlineFormsController < ApplicationController
103
110
 
104
111
  @object.inline_forms_attribute_list = @inline_forms_attribute_list if @inline_forms_attribute_list
105
112
  respond_to do |format|
106
- format.html { render_turbo_new } if associated_list_html_allowed? || !@Klass.not_accessible_through_html?
107
- format.js { }
113
+ format.html { render_turbo_new } if html_list_flow_allowed?
108
114
  end
109
115
  end
110
116
 
@@ -128,7 +134,7 @@ class InlineFormsController < ApplicationController
128
134
  attributes = @inline_forms_attribute_list || @object.inline_forms_attribute_list
129
135
  attributes.each do | attribute, name, form_element |
130
136
  InlineForms.assert_plain_text_column!(object: @object, attribute: attribute, form_element: form_element)
131
- send("#{form_element.to_s}_update", @object, attribute) unless form_element == :tree || form_element == :associated || (cancan_enabled? && cannot?(:read, @object, attribute))
137
+ send("#{form_element.to_s}_update", @object, attribute) unless form_element == :associated || (cancan_enabled? && cannot?(:read, @object, attribute))
132
138
  end
133
139
  @parent_class = params[:parent_class]
134
140
  @parent_id = params[:parent_id]
@@ -150,16 +156,14 @@ class InlineFormsController < ApplicationController
150
156
  @objects = @objects.where(conditions).paginate(:page => params[:page])
151
157
  @object = nil
152
158
  respond_to do |format|
153
- format.html { render_associated_list_frame } if associated_list_html_allowed?
154
- format.js { render :list }
159
+ format.html { render_list_frame_after_save } if html_list_flow_allowed?
155
160
  end
156
161
  else
157
162
  flash.now[:header] = ["Kan #{@object.class.to_s.underscore} niet aanmaken."]
158
163
  flash.now[:error] = @object.errors.to_a
159
164
  respond_to do |format|
160
165
  @object.inline_forms_attribute_list = attributes
161
- format.html { render_turbo_new } if associated_list_html_allowed? || !@Klass.not_accessible_through_html?
162
- format.js { render :new }
166
+ format.html { render_turbo_new } if html_list_flow_allowed?
163
167
  end
164
168
  end
165
169
  end
@@ -203,10 +207,8 @@ class InlineFormsController < ApplicationController
203
207
  @attributes = @object.inline_forms_attribute_list
204
208
  if close
205
209
  format.html { render_row_turbo(:close) } if row_html_turbo_allowed?
206
- format.js { render :close }
207
210
  else
208
211
  format.html { render_row_turbo(:show) } if row_html_turbo_allowed?
209
- format.js { render :show }
210
212
  end
211
213
  end
212
214
  else
@@ -223,7 +225,6 @@ class InlineFormsController < ApplicationController
223
225
  @object.soft_delete(current_user)
224
226
  respond_to do |format|
225
227
  format.html { render_row_turbo(:close) } if row_html_turbo_allowed?
226
- format.js { render :close }
227
228
  end
228
229
  end
229
230
 
@@ -234,7 +235,6 @@ class InlineFormsController < ApplicationController
234
235
  @object.soft_restore
235
236
  respond_to do |format|
236
237
  format.html { render_row_turbo(:close) } if row_html_turbo_allowed?
237
- format.js { render :close }
238
238
  end
239
239
  end
240
240
 
@@ -247,23 +247,42 @@ class InlineFormsController < ApplicationController
247
247
  @object.destroy
248
248
  respond_to do |format|
249
249
  format.html { render_row_turbo_destroyed } if row_html_turbo_allowed?
250
- format.js { render :record_destroyed }
251
250
  end
252
251
  end
253
252
  end
254
253
 
255
254
  # :revert works like undo.
256
255
  # Thanks Ryan Bates: http://railscasts.com/episodes/255-undo-with-paper-trail
256
+ #
257
+ # Two reify paths:
258
+ #
259
+ # * Primary version: the reified object IS the row (Apartment, Photo, ...).
260
+ # Save it and we're done; CarrierWave keeps the previous file on disk
261
+ # (see app/uploaders/image_uploader.rb) so a restored `image` column
262
+ # still points at real bytes.
263
+ # * ActionText (rich_text) version: the reified object is the
264
+ # `ActionText::RichText` row hanging off the parent. Save the rich text,
265
+ # then `touch` the parent so any timestamp display refreshes. Frame ids
266
+ # below derive from `@parent` for both branches.
257
267
  def revert
258
268
  @update_span = params[:update]
259
269
  if current_user.role? :superadmin
260
270
  @version = PaperTrail::Version.find(params[:id])
261
271
  @object = @version.reify
262
- @object.save!
263
- authorize!(:revert, @object) if cancan_enabled?
272
+ if defined?(ActionText::RichText) && @object.is_a?(ActionText::RichText)
273
+ @rich_text_record = @object
274
+ @parent = @rich_text_record.record
275
+ @rich_text_record.save!
276
+ @parent.touch if @parent.respond_to?(:touch)
277
+ else
278
+ @parent = @object
279
+ @parent.save!
280
+ end
281
+ authorize!(:revert, @parent) if cancan_enabled?
282
+ return unless row_html_turbo_allowed?
283
+
264
284
  respond_to do |format|
265
- format.html { render_row_turbo(:close) } if row_html_turbo_allowed?
266
- format.js { render :close }
285
+ format.turbo_stream { render_revert_turbo_streams }
267
286
  end
268
287
  end
269
288
  end
@@ -282,6 +301,30 @@ class InlineFormsController < ApplicationController
282
301
 
283
302
  private
284
303
 
304
+ # Versions list lives in +<turbo-frame id="…_versions">+; POST +restore+ would otherwise
305
+ # send +Turbo-Frame: …_versions+ while +row_close+ only returns the row frame. Stream
306
+ # replaces both the row and the versions panel in one response.
307
+ def render_revert_turbo_streams
308
+ row_id = helpers.inline_forms_row_turbo_frame_id(@parent)
309
+ versions_id = "#{@parent.class.name.underscore}_#{@parent.id}_versions"
310
+ row_html = render_to_string(
311
+ "inline_forms/row_close",
312
+ layout: false,
313
+ formats: [:html],
314
+ locals: { update_span: row_id, object: @parent, inline_forms_turbo_row: true }
315
+ )
316
+ versions_html = render_to_string(
317
+ "inline_forms/versions_panel",
318
+ layout: false,
319
+ formats: [:html],
320
+ locals: { update_span: versions_id, object: @parent, inline_forms_turbo_row: true }
321
+ )
322
+ render turbo_stream: [
323
+ turbo_stream.replace(row_id, row_html),
324
+ turbo_stream.replace(versions_id, versions_html)
325
+ ]
326
+ end
327
+
285
328
  # HTML field edit/show inside a +<turbo-frame>+ (Step 3). Scalar fields no longer
286
329
  # use UJS; +format.html+ is always registered for edit/update/single-attribute show.
287
330
  #
@@ -289,9 +332,8 @@ class InlineFormsController < ApplicationController
289
332
  # +*_show+ helpers it wraps) to emit Turbo data attributes. The flag is set in
290
333
  # +_show.html.erb+ when a row first opens, but bare +field_show+ / +field_edit+
291
334
  # responses (on +cancel+ / +update+) do not re-render +_show+. Without setting
292
- # it here the link in the swapped frame falls back to +remote: true+, which the
293
- # legacy +jquery_ujs+ bundle intercepts as a JS request -- the controller only
294
- # registers +format.html+, so the click silently fails (no swap, no edit form).
335
+ # it here the link in the swapped frame would not target the field frame and
336
+ # inline edit would not open reliably.
295
337
  def render_turbo_field(template, turbo_field_show: false)
296
338
  @turbo_frame = true if template == :field_edit
297
339
  @turbo_field_show_turbo_frame = turbo_field_show
@@ -315,10 +357,23 @@ class InlineFormsController < ApplicationController
315
357
  end
316
358
 
317
359
  # Nested has_many +new+ / cancel / +create+ inside a parent +<turbo-frame>+ (e.g. Apartment → Photo).
360
+ def html_list_flow_allowed?
361
+ params[:update].present? && (@parent_class.present? || !@Klass.not_accessible_through_html?)
362
+ end
363
+
318
364
  def associated_list_html_allowed?
319
365
  @parent_class.present? && params[:update].present?
320
366
  end
321
367
 
368
+ def list_frame_id?(update)
369
+ update.present? && update.to_s.end_with?("_list")
370
+ end
371
+
372
+ def render_list_frame_after_save
373
+ @ul_needed = true
374
+ render "inline_forms/create_list_frame", layout: associated_list_frame_layout
375
+ end
376
+
322
377
  def associated_list_frame_layout
323
378
  # Use full inline_forms chrome so the swapped frame is styled; Turbo extracts
324
379
  # the matching <turbo-frame id="…"> from the response body.
@@ -1,8 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module InlineFormsHelper
3
- # load form elements. Each element goes into a separate file
4
- # and defines a _show, _edit and _update method.
5
- #
3
+ # Load active form elements (top-level *.rb only). Retired elements live under
4
+ # archived/form_elements/ see archived/README.md.
6
5
  INLINE_FORMS_PATH = File.dirname(__FILE__) + "/form_elements/"
7
6
  Dir[INLINE_FORMS_PATH + "*.rb"].each do |form_element|
8
7
  require form_element
@@ -54,12 +53,36 @@ module InlineFormsHelper
54
53
 
55
54
  # Turbo Frames navigation for row toolbar, versions, and nested +new+ (Step 3).
56
55
  # +update_span+ must match the target +<turbo-frame id="…">+.
57
- def inline_forms_turbo_link_data(update_span, method: :get)
56
+ #
57
+ # When +turbo_stream: true+ (e.g. restore from inside +*_versions+ frame), the client
58
+ # requests +text/vnd.turbo-stream.html+ so Turbo does not expect a single matching
59
+ # frame in the response — POSTs from nested +…_versions+ frames otherwise send
60
+ # +Turbo-Frame: …_versions+ while the server returns the row frame (+Content missing+).
61
+ def inline_forms_turbo_link_data(update_span, method: :get, turbo_stream: false)
58
62
  data = { turbo: true, turbo_frame: update_span }
59
63
  data[:turbo_method] = method.to_s.downcase unless method == :get
64
+ data[:turbo_stream] = true if turbo_stream
60
65
  { data: data }
61
66
  end
62
67
 
68
+ # +<turbo-frame>+ id for a list row's open/close/revert target.
69
+ # Top-level models: +apartment_5+. Nested +not_accessible_through_html?+ children
70
+ # (e.g. Photo under Apartment): +apartment_5_photo_2+ — must match +_list.html.erb+.
71
+ def inline_forms_row_turbo_frame_id(object)
72
+ frame_id = "#{object.class.name.underscore}_#{object.id}"
73
+ return frame_id unless object.class.respond_to?(:not_accessible_through_html?) &&
74
+ object.class.not_accessible_through_html?
75
+
76
+ belongs_to = object.class.reflect_on_all_associations(:belongs_to)
77
+ .find { |assoc| !assoc.polymorphic? }
78
+ return frame_id unless belongs_to
79
+
80
+ parent = object.public_send(belongs_to.name)
81
+ return frame_id unless parent
82
+
83
+ "#{parent.class.name.underscore}_#{parent.id}_#{object.class.name.underscore}_#{object.id}"
84
+ end
85
+
63
86
  private
64
87
 
65
88
  def validation_hints_as_list_for(object, attribute)
@@ -74,12 +97,8 @@ module InlineFormsHelper
74
97
  close: true
75
98
  )
76
99
  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
100
+ opts[:data] = { turbo: true, turbo_frame: "_self" }
101
+ link_to "<i class='fi-x'></i>".html_safe, path, opts
83
102
  end
84
103
 
85
104
  # delete link. Mind the difference between delete and destroy.
@@ -89,12 +108,12 @@ module InlineFormsHelper
89
108
  if object.deleted? && (cancan_disabled? || (can? :soft_restore, object))
90
109
  path = send("soft_restore_#{object.class.to_s.underscore}_path", object, update: update_span)
91
110
  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 })
111
+ opts.merge!(inline_forms_turbo_link_data(update_span, method: :post))
93
112
  soft = link_to "<i class='fi-refresh'></i>".html_safe, path, opts
94
113
  elsif !object.deleted? && (cancan_disabled? || (can? :soft_delete, object))
95
114
  path = send("soft_delete_#{object.class.to_s.underscore}_path", object, update: update_span)
96
115
  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 })
116
+ opts.merge!(inline_forms_turbo_link_data(update_span, method: :post))
98
117
  soft = link_to "<i class='fi-trash'></i>".html_safe, path, opts
99
118
  end
100
119
  end
@@ -107,7 +126,7 @@ module InlineFormsHelper
107
126
  if cancan_disabled? || (can? :destroy, object)
108
127
  path = polymorphic_path(object, update: update_span)
109
128
  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 })
129
+ opts.merge!(inline_forms_turbo_link_data(update_span, method: :delete))
111
130
  hard = link_to "&nbsp;&nbsp;<font color='FF0000'><i class='fi-x'></i></font>".html_safe, path, opts
112
131
  end
113
132
  hard.html_safe
@@ -115,13 +134,8 @@ module InlineFormsHelper
115
134
 
116
135
  # new link
117
136
  #
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).
137
+ # +turbo_row:+ kept for API compatibility; navigation always targets +update_span+
138
+ # as a +<turbo-frame>+ (no jquery-ujs).
125
139
  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
140
  path = send(
127
141
  path_to_new,
@@ -133,7 +147,7 @@ module InlineFormsHelper
133
147
  class: html_class,
134
148
  title: t("inline_forms.view.add_new", model: model.model_name.human)
135
149
  }
136
- opts.merge!(turbo_row && parent_class.present? ? inline_forms_turbo_link_data(update_span) : { remote: true })
150
+ opts.merge!(inline_forms_turbo_link_data(update_span))
137
151
  out = link_to "<i class='fi-plus'></i>".html_safe, path, opts
138
152
  if cancan_enabled?
139
153
  if can? :create, model
@@ -154,7 +168,7 @@ module InlineFormsHelper
154
168
  if defined?(PaperTrail) && object.respond_to?(:versions)
155
169
  path = send(path_to_versions_list, object, update: update_span)
156
170
  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 })
171
+ opts.merge!(inline_forms_turbo_link_data(update_span))
158
172
  raw link_to("<i class='fi-list'></i>".html_safe, path, opts)
159
173
  end
160
174
  end
@@ -169,7 +183,7 @@ module InlineFormsHelper
169
183
  close: true
170
184
  )
171
185
  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 })
186
+ opts.merge!(inline_forms_turbo_link_data(update_span))
173
187
  link_to "<i class='fi-x'></i>".html_safe, path, opts
174
188
  end
175
189
 
@@ -198,11 +212,10 @@ module InlineFormsHelper
198
212
  )
199
213
  opts = { class: "inline_forms-field-cancel" }
200
214
  if turbo_frame
201
- # Inside a <turbo-frame>: plain GET link; no data-method (jquery-ujs fights Turbo).
215
+ # Inside a <turbo-frame>: plain GET link; no data-method (legacy ujs fought Turbo).
202
216
  opts[:data] = { turbo: true, turbo_frame: "_self" }
203
217
  else
204
- opts[:remote] = true
205
- opts[:method] = :get
218
+ opts[:data] = { turbo: true, turbo_frame: update_span }
206
219
  end
207
220
  link_to path, opts do
208
221
  tag.input(
@@ -218,7 +231,8 @@ module InlineFormsHelper
218
231
  # link_to_inline_edit
219
232
  #
220
233
  # Pass +from_callee:+ +__callee__+ from the enclosing +*_show+ method so the edit route receives the correct form element name.
221
- # When +turbo_frame:+ is true the link omits +remote: true+; navigation is handled by the enclosing +<turbo-frame>+.
234
+ # When +turbo_frame:+ is true the link targets +_self+; otherwise it targets the
235
+ # field frame id (+css_class_id+) so edit works without a surrounding +_show+ wrap.
222
236
  def link_to_inline_edit(object, attribute, attribute_value='', from_callee:, turbo_frame: false)
223
237
  form_element = InlineForms.form_element_string_from_callee(from_callee)
224
238
  attribute_value = attribute_value.to_s
@@ -231,7 +245,7 @@ module InlineFormsHelper
231
245
  link_opts = if use_turbo_frame
232
246
  { data: { turbo: true, turbo_frame: "_self" } }
233
247
  else
234
- { remote: true }
248
+ { data: { turbo: true, turbo_frame: css_class_id } }
235
249
  end
236
250
  link_to value,
237
251
  edit_polymorphic_path(
@@ -1,15 +1,17 @@
1
- <% row_turbo = @inline_forms_turbo_row %>
2
- <% presentation_link_opts = row_turbo ? { data: { turbo: true, turbo_frame: @update_span } } : { remote: true } %>
3
- <% if cancan_disabled? || ( can? :soft_delete, @object ) %>
1
+ <% update_span = local_assigns[:update_span] || @update_span -%>
2
+ <% object = local_assigns[:object] || @object -%>
3
+ <% inline_forms_turbo_row = local_assigns.fetch(:inline_forms_turbo_row, @inline_forms_turbo_row) -%>
4
+ <% presentation_link_opts = { data: { turbo: true, turbo_frame: update_span } } %>
5
+ <% if cancan_disabled? || ( can? :soft_delete, object ) %>
4
6
  <div class="small-1 column">
5
- <%= link_to_soft_delete(@object, @update_span, turbo_row: row_turbo) -%>
6
- <%= link_to_destroy(@object, @update_span, turbo_row: row_turbo) -%>
7
+ <%= link_to_soft_delete(object, update_span, turbo_row: inline_forms_turbo_row) -%>
8
+ <%= link_to_destroy(object, update_span, turbo_row: inline_forms_turbo_row) -%>
7
9
  </div>
8
10
  <div class="small-11 column">
9
- <%= link_to h(@object._presentation), polymorphic_path(@object, :update => @update_span), presentation_link_opts -%>
11
+ <%= link_to h(object._presentation), polymorphic_path(object, :update => update_span), presentation_link_opts -%>
10
12
  </div>
11
13
  <% else %>
12
14
  <div class="small-12 column">
13
- <%= link_to h(@object._presentation), polymorphic_path(@object, :update => @update_span), presentation_link_opts -%>
15
+ <%= link_to h(object._presentation), polymorphic_path(object, :update => update_span), presentation_link_opts -%>
14
16
  </div>
15
17
  <% end %>
@@ -1,4 +1,4 @@
1
- <% @BUTTONS_UNDER = [ "text_area", "kansen_slider", "rich_text" ] %>
1
+ <% @BUTTONS_UNDER = [ "text_area", "rich_text" ] %>
2
2
  <% edit_form_opts = { method: :put, multipart: true, class: "edit_form", abide: true } %>
3
3
  <% edit_form_opts[:remote] = true unless @turbo_frame %>
4
4
  <%= form_tag polymorphic_path(@object,