# frozen_string_literal: true
module NfgUi
module Bootstrap
module Components
# Base Component
# Defines conventional, shared behavior across
# Bootstrap components
class Base
include ActionView::Helpers::TagHelper
include ActionView::Helpers::TextHelper
include ActionView::Helpers::AssetTagHelper
include ActionView::Context
attr_reader :body
attr_accessor :options,
:view_context
def initialize(component_options, view_context)
self.options = defaults.merge!(component_options)
self.view_context = view_context
@body = options.fetch(:body, '')
utility_initialize
component_initialize
end
# This base render handles many of the components and
# can be changed to have a different base element by
# overriding the base_element.
# in some cases, the child component can also call
# super with a block to have this render as the wrapping
# element.
def render
content_tag(base_element, html_options) do
(block_given? ? yield : body)
end
end
# This is used to help identify where to find partials for rendering components.
#
# Set the component family, e.g.: :breadcrumb
# on any sibling components.
#
# For example:
# BreadcrumbItem & Breadcrumb are members of the :breadcrumb component_family
def component_family
nil
end
def data
options[:data] || {}
end
def html_options
options.except(*non_html_attribute_options.uniq)
.merge!(id: id,
class: css_classes,
data: data,
href: href,
style: style,
**assistive_html_attributes)
.reject { |_k, v| v.blank? } # prevent empty attributes from showing up
# Example:
Text
end
# Use view_context url_for method to ensure that
# when objects are passed into href, e.g.
# `href: @admin` will convert it to the
# correct path
#
# Likewise that hashes are also parsed correctly
# example:
# href: { controller: 'admin', action: 'show', id: 7 }
def href
options_href = options[:href]
return if options_href.blank?
view_context.url_for(options_href)
end
def id
options[:id]
end
def style
options[:style]
end
private
# Assigned on individual components as needed
# Ex: { role: 'alert' }
#
# If aria assistive html is needed, see:
# Bootstrap::Utilities::AriaAssistable
# avoid passing aria to assistive_html_attributes directly
def assistive_html_attributes
@assistive_html_attributes ||= {}
end
# For components that inherit bootstrap, provide a second
# layer of initialization, for example:
# to initialize traits on design system components
# (which are not available on bootstrap)
def component_initialize; end
def utility_initialize; end
# the base_element is used in the default render for all components
# as the outer wrapping element. Typically, this is a div, but
# can be overriddent as a different static element in a child class
# or as a dynamic element in the child class.
# this allows most child components to not have to have their
# own render statement if their wrapping element is not a div
def base_element
:div
end
# Fallback component css class name.
# Overwritten within individual classes for situations like
# Button's css class is 'btn'...
# Example: returns 'alert' from NfgUi::Bootstrap::Components::Alert
def component_css_class
@component_css_class ||= component_class_name_string.underscore.dasherize.downcase
end
def component_class_name_string
self.class.name.demodulize.to_s
end
# Manage or adjust the css_classes of the component by
# adding a new string of css classes to this method
# ex: super.push('new-class')
def css_classes
@css_classes ||= [component_css_class, options[:class]].reject(&:nil?).uniq.join(' ').squish
end
def defaults
{
# HTML Defaults
class: '',
id: nil,
# Content
# heading: (nil if heading.present?),
body: nil,
data: {},
# Configuration
# traits: ([] if traits.present?)
}
end
# Remove attributes from html_options that shouldn't show up in the
# html element, ex:
def non_html_attribute_options
@non_html_attribute_options ||= %i[body heading traits]
end
end
end
end
end