From 1da0c03798e2e75e6b2c0d5ff808b1b655600abd Mon Sep 17 00:00:00 2001 From: Harshal LADHE Date: Mon, 15 May 2023 18:09:02 +0530 Subject: [PATCH] Added component to mark `labels` as `required` --- .../stylesheets/rails_bootstrap_form.css | 11 +++- lib/rails_bootstrap_form/components.rb | 2 + lib/rails_bootstrap_form/components/labels.rb | 1 + .../components/required_field.rb | 64 +++++++++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 lib/rails_bootstrap_form/components/required_field.rb diff --git a/app/assets/stylesheets/rails_bootstrap_form.css b/app/assets/stylesheets/rails_bootstrap_form.css index 8b13789..2c8a441 100644 --- a/app/assets/stylesheets/rails_bootstrap_form.css +++ b/app/assets/stylesheets/rails_bootstrap_form.css @@ -1 +1,10 @@ - +label.is-invalid, .invalid-feedback { + color: var(--bs-danger); +} +label.required::after { + color: var(--bs-danger); + content: "*"; + padding-left: .25rem; + top: -2px; + font-weight: bolder; +} diff --git a/lib/rails_bootstrap_form/components.rb b/lib/rails_bootstrap_form/components.rb index cf71908..8a73735 100644 --- a/lib/rails_bootstrap_form/components.rb +++ b/lib/rails_bootstrap_form/components.rb @@ -8,8 +8,10 @@ module Components autoload :HelpText autoload :Labels + autoload :RequiredField include HelpText include Labels + include RequiredField end end diff --git a/lib/rails_bootstrap_form/components/labels.rb b/lib/rails_bootstrap_form/components/labels.rb index 2333e69..151b728 100644 --- a/lib/rails_bootstrap_form/components/labels.rb +++ b/lib/rails_bootstrap_form/components/labels.rb @@ -20,6 +20,7 @@ def label(attribute, bootstrap_options) def label_classes(attribute, bootstrap_options) classes = [bootstrap_options.label_class, bootstrap_options.additional_label_class] classes << bootstrap_options.hide_class if bootstrap_options.hide_label + classes << "required" if is_attribute_required?(attribute) classes.flatten.compact end diff --git a/lib/rails_bootstrap_form/components/required_field.rb b/lib/rails_bootstrap_form/components/required_field.rb new file mode 100644 index 0000000..fa100e0 --- /dev/null +++ b/lib/rails_bootstrap_form/components/required_field.rb @@ -0,0 +1,64 @@ +# -*- encoding: utf-8 -*- +# -*- frozen_string_literal: true -*- +# -*- warn_indent: true -*- + +module RailsBootstrapForm + module Components + module RequiredField + extend ActiveSupport::Concern + + def self.included(base_class) + def is_field_required?(attribute, options) + return false unless attribute + + if options.key?(:required) + options[:required] + else + is_attribute_required?(attribute) + end + end + + def required_field_options(options, attribute) + required = is_field_required?(attribute, options) + + {}.tap do |option| + option[:aria] = {required: true} if required + option[:required] = required + end + end + + def is_attribute_required?(attribute) + return false unless attribute + + target = object.instance_of?(Class) ? object : object.class + return false unless target.respond_to?(:validators_on) + + has_presence_validator?(target_validators(target, attribute)) || + is_required_association?(target, attribute) + end + + def target_validators(target, attribute) + target.validators_on(attribute).map(&:class) + end + + def has_presence_validator?(target_validators) + target_validators.include?(ActiveModel::Validations::PresenceValidator) || + (defined?(ActiveRecord::Validations::PresenceValidator) && + target_validators.include?(ActiveRecord::Validations::PresenceValidator)) + end + + def is_required_association?(target, attribute) + target.reflections.find do |name, a| + next unless a.is_a?(ActiveRecord::Reflection::BelongsToReflection) + next unless a.foreign_key == attribute.to_s + + has_presence_validator?(target_validators(target, name)) + end + end + + private :is_field_required?, :required_field_options, :target_validators, + :has_presence_validator?, :is_required_association? + end + end + end +end