Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge remote-tracking branch 'origin/bootstrap-2.0'

Conflicts:
	lib/twitter_bootstrap_form_for/form_builder.rb
  • Loading branch information...
commit 1dd4e0ac358fcf8b77fc960ff4376153d5cc1123 2 parents b581853 + a1ed217
@jlogsdon authored
View
46 README.markdown
@@ -18,10 +18,11 @@ Just Rails. But you were going to use that anyway, weren't you?
## Syntax ##
```haml
-= twitter_bootstrap_form_for @user do |user|
+/ supports both vertical and horizontal forms
+= twitter_bootstrap_form_for @user, :html => { :class => 'form-horizontal'} do |user|
/ wraps a section in a fieldset with the provided legend text
- = user.inputs 'Sign up', :class => 'sign_up' do
+ = user.fieldset 'Sign up', :class => 'sign_up' do
/ generates a standard email field
= user.email_field :email, :placeholder => 'me@example.com'
@@ -40,29 +41,29 @@ Just Rails. But you were going to use that anyway, weren't you?
%span.add-on @
/ select fields now have the second parameter as a label
- = user.date_select :born_on, 'Born on', {}, :class => 'small'
+ = user.date_select :born_on, 'Born on', {}, :class => 'span2'
- / inline inputs are not automatically labeled
- = user.inline 'Interests' do |inline|
- #{inline.text_field :interest_1, :class => 'small'},
- #{inline.text_field :interest_2, :class => 'small'}, and
- #{inline.text_field :interest_3, :class => 'small'}
+ / inline inputs
+ = user.label 'Interests' do |controls|
+ #{controls.text_field :interest_1, :class => 'span2 inline'},
+ #{controls.text_field :interest_2, :class => 'span2 inline'}, and
+ #{controls.text_field :interest_3, :class => 'span2 inline'}
/ group of radio buttons
- = user.toggles 'Email Preferences' do
- = user.radio_button :email, 'HTML Email', :html, :checked => true
- = user.radio_button :email, 'Plain Text', :plain
+ = user.label 'Email Preferences' do |controls|
+ = controls.radio_button :email, :html, 'HTML Email', :checked => true
+ = controls.radio_button :email, :plain, 'Plain Text'
/ group of checkboxes
- = user.toggles 'Agreements' do
- = user.check_box :agree, 'I agree to the abusive Terms and Conditions'
- = user.check_box :spam, 'I agree to receive all sorts of spam'
- = user.check_box :spammer, 'I agree to let the site spam others through my Twitter account'
-
- / wraps buttons in a distinctive style
- = user.actions do
- = user.submit 'Sign up'
- = button_tag 'Cancel', :type => 'reset', :class => 'btn'
+ = user.label 'Agreements' do |controls|
+ = controls.check_box :agree, 'I agree to the abusive Terms and Conditions'
+ = controls.check_box :spam, 'I agree to receive all sorts of spam'
+ = controls.check_box :spammer, 'I agree to let the site spam others through my Twitter account'
+
+ / wraps buttons in a distinctive style
+ = user.actions do
+ = user.submit 'Sign up'
+ = user.button 'Cancel'
```
That code produces the following output, with no custom stylesheets.
@@ -82,9 +83,4 @@ simple:
* the last options hash accepts an `:add_on` key
* if a block is passed, the HTML it outputs is placed immediately after the input
-## Known Bugs ##
-
- - inline fields don't receive error markup ([issue #28])
-
[Twitter Bootstrap]: http://twitter.github.com/bootstrap/
-[issue #28]: https://github.com/stouset/twitter_bootstrap_form_for/issues/28
View
BIN  examples/screenshot.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
222 lib/twitter_bootstrap_form_for/form_builder.rb
@@ -26,32 +26,59 @@ class TwitterBootstrapFormFor::FormBuilder < ActionView::Helpers::FormBuilder
# Wraps the contents of the block passed in a fieldset with optional
# +legend+ text.
#
- def inputs(legend = nil, options = {}, &block)
+ def fieldset(legend = nil, options = {})
template.content_tag(:fieldset, options) do
template.concat template.content_tag(:legend, legend) unless legend.nil?
- block.call
+ yield
end
end
#
- # Wraps groups of toggles (radio buttons, checkboxes) with a single label
- # and the appropriate markup. All toggle buttons should be rendered
- # inside of here, and will not look correct unless they are.
+ # Wraps action buttons into their own styled container.
+ #
+ def actions(&block)
+ template.content_tag(:div, :class => 'form-actions', &block)
+ end
+
#
- def toggles(label = nil, &block)
- template.content_tag(:div, :class => 'clearfix') do
- template.concat template.content_tag(:label, label)
- template.concat template.content_tag(:div, :class => "input") {
- template.content_tag(:ul, :class => "inputs-list") { block.call }
+ # Attaches a label to the inputs rendered inside of the block passed to it.
+ # Associates the label with the input for the +attribute+ given. If +text+
+ #is passed, uses that as the text for the label; otherwise humanizes the
+ # +attribute+ name.
+ #
+ def label(attribute, text = '', options = {}, &block)
+ text, attribute = attribute, nil if attribute.kind_of? String
+
+ options = { :class => 'control-label' }.merge(options)
+ id = _wrapper_id attribute, 'control_group'
+ classes = _wrapper_classes attribute, 'control-group'
+
+ template.content_tag(:div, :id => id, :class => classes) do
+ template.concat case
+ when attribute && text then super(attribute, text, options, &nil)
+ when attribute then super(attribute, nil, options, &nil)
+ when text then template.label_tag(nil, text, options, &nil)
+ end
+
+ template.concat template.content_tag(:div, :class => 'controls') {
+ template.fields_for(
+ self.object_name,
+ self.object,
+ self.options.merge(:builder => TwitterBootstrapFormFor::FormControls),
+ &block
+ )
}
end
end
#
- # Wraps action buttons into their own styled container.
+ # Renders a button with default classes to style it as a form button.
#
- def actions(&block)
- template.content_tag(:div, :class => 'actions', &block)
+ def button(value = nil, options = {})
+ super value, {
+ :type => 'button',
+ :class => 'btn',
+ }.merge(options)
end
#
@@ -59,124 +86,50 @@ def actions(&block)
# button.
#
def submit(value = nil, options = {})
- options[:class] ||= 'btn primary'
-
- super value, options
- end
-
- #
- # Creates bootstrap wrapping before yielding a plain old rails builder
- # to the supplied block.
- #
- def inline(label = nil, &block)
- template.content_tag(:div, :class => 'clearfix') do
- template.concat template.content_tag(:label, label) if label.present?
- template.concat template.content_tag(:div, :class => 'input') {
- template.content_tag(:div, :class => 'inline-inputs') do
- template.fields_for(
- self.object_name,
- self.object,
- self.options.merge(:builder => ActionView::Helpers::FormBuilder),
- &block
- )
- end
- }
- end
+ self.button value, {
+ :type => 'submit',
+ :class => 'btn btn-primary',
+ }.merge(options)
end
INPUTS.each do |input|
define_method input do |attribute, *args, &block|
- options = args.extract_options!
- label = args.first.nil? ? '' : args.shift
- classes = [ 'input' ]
- classes << ('input-' + options.delete(:add_on).to_s) if options[:add_on]
-
- self.div_wrapper(attribute) do
- template.concat self.label(attribute, label) if label
- template.concat template.content_tag(:div, :class => classes.join(' ')) {
- template.concat super(attribute, *(args << options))
- template.concat error_span(attribute)
- block.call if block.present?
- }
- end
- end
- end
+ options = args.extract_options!
+ text = args.any? ? args.shift : ''
- TOGGLES.each do |toggle|
- define_method toggle do |attribute, *args, &block|
- label = args.first.nil? ? '' : args.shift
- target = self.object_name.to_s + '_' + attribute.to_s
- label_attrs = toggle == :check_box ? { :for => target } : {}
-
- template.content_tag(:li) do
- template.concat template.content_tag(:label, label_attrs) {
- template.concat super(attribute, *args)
- template.concat ' ' # give the input and span some room
- template.concat template.content_tag(:span, label)
- }
- if toggle == :check_box
- template.concat template.content_tag(:div, :class => "clearfix error") {
- template.concat error_span(attribute)
- } if errors_on?(attribute)
- end
+ self.label(attribute, text) do |builder|
+ builder.send(input, attribute, *(args << options), &block)
end
end
end
protected
- #
- # Wraps the contents of +block+ inside a +tag+ with an appropriate class and
- # id for the object's +attribute+. HTML options can be overridden by passing
- # an +options+ hash.
- #
- def div_wrapper(attribute, options = {}, &block)
- options[:id] = _wrapper_id attribute, options[:id]
- options[:class] = _wrapper_classes attribute, options[:class], 'clearfix'
-
- template.content_tag :div, options, &block
- end
-
- def error_span(attribute, options = {})
- options[:class] ||= 'help-inline'
-
- template.content_tag(
- :span, self.errors_for(attribute),
- :class => options[:class]
- ) if self.errors_on?(attribute)
- end
-
def errors_on?(attribute)
self.object.errors[attribute].present? if self.object.respond_to?(:errors)
end
- def errors_for(attribute)
- self.object.errors[attribute].try(:join, ', ')
- end
-
private
#
# Returns an HTML id to uniquely identify the markup around an input field.
- # If a +default+ is provided, it uses that one instead.
#
- def _wrapper_id(attribute, default = nil)
- default || [
+ def _wrapper_id(attribute, suffix = nil)
+ [
_object_name + _object_index,
_attribute_name(attribute),
- 'input'
- ].join('_')
+ suffix,
+ ].compact.join('_') if attribute
end
#
# Returns any classes necessary for the wrapper div around fields for
# +attribute+, such as 'errors' if any errors are present on the attribute.
- # This merges any +classes+ passed in.
+ # Merges any +classes+ passed in.
#
def _wrapper_classes(attribute, *classes)
- classes.compact.tap do |klasses|
- klasses.push 'error' if self.errors_on?(attribute)
- end.join(' ')
+ classes.push 'error' if attribute and self.errors_on?(attribute)
+ classes.compact.join(' ')
end
def _attribute_name(attribute)
@@ -195,3 +148,68 @@ def _object_index
end.to_s
end
end
+
+class TwitterBootstrapFormFor::FormControls < ActionView::Helpers::FormBuilder
+ attr_reader :template
+ attr_reader :object
+ attr_reader :object_name
+
+ TwitterBootstrapFormFor::FormBuilder::INPUTS.each do |input|
+ define_method input do |attribute, *args, &block|
+ options = args.extract_options!
+ add_on = options.delete(:add_on)
+ tag = add_on.present? ? :div : :span
+ classes = [ "input", add_on ].compact.join('-')
+
+ template.content_tag(tag, :class => classes) do
+ template.concat super attribute, *(args << options)
+ template.concat self.error_span(attribute) if self.errors_on?(attribute)
+ block.call if block.present?
+ end
+ end
+ end
+
+ def check_box(attribute, text, options = {}, checked_value = 1, unchecked_value = 0)
+ klasses = _merge_classes 'checkbox', options.delete(:inline) && 'inline'
+
+ self.label(attribute, :class => klasses) do
+ template.concat super(attribute, options, checked_value, unchecked_value)
+ template.concat text
+ yield if block_given?
+ end
+ end
+
+ def radio_button(attribute, value, text = nil, options = {})
+ klasses = _merge_classes 'radio', options.delete(:inline) && 'inline'
+
+ self.label(attribute, :class => klasses) do
+ template.concat super(attribute, value, options)
+ template.concat text || value.to_s.humanize.titleize
+ yield if block_given?
+ end
+ end
+
+ protected
+
+ def error_span(attribute, options = {})
+ options[:class] = _merge_classes options[:class], 'help-inline'
+
+ template.content_tag :span,
+ self.errors_for(attribute),
+ :class => options[:class]
+ end
+
+ def errors_for(attribute)
+ self.object.errors[attribute].try(:join, ', ')
+ end
+
+ def errors_on?(attribute)
+ self.object.errors[attribute].present? if self.object.respond_to?(:errors)
+ end
+
+ private
+
+ def _merge_classes(string, *classes)
+ string.to_s.split(' ').push(*classes.compact).join(' ')
+ end
+end
View
2  lib/twitter_bootstrap_form_for/form_helpers.rb
@@ -18,7 +18,7 @@ module TwitterBootstrapFormFor::FormHelpers
private
- BLANK_FIELD_ERROR_PROC = lambda {|input, _| input }
+ BLANK_FIELD_ERROR_PROC = lambda {|input, *_| input }
def _override_field_error_proc
original_field_error_proc = ::ActionView::Base.field_error_proc
View
2  lib/twitter_bootstrap_form_for/version.rb
@@ -1,3 +1,3 @@
module TwitterBootstrapFormFor
- VERSION = '1.0.5'
+ VERSION = '2.0.1.0.rc1'
end
Please sign in to comment.
Something went wrong with that request. Please try again.