2024-07-16 14:08:24 -04:00
|
|
|
class StyledFormBuilder < ActionView::Helpers::FormBuilder
|
|
|
|
# Fields that visually inherit from "text field"
|
|
|
|
class_attribute :text_field_helpers, default: field_helpers - [ :label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field ]
|
|
|
|
|
|
|
|
# Wraps "text" inputs with custom structure + base styles
|
|
|
|
text_field_helpers.each do |selector|
|
|
|
|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
|
|
|
def #{selector}(method, options = {})
|
2024-09-20 08:38:19 -04:00
|
|
|
merged_options = { class: "form-field__input" }.merge(options)
|
|
|
|
label = build_label(method, options)
|
|
|
|
field = super(method, merged_options)
|
|
|
|
|
|
|
|
build_styled_field(label, field, merged_options)
|
2024-07-16 14:08:24 -04:00
|
|
|
end
|
|
|
|
RUBY_EVAL
|
|
|
|
end
|
|
|
|
|
|
|
|
def radio_button(method, tag_value, options = {})
|
2024-09-20 08:38:19 -04:00
|
|
|
merged_options = { class: "form-field__radio" }.merge(options)
|
|
|
|
|
|
|
|
super(method, tag_value, merged_options)
|
2024-07-16 14:08:24 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def select(method, choices, options = {}, html_options = {})
|
2024-09-20 08:38:19 -04:00
|
|
|
merged_html_options = { class: "form-field__input" }.merge(html_options)
|
|
|
|
|
2024-10-23 11:20:55 -04:00
|
|
|
label = build_label(method, options.merge(required: merged_html_options[:required]))
|
2024-09-20 08:38:19 -04:00
|
|
|
field = super(method, choices, options, merged_html_options)
|
|
|
|
|
|
|
|
build_styled_field(label, field, options, remove_padding_right: true)
|
2024-07-16 14:08:24 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
|
2024-09-20 08:38:19 -04:00
|
|
|
merged_html_options = { class: "form-field__input" }.merge(html_options)
|
|
|
|
|
2024-10-23 11:20:55 -04:00
|
|
|
label = build_label(method, options.merge(required: merged_html_options[:required]))
|
2024-09-20 08:38:19 -04:00
|
|
|
field = super(method, collection, value_method, text_method, options, merged_html_options)
|
|
|
|
|
|
|
|
build_styled_field(label, field, options, remove_padding_right: true)
|
|
|
|
end
|
|
|
|
|
2024-10-09 14:59:18 -04:00
|
|
|
def money_field(amount_method, options = {})
|
2024-09-20 08:38:19 -04:00
|
|
|
@template.render partial: "shared/money_field", locals: {
|
|
|
|
form: self,
|
|
|
|
amount_method:,
|
2024-10-09 14:59:18 -04:00
|
|
|
currency_method: options[:currency_method] || :currency,
|
2024-09-20 08:38:19 -04:00
|
|
|
**options
|
|
|
|
}
|
2024-07-16 14:08:24 -04:00
|
|
|
end
|
|
|
|
|
2025-04-30 18:14:22 -04:00
|
|
|
# A custom styled "toggle" switch input. Underlying input is a `check_box` (uses same API)
|
|
|
|
def toggle(method, options = {}, checked_value = "1", unchecked_value = "0")
|
|
|
|
if object
|
|
|
|
id = "#{object.id}_#{object_name}_#{method}"
|
|
|
|
name = "#{object_name}[#{method}]"
|
|
|
|
checked = object.send(method)
|
|
|
|
else
|
|
|
|
id = "#{method}_toggle_id"
|
|
|
|
name = method
|
|
|
|
checked = options[:checked]
|
|
|
|
end
|
2024-10-24 11:02:27 -04:00
|
|
|
|
2025-04-30 18:14:22 -04:00
|
|
|
@template.render(
|
|
|
|
ToggleComponent.new(
|
|
|
|
id: id,
|
|
|
|
name: name,
|
|
|
|
checked: checked,
|
|
|
|
disabled: options[:disabled],
|
|
|
|
checked_value: checked_value,
|
|
|
|
unchecked_value: unchecked_value,
|
|
|
|
**options
|
|
|
|
)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
def submit(value = nil, options = {})
|
|
|
|
# Rails superclass logic to extract the submit text
|
2024-07-16 14:08:24 -04:00
|
|
|
value, options = nil, value if value.is_a?(Hash)
|
2025-04-30 18:14:22 -04:00
|
|
|
value ||= submit_default_value
|
|
|
|
|
|
|
|
@template.render(
|
|
|
|
ButtonComponent.new(
|
|
|
|
text: value,
|
|
|
|
data: (options[:data] || {}).merge({ turbo_submits_with: "Submitting..." }),
|
|
|
|
full_width: true
|
|
|
|
)
|
|
|
|
)
|
2024-07-16 14:08:24 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
2024-09-20 08:38:19 -04:00
|
|
|
def build_styled_field(label, field, options, remove_padding_right: false)
|
|
|
|
if options[:inline]
|
|
|
|
label + field
|
|
|
|
else
|
|
|
|
@template.tag.div class: [ "form-field", options[:container_class], ("pr-0" if remove_padding_right) ] do
|
|
|
|
label + field
|
|
|
|
end
|
2024-07-16 14:08:24 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-09-20 08:38:19 -04:00
|
|
|
def build_label(method, options)
|
2024-07-22 16:04:55 +02:00
|
|
|
return "".html_safe unless options[:label]
|
2024-10-23 11:20:55 -04:00
|
|
|
|
|
|
|
label_text = options[:label]
|
|
|
|
|
|
|
|
if options[:required]
|
|
|
|
label_text = @template.safe_join([
|
|
|
|
label_text == true ? method.to_s.humanize : label_text,
|
|
|
|
@template.tag.span("*", class: "text-red-500 ml-0.5")
|
|
|
|
])
|
|
|
|
end
|
|
|
|
|
|
|
|
return label(method, class: "form-field__label") if label_text == true
|
|
|
|
label(method, label_text, class: "form-field__label")
|
2024-07-16 14:08:24 -04:00
|
|
|
end
|
|
|
|
end
|