Module: PhlexForms::Builder

Included in:
Forms::FieldsForBuilder, Forms::Form
Defined in:
lib/phlex_forms/builder.rb

Overview

The shared form-builder surface, included by Forms::Form, Forms::Field, and Forms::FieldsForBuilder so the three no longer copy-paste the API three times.

Two layers:

* The Control-first primary verb `field` — renders label + input +
error/hint in one call, inferring the input type and the `required` flag
from the model.

* Lower-level escape hatches (`Input`, `Select`, `Textarea`, `Checkbox`,
`Toggle`, `FileInput`, `Hidden`, `Label`, `Control`, `submit`) with the
same signatures they had before extraction, for when a caller needs full
control (custom Stimulus wiring, bespoke layout, ...).

Hosts mix this in and provide: render, model, scope, errors, and a field(name)-style field_object that returns a Forms::Field for a name.

Constant Summary collapse

INPUT_TYPE_MODIFIERS =

Positional symbols that name an input type (so f.Input(:x, :email) works).

%i[
  text email password tel url search number date time datetime
  color range file hidden month week
].freeze
INPUT_TYPE_INFERENCE =

Attribute-name -> input type inference. One canonical map (previously duplicated and divergent across Form and Field).

{
  email: :email,
  password: :password, password_confirmation: :password,
  current_password: :password, new_password: :password,
  new_password_confirmation: :password,
  phone: :tel, telephone: :tel, tel: :tel, mobile: :tel,
  url: :url, website: :url, homepage: :url,
  search: :search, query: :search,
  date: :date, birthday: :date, birthdate: :date, born_on: :date,
  started_at: :date, ended_at: :date,
  time: :time,
  datetime: :datetime, timestamp: :datetime,
  color: :color, colour: :color
}.freeze

Instance Method Summary collapse

Instance Method Details

#Checkbox(name, *modifiers, **options) ⇒ Object



99
100
101
102
# File 'lib/phlex_forms/builder.rb', line 99

def Checkbox(name, *modifiers, **options)
  fo = field_object(name)
  render fo.checkbox(*modifiers, **fo.apply_validations(options))
end

#Control(name) ⇒ Object



126
127
128
# File 'lib/phlex_forms/builder.rb', line 126

def Control(name, **, &)
  render field_object(name).control(**, &)
end

#field(name, *modifiers, label: nil, hint: nil, as: nil, required: nil, choices: nil, **options) ⇒ Object

Render a complete field: label + control-wrapped input + error/hint.

f.field :email                          # type=email, label auto, required inferred
f.field :role, as: :select, choices: roles
f.field :bio, as: :textarea, rows: 6
f.field :accept, as: :toggle
f.field :name, :primary, label: "Full name", hint: "As on your ID"

Options:

label:    label text (defaults to the model's humanized attribute name;
        pass false to omit the label)
hint:     help text shown when there is no error
as:       override the rendered control (:select, :textarea, :toggle,
        :checkbox, :file, :hidden, and text-like types)
required: force the required flag (otherwise inferred from validations)
choices:  choices for :select

Remaining positional modifiers/keywords pass through to the inner input.



65
66
67
68
69
70
71
72
73
74
75
# File 'lib/phlex_forms/builder.rb', line 65

def field(name, *modifiers, label: nil, hint: nil, as: nil, required: nil,
          choices: nil, **options)
  fo = field_object(name)
  req = required.nil? ? fo.required? : required
  label_text = label == false ? nil : (label || fo.field_label)
  options = fo.apply_validations(options)

  render fo.control(label: label_text, hint:, required: req) do
    render_field_input(fo, name, as, modifiers, choices:, required: req, **options)
  end
end

#FileInput(name, *modifiers, **options) ⇒ Object



113
114
115
116
# File 'lib/phlex_forms/builder.rb', line 113

def FileInput(name, *modifiers, **options)
  fo = field_object(name)
  render fo.file(*modifiers, **fo.apply_validations(options))
end

#Hidden(name) ⇒ Object



118
119
120
# File 'lib/phlex_forms/builder.rb', line 118

def Hidden(name, **)
  render field_object(name).hidden(**)
end

#Input(name, *modifiers, **options) ⇒ Object


Escape-hatch component API (stable signatures)



81
82
83
84
85
86
87
# File 'lib/phlex_forms/builder.rb', line 81

def Input(name, *modifiers, **options)
  fo = field_object(name)
  type = resolve_input_type(name, modifiers)
  type = :"datetime-local" if type == :datetime
  remaining = modifiers - INPUT_TYPE_MODIFIERS
  render fo.input(*remaining, type:, **fo.apply_validations(options))
end

#Label(name, text = nil, *modifiers) ⇒ Object



122
123
124
# File 'lib/phlex_forms/builder.rb', line 122

def Label(name, text = nil, *modifiers, **, &)
  render field_object(name).label(text, *modifiers, **, &)
end

#Radio(name, value, *modifiers) ⇒ Object



104
105
106
# File 'lib/phlex_forms/builder.rb', line 104

def Radio(name, value, *modifiers, **)
  render field_object(name).radio(value, *modifiers, **)
end

#Select(name, *modifiers, choices: nil, **options) ⇒ Object



89
90
91
92
# File 'lib/phlex_forms/builder.rb', line 89

def Select(name, *modifiers, choices: nil, **options)
  fo = field_object(name)
  render fo.choices_select(choices, *modifiers, **fo.apply_validations(options))
end

#Textarea(name, *modifiers, **options) ⇒ Object



94
95
96
97
# File 'lib/phlex_forms/builder.rb', line 94

def Textarea(name, *modifiers, **options)
  fo = field_object(name)
  render fo.textarea(*modifiers, **fo.apply_validations(options))
end

#Toggle(name, *modifiers, **options) ⇒ Object



108
109
110
111
# File 'lib/phlex_forms/builder.rb', line 108

def Toggle(name, *modifiers, **options)
  fo = field_object(name)
  render fo.toggle(*modifiers, **fo.apply_validations(options))
end