require 'hanami/helpers/form_helper/html_node' require 'hanami/helpers/form_helper/values' require 'hanami/helpers/html_helper/html_builder' require 'hanami/helpers/escape_helper' require 'hanami/utils/string' module Hanami module Helpers module FormHelper # Form builder # # @since 0.2.0 # # @see Hanami::Helpers::HtmlHelper::HtmlBuilder class FormBuilder < ::Hanami::Helpers::HtmlHelper::HtmlBuilder # rubocop:disable Metrics/ClassLength # Set of HTTP methods that are understood by web browsers # # @since 0.2.0 # @api private BROWSER_METHODS = %w[GET POST].freeze # Set of HTTP methods that should NOT generate CSRF token # # @since 0.2.0 # @api private EXCLUDED_CSRF_METHODS = %w[GET].freeze # Checked attribute value # # @since 0.2.0 # @api private # # @see Hanami::Helpers::FormHelper::FormBuilder#radio_button CHECKED = 'checked'.freeze # Selected attribute value for option # # @since 0.2.0 # @api private # # @see Hanami::Helpers::FormHelper::FormBuilder#select SELECTED = 'selected'.freeze # Separator for accept attribute of file input # # @since 0.2.0 # @api private # # @see Hanami::Helpers::FormHelper::FormBuilder#file_input ACCEPT_SEPARATOR = ','.freeze # Replacement for input id interpolation # # @since 0.2.0 # @api private # # @see Hanami::Helpers::FormHelper::FormBuilder#_input_id INPUT_ID_REPLACEMENT = '-\k'.freeze # Default value for unchecked check box # # @since 0.2.0 # @api private # # @see Hanami::Helpers::FormHelper::FormBuilder#check_box DEFAULT_UNCHECKED_VALUE = '0'.freeze # Default value for checked check box # # @since 0.2.0 # @api private # # @see Hanami::Helpers::FormHelper::FormBuilder#check_box DEFAULT_CHECKED_VALUE = '1'.freeze # ENCTYPE_MULTIPART = 'multipart/form-data'.freeze include Helpers::EscapeHelper self.html_node = ::Hanami::Helpers::FormHelper::HtmlNode # Instantiate a form builder # # @overload initialize(form, attributes, params, &blk) # Top level form # @param form [Hanami::Helpers:FormHelper::Form] the form # @param attributes [::Hash] a set of HTML attributes # @param params [Hanami::Action::Params] request params # @param blk [Proc] a block that describes the contents of the form # # @overload initialize(form, attributes, params, &blk) # Nested form # @param form [Hanami::Helpers:FormHelper::Form] the form # @param attributes [Hanami::Helpers::FormHelper::Values] user defined # values # @param blk [Proc] a block that describes the contents of the form # # @return [Hanami::Helpers::FormHelper::FormBuilder] the form builder # # @since 0.2.0 # @api private def initialize(form, attributes, context = nil, &blk) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength super() @context = context @blk = blk @verb = nil @csrf_token = nil # Nested form if @context.nil? && attributes.is_a?(Values) @values = attributes @attributes = {} @name = form else @form = form @name = form.name @values = Values.new(form.values, @context.params) @attributes = attributes @verb_method = verb_method @csrf_token = csrf_token end end # Resolves all the nodes and generates the markup # # @return [Hanami::Utils::Escape::SafeString] the output # # @since 0.2.0 # @api private # # @see Hanami::Helpers::HtmlHelper::HtmlBuilder#to_s # @see http://www.rubydoc.info/gems/hanami-utils/Hanami/Utils/Escape/SafeString def to_s if toplevel? _method_override! form(@blk, @attributes) end super end # Nested fields # # The inputs generated by the wrapped block will be prefixed with the given name # It supports infinite levels of nesting. # # @param name [Symbol] the nested name, it's used to generate input # names, ids, and to lookup params to fill values. # # @since 0.2.0 # # @example Basic usage # <%= # form_for :delivery, routes.deliveries_path do # text_field :customer_name # # fields_for :address do # text_field :street # end # # submit 'Create' # end # %> # # #
# # # # # #
# # @example Multiple levels of nesting # <%= # form_for :delivery, routes.deliveries_path do # text_field :customer_name # # fields_for :address do # text_field :street # # fields_for :location do # text_field :city # text_field :country # end # end # # submit 'Create' # end # %> # # #
# # # # # # # #
def fields_for(name) current_name = @name @name = _input_name(name) yield(name) ensure @name = current_name end # Nested collections # # Supports nesting for collections, with infinite # levels of nesting. # # @param name [Symbol] the nested name, it's used to generate input # names, ids, and to lookup params to fill values. # # @example Basic usage # <%= # form_for :delivery, routes.deliveries_path do # text_field :customer_name # # fields_for_collection :addresses do # text_field :street # end # # submit 'Create' # end # %> # # #
# # # # # # #
def fields_for_collection(name, &block) current_name = @name base_value = _value(name) @name = _input_name(name) base_value.count.times do |index| fields_for(index, &block) end ensure @name = current_name end # Label tag # # The first param content can be a Symbol that represents # the target field (Eg. :extended_title), or a String # which is used as it is. # # @param content [Symbol,String] the field name or a content string # @param attributes [Hash] HTML attributes to pass to the label tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # label :extended_title # %> # # # # # @example HTML attributes # <%= # # ... # label :title, class: "form-label" # %> # # # # # @example Custom content # <%= # # ... # label 'Title', for: :extended_title # %> # # # # # @example Custom "for" attribute # <%= # # ... # label :extended_title, for: 'ext-title' # %> # # # # # @example Nested fields usage # <%= # # ... # fields_for :address do # label :city # text_field :city # end # %> # # # # def label(content, attributes = {}) attributes = { for: _for(content, attributes.delete(:for)) }.merge(attributes) content = case content when String, Hanami::Utils::String content else Utils::String.capitalize(content) end super(content, attributes) end # Fieldset # # @param content [Symbol,String,NilClass] the content # @param attributes [Hash] HTML attributes to pass to the label tag # # @since 1.0.0 # # @example Basic usage # <%= # # ... # fieldset do # legend "Author" # # fields_for :author do # label :name # text_field :name # end # end # %> # # #
# Author # # #
def fieldset(content = nil, attributes = {}) # This is here only for documentation purposes super end # Check box # # It renders a check box input. # # When a form is submitted, browsers don't send the value of unchecked # check boxes. If an user unchecks a check box, their browser won't send # the unchecked value. On the server side the corresponding value is # missing, so the application will assume that the user action never # happened. # # To solve this problem the form renders a hidden field with the # "unchecked value". When the user unchecks the input, the browser will # ignore it, but it will still send the value of the hidden input. See # the examples below. # # When editing a resource, the form automatically assigns the # checked="checked" attribute. # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # @option attributes [String] :checked_value (defaults to "1") # @option attributes [String] :unchecked_value (defaults to "0") # # @since 0.2.0 # # @example Basic usage # <%= # check_box :free_shipping # %> # # # # # # @example HTML Attributes # <%= # check_box :free_shipping, class: "form-check-input" # %> # # # # # # @example Specify (un)checked values # <%= # check_box :free_shipping, checked_value: 'true', unchecked_value: 'false' # %> # # # # # # @example Automatic "checked" attribute # # For this example the params are: # # # # { delivery: { free_shipping: '1' } } # <%= # check_box :free_shipping # %> # # # # # # @example Force "checked" attribute # # For this example the params are: # # # # { delivery: { free_shipping: '0' } } # <%= # check_box :free_shipping, checked: 'checked' # %> # # # # # # @example Multiple check boxes # <%= # check_box :languages, name: 'book[languages][]', value: 'italian', id: nil # check_box :languages, name: 'book[languages][]', value: 'english', id: nil # %> # # # # # # @example Automatic "checked" attribute for multiple check boxes # # For this example the params are: # # # # { book: { languages: ['italian'] } } # <%= # check_box :languages, name: 'book[languages][]', value: 'italian', id: nil # check_box :languages, name: 'book[languages][]', value: 'english', id: nil # %> # # # # def check_box(name, attributes = {}) _hidden_field_for_check_box(name, attributes) input _attributes_for_check_box(name, attributes) end # Color input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # color_field :background # %> # # # # # @example HTML Attributes # <%= # # ... # color_field :background, class: "form-control" # %> # # # def color_field(name, attributes = {}) input _attributes(:color, name, attributes) end # Date input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # date_field :birth_date # %> # # # # # @example HTML Attributes # <%= # # ... # date_field :birth_date, class: "form-control" # %> # # # def date_field(name, attributes = {}) input _attributes(:date, name, attributes) end # Datetime input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # datetime_field :delivered_at # %> # # # # # @example HTML Attributes # <%= # # ... # datetime_field :delivered_at, class: "form-control" # %> # # # def datetime_field(name, attributes = {}) input _attributes(:datetime, name, attributes) end # Datetime Local input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # datetime_local_field :delivered_at # %> # # # # # @example HTML Attributes # <%= # # ... # datetime_local_field :delivered_at, class: "form-control" # %> # # # def datetime_local_field(name, attributes = {}) input _attributes(:'datetime-local', name, attributes) end # Time field # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 1.0.0 # # @example Basic usage # <%= # # ... # time_field :release_hour # %> # # # # # @example HTML Attributes # <%= # # ... # time_field :release_hour, class: "form-control" # %> # # # def time_field(name, attributes = {}) input _attributes(:time, name, attributes) end # Month field # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 1.0.0 # # @example Basic usage # <%= # # ... # month_field :release_month # %> # # # # # @example HTML Attributes # <%= # # ... # month_field :release_month, class: "form-control" # %> # # # def month_field(name, attributes = {}) input _attributes(:month, name, attributes) end # Week field # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 1.0.0 # # @example Basic usage # <%= # # ... # week_field :release_week # %> # # # # # @example HTML Attributes # <%= # # ... # week_field :release_week, class: "form-control" # %> # # # def week_field(name, attributes = {}) input _attributes(:week, name, attributes) end # Email input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # email_field :email # %> # # # # # @example HTML Attributes # <%= # # ... # email_field :email, class: "form-control" # %> # # # def email_field(name, attributes = {}) input _attributes(:email, name, attributes) end # URL input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 1.0.0 # # @example Basic usage # <%= # # ... # url_field :website # %> # # # # # @example HTML Attributes # <%= # # ... # url_field :website, class: "form-control" # %> # # # def url_field(name, attributes = {}) attrs = attributes.dup attrs[:value] = escape_url(attrs.fetch(:value) { _value(name) }) input _attributes(:url, name, attrs) end # Telephone input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 1.0.0 # # @example Basic usage # <%= # # ... # tel_field :telephone # %> # # # # # @example HTML Attributes # <%= # # ... # telurl_field :telephone, class: "form-control" # %> # # # def tel_field(name, attributes = {}) input _attributes(:tel, name, attributes) end # Hidden input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # hidden_field :customer_id # %> # # # def hidden_field(name, attributes = {}) input _attributes(:hidden, name, attributes) end # File input # # **PLEASE REMEMBER TO ADD enctype: 'multipart/form-data' ATTRIBUTE TO THE FORM** # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # @option attributes [String,Array] :accept Optional set of accepted MIME Types # @option attributes [TrueClass,FalseClass] :multiple Optional, allow multiple file upload # # @since 0.2.0 # # @example Basic usage # <%= # # ... # file_field :avatar # %> # # # # # @example HTML Attributes # <%= # # ... # file_field :avatar, class: "avatar-upload" # %> # # # # # @example Accepted MIME Types # <%= # # ... # file_field :resume, accept: 'application/pdf,application/ms-word' # %> # # # # # @example Accepted MIME Types (as array) # <%= # # ... # file_field :resume, accept: ['application/pdf', 'application/ms-word'] # %> # # # # # @example Accepted multiple file upload (as array) # <%= # # ... # file_field :resume, multiple: true # %> # # # def file_field(name, attributes = {}) attributes[:accept] = Array(attributes[:accept]).join(ACCEPT_SEPARATOR) if attributes.key?(:accept) attributes = { type: :file, name: _displayed_input_name(name), id: _input_id(name) }.merge(attributes) input(attributes) end # Number input # # You can also make use of the `max`, `min`, and `step` attributes for # the HTML5 number field. # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the number input # # @example Basic usage # <%= # # ... # number_field :percent_read # %> # # # # # @example Advanced attributes # <%= # # ... # number_field :priority, min: 1, max: 10, step: 1 # %> # # # def number_field(name, attributes = {}) input _attributes(:number, name, attributes) end # Range input # # You can also make use of the `max`, `min`, and `step` attributes for # the HTML5 number field. # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the number input # # @since 1.0.0 # # @example Basic usage # <%= # # ... # range_field :discount_percentage # %> # # # # # @example Advanced attributes # <%= # # ... # range_field :discount_percentage, min: 1, max: 10, step: 1 # %> # # # def range_field(name, attributes = {}) input _attributes(:range, name, attributes) end # Text-area input # # @param name [Symbol] the input name # @param content [String] the content of the textarea # @param attributes [Hash] HTML attributes to pass to the textarea tag # # @since 0.2.5 # # @example Basic usage # <%= # # ... # text_area :hobby # %> # # # # # @example Set content # <%= # # ... # text_area :hobby, 'Football' # %> # # # # # @example Set content and HTML attributes # <%= # # ... # text_area :hobby, 'Football', class: 'form-control' # %> # # # # # @example Omit content and specify HTML attributes # <%= # # ... # text_area :hobby, class: 'form-control' # %> # # # # # @example Force blank value # <%= # # ... # text_area :hobby, '', class: 'form-control' # %> # # # def text_area(name, content = nil, attributes = {}) if content.respond_to?(:to_hash) attributes = content content = nil end attributes = { name: _displayed_input_name(name), id: _input_id(name) }.merge(attributes) textarea(content || _value(name), attributes) end # Text input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # text_field :first_name # %> # # # # # @example HTML Attributes # <%= # # ... # text_field :first_name, class: "form-control" # %> # # # def text_field(name, attributes = {}) input _attributes(:text, name, attributes) end alias input_text text_field # Search input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 1.0.0 # # @example Basic usage # <%= # # ... # search_field :q # %> # # # # # @example HTML Attributes # <%= # # ... # search_field :q, class: "form-control" # %> # # # def search_field(name, attributes = {}) input _attributes(:search, name, attributes) end # Radio input # # If request params have a value that corresponds to the given value, # it automatically sets the checked attribute. # This Hanami::Controller integration happens without any developer intervention. # # @param name [Symbol] the input name # @param value [String] the input value # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # radio_button :category, 'Fiction' # radio_button :category, 'Non-Fiction' # %> # # # # # # @example HTML Attributes # <%= # # ... # radio_button :category, 'Fiction', class: "form-check" # radio_button :category, 'Non-Fiction', class: "form-check" # %> # # # # # # @example Automatic checked value # # Given the following params: # # # # book: { # # category: 'Non-Fiction' # # } # # <%= # # ... # radio_button :category, 'Fiction' # radio_button :category, 'Non-Fiction' # %> # # # # def radio_button(name, value, attributes = {}) attributes = { type: :radio, name: _displayed_input_name(name), value: value }.merge(attributes) attributes[:checked] = CHECKED if _value(name).to_s == value.to_s input(attributes) end # Password input # # @param name [Symbol] the input name # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # password_field :password # %> # # # def password_field(name, attributes = {}) input({ type: :password, name: _displayed_input_name(name), id: _input_id(name), value: nil }.merge(attributes)) end # Select input # # @param name [Symbol] the input name # @param values [Hash] a Hash to generate tags. # @param attributes [Hash] HTML attributes to pass to the input tag # # Values is used to generate the list of <option> tags, it is an # Enumerable of pairs of content (the displayed text) and value (the tag's # attribute), in that respective order (please refer to the examples for more clarity). # # If request params have a value that corresponds to one of the given values, # it automatically sets the selected attribute on the tag. # This Hanami::Controller integration happens without any developer intervention. # # @since 0.2.0 # # @example Basic usage # <%= # # ... # values = Hash['Italy' => 'it', 'United States' => 'us'] # select :store, values # %> # # # # # @example HTML Attributes # <%= # # ... # values = Hash['Italy' => 'it', 'United States' => 'us'] # select :store, values, class: "form-control" # %> # # # # # @example Automatic selected option # # Given the following params: # # # # book: { # # store: 'it' # # } # # <%= # # ... # values = Hash['Italy' => 'it', 'United States' => 'us'] # select :store, values # %> # # # # # @example Prompt option # <%= # # ... # values = Hash['Italy' => 'it', 'United States' => 'us'] # select :store, values, options: { prompt: 'Select a store' } # %> # # # # # @example Selected option # <%= # # ... # values = Hash['Italy' => 'it', 'United States' => 'us'] # select :store, values, options: { selected: book.store } # %> # # # # # @example Prompt option and HTML attributes # <%= # # ... # values = Hash['Italy' => 'it', 'United States' => 'us'] # select :store, values, options: { prompt: 'Select a store' }, class: "form-control" # %> # # # # # @example Multiple select # <%= # # ... # values = Hash['Italy' => 'it', 'United States' => 'us'] # select :stores, values, multiple: true # %> # # # # # @example Multiple select and HTML attributes # <%= # # ... # values = Hash['Italy' => 'it', 'United States' => 'us'] # select :stores, values, multiple: true, class: "form-control" # %> # # # # # @example Array with repeated entries # <%= # # ... # values = [['Italy', 'it'], # ['---', ''], # ['Afghanistan', 'af'], # ... # ['Italy', 'it'], # ... # ['Zimbabwe', 'zw']] # select :stores, values # %> # # # def select(name, values, attributes = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength options = attributes.delete(:options) { {} } attributes = { name: _select_input_name(name, attributes[:multiple]), id: _input_id(name) }.merge(attributes) prompt = options.delete(:prompt) selected = options.delete(:selected) super(attributes) do option(prompt) unless prompt.nil? already_selected = nil values.each do |content, value| if (attributes[:multiple] || !already_selected) && (already_selected = _select_option_selected?(value, selected, _value(name), attributes[:multiple])) option(content, { value: value, selected: SELECTED }.merge(options)) else option(content, { value: value }.merge(options)) end end end end # Datalist input # # @param name [Symbol] the input name # @param values [Array,Hash] a collection that is transformed into tags. # @param list [String] the name of list for the text input, it's also the id of datalist # @param attributes [Hash] HTML attributes to pass to the input tag # # @since 0.4.0 # # @example Basic Usage # <%= # # ... # values = ['Italy', 'United States'] # datalist :stores, values, 'books' # %> # # # # # # # # # @example Options As Hash # <%= # # ... # values = Hash['Italy' => 'it', 'United States' => 'us'] # datalist :stores, values, 'books' # %> # # # # # # # # # @example Specify Custom Attributes For Datalist Input # <%= # # ... # values = ['Italy', 'United States'] # datalist :stores, values, 'books', datalist: { class: 'form-control' } # %> # # # # # # # # # @example Specify Custom Attributes For Options List # <%= # # ... # values = ['Italy', 'United States'] # datalist :stores, values, 'books', options: { class: 'form-control' } # %> # # # # # # # def datalist(name, values, list, attributes = {}) # rubocop:disable Metrics/MethodLength attrs = attributes.dup options = attrs.delete(:options) || {} datalist = attrs.delete(:datalist) || {} attrs[:list] = list datalist[:id] = list text_field(name, attrs) super(datalist) do values.each do |value, content| option(content, { value: value }.merge(options)) end end end # Button # # @param content [String] The content # @param attributes [Hash] HTML attributes to pass to the button tag # # @since 1.0.0 # # @example Basic usage # <%= # # ... # button 'Click me' # %> # # # # # @example HTML Attributes # <%= # # ... # button 'Click me', class: "btn btn-secondary" # %> # # # def button(content, attributes = {}) # This is here only for documentation purposes super end # Image button # # Visual submit button # # **Please note:** for security reasons, please use the absolute URL of the image # # @param source [String] The **absolute URL** of the image # @param attributes [Hash] HTML attributes to pass to the button tag # # @since 1.0.0 # # @example Basic usage # <%= # # ... # image_button "https://hanamirb.org/assets/button.png" # %> # # # # # @example HTML Attributes # <%= # # ... # image_button "https://hanamirb.org/assets/button.png", name: "image", width: "50" # %> # # # def image_button(source, attributes = {}) attrs = attributes.dup attrs[:type] = :image attrs[:src] = escape_url(source) input attrs end # Submit button # # @param content [String] The content # @param attributes [Hash] HTML attributes to pass to the button tag # # @since 0.2.0 # # @example Basic usage # <%= # # ... # submit 'Create' # %> # # # # # @example HTML Attributes # <%= # # ... # submit 'Create', class: "btn btn-primary" # %> # # # def submit(content, attributes = {}) attributes = { type: :submit }.merge(attributes) button(content, attributes) end protected # A set of options to pass to the sub form helpers. # # @api private # @since 0.2.0 def options Hash[name: @name, values: @values, verb: @verb, csrf_token: @csrf_token] end private # Check the current builder is top-level # # @api private # @since 0.2.0 def toplevel? @attributes.any? end # Prepare for method override # # @api private # @since 0.2.0 def _method_override! if BROWSER_METHODS.include?(@verb_method) @attributes[:method] = @verb_method else @attributes[:method] = DEFAULT_METHOD @verb = @verb_method end end # Return the method from attributes # # @api private def verb_method (@attributes.fetch(:method) { DEFAULT_METHOD }).to_s.upcase end # Return CSRF Protection token from view context # # @api private # @since 0.2.0 def csrf_token @context.csrf_token if @context.respond_to?(:csrf_token) && !EXCLUDED_CSRF_METHODS.include?(@verb_method) end # Return a set of default HTML attributes # # @api private # @since 0.2.0 def _attributes(type, name, attributes) attrs = { type: type, name: _displayed_input_name(name), id: _input_id(name), value: _value(name) } attrs.merge!(attributes) attrs[:value] = escape_html(attrs[:value]) attrs end # Full input name, used to construct the input # attributes. # # @api private # @since 0.2.0 def _input_name(name) "#{@name}[#{name}]" end # Input name HTML attribute # # @api private # @since 1.0.0 def _displayed_input_name(name) _input_name(name).gsub(/\[\d+\]/, '[]') end # Input id HTML attribute # # @api private # @since 0.2.0 def _input_id(name) name = _input_name(name).gsub(/\[(?[[[:word:]]\-]*)\]/, INPUT_ID_REPLACEMENT) Utils::String.dasherize(name) end # Input value HTML attribute # # @api private # @since 0.2.0 def _value(name) @values.get( *_input_name(name).split(/[\[\]]+/).map(&:to_sym) ) end # Input for HTML attribute # # @api private # @since 0.2.0 def _for(content, name) case name when String, Hanami::Utils::String name else _input_id(name || content) end end # Hidden field for check box # # @api private # @since 0.2.0 # # @see Hanami::Helpers::FormHelper::FormBuilder#check_box def _hidden_field_for_check_box(name, attributes) return unless attributes[:value].nil? || !attributes[:unchecked_value].nil? input( type: :hidden, name: attributes[:name] || _displayed_input_name(name), value: attributes.delete(:unchecked_value) || DEFAULT_UNCHECKED_VALUE ) end # HTML attributes for check box # # @api private # @since 0.2.0 # # @see Hanami::Helpers::FormHelper::FormBuilder#check_box def _attributes_for_check_box(name, attributes) attributes = { type: :checkbox, name: _displayed_input_name(name), id: _input_id(name), value: attributes.delete(:checked_value) || DEFAULT_CHECKED_VALUE }.merge(attributes) attributes[:checked] = CHECKED if _check_box_checked?(attributes[:value], _value(name)) attributes end # @api private def _select_input_name(name, multiple) select_name = _displayed_input_name(name) select_name = "#{select_name}[]" if multiple select_name end # TODO: this has to be refactored # # @api private # # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/PerceivedComplexity def _select_option_selected?(value, selected, input_value, multiple) value == selected || (multiple && (selected.is_a?(Array) && selected.include?(value))) || (!input_value.nil? && (value.to_s == input_value.to_s)) || (multiple && (input_value.is_a?(Array) && input_value.include?(value))) end # rubocop:enable Metrics/PerceivedComplexity # rubocop:enable Metrics/CyclomaticComplexity # @api private def _check_box_checked?(value, input_value) !input_value.nil? && (input_value.to_s == value.to_s || input_value.is_a?(TrueClass) || input_value.is_a?(Array) && input_value.include?(value)) end end end end end