plutonium 0.59.0 → 0.60.0
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-auth/SKILL.md +7 -1
- data/CHANGELOG.md +6 -0
- data/app/assets/plutonium.css +1 -1
- data/docs/reference/auth/accounts.md +7 -0
- data/docs/reference/resource/definition.md +129 -0
- data/docs/reference/ui/forms.md +51 -21
- data/docs/superpowers/plans/2026-06-14-form-sectioning.md +917 -0
- data/docs/superpowers/plans/2026-06-14-form-sectioning.md.tasks.json +40 -0
- data/docs/superpowers/specs/2026-06-14-form-sectioning-design.md +237 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/rodauth/admin_generator.rb +5 -2
- data/lib/generators/pu/rodauth/migration_generator.rb +1 -1
- data/lib/generators/pu/rodauth/templates/app/interactions/resend_admin_interaction.rb.tt +18 -0
- data/lib/generators/pu/rodauth/views_generator.rb +1 -1
- data/lib/plutonium/definition/base.rb +1 -0
- data/lib/plutonium/definition/form_layout.rb +144 -0
- data/lib/plutonium/interaction/base.rb +1 -0
- data/lib/plutonium/package/engine.rb +17 -7
- data/lib/plutonium/ui/form/components/section.rb +58 -0
- data/lib/plutonium/ui/form/resource.rb +85 -7
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- data/src/css/slim_select.css +11 -2
- metadata +8 -2
|
@@ -98,11 +98,81 @@ module Plutonium
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def render_fields
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
sections = resolve_form_layout
|
|
102
|
+
if sections.nil?
|
|
103
|
+
fields_wrapper {
|
|
104
|
+
resource_fields.each { |name| render_resource_field name }
|
|
104
105
|
}
|
|
105
|
-
|
|
106
|
+
else
|
|
107
|
+
sections.each { |rs| render_form_section(rs) }
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Resolve the whole form layout for THIS render, in one pass: drop
|
|
112
|
+
# condition-hidden sections and evaluate any proc-valued options, all in
|
|
113
|
+
# the form instance context (where `object`, `current_user`, `params` and
|
|
114
|
+
# helpers live — same context as input/section `condition:`). Returns nil
|
|
115
|
+
# when no form_layout is declared (caller falls back to a single grid).
|
|
116
|
+
def resolve_form_layout
|
|
117
|
+
sections = resource_definition.resolve_form_sections(resource_fields)
|
|
118
|
+
return nil if sections.nil?
|
|
119
|
+
|
|
120
|
+
sections.filter_map do |resolved|
|
|
121
|
+
section = resolved.section
|
|
122
|
+
condition = section.condition
|
|
123
|
+
next if condition && !instance_exec(&condition)
|
|
124
|
+
|
|
125
|
+
# `columns` stays a validated literal; everything else may be a proc.
|
|
126
|
+
options = section.options.to_h do |key, value|
|
|
127
|
+
[key, (key != :condition && value.is_a?(Proc)) ? instance_exec(&value) : value]
|
|
128
|
+
end
|
|
129
|
+
Plutonium::Definition::FormLayout::ResolvedSection.new(
|
|
130
|
+
section: Plutonium::Definition::FormLayout::Section.new(
|
|
131
|
+
key: section.key, fields: section.fields, options: options.freeze
|
|
132
|
+
),
|
|
133
|
+
fields: resolved.fields
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Pure presentation — the section is already resolved (visible, options
|
|
139
|
+
# evaluated) by resolve_form_layout.
|
|
140
|
+
def render_form_section(resolved)
|
|
141
|
+
section = resolved.section
|
|
142
|
+
render Plutonium::UI::Form::Components::Section.new(
|
|
143
|
+
resolved, grid_class: section_grid_class(section.columns)
|
|
144
|
+
) do
|
|
145
|
+
# Inside a multi-column section, let fields flow into the grid cells
|
|
146
|
+
# instead of forcing each to span the full row (see col-span default
|
|
147
|
+
# in render_simple_resource_field).
|
|
148
|
+
previous = @section_columns
|
|
149
|
+
@section_columns = section.columns
|
|
150
|
+
begin
|
|
151
|
+
resolved.fields.each { |name| render_resource_field name }
|
|
152
|
+
ensure
|
|
153
|
+
@section_columns = previous
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# True while rendering fields inside a section that declared an explicit
|
|
159
|
+
# column count > 1. Such fields default to a single grid cell rather than
|
|
160
|
+
# `col-span-full`, so the section's grid actually lays out in columns.
|
|
161
|
+
def in_multi_column_section?
|
|
162
|
+
@section_columns.to_i > 1
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# nil → the form's default responsive grid; an Integer overrides columns.
|
|
166
|
+
def section_grid_class(columns)
|
|
167
|
+
return themed(:fields_wrapper, nil) if columns.nil?
|
|
168
|
+
|
|
169
|
+
base = "grid gap-6 grid-flow-row-dense grid-cols-1"
|
|
170
|
+
case columns.to_i
|
|
171
|
+
when 1 then base
|
|
172
|
+
when 2 then "#{base} md:grid-cols-2"
|
|
173
|
+
when 3 then "#{base} md:grid-cols-2 lg:grid-cols-3"
|
|
174
|
+
else "#{base} md:grid-cols-2 2xl:grid-cols-#{columns.to_i}"
|
|
175
|
+
end
|
|
106
176
|
end
|
|
107
177
|
|
|
108
178
|
def render_actions
|
|
@@ -232,10 +302,18 @@ module Plutonium
|
|
|
232
302
|
end
|
|
233
303
|
else
|
|
234
304
|
wrapper_options = input_options[:wrapper] || {}
|
|
305
|
+
# Only supply a default column span when the field hasn't declared its
|
|
306
|
+
# own (via `wrapper: {class: "col-span-..."}`). A field-level col-span
|
|
307
|
+
# ALWAYS wins — including inside a section with `columns:` — so authors
|
|
308
|
+
# can opt a single field back to full width in a multi-column section,
|
|
309
|
+
# or vice versa.
|
|
310
|
+
# TODO: remove the string check once theming supports class merges.
|
|
235
311
|
if !wrapper_options[:class] || !wrapper_options[:class].include?("col-span")
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
|
|
312
|
+
# In a multi-column section the field flows into a single grid cell
|
|
313
|
+
# (no col-span), so the declared `columns:` actually takes effect.
|
|
314
|
+
# Everywhere else fields span the full row.
|
|
315
|
+
default_span = in_multi_column_section? ? nil : "col-span-full"
|
|
316
|
+
wrapper_options[:class] = tokens(default_span, wrapper_options[:class])
|
|
239
317
|
end
|
|
240
318
|
|
|
241
319
|
render form.field(name, **field_options).wrapped(
|
data/lib/plutonium/version.rb
CHANGED
data/package.json
CHANGED
data/src/css/slim_select.css
CHANGED
|
@@ -43,9 +43,11 @@
|
|
|
43
43
|
border-radius: var(--pu-radius-md);
|
|
44
44
|
color: var(--pu-text);
|
|
45
45
|
}
|
|
46
|
+
|
|
46
47
|
.ss-main:hover {
|
|
47
48
|
border-color: var(--pu-border-strong);
|
|
48
49
|
}
|
|
50
|
+
|
|
49
51
|
.ss-main:focus,
|
|
50
52
|
.ss-main[aria-expanded="true"] {
|
|
51
53
|
border-color: var(--pu-input-focus-ring);
|
|
@@ -196,9 +198,11 @@
|
|
|
196
198
|
font-size: inherit;
|
|
197
199
|
line-height: inherit;
|
|
198
200
|
}
|
|
201
|
+
|
|
199
202
|
.ss-content .ss-search input::placeholder {
|
|
200
203
|
color: var(--pu-input-placeholder);
|
|
201
204
|
}
|
|
205
|
+
|
|
202
206
|
.ss-content .ss-search input:focus {
|
|
203
207
|
border-color: var(--pu-input-focus-ring);
|
|
204
208
|
box-shadow: 0 0 0 3px theme(colors.primary.500 / 15%);
|
|
@@ -225,7 +229,7 @@
|
|
|
225
229
|
|
|
226
230
|
/* List */
|
|
227
231
|
.ss-content .ss-list {
|
|
228
|
-
@apply flex-auto h-auto overflow-x-hidden overflow-y-auto;
|
|
232
|
+
@apply flex-auto h-auto overflow-x-hidden overflow-y-auto mb-2;
|
|
229
233
|
}
|
|
230
234
|
|
|
231
235
|
.ss-content .ss-list .ss-error {
|
|
@@ -345,10 +349,12 @@
|
|
|
345
349
|
.ss-main.ss-invalid {
|
|
346
350
|
@apply border-danger-500 bg-danger-50/50 text-danger-900;
|
|
347
351
|
}
|
|
352
|
+
|
|
348
353
|
.ss-main.ss-invalid:focus,
|
|
349
354
|
.ss-main.ss-invalid[aria-expanded="true"] {
|
|
350
355
|
box-shadow: 0 0 0 3px theme(colors.danger.500 / 15%);
|
|
351
356
|
}
|
|
357
|
+
|
|
352
358
|
.dark .ss-main.ss-invalid {
|
|
353
359
|
@apply bg-danger-950/20 border-danger-500/70 text-danger-200;
|
|
354
360
|
}
|
|
@@ -360,10 +366,12 @@
|
|
|
360
366
|
.ss-main.ss-valid {
|
|
361
367
|
@apply border-success-500 bg-success-50/50 text-success-900;
|
|
362
368
|
}
|
|
369
|
+
|
|
363
370
|
.ss-main.ss-valid:focus,
|
|
364
371
|
.ss-main.ss-valid[aria-expanded="true"] {
|
|
365
372
|
box-shadow: 0 0 0 3px theme(colors.success.500 / 15%);
|
|
366
373
|
}
|
|
374
|
+
|
|
367
375
|
.dark .ss-main.ss-valid {
|
|
368
376
|
@apply bg-success-950/20 border-success-500/70 text-success-200;
|
|
369
377
|
}
|
|
@@ -385,7 +393,8 @@
|
|
|
385
393
|
width: 100% !important;
|
|
386
394
|
border-radius: 0 !important;
|
|
387
395
|
margin: 0 !important;
|
|
388
|
-
pointer-events: none !important;
|
|
396
|
+
pointer-events: none !important;
|
|
397
|
+
/* Disabled by default */
|
|
389
398
|
}
|
|
390
399
|
|
|
391
400
|
/* When active (dropdown is expanded), enable pointer events */
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: plutonium
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.60.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Froelich
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-06-
|
|
10
|
+
date: 2026-06-14 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: zeitwerk
|
|
@@ -685,6 +685,8 @@ files:
|
|
|
685
685
|
- docs/superpowers/plans/2026-06-02-structured-inputs.md.tasks.json
|
|
686
686
|
- docs/superpowers/plans/2026-06-04-sqlite-tune-maintenance-generators.md
|
|
687
687
|
- docs/superpowers/plans/2026-06-04-sqlite-tune-maintenance-generators.md.tasks.json
|
|
688
|
+
- docs/superpowers/plans/2026-06-14-form-sectioning.md
|
|
689
|
+
- docs/superpowers/plans/2026-06-14-form-sectioning.md.tasks.json
|
|
688
690
|
- docs/superpowers/specs/2026-04-08-plutonium-skills-overhaul-design.md
|
|
689
691
|
- docs/superpowers/specs/2026-04-14-plutonium-testing-design.md
|
|
690
692
|
- docs/superpowers/specs/2026-05-07-ui-layout-overhaul-design.md
|
|
@@ -696,6 +698,7 @@ files:
|
|
|
696
698
|
- docs/superpowers/specs/2026-06-01-structured-inputs-design.md
|
|
697
699
|
- docs/superpowers/specs/2026-06-04-sqlite-tune-maintenance-generators-design.md
|
|
698
700
|
- docs/superpowers/specs/2026-06-12-export-csv-default-action-design.md
|
|
701
|
+
- docs/superpowers/specs/2026-06-14-form-sectioning-design.md
|
|
699
702
|
- esbuild.config.js
|
|
700
703
|
- exe/pug
|
|
701
704
|
- gemfiles/rails_7.gemfile
|
|
@@ -904,6 +907,7 @@ files:
|
|
|
904
907
|
- lib/generators/pu/rodauth/templates/app/controllers/plugin_controller.rb.tt
|
|
905
908
|
- lib/generators/pu/rodauth/templates/app/controllers/rodauth_controller.rb.tt
|
|
906
909
|
- lib/generators/pu/rodauth/templates/app/interactions/invite_admin_interaction.rb.tt
|
|
910
|
+
- lib/generators/pu/rodauth/templates/app/interactions/resend_admin_interaction.rb.tt
|
|
907
911
|
- lib/generators/pu/rodauth/templates/app/mailers/account_mailer.rb.tt
|
|
908
912
|
- lib/generators/pu/rodauth/templates/app/mailers/rodauth_mailer.rb.tt
|
|
909
913
|
- lib/generators/pu/rodauth/templates/app/models/account.rb.tt
|
|
@@ -995,6 +999,7 @@ files:
|
|
|
995
999
|
- lib/plutonium/definition/base.rb
|
|
996
1000
|
- lib/plutonium/definition/config_attr.rb
|
|
997
1001
|
- lib/plutonium/definition/defineable_props.rb
|
|
1002
|
+
- lib/plutonium/definition/form_layout.rb
|
|
998
1003
|
- lib/plutonium/definition/index_views.rb
|
|
999
1004
|
- lib/plutonium/definition/inheritable_config_attr.rb
|
|
1000
1005
|
- lib/plutonium/definition/metadata.rb
|
|
@@ -1131,6 +1136,7 @@ files:
|
|
|
1131
1136
|
- lib/plutonium/ui/form/components/json.rb
|
|
1132
1137
|
- lib/plutonium/ui/form/components/key_value_store.rb
|
|
1133
1138
|
- lib/plutonium/ui/form/components/resource_select.rb
|
|
1139
|
+
- lib/plutonium/ui/form/components/section.rb
|
|
1134
1140
|
- lib/plutonium/ui/form/components/secure_association.rb
|
|
1135
1141
|
- lib/plutonium/ui/form/components/secure_polymorphic_association.rb
|
|
1136
1142
|
- lib/plutonium/ui/form/components/sticky_footer.rb
|