Skip to content

Commit

Permalink
Merge pull request #2 from claudiob/add-form-for
Browse files Browse the repository at this point in the history
Add form for
  • Loading branch information
claudiofullscreen committed Aug 22, 2014
2 parents 83bd103 + 6b42581 commit 7e5a069
Show file tree
Hide file tree
Showing 38 changed files with 1,089 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -19,4 +19,5 @@ tmp
*.so
*.o
*.a
spec/dummy
mkmf.log
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -6,6 +6,10 @@ For more information about changelogs, check
[Keep a Changelog](http://keepachangelog.com) and
[Vandamme](http://tech-angels.github.io/vandamme).

## 0.0.5 - 2014-08-22

* [FEATURE] Add `form_for` and form helpers for every type of field

## 0.0.4 - 2014-08-17

* [FEATURE] Add `modal`
Expand Down
55 changes: 50 additions & 5 deletions README.md
Expand Up @@ -46,7 +46,7 @@ How to install

Bh is meant to be included in Rails apps by adding this line to the Gemfile:

gem 'bh', '~> 0.0.4'
gem 'bh', '~> 0.0.5'

Since the gem follows [Semantic Versioning](http://semver.org),
indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
Expand Down Expand Up @@ -322,22 +322,22 @@ Complex panels
--------------

```rhtml
<%= panel do %>
<%= panel tag: :aside do %>
<div class='panel-body'>You accepted the Terms of service. <%= glyphicon :ok %></div>
<div class='panel-footer'><h4>Thanks</h4></div>
<% end %>
```

will generate the HTML to render a panel with HTML body and footer:
will generate the HTML to render an aside panel with HTML body and footer:

```html
<div class="panel panel-default">
<aside class="panel panel-default">
<div class="panel-body">
You accepted the Terms of service.
<span class="glyphicon glyphicon-ok"></span>
</div>
<div class="panel-footer"><h4>Thanks</h4></div>
</div>
</aside>
```

![panel-complex](https://cloud.githubusercontent.com/assets/7408595/3941819/da569586-2543-11e4-8640-3f0a72077aca.png)
Expand Down Expand Up @@ -491,6 +491,51 @@ the caption "Continue" that toggles a small modal with a title and HTML content:

![modal-complex](https://cloud.githubusercontent.com/assets/7408595/3943922/b47620a8-25d8-11e4-9e0c-803d8a104bff.png)

FormForHelper
=============

To include [Boostrap forms](http://getbootstrap.com/css/#forms)
in your Rails views, you can use the
[form_for](http://rubydoc.info/github/Fullscreen/bh/master/Bh/FormForHelper)
helper.

By default, Bh does not override the `form_for` method provided by [ActionView](http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for).
To apply Bootstrap classes and attributes, you **must** set the `:layout` option to

* `:basic`, in order to get a [Basic form](http://getbootstrap.com/css/#forms-example)
* `:horizontal`, in order to get a [Horizontal form](http://getbootstrap.com/css/#forms-horizontal)
* `:inline`, in order to get an [Inline form](http://getbootstrap.com/css/#forms-inline)

Here is how a form with a text field and a submit button looks like with each layout:

```rhtml
<%= form_for @user, layout: :basic do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
```

![form-for-basic](https://cloud.githubusercontent.com/assets/7408595/4015592/30611478-2a2c-11e4-8e62-b60e2151ff12.png)


```rhtml
<%= form_for @user, layout: :horizontal do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
```

![form-for-horizontal](https://cloud.githubusercontent.com/assets/7408595/4015593/30620ba8-2a2c-11e4-90c9-8340b5ddc113.png)

```rhtml
<%= form_for @user, layout: :inline do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
```

![form-for-inline](https://cloud.githubusercontent.com/assets/7408595/4015591/30609b74-2a2c-11e4-989e-e509d72ed224.png)


How to release new versions
===========================
Expand Down
1 change: 1 addition & 0 deletions bh.gemspec
Expand Up @@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'rake' #, '~> 10.0'
spec.add_development_dependency 'yard' #, '~> 0.8.0'
spec.add_development_dependency 'coveralls' #, '~> 0.7.0'
spec.add_development_dependency 'activemodel'
end
1 change: 1 addition & 0 deletions gemfiles/Gemfile.rails-3.x
Expand Up @@ -2,4 +2,5 @@ source 'http://rubygems.org'

gem 'activesupport', '~> 3.0'
gem 'actionpack', '~> 3.0'
gem 'activemodel', '~> 3.0'
gemspec path: '../'
1 change: 1 addition & 0 deletions gemfiles/Gemfile.rails-4.x
Expand Up @@ -2,4 +2,5 @@ source 'http://rubygems.org'

gem 'activesupport', '~> 4.0'
gem 'actionpack', '~> 4.0'
gem 'activemodel', '~> 4.0'
gemspec path: '../'
41 changes: 41 additions & 0 deletions lib/bh/form_builders/form_builder.rb
@@ -0,0 +1,41 @@
require 'bh/helpers/form/check_box_helper'
require 'bh/helpers/form/field_helper'
require 'bh/helpers/form/fieldset_helper'
require 'bh/helpers/form/fields_for_helper'
require 'bh/helpers/form/legend_helper'
require 'bh/helpers/form/radio_button_helper'
require 'bh/helpers/form/select_helper'
require 'bh/helpers/form/static_control_helper'
require 'bh/helpers/form/submit_helper'

module Bh
class FormBuilder < ActionView::Helpers::FormBuilder
include Form::CheckBoxHelper
include Form::FieldHelper
include Form::FieldsetHelper
include Form::FieldsForHelper
include Form::LegendHelper
include Form::RadioButtonHelper
include Form::SelectHelper
include Form::StaticControlHelper
include Form::SubmitHelper

# @note: field_helpers are returned as symbols in ActionView 4 and as
# strings in ActionView 3
def self.textual_field_helpers
non_textual_field_helpers = %w(label hidden_field range_field check_box
radio_button select submit fields_for label)
field_helpers.map(&:to_s) - non_textual_field_helpers
end

# Use the same template for all the textual field helpers such as
# email_field, password_field, etc.
# Exclude the ones that should not have additional styles.
# Do not show error icons on number_field not to cover the sliders.
textual_field_helpers.each do |field_type|
define_method field_type do |method, options = {}|
field(method, field_type, options) { super method, options }
end
end
end
end
7 changes: 2 additions & 5 deletions lib/bh/helpers/alert_helper.rb
@@ -1,11 +1,8 @@
require 'action_view'
require 'bh/helpers/base_helper'

module Bh
module AlertHelper
include ActionView::Helpers::TagHelper # for content_tag
include ActionView::Context # for capture
include ActionView::Helpers::OutputSafetyHelper # for safe_join

include BaseHelper
# Returns an HTML block tag that follows the Bootstrap documentation
# on how to display *alert boxes*.
# Alert boxes provide contextual feedback messages for typical user
Expand Down
17 changes: 17 additions & 0 deletions lib/bh/helpers/base_helper.rb
@@ -0,0 +1,17 @@
require 'action_view'

module Bh
module BaseHelper
include ActionView::Helpers::TagHelper # for content_tag
include ActionView::Context # for capture
include ActionView::Helpers::OutputSafetyHelper # for safe_join
include ActionView::Helpers::RenderingHelper # for render

private

def append_class!(hash, new_class)
existing_class = hash[:class]
hash[:class] = [existing_class, new_class].compact.join ' '
end
end
end
112 changes: 112 additions & 0 deletions lib/bh/helpers/form/base_helper.rb
@@ -0,0 +1,112 @@
require 'bh/helpers/glyphicon_helper'

module Bh
module Form
module BaseHelper
include GlyphiconHelper # for glyphicon

private

def base_field(method, field_type, options = {}, &block)
errors = (object.errors.get method if object) || {}
label = label_for method, errors, options
field = field_container(options) do
field_tags errors, field_type, &block
end
label_and_field = safe_join [label, field].compact
label_and_field_container label_and_field, field_type, errors
end


def field_container(options = {}, &block)
if horizontal_form?
klass = [('col-sm-offset-3' if options[:offset]), 'col-sm-9']
content_tag :div, class: klass.compact.join(' '), &block
else
yield
end
end

def field_tags(errors, field_type, &block)
tags = [@template.capture(&block)]
tags << error_icon_tag if errors.any? && show_error_icon?(field_type)
tags << error_help_tag(errors) if errors.any? && show_error_help?
safe_join tags
end

def label_and_field_container(label_and_field, field_type, errors = {})
klass = ['form-group']
if errors.any?
klass << 'has-error'
klass << 'has-feedback' if show_error_icon?(field_type)
end
content_tag :div, label_and_field, class: klass.join(' ')
end


def show_error_icon?(field_type)
hide = [:checkbox, :number_field, :radio_button, :select, :legend].include? field_type
@options.fetch(:errors, {}).fetch(:icons, true) && !hide
end


def error_icon_tag
glyphicon :remove, class: 'form-control-feedback'
end

def error_help_tag(errors)
klass = ['help-block', 'text-left']
klass << 'sr-only' if inline_form?
content_tag :span, errors.to_sentence, class: klass.join(' ')
end

def label_for(method, errors, options)
if options.delete(:use_label) { true }
args = [method, options.delete(:label), label_options(errors)]
label *args.compact
end
end

def label_options(errors = {})
klass = []
klass << 'sr-only' if inline_form?
klass << 'col-sm-3' if horizontal_form?
klass << 'control-label' if horizontal_form?
klass << 'control-label' if basic_form? && errors.any?
{class: klass.join(' ')} if klass.any?
end

# Rails adds <div class='field_with_errors'> which messes up
# Bootstrap inline form unless the label is inserted within
# the div itself.
def label_and_field(container_class, method, options = {}, &block)
label_and_field = @template.capture(&block)
label = options.delete(:label)
if index = label_and_field =~ %r{</div>$}
label_and_field.insert index, " #{label}"
else
label_and_field.concat " #{label}"
end
content_tag :div, class: container_class do
content_tag :label, label_and_field
end
end

def show_error_help?
@options.fetch(:errors, {}).fetch(:messages, :inline).to_s == 'inline'
end

def inline_form?
@options[:layout].to_s == 'inline'
end

def horizontal_form?
@options[:layout].to_s == 'horizontal'
end

def basic_form?
!inline_form? && !horizontal_form?
end
end
end
end
35 changes: 35 additions & 0 deletions lib/bh/helpers/form/check_box_helper.rb
@@ -0,0 +1,35 @@
require 'bh/helpers/form/base_helper'

module Bh
module Form
module CheckBoxHelper
include BaseHelper

def check_box(method, options = {}, checked_value = '1', unchecked_value = '0')
block = -> { super method, options, checked_value, unchecked_value }
if options.delete(:inline_label) { true }
check_box_with_inline_label method, options, &block
else
check_box_with_block_label method, options, &block
end
end

private

def check_box_with_block_label(method, options = {}, &block)
append_class! options, 'form-control' unless inline_form?
append_class! options, 'checkbox' if horizontal_form?
options[:label] ||= method.to_s.humanize
base_field method, :checkbox, options, &block
end

def check_box_with_inline_label(method, options = {}, &block)
options.merge! offset: true, use_label: false
options[:label] ||= method.to_s.humanize
base_field method, :checkbox, options do
label_and_field 'checkbox', method, options, &block
end
end
end
end
end
15 changes: 15 additions & 0 deletions lib/bh/helpers/form/field_helper.rb
@@ -0,0 +1,15 @@
require 'bh/helpers/form/base_helper'

module Bh
module Form
module FieldHelper
include BaseHelper

def field(method, field_type, options = {}, &block)
options[:placeholder] ||= method.to_s.humanize
append_class! options, 'form-control'
base_field method, field_type, options, &block
end
end
end
end
17 changes: 17 additions & 0 deletions lib/bh/helpers/form/fields_for_helper.rb
@@ -0,0 +1,17 @@
require 'bh/helpers/form/fieldset_helper'

module Bh
module Form
module FieldsForHelper
include BaseHelper
include FieldsetHelper # for fieldset

def fields_for(record_object = nil, fields_options = {}, &block)
fields_options[:layout] ||= @options[:layout]
fields_options[:errors] ||= @options[:errors]
title = fields_options.delete(:title) { record_object.to_s.humanize }
fieldset(title) { super record_object, fields_options, &block }
end
end
end
end

0 comments on commit 7e5a069

Please sign in to comment.