Files

114 lines
4.6 KiB
Ruby
Raw Permalink Normal View History

2025-11-24 08:22:44 -05:00
class TailwindFormBuilder < ActionView::Helpers::FormBuilder
class_attribute :text_field_helpers, default: field_helpers - [:label, :check_box, :radio_button, :fields_for, :fields, :hidden_field, :file_field]
# leans on the FormBuilder class_attribute `field_helpers`
# you'll want to add a method for each of the specific helpers listed here if you want to style them
2026-05-06 13:28:16 -04:00
TEXT_FIELD_STYLE = "flex bg-gray-200 rounded py-2 px-4 text-bluetang font-semibold leading-tight focus:outline-none focus:bg-white border border-platinum".freeze
SELECT_FIELD_STYLE = "block bg-gray-200 text-bluetang py-2 px-4 font-semibold rounded leading-tight focus:outline-none focus:bg-white border border-platinum".freeze
SUBMIT_BUTTON_STYLE = "cursor-pointer font-bold text-lg text-platinum hover:text-bronze bg-cobalt-vivid hover:bg-deepcove border-2 border-cobalt-vivid py-2 px-4 rounded".freeze
2025-11-24 08:22:44 -05:00
text_field_helpers.each do |field_method|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{field_method}(method, options = {})
if options.delete(:tailwindified)
super
else
text_like_field(#{field_method.inspect}, method, options)
end
end
RUBY_EVAL
end
def submit(value = nil, options = {})
custom_opts, opts = partition_custom_opts(options)
classes = apply_style_classes(SUBMIT_BUTTON_STYLE, custom_opts)
super(value, {class: classes}.merge(opts))
end
def select(method, choices = nil, options = {}, html_options = {}, &block)
custom_opts, opts = partition_custom_opts(options)
classes = apply_style_classes(SELECT_FIELD_STYLE, custom_opts, method)
labels = labels(method, custom_opts[:label], options)
field = super(method, choices, opts, html_options.merge({class: classes}), &block)
labels + field
end
def file_field(method, options = {})
options[:class] = Array(options[:class]) << "block w-full px-1 py-[1px] text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100"
# You can add more classes as needed for padding, margin, etc., e.g., p-2.5
super(method, options)
end
2025-11-24 08:22:44 -05:00
private
def text_like_field(field_method, object_method, options = {})
custom_opts, opts = partition_custom_opts(options)
classes = apply_style_classes(TEXT_FIELD_STYLE, custom_opts, object_method)
2026-05-06 13:28:16 -04:00
puts "classes: #{classes}"
2025-11-24 08:22:44 -05:00
field = send(field_method, object_method, {
class: classes,
title: errors_for(object_method)&.join(" ")
}.compact.merge(opts).merge({tailwindified: true}))
labels = labels(object_method, custom_opts[:label], options)
labels + field
end
def labels(object_method, label_options, field_options)
label = tailwind_label(object_method, label_options, field_options)
error_label = error_label(object_method, field_options)
2026-05-06 13:28:16 -04:00
@template.content_tag("div", label + error_label, {class: "flex justify-between"})
2025-11-24 08:22:44 -05:00
end
def tailwind_label(object_method, label_options, field_options)
text, label_opts = if label_options.present?
[label_options[:text], label_options.except(:text)]
else
2026-03-03 22:53:21 -05:00
[object_method.to_s.titleize, {}]
2025-11-24 08:22:44 -05:00
end
2026-05-06 13:28:16 -04:00
label_classes = label_opts[:class] || "block shrink-0 text-platinum font-bold md:text-right mb-1 md:mb-0 pr-4"
label_classes += " dark:text-brightlava" if field_options[:disabled]
2025-11-24 08:22:44 -05:00
label(object_method, text, {
class: label_classes
}.merge(label_opts.except(:class)))
end
def error_label(object_method, options)
if errors_for(object_method).present?
error_message = @object.errors[object_method].collect(&:titleize).join(", ")
2026-05-06 13:28:16 -04:00
tailwind_label(object_method, {text: error_message, class: " font-bold text-right text-bronze"}, options)
2025-11-24 08:22:44 -05:00
end
end
def border_color_classes(object_method)
if errors_for(object_method).present?
2026-05-06 13:28:16 -04:00
" border-4 dark:border-bronze focus:border-atmosphere"
2025-11-24 08:22:44 -05:00
else
2026-05-06 13:28:16 -04:00
""
# " border border-platinum focus:border-yellow-700"
2025-11-24 08:22:44 -05:00
end
end
def apply_style_classes(classes, custom_opts, object_method = nil)
classes + border_color_classes(object_method) + " #{custom_opts[:class]}"
end
CUSTOM_OPTS = [:label, :class].freeze
def partition_custom_opts(opts)
opts.partition { |k, v| CUSTOM_OPTS.include?(k) }.map(&:to_h)
end
def errors_for(object_method)
return unless @object.present? && object_method.present?
@object.errors[object_method]
end
end