Module: Plutonium::Definition::FormLayout

Extended by:
ActiveSupport::Concern
Included in:
Base, Interaction::Base
Defined in:
lib/plutonium/definition/form_layout.rb

Overview

Declarative form sectioning. Mixed into both resource definitions and interactions (mirrors StructuredInputs). The layout references field KEYS only and carries section-level options; per-field config stays on ‘input`.

Examples:

form_layout do
  section :identity, :name, :email, label: "Your identification"
  section :address, :street, :city, collapsible: true, columns: 2,
    condition: -> { object.requires_address? }
  ungrouped label: "Other"
end

Defined Under Namespace

Classes: Builder, ResolvedSection, Section

Constant Summary collapse

UNGROUPED_KEY =
:ungrouped

Instance Method Summary collapse

Instance Method Details

#defined_form_layoutObject

Instance access — the form render path holds a definition/interaction instance (mirrors the defineable_prop convention).



95
96
97
# File 'lib/plutonium/definition/form_layout.rb', line 95

def defined_form_layout
  self.class.defined_form_layout
end

#resolve_form_sections(resource_fields) ⇒ Object

Resolve the policy-filtered field list into ordered ResolvedSections. Returns nil when no layout is declared (caller falls back to one grid).



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/plutonium/definition/form_layout.rb', line 101

def resolve_form_sections(resource_fields)
  layout = defined_form_layout
  return nil unless layout

  resource_fields = resource_fields.map(&:to_sym)
  known = resource_fields.to_set

  # First-section-wins assignment: each field is claimed by the first
  # section that lists it. A field a section lists but that isn't in the
  # currently-permitted set (policy, per-action, entity scoping, nesting)
  # is simply skipped — it never renders and is never an error.
  owner = {}
  layout.each do |section|
    next if section.ungrouped?
    section.fields.each do |f|
      owner[f] ||= section.key if known.include?(f)
    end
  end
  leftovers = resource_fields.reject { |f| owner.key?(f) }

  resolved = layout.map do |section|
    fields =
      if section.ungrouped?
        leftovers
      else
        section.fields.select { |f| owner[f] == section.key }
      end
    ResolvedSection.new(section:, fields:)
  end

  unless layout.any?(&:ungrouped?)
    implicit = ResolvedSection.new(
      section: Section.new(key: UNGROUPED_KEY, fields: [].freeze, options: {}.freeze),
      fields: leftovers
    )
    resolved.push(implicit)
  end

  resolved
end