Module: LcpRuby::FormHelper

Includes:
AssociationOptionsBuilder
Defined in:
app/helpers/lcp_ruby/form_helper.rb

Constant Summary

Constants included from AssociationOptionsBuilder

AssociationOptionsBuilder::MAX_SELECT_OPTIONS

Instance Method Summary collapse

Instance Method Details

#render_form_input(form, field_name, input_type, field_config, field_def) ⇒ Object

Dispatches form rendering by input_type. Accepts any field_def implementing the duck-typed contract:

* Metadata::FieldDefinition — backed by an AR column
* LcpRuby::VirtualFields::VirtualField — synthetic field, no
  backing column (used by filter forms and other future
  consumers — wizards, virtual show pages, …)

Both implement:

#type, #enum?, #enum_value_names,
#enum_label_for(value, model_name:), #attachment_options.

See spec § 5 step 8 for the exhaustive callsite audit.



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'app/helpers/lcp_ruby/form_helper.rb', line 15

def render_form_input(form, field_name, input_type, field_config, field_def)
  input_options = field_config["input_options"] || {}

  case input_type.to_s
  when "text", "textarea", "rich_text_editor"
    opts = { placeholder: field_config["placeholder"] }
    opts[:rows] = input_options["rows"] if input_options["rows"]
    opts[:maxlength] = input_options["max_length"] if input_options["max_length"]
    if field_def&.type == "json"
      raw_value = form.object&.public_send(field_name)
      opts[:value] = pretty_print_json_value(raw_value) if raw_value.present?
    end
    if input_options["show_counter"] && input_options["max_length"]
      (:div, data: { controller: "lcp-char-counter" }) do
        form.text_area(field_name, **opts.compact.merge(
          data: { "lcp-char-counter-target": "input", action: "input->lcp-char-counter#update" }
        )) +
          (:span, "", class: "lcp-char-counter",
            data: { "lcp-char-counter-target": "display" })
      end
    else
      form.text_area(field_name, **opts.compact)
    end
  when "select"
    render_select_input(form, field_name, field_config, field_def)
  when "radio"
    render_radio_input(form, field_name, field_config, field_def)
  when "number"
    opts = { step: input_options["step"] || "any", placeholder: field_config["placeholder"] }
    opts[:min] = input_options["min"] if input_options["min"]
    opts[:max] = input_options["max"] if input_options["max"]
    form.number_field(field_name, **opts.compact)
  when "date_picker", "date"
    form.date_field(field_name)
  when "datetime"
    form.datetime_local_field(field_name)
  when "date_range"
    render_date_range(form, field_name, field_config)
  when "boolean", "checkbox"
    form.check_box(field_name)
  when "association_select"
    render_association_select(form, field_name, field_config)
  when "multi_select"
    render_multi_select(form, field_name, field_config, field_def)
  when "tree_select"
    render_tree_select(form, field_name, field_config)
  when "email"
    form.email_field(field_name,
      placeholder: field_config["placeholder"],
      autofocus: field_config["autofocus"])
  when "tel"
    form.telephone_field(field_name,
      placeholder: field_config["placeholder"],
      autofocus: field_config["autofocus"])
  when "url"
    form.url_field(field_name,
      placeholder: field_config["placeholder"],
      autofocus: field_config["autofocus"])
  when "enum"
    render_select_input(form, field_name, field_config, field_def)
  when "color"
    form.color_field(field_name)
  when "file_upload"
    render_file_upload_input(form, field_name, field_config, field_def)
  when "slider"
    render_slider_input(form, field_name, input_options)
  when "toggle"
    render_toggle_input(form, field_name)
  when "rating"
    render_rating_input(form, field_name, input_options)
  when "array_input", "tags"
    render_array_input(form, field_name, field_config, field_def)
  when "field_picker"
    render_field_picker_input(form, field_name, field_config)
  when "formatting_options"
    render_formatting_options_input(form, field_name, field_config)
  when "import_mapper"
    render_import_mapper_input(form, field_name, field_config)
  when "hidden"
    raw_value = form.object&.respond_to?(field_name) ? form.object.send(field_name) : nil
    value = case raw_value
    when Hash, Array then raw_value.to_json
    else raw_value.to_s
    end
    form.hidden_field(field_name, value: value)
  else
    form.text_field(field_name,
      placeholder: field_config["placeholder"],
      autofocus: field_config["autofocus"])
  end
end

#render_json_field_input(name, value, input_type, field_config, field_def, field_options: nil, field_name: nil) ⇒ Object

Render a form input for JSON field items using raw name attributes (no form builder). Also used by render_manage_input for the manage page. Resolves select options from: field_options override > enum definition > input_options.values > options array > field_config.



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
141
142
143
144
145
146
147
148
149
# File 'app/helpers/lcp_ruby/form_helper.rb', line 111

def render_json_field_input(name, value, input_type, field_config, field_def, field_options: nil, field_name: nil)
  # Controller-provided select list override (used by manage page)
  if field_options.is_a?(Hash) && field_name && field_options[field_name].is_a?(Array)
    opts = field_options[field_name].map { |v| [ v.humanize, v ] }
    return select_tag(name, options_for_select([ [ "", "" ] ] + opts, value.to_s))
  end

  # Enum fields
  if field_def&.enum?
    options = field_def.enum_value_names.map { |v| [ field_def.enum_label_for(v), v ] }
    options_html = options_for_select([ [ "", "" ] ] + options, value.to_s)
    return select_tag(name, options_html)
  end

  case input_type.to_s
  when "boolean"
    hidden = hidden_field_tag(name, "0")
    checkbox = check_box_tag(name, "1", value == true || value == "1" || value == 1 || value == "true")
    hidden + checkbox
  when "number", "integer", "float", "decimal"
    number_field_tag(name, value, step: "any", placeholder: field_config["placeholder"])
  when "text", "textarea"
    text_area_tag(name, value, placeholder: field_config["placeholder"])
  when "date", "date_picker"
    date_field_tag(name, value)
  when "datetime"
    datetime_field_tag(name, value)
  when "select"
    select_values = resolve_raw_select_options(field_config)
    if select_values
      opts = select_values.map { |o| [ o.to_s.humanize, o ] }
      select_tag(name, options_for_select([ [ "", "" ] ] + opts, value.to_s))
    else
      text_field_tag(name, value, placeholder: field_config["placeholder"])
    end
  else
    text_field_tag(name, value, placeholder: field_config["placeholder"])
  end
end

#render_manage_input(prefix, field_name, input_type, field_config, field_def, record, field_options) ⇒ Object

Render a form input for the manage page, using raw name prefix instead of a form builder. Delegates to render_json_field_input after resolving name/value and field_options override.



153
154
155
156
157
158
159
# File 'app/helpers/lcp_ruby/form_helper.rb', line 153

def render_manage_input(prefix, field_name, input_type, field_config, field_def, record, field_options)
  name = "#{prefix}[#{field_name}]"
  value = record.respond_to?(field_name) ? record.send(field_name) : nil

  render_json_field_input(name, value, input_type, field_config, field_def,
                          field_options: field_options, field_name: field_name)
end