inline_forms 7.13.5 → 7.13.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1852b024dd7d8d0b8f7b6d76e2ca17d4f8b9ad85dc6d2db6a4d11195164469e9
4
- data.tar.gz: 6b9cfc0e042dc2d0146b282c779cc1cfaa547c7aaf0495bf897c125feaa16b45
3
+ metadata.gz: d3b2fa6061d683fe127827b1cb382621af92b8063b61eca29998c06422feda4c
4
+ data.tar.gz: 298837a9010e3265adf7e0887828e813e8c7596b617e719f2f6c6e0b1fdf344f
5
5
  SHA512:
6
- metadata.gz: e6ddeccc3034c47c4e0ebd087e90222b3589b767bc24891f73f8caedd89cc24d06b3cd02b8242075ca8e2c27710aee494084b422eca61e6c8b110c7dc3ecac5a
7
- data.tar.gz: 01b1d421b8d8ec544c4b41cb42e13d0f58634aee2ff07021edc5219adbd2b3cc4f6f4b5a0f7d8308e6a6e82eb457081965735a285065e465c3c36a716a33f71e
6
+ metadata.gz: 8e08926a15fe6d6b9385757191b1ba21eb7dedd089247926e54302e81ac3da31fc05acd97f9e7706056b5b6abbca87aeb988528eb1f95bee37775f57df5e85a8
7
+ data.tar.gz: b810adc34432956a4cf43ca7c59dc6994cda0d59299ef84d3e8a333397ac642eade0cbf28e2097fec69cb3f2c63e5aafed8052e3865f8d6138c7be3f5796d0ff
data/CHANGELOG.md CHANGED
@@ -4,6 +4,63 @@ All notable changes to this project are documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [7.13.8] - 2026-05-20
8
+
9
+ ### Added
10
+
11
+ - **`README.rdoc`: "Where to put the `tabs_tag` block (five patterns)" section** under `== Per-resource Turbo tabs`. Documents the five common ways to wire a Turbo-driven tab strip in an inline_forms app, each with a runnable code snippet and a "best when ..." recommendation:
12
+ 1. **Inlined in the show view** — drop the `tabs_tag` block straight into `show_with_tabs.html.erb`, no partial.
13
+ 2. **Dedicated tab-strip partial** (the `--example` app's pattern) — strip lives in `_<resource>_tabs.html.erb` and iterates over a controller constant.
14
+ 3. **Helper-driven, reusable across resources** — extract into `InlineFormsTabsHelper#inline_forms_turbo_tabs_for(object, tabs, update:, i18n_scope:)` so multiple resources opt in with one line.
15
+ 4. **One partial per tab content** — `app/views/<resource>/tabs/_<tab>.html.erb` per tab; each tab owns its own markup (charts, custom forms, mixed-resource pages) instead of going through `inline_forms/_show`.
16
+ 5. **Grouped tab strips** — render two or more `tabs_tag` blocks side-by-side (e.g. an "info" group + a "process" group on a `Client` detail page), each with its own `open_tabs` class / id but sharing `set_tab` / `current_tab?` so the active highlight always lands on the one tab matching `params[:tab]`.
17
+ The section closes by noting that every option uses the same Turbo wiring (`link_options: { data: { turbo_frame: @update_span } }` on the `<a>`, surrounding `<turbo-frame id="<%= @update_span %>">` in the show view, controller `set_tab` + `params[:tab]`) and that the `InlineForms::TurboTabsBuilder` choice is independent of the partial layout.
18
+
19
+ ### Changed
20
+
21
+ - **`InlineForms::VERSION`** and **`InlineFormsInstaller::VERSION`** → `7.13.8`.
22
+
23
+ ## [7.13.7] - 2026-05-20
24
+
25
+ ### Added
26
+
27
+ - **Example app seed migration `SeedExampleApartmentsAndOwners`** (`installer_core.rb`). Replaces the old `SeedKonferenshaPhotos` migration. Seeds three apartments (`Apt 1`, `Apt 2`, `Apt 3`) with one photo gallery each (`db/seed_images/apt<N>_*.png`) and three owners — Maria Martinez (owns Apt 1 + Apt 2), Jean-Pierre Dupont (owns Apt 3), Akira Tanaka (owns none) — so the new per-owner `:check_list` sub-tab has zero / one / many cases out of the box. Idempotent via `find_or_create_by!`.
28
+ - **`pics/` shipped as 9 CC0 PNG placeholders** (640x480 pastel + apartment label) generated with ImageMagick. The folder is still `.gitignore`d, but the gem repo's working copy is now populated so `inline_forms create MyApp --example` picks them up without any extra steps. The old conference jpgs (`dsc*.jpg`) were removed.
29
+
30
+ ### Changed
31
+
32
+ - **`InlineForms::TurboTabsBuilder` now emits Foundation-friendly active markup.** The active tab label is rendered as an hrefless `<a aria-current="page" aria-selected="true">` (previously a plain `<span>`) so Foundation 6's `.tabs-title.is-active > a` / `[aria-selected='true']` rules in `_tabs.scss` style it identically to the inactive tabs. The active `<li>` keeps the `active_class` (now `is-active` in the example app via `tabs_tag active_class: "is-active"`), letting the bundled `foundation-tabs` mixin render the strip horizontally with the correct padding / colors instead of as a bare bullet list.
33
+ - **`app/views/owners/_owner_tabs.html.erb`** now passes `class: "tabs-title"` per tab + `active_class: "is-active"` and `data-tabs: ""` on the wrapping `<ul class="tabs">`, matching the Foundation 6 tabs DOM contract.
34
+ - **Owner#apartments is now a `:check_list`** (was `:associated`). The user picks from a checklist of *existing* apartments rather than building a new nested apartment under the owner; assignment is done via Rails' built-in `apartment_ids=` setter on the `has_many`, which sets / clears `apartments.owner_id` accordingly. `CheckListHelper` already works against `has_many`, no helper changes needed; only the form-element kind in `Owner#inline_forms_attribute_list` changes.
35
+ - **`README.rdoc`** documents the seed data, the `:check_list` change, and the new Foundation 6 tab markup.
36
+ - **`InlineForms::VERSION`** and **`InlineFormsInstaller::VERSION`** → `7.13.7`.
37
+
38
+ ### Removed
39
+
40
+ - **`SeedKonferenshaPhotos` migration** in generated apps. Replaced by `SeedExampleApartmentsAndOwners` (see above). The matching `pics/dsc*.jpg` conference photos were dropped from the gem repo.
41
+
42
+ ## [7.13.6] - 2026-05-20
43
+
44
+ ### Added
45
+
46
+ - **`InlineForms::TurboTabsBuilder`** (`lib/inline_forms/turbo_tabs_builder.rb`) — drop-in subclass of `TabsOnRails::Tabs::TabsBuilder` that threads a new `:link_options` item option through to the tab's `<a>` (upstream 3.0's `tab_for(tab, name, url_options, item_options = {})` only annotates the surrounding `<li>`). Turbo-shaped replacement for the historical `acesuares/tabs_on_rails` `update_remote_before_action` branch (which added `:remote => true` for UJS); now any `data: { turbo_frame: "…" }` (or other html option) survives the builder and lands on the link, enabling per-resource Turbo-Frame tab swaps without re-introducing the fork. Active-tab highlighting is unchanged (still driven by `set_tab` / `current_tab?`).
47
+ - **Example app `Owner` model + per-owner sub-tabs** (`installer_core.rb`, `lib/installer_templates/example_app_views/owners/`):
48
+ - `rails g inline_forms Owner name:string birthdate:date address:string city:string country:string apartments:has_many apartments:associated _enabled:yes _presentation:'#{name}'`
49
+ - Migration `add_owner_to_apartments` adds `owner_id` (nullable FK).
50
+ - `Apartment` gains `belongs_to :owner, optional: true` and a leading `[ :owner, "owner", :dropdown ]` entry in `inline_forms_attribute_list`.
51
+ - `OwnersController#show` is overridden to render `owners/show_with_tabs.html.erb` with one of two attribute subsets driven by `params[:tab]`: `naw` (name, birthdate, address, city, country) or `apartments` (name + the associated apartments list). `name` appears on both tabs by design.
52
+ - `app/views/owners/_owner_tabs.html.erb` uses `tabs_tag builder: InlineForms::TurboTabsBuilder` so each tab link gets `data-turbo-frame="<row frame>"` and switching tabs is a single Turbo partial swap. Field-level inline edit, cancel and close still delegate to the stock controller flow via `super`.
53
+ - Example app header (`example_app_views/inline_forms/_header.html.erb`) surfaces an **Owners** link in the More menu.
54
+
55
+ ### Fixed
56
+
57
+ - **`InlineForms::FormElements::DropdownHelper#dropdown_update` no longer raises `NoMethodError` on top-level POSTs that omit the wrapper key.** Inline edit posts the value under `params[:_<model>][:<attr>_id]`; top-level create flows that bypass that wrapper (existing integration tests posting `{name:, title:}` directly) used to blow up on `nil[:owner_id]` as soon as the attribute list grew a `:dropdown` entry (e.g. the new `[ :owner, :dropdown ]` on `Apartment`). The helper now treats a missing wrapper as "do not touch the foreign key" and leaves the attribute alone, matching the intent of `dropdown_show` / inline edit.
58
+
59
+ ### Changed
60
+
61
+ - **`README.rdoc`** documents the new `Owner` model and the `InlineForms::TurboTabsBuilder` pattern (the per-resource tab strip is the only piece of `tabs_on_rails` that benefits from the builder; the rest of inline_forms still uses `set_tab` only).
62
+ - **`InlineForms::VERSION`** and **`InlineFormsInstaller::VERSION`** → `7.13.6`.
63
+
7
64
  ## [7.13.5] - 2026-05-19
