plutonium 0.60.1 → 0.60.3
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 +4 -4
- data/.claude/skills/plutonium-resource/SKILL.md +24 -0
- data/.claude/skills/plutonium-ui/SKILL.md +31 -2
- data/CHANGELOG.md +15 -0
- data/app/assets/plutonium.css +1 -1
- data/docs/reference/resource/definition.md +7 -2
- data/docs/reference/ui/layouts.md +34 -5
- data/lib/plutonium/core/controller.rb +16 -2
- data/lib/plutonium/portal/engine.rb +11 -0
- data/lib/plutonium/ui/form/components/section.rb +23 -8
- data/lib/plutonium/ui/form/resource.rb +10 -0
- data/lib/plutonium/ui/layout/base.rb +6 -1
- data/lib/plutonium/ui/layout/resource_layout.rb +8 -2
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f3d9d716f53c4604a7c9189a74cdfd84ee880e5305860331a8a840fa8302111c
|
|
4
|
+
data.tar.gz: cd72d23c2c9d63ddf51db97d29834d7d49ec8a42babd1a9f7508eaa3b5a99630
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c93a955e88b5ab92c35f1ded962aa8a360eb3fae6a906ffef119493cc8d84327ab5e67fa4a00f225bb4c6560a191c1ebe61d0084b3b493377bb34b0781ce99bf
|
|
7
|
+
data.tar.gz: 6198cc3eb04146af99e9ac660463e7bb1a84885c09166fee4ef0508f3f552557459b81523649e9057f822ba922833ab2ac0d73a46a0101ac6bccde1ae8b89949
|
|
@@ -834,6 +834,30 @@ end
|
|
|
834
834
|
|
|
835
835
|
`modal:` is the default for framework `:new` / `:edit` *and* every interactive action on this definition. Per-action `modal:` / `size:` overrides win.
|
|
836
836
|
|
|
837
|
+
## Form Layout (`form_layout`)
|
|
838
|
+
|
|
839
|
+
Group form fields into sections **declaratively in the definition** — no `Form` subclass, no view code. Prefer this over hand-rolling a `section` helper in a custom `form_template`.
|
|
840
|
+
|
|
841
|
+
```ruby
|
|
842
|
+
class PostDefinition < ResourceDefinition
|
|
843
|
+
form_layout do
|
|
844
|
+
section :identity, :name, :email, label: "Identity", description: "Who this is"
|
|
845
|
+
section :address, :street, :city,
|
|
846
|
+
collapsible: true, collapsed: -> { object.persisted? }, columns: 2,
|
|
847
|
+
condition: -> { object.requires_address? } # hide the whole section as a unit
|
|
848
|
+
ungrouped label: "Other" # bucket for unlisted fields; position = where it renders
|
|
849
|
+
end
|
|
850
|
+
end
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
- **Layout references field KEYS only** — all per-field config (`as:`, `hint:`, blocks, per-field `condition:`) stays on `input`. Never duplicated here.
|
|
854
|
+
- **Options**: `label:`, `description:`, `collapsible:`, `collapsed:`, `columns:` (positive Integer, literal only), `condition:`. Every option except `columns:` may be a **proc** resolved at render in the form context (`object`, `current_user`, `params`, helpers).
|
|
855
|
+
- **Absent fields are skipped.** A key the section lists that isn't in the permitted set (policy, per-action, scoping, nesting, or a typo) is silently dropped — never an error. The same layout serves a richly-permitted `edit` and a minimal `new`.
|
|
856
|
+
- **🚨 Zero-field sections drop entirely** — no heading, no grid. So `+ New` (fewer permitted attributes) won't sprout empty headings. This checks *field presence only*; per-field `condition:` runs later, so to hide a whole section by state, gate it with the **section's own `condition:`**, not by hiding every field inside it.
|
|
857
|
+
- **Works on interactions too** (`Plutonium::Interaction::Base`) — groups `attribute` declarations. There `object` is the interaction instance; for record actions the record is `object.resource`.
|
|
858
|
+
|
|
859
|
+
Full DSL reference: [Resource › Definition › Form layout](/reference/resource/definition#form-layout).
|
|
860
|
+
|
|
837
861
|
## Metadata Panel (show page)
|
|
838
862
|
|
|
839
863
|
Declares fields rendered in the show page's right-side aside as label/value rows.
|
|
@@ -182,7 +182,21 @@ end
|
|
|
182
182
|
|
|
183
183
|
## Custom layouts
|
|
184
184
|
|
|
185
|
-
### Sectioned
|
|
185
|
+
### Sectioned — prefer the `form_layout` DSL
|
|
186
|
+
|
|
187
|
+
**For grouping fields into sections, don't hand-roll a `Form` subclass — declare `form_layout` in the definition.** It handles headings, descriptions, collapsible `<details>`, per-section `columns:`, `condition:`-based visibility, and **auto-drops sections that resolve to zero fields** (so `+ New` doesn't sprout empty headings). See [[plutonium-resource]] › Form Layout.
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
class PostDefinition < ResourceDefinition
|
|
191
|
+
form_layout do
|
|
192
|
+
section :basic, :title, :slug, label: "Basic"
|
|
193
|
+
section :publishing, :published_at, :category, label: "Publishing", columns: 2
|
|
194
|
+
ungrouped label: "Other"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Only drop to a custom `Form#form_template` when you need layout the DSL can't express (arbitrary wrapper markup, interleaved non-field content). The escape hatch:
|
|
186
200
|
|
|
187
201
|
```ruby
|
|
188
202
|
class Form < Form
|
|
@@ -211,6 +225,8 @@ class Form < Form
|
|
|
211
225
|
end
|
|
212
226
|
```
|
|
213
227
|
|
|
228
|
+
A hand-rolled `section` like this renders its heading unconditionally — that's exactly the empty-heading problem `form_layout` avoids. If you must hand-roll, guard empty sections yourself.
|
|
229
|
+
|
|
214
230
|
### Two-column
|
|
215
231
|
|
|
216
232
|
```ruby
|
|
@@ -506,7 +522,20 @@ Plutonium.configure do |config|
|
|
|
506
522
|
end
|
|
507
523
|
```
|
|
508
524
|
|
|
509
|
-
`:plain` keeps the Topbar but drops the icon rail.
|
|
525
|
+
`:plain` keeps the Topbar but drops the icon rail. **Shell resolves global → engine → controller**, each overriding the one above (`nil` falls through); read it with `controller.shell`:
|
|
526
|
+
|
|
527
|
+
```ruby
|
|
528
|
+
config.shell = :plain # 1. global default
|
|
529
|
+
# 2. per-engine — inside the engine's config.after_initialize (with scope_to_entity)
|
|
530
|
+
class CustomerPortal::Engine
|
|
531
|
+
config.after_initialize { shell :plain }
|
|
532
|
+
end
|
|
533
|
+
class DashboardController; shell :modern; end # 3. per-controller (overrides engine/global)
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
`shell` takes a symbol so the class body works too, but the generated engine already has a `config.after_initialize` block (home of `scope_to_entity`) — keep it there for consistency.
|
|
537
|
+
|
|
538
|
+
Alongside `shell`, the controller-only `rail` DSL flips just the rail (inherited `class_attribute`, so a portal opts in/out once in its concern) — `rail false` / `rail true`; `rail nil` (default) inherits the resolved shell, `rail?` reads the resolved value:
|
|
510
539
|
|
|
511
540
|
```ruby
|
|
512
541
|
module CustomerPortal::Concerns::Controller
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [0.60.3] - 2026-06-15
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- *(layout)* Drop form_layout sections that resolve to zero fields
|
|
6
|
+
- *(layout)* Refine form_layout section header styling
|
|
7
|
+
|
|
8
|
+
### 🚜 Refactor
|
|
9
|
+
|
|
10
|
+
- *(layout)* Move engine shell to Portal::Engine with live cascade
|
|
11
|
+
## [0.60.2] - 2026-06-15
|
|
12
|
+
|
|
13
|
+
### 🚀 Features
|
|
14
|
+
|
|
15
|
+
- *(layout)* Resolve shell across global, engine, and controller tiers
|
|
1
16
|
## [0.60.1] - 2026-06-15
|
|
2
17
|
|
|
3
18
|
### 🚀 Features
|