8
65
 
9
66
  ### Changed
data/README.rdoc CHANGED
@@ -26,6 +26,18 @@ If you want to install the example application:
26
26
 
27
27
  Then point your browser to http://localhost:3000/apartments and log in with admin@example.com / admin999. The example also adds integration and model tests; run +bundle exec rails test+ in +MyApp+, then start the server with +bundle exec rails s+ when you want the UI.
28
28
 
29
+ The example app now ships three models:
30
+
31
+ * +Apartment+ — top-level resource at +/apartments+ (root).
32
+ * +Photo+ — nested under Apartment (+has_many+ / +belongs_to+).
33
+ * +Owner+ — top-level resource at +/owners+. An Owner +has_many+ Apartments; an Apartment +belongs_to+ a single (optional) Owner. The Owner detail panel at +/owners/:id+ ships two sub-tabs, +NAW+ (name, birthdate, address, city, country) and +Apartments+ (name + the owned-apartments checklist). +name+ deliberately appears on both tabs.
34
+
35
+ On the +Apartments+ tab, +Owner#apartments+ is rendered as a +:check_list+ of *existing* apartments rather than the default +:associated+ "build new nested row" panel, so the user re-assigns the +apartments.owner_id+ FK by ticking checkboxes (using Rails' built-in +apartment_ids=+ setter on +has_many+).
36
+
37
+ The tabs are wired with +tabs_on_rails+ (+set_tab+ / +current_tab?+) and +InlineForms::TurboTabsBuilder+, a small subclass of +TabsOnRails::Tabs::TabsBuilder+ that threads HTML options through to the tab's +<a>+ (upstream 3.0 can only annotate the +<li>+). Each tab link carries +data-turbo-frame+ targeting the surrounding row +<turbo-frame>+, so switching tabs is a single Turbo partial swap — no UJS, no +data-remote+. The active tab is emitted as an hrefless +<a aria-current="page" aria-selected="true">+ inside +<li class="tabs-title is-active">+ so Foundation 6's tabs CSS (+.tabs-title.is-active > a+ / +[aria-selected='true']+) styles it without needing custom overrides.
38
+
39
+ The example app also seeds three apartments (+Apt 1+, +Apt 2+, +Apt 3+, each with three CC0 placeholder photos from the gem's +pics/+ folder) and three owners — +Maria Martinez+ (owns +Apt 1+ + +Apt 2+), +Jean-Pierre Dupont+ (owns +Apt 3+), and +Akira Tanaka+ (owns none) — so the +has_many+ panel has at least one zero / one / many case to click through.
40
+
29
41
  You can install the example application manually if you like:
30
42
 
31
43
  inline_forms create MyApp
@@ -33,10 +45,198 @@ You can install the example application manually if you like:
33
45
  rails g inline_forms Picture name:string caption:string image:image_field description:plain_text apartment:belongs_to _presentation:'#{name}'
34
46
  rails generate uploader Image
35
47
  rails g inline_forms Apartment name:string title:string description:rich_text pictures:has_many pictures:associated _enabled:yes _presentation:'#{name}'
48
+ rails g inline_forms Owner name:string birthdate:date address:string city:string country:string apartments:has_many apartments:associated _enabled:yes _presentation:'#{name}'
49
+ # Add the apartments→owner FK by hand:
50
+ rails g migration AddOwnerToApartments owner:references
51
+ # Then in app/models/apartment.rb, add (under has_paper_trail):
52
+ # belongs_to :owner, optional: true
53
+ # and prepend `[ :owner, "owner", :dropdown ],` to inline_forms_attribute_list.
36
54
  bundle exec rake db:migrate
37
55
  rails s
38
56
 
39
- Then point your browser to http://localhost:3000/apartments and log in with admin@example.com / admin999
57
+ Then point your browser to http://localhost:3000/apartments and log in with admin@example.com / admin999. Owners live at http://localhost:3000/owners and demonstrate the per-resource Turbo tabs.
58
+
59
+ == Per-resource Turbo tabs (+InlineForms::TurboTabsBuilder+)
60
+
61
+ Upstream +tabs_on_rails 3.0+ (+TabsOnRails::Tabs::TabsBuilder#tab_for(tab, name, url_options, item_options = {})+) only applies the 4th argument to the +<li>+ wrapper; nothing is forwarded to the +<a>+. That used to be fine under Rails UJS (every link with +data-remote="true"+ was hijacked into an XHR regardless), but Turbo needs the data attribute on the +<a>+ itself (typically +data-turbo-frame="..."+).
62
+
63
+ The old +acesuares/tabs_on_rails+ fork (+update_remote_before_action+) patched +tab_for+ to thread html options into +link_to+. That fork was retired in 7.13.5; +InlineForms::TurboTabsBuilder+ is its Turbo-shaped replacement. It accepts a new +:link_options+ key on the per-tab call and forwards it to +link_to+:
64
+
65
+ <%= tabs_tag builder: InlineForms::TurboTabsBuilder,
66
+ active_class: "is-active",
67
+ open_tabs: { class: "tabs owner_tabs",
68
+ id: "owner_#{@object.id}_tabs",
69
+ "data-tabs": "" } do |tab| %>
70
+ <%= tab.naw "NAW",
71
+ owner_path(@object, tab: :naw, update: @update_span),
72
+ class: "tabs-title",
73
+ link_options: { data: { turbo_frame: @update_span } } %>
74
+ <%= tab.apartments "Apartments",
75
+ owner_path(@object, tab: :apartments, update: @update_span),
76
+ class: "tabs-title",
77
+ link_options: { data: { turbo_frame: @update_span } } %>
78
+ <% end %>
79
+
80
+ Active-tab highlighting is unchanged from upstream (still driven by +set_tab :foo+ / +current_tab?+); the controller picks which attribute subset to render and just calls +render "owners/show_with_tabs"+. See +app/controllers/owners_controller.rb+ and +app/views/owners/+ in a freshly generated +--example+ app for the full pattern.
81
+
82
+ === Where to put the +tabs_tag+ block (four patterns)
83
+
84
+ +tabs_on_rails+ and +InlineForms::TurboTabsBuilder+ don't care where you call +tabs_tag+ — they just produce the +<ul class="tabs">+ wherever you put the block. The example app's split into +show_with_tabs.html.erb+ + +_owner_tabs.html.erb+ is a stylistic choice; below are the four common shapes, from most-inline to most-decoupled. Pick whichever fits your app:
85
+
86
+ 1. *Inlined in the show view* — drop the +tabs_tag+ block straight into +app/views/<resource>/show_with_tabs.html.erb+ (or similar) and skip the partial entirely.
87
+
88
+ <%# app/views/owners/show_with_tabs.html.erb %>
89
+ <turbo-frame id="<%= @update_span %>">
90
+ <%= tabs_tag builder: InlineForms::TurboTabsBuilder,
91
+ active_class: "is-active",
92
+ open_tabs: { class: "tabs", id: "owner_#{@object.id}_tabs",
93
+ "data-tabs": "" } do |tab| %>
94
+ <%= tab.naw "NAW", owner_path(@object, tab: :naw, update: @update_span),
95
+ class: "tabs-title",
96
+ link_options: { data: { turbo_frame: @update_span } } %>
97
+ <%# ... more tabs ... %>
98
+ <% end %>
99
+ <%= render partial: "inline_forms/show" %>
100
+ </turbo-frame>
101
+
102
+ Best when the strip is one-off and you won't reuse it.
103
+
104
+ 2. *Dedicated tab-strip partial* (what the +--example+ app does). Keep the show view tiny and move the strip into +app/views/<resource>/_<resource>_tabs.html.erb+. The +inline_forms+ example uses this so the +tabs_tag+ block can be iterated over a +OWNER_TABS+ constant:
105
+
106
+ <%# app/views/owners/_owner_tabs.html.erb %>
107
+ <%= tabs_tag builder: InlineForms::TurboTabsBuilder,
108
+ active_class: "is-active",
109
+ open_tabs: { class: "tabs", id: "owner_#{@object.id}_tabs",
110
+ "data-tabs": "" } do |tab| %>
111
+ <% (@inline_forms_owner_tabs || OwnersController::OWNER_TABS).each do |t| %>
112
+ <%= tab.send(t, t("owner_tabs.#{t}", default: t.titleize),
113
+ owner_path(@object, tab: t, update: @update_span),
114
+ class: "tabs-title",
115
+ link_options: { data: { turbo_frame: @update_span } }) %>
116
+ <% end %>
117
+ <% end %>
118
+
119
+ <%# app/views/owners/show_with_tabs.html.erb %>
120
+ <turbo-frame id="<%= @update_span %>">
121
+ <%= render partial: "owners/_owner_tabs" %>
122
+ <%= render partial: "inline_forms/show" %>
123
+ </turbo-frame>
124
+
125
+ Best when the same tab strip needs to appear above several views (e.g. +show+, +edit+, custom report) and the controller drives the list of tabs.
126
+
127
+ 3. *Helper-driven, reusable across resources* — extract the +tabs_tag+ call into a view helper (e.g. +InlineFormsTabsHelper#inline_forms_turbo_tabs_for+) so multiple resources can share the same strip with one line:
128
+
129
+ # app/helpers/inline_forms_tabs_helper.rb
130
+ module InlineFormsTabsHelper
131
+ def inline_forms_turbo_tabs_for(object, tabs, update:, i18n_scope: nil)
132
+ tabs_tag builder: InlineForms::TurboTabsBuilder,
133
+ active_class: "is-active",
134
+ open_tabs: { class: "tabs", id: "#{object.class.name.underscore}_#{object.id}_tabs",
135
+ "data-tabs": "" } do |tab|
136
+ tabs.each do |t|
137
+ label = t("#{i18n_scope}.#{t}", default: t.to_s.titleize) if i18n_scope
138
+ label ||= t.to_s.titleize
139
+ concat tab.send(t, label,
140
+ polymorphic_path(object, tab: t, update: update),
141
+ class: "tabs-title",
142
+ link_options: { data: { turbo_frame: update } })
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ <%# in any show view %>
149
+ <%= inline_forms_turbo_tabs_for(@object, OwnersController::OWNER_TABS,
150
+ update: @update_span,
151
+ i18n_scope: "owner_tabs") %>
152
+
153
+ Best when you have several resources that all need a per-row tab strip and you don't want to duplicate the +tabs_tag+ boilerplate.
154
+
155
+ 4. *One partial per tab content* — keep the strip partial (option 2), but make the show view +render "owners/tabs/#{params[:tab]}"+ and ship a separate +_naw.html.erb+ / +_apartments.html.erb+ per tab. Each tab partial owns its own markup (custom forms, charts, lists of foreign objects, ...) instead of going through +inline_forms/_show+:
156
+
157
+ <%# app/views/owners/show_with_tabs.html.erb %>
158
+ <turbo-frame id="<%= @update_span %>">
159
+ <%= render partial: "owners/_owner_tabs" %>
160
+ <%= render "owners/tabs/#{params[:tab].presence || 'naw'}" %>
161
+ </turbo-frame>
162
+
163
+ <%# app/views/owners/tabs/_naw.html.erb %>
164
+ <%= render partial: "inline_forms/show" %> <%# stock inline_forms behaviour %>
165
+
166
+ <%# app/views/owners/tabs/_apartments.html.erb %>
167
+ <h3>Owned apartments (<%= @object.apartments.size %>)</h3>
168
+ <ul>
169
+ <% @object.apartments.order(:name).each do |apt| %>
170
+ <li><%= link_to apt.name, apartment_path(apt) %> &mdash; <%= apt.title %></li>
171
+ <% end %>
172
+ </ul>
173
+ <%# ... or a chart, an upload form, an external API widget, anything ... %>
174
+
175
+ Best when tabs need wildly different markup that the inline_forms attribute-list shape can't express (custom dashboards, mixed-resource pages, embedded reports).
176
+
177
+ 5. *Grouped tab strips* — render *two or more* +tabs_tag+ blocks side-by-side (or stacked with a separator) when the resource has logically distinct groups of tabs that share the same +set_tab+ machinery but should be visually separated. Common example: an "info" group (name, contact, notes) and a "process" group (intake, assessment, plan) on a +Client+ detail page:
178
+
179
+ # app/controllers/clients_controller.rb
180
+ class ClientsController < InlineFormsController
181
+ set_tab :client
182
+ INFO_TABS = %w[naw contact notes].freeze
183
+ PROCESS_TABS = %w[intake assessment plan].freeze
184
+ ALL_TABS = (INFO_TABS + PROCESS_TABS).freeze
185
+ TAB_FIELDS = {
186
+ "naw" => %i[name birthdate address city country],
187
+ "contact" => %i[name email phone],
188
+ "intake" => %i[name intake_date intake_notes],
189
+ # ... one entry per tab; `name` repeated where it should appear
190
+ }.freeze
191
+
192
+ def show
193
+ return super if params[:form_element] || params[:attribute] || params[:close]
194
+ @object = Client.find(params[:id])
195
+ @update_span = params[:update].presence || "client_#{@object.id}"
196
+ tab = ALL_TABS.include?(params[:tab].to_s) ? params[:tab].to_s : ALL_TABS.first
197
+ set_tab tab.to_sym
198
+ @inline_forms_attribute_list = TAB_FIELDS.fetch(tab).map { |a|
199
+ @object.inline_forms_attribute_list.find { |attr, _, _| attr == a }
200
+ }
201
+ render "clients/show_with_tabs",
202
+ layout: turbo_frame_request? ? "turbo_rails/frame" : "inline_forms"
203
+ end
204
+ end
205
+
206
+ <%# app/views/clients/_client_tabs.html.erb -- two separate strips %>
207
+ <%= tabs_tag builder: InlineForms::TurboTabsBuilder,
208
+ active_class: "is-active",
209
+ open_tabs: { class: "tabs info_tabs",
210
+ id: "client_#{@object.id}_info_tabs",
211
+ "data-tabs": "" } do |tab| %>
212
+ <% ClientsController::INFO_TABS.each do |t| %>
213
+ <%= tab.send(t, t("client_tabs.#{t}", default: t.titleize),
214
+ client_path(@object, tab: t, update: @update_span),
215
+ class: "tabs-title",
216
+ link_options: { data: { turbo_frame: @update_span } }) %>
217
+ <% end %>
218
+ <% end %>
219
+
220
+ <hr class="tab_group_separator">
221
+
222
+ <%= tabs_tag builder: InlineForms::TurboTabsBuilder,
223
+ active_class: "is-active",
224
+ open_tabs: { class: "tabs process_tabs",
225
+ id: "client_#{@object.id}_process_tabs",
226
+ "data-tabs": "" } do |tab| %>
227
+ <% ClientsController::PROCESS_TABS.each do |t| %>
228
+ <%= tab.send(t, t("client_tabs.#{t}", default: t.titleize),
229
+ client_path(@object, tab: t, update: @update_span),
230
+ class: "tabs-title",
231
+ link_options: { data: { turbo_frame: @update_span } }) %>
232
+ <% end %>
233
+ <% end %>
234
+
235
+ Each +tabs_tag+ call is fully independent — different +open_tabs+ classes, different +id+s — but they share +set_tab+ / +current_tab?+, so the active highlight is always on the one tab whose name matches +params[:tab]+, regardless of which strip it sits in. Best when tabs fall into clearly distinct categories on the same page (info vs. workflow, read-only vs. write, primary vs. admin) and you want CSS / spacing control between the groups.
236
+
237
+ The +--example+ app uses option *2* because the only per-tab difference is which attribute subset to render, and +inline_forms/_show+ already drives off +@inline_forms_attribute_list+ — so a single filter in +OwnersController#show+ is enough and a per-tab partial would be over-engineering. For real apps with heterogeneous tabs, option *4* is usually a better fit; option *5* layers on top of any of the others when you need visual grouping.
238
+
239
+ In every case the Turbo wiring is the same: +link_options: { data: { turbo_frame: @update_span } }+ on the +<a>+, surrounding +<turbo-frame id="<%= @update_span %>">+ in the show view, and a controller that picks the active tab via +set_tab+ + +params[:tab]+. The +InlineForms::TurboTabsBuilder+ choice is independent of which partial layout you adopt.
40
240
 
41
241
  == Generated application +rails-i18n+
42
242
 
@@ -42,8 +42,16 @@ module InlineForms
42
42
  end
43
43
 
44
44
  def dropdown_update(object, attribute)
45
+ # Inline edit posts the value under the leading-underscore wrapper key
46
+ # (`params[:_apartment][:owner_id]`); top-level create through a
47
+ # bypassing form (e.g. integration tests posting `{name:, title:}`
48
+ # directly) may not include that wrapper at all. Treat a missing
49
+ # wrapper as "do not touch the foreign key" so unrelated attribute
50
+ # walks do not raise NoMethodError on nil[:owner_id].
51
+ scope = params[('_' + object.class.to_s.underscore).to_sym]
52
+ return if scope.blank?
45
53
  foreign_key = object.class.reflect_on_association(attribute.to_sym).options[:foreign_key] || attribute.to_s.foreign_key.to_sym
46
- object[foreign_key] = params[('_' + object.class.to_s.underscore).to_sym][attribute.to_s.foreign_key.to_sym]
54
+ object[foreign_key] = scope[attribute.to_s.foreign_key.to_sym]
47
55
  end
48
56
 
49
57
  def dropdown_info(object, attribute)
@@ -0,0 +1,63 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require "tabs_on_rails"
3
+
4
+ module InlineForms
5
+ # Tabs_on_rails 3.0 (weppos, RubyGems) only threads the 4th argument of
6
+ # `tab_for` into the surrounding `<li>` element; it has no way of putting
7
+ # html attributes on the actual `<a>`. That made the gem fine for the
8
+ # old UJS path (links did not need any extra data attributes — UJS would
9
+ # turn anything with `data-remote="true"` into AJAX) but useless under
10
+ # Turbo, where partial-swap behavior requires `data-turbo-frame="…"` (or
11
+ # similar) on the link itself.
12
+ #
13
+ # The historical workaround was the `acesuares/tabs_on_rails` fork
14
+ # (`update_remote_before_action`), which patched `tab_for` to thread
15
+ # html options into `link_to`. That fork was dropped in 7.13.5; this
16
+ # builder is its Turbo-shaped replacement.
17
+ #
18
+ # Usage:
19
+ #
20
+ # <%= tabs_tag builder: InlineForms::TurboTabsBuilder,
21
+ # open_tabs: { class: "owner_tabs", id: "owner_#{@object.id}_tabs" } do |tab| %>
22
+ # <%= tab.naw "NAW",
23
+ # owner_path(@object, tab: :naw, update: @update_span),
24
+ # link_options: { data: { turbo_frame: @update_span } } %>
25
+ # <%= tab.apartments "Apartments",
26
+ # owner_path(@object, tab: :apartments, update: @update_span),
27
+ # link_options: { data: { turbo_frame: @update_span } } %>
28
+ # <% end %>
29
+ #
30
+ # `link_options:` is consumed by the builder and forwarded to `link_to`;
31
+ # everything else still applies to the `<li>` exactly like the upstream
32
+ # `TabsBuilder`. Active-tab highlighting still uses tabs_on_rails'
33
+ # `current_tab?` (driven by controller `set_tab :foo`).
34
+ class TurboTabsBuilder < TabsOnRails::Tabs::TabsBuilder
35
+ def tab_for(tab, name, url_options, item_options = {})
36
+ link_options = item_options.delete(:link_options) || {}
37
+ active = current_tab?(tab)
38
+
39
+ if active
40
+ active_class = @options[:active_class] || "current"
41
+ existing = item_options[:class].to_s.split(/\s+/).reject(&:empty?)
42
+ item_options[:class] = (existing + [active_class]).uniq.join(" ")
43
+ end
44
+
45
+ # The active label is rendered as an `<a>` *without* an href so that
46
+ # CSS frameworks (Foundation 6, Bootstrap, ...) that style
47
+ # `.tabs-title.is-active > a` (or use `[aria-selected='true']`) pick
48
+ # it up just like the inactive tabs. Skipping the href keeps the
49
+ # active tab non-clickable, and `aria-current="page"` advertises
50
+ # the selection state to assistive tech / Foundation's CSS.
51
+ content = if active
52
+ @context.content_tag(:a, name,
53
+ link_options.merge("aria-current" => "page",
54
+ "aria-selected" => "true"))
55
+ else
56
+ @context.link_to(name, url_options,
57
+ link_options.merge("aria-selected" => "false"))
58
+ end
59
+
60
+ @context.content_tag(:li, content, item_options)
61
+ end
62
+ end
63
+ end
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module InlineForms
3
- VERSION = "7.13.5"
3
+ VERSION = "7.13.8"
4
4
  end
data/lib/inline_forms.rb CHANGED
@@ -4,6 +4,7 @@ require "inline_forms/form_element_from_callee"
4
4
  require "inline_forms/archived_form_elements"
5
5
  require "inline_forms/form_element_registry"
6
6
  require "inline_forms/form_elements"
7
+ require "inline_forms/turbo_tabs_builder"
7
8
  # InlineForms is a Rails Engine that let you setup an admin interface quick and
8
9
  # easy. Please install it as a gem or include it in your Gemfile.
9
10
  module InlineForms
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inline_forms
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.13.5
4
+ version: 7.13.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ace Suares
@@ -555,6 +555,7 @@ files:
555
555
  - lib/inline_forms/form_elements/time_helper.rb
556
556
  - lib/inline_forms/gem_files.rb
557
557
  - lib/inline_forms/translation_record.rb
558
+ - lib/inline_forms/turbo_tabs_builder.rb
558
559
  - lib/inline_forms/version.rb
559
560
  - lib/locales/inline_forms.en.yml
560
561
  - lib/locales/inline_forms.nl.yml