formular 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +29 -0
  4. data/CHANGELOG.md +3 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +105 -0
  8. data/Rakefile +12 -0
  9. data/formular.gemspec +33 -0
  10. data/lib/formular.rb +8 -0
  11. data/lib/formular/attributes.rb +45 -0
  12. data/lib/formular/builder.rb +52 -0
  13. data/lib/formular/builders/basic.rb +64 -0
  14. data/lib/formular/builders/bootstrap3.rb +28 -0
  15. data/lib/formular/builders/bootstrap3_horizontal.rb +32 -0
  16. data/lib/formular/builders/bootstrap3_inline.rb +10 -0
  17. data/lib/formular/builders/bootstrap4.rb +36 -0
  18. data/lib/formular/builders/bootstrap4_horizontal.rb +39 -0
  19. data/lib/formular/builders/bootstrap4_inline.rb +15 -0
  20. data/lib/formular/builders/foundation6.rb +28 -0
  21. data/lib/formular/element.rb +135 -0
  22. data/lib/formular/element/bootstrap3.rb +70 -0
  23. data/lib/formular/element/bootstrap3/checkable_control.rb +105 -0
  24. data/lib/formular/element/bootstrap3/column_control.rb +45 -0
  25. data/lib/formular/element/bootstrap3/horizontal.rb +146 -0
  26. data/lib/formular/element/bootstrap3/input_group.rb +83 -0
  27. data/lib/formular/element/bootstrap4.rb +49 -0
  28. data/lib/formular/element/bootstrap4/checkable_control.rb +94 -0
  29. data/lib/formular/element/bootstrap4/custom_control.rb +120 -0
  30. data/lib/formular/element/bootstrap4/horizontal.rb +87 -0
  31. data/lib/formular/element/foundation6.rb +69 -0
  32. data/lib/formular/element/foundation6/checkable_control.rb +72 -0
  33. data/lib/formular/element/foundation6/input_group.rb +88 -0
  34. data/lib/formular/element/foundation6/wrapped_control.rb +21 -0
  35. data/lib/formular/element/module.rb +35 -0
  36. data/lib/formular/element/modules/checkable.rb +96 -0
  37. data/lib/formular/element/modules/collection.rb +17 -0
  38. data/lib/formular/element/modules/container.rb +60 -0
  39. data/lib/formular/element/modules/control.rb +42 -0
  40. data/lib/formular/element/modules/error.rb +48 -0
  41. data/lib/formular/element/modules/hint.rb +36 -0
  42. data/lib/formular/element/modules/label.rb +30 -0
  43. data/lib/formular/element/modules/wrapped_control.rb +73 -0
  44. data/lib/formular/elements.rb +295 -0
  45. data/lib/formular/helper.rb +53 -0
  46. data/lib/formular/html_block.rb +70 -0
  47. data/lib/formular/path.rb +30 -0
  48. data/lib/formular/version.rb +3 -0
  49. metadata +247 -0
@@ -0,0 +1,87 @@
1
+ require 'formular/element'
2
+ require 'formular/elements'
3
+ require 'formular/element/module'
4
+ require 'formular/element/bootstrap4'
5
+ require 'formular/element/bootstrap4/checkable_control'
6
+ require 'formular/element/bootstrap4/custom_control'
7
+ require 'formular/element/bootstrap3/horizontal'
8
+ module Formular
9
+ class Element
10
+ module Bootstrap4
11
+ module Horizontal
12
+ class Label < Formular::Element::Label
13
+ set_default :class, :column_class
14
+
15
+ def column_class
16
+ builder.class.column_classes[:left_column] + ['col-form-label']
17
+ end
18
+ end # class Label
19
+
20
+ class Legend < Formular::Element::Legend
21
+ set_default :class, :column_class
22
+
23
+ def column_class
24
+ builder.class.column_classes[:left_column] + ['col-form-legend']
25
+ end
26
+ end # class Legend
27
+
28
+ class Input < Formular::Element::Bootstrap4::Input
29
+ include Formular::Element::Bootstrap3::Horizontal::WrappedControl
30
+ end # class Input
31
+
32
+ class CustomFile < Formular::Element::Bootstrap4::CustomFile
33
+ include Formular::Element::Bootstrap3::Horizontal::WrappedControl
34
+ end # class CustomFile
35
+
36
+ class CustomSelect < Formular::Element::Bootstrap4::CustomSelect
37
+ include Formular::Element::Bootstrap3::Horizontal::WrappedControl
38
+ end # class CustomFile
39
+
40
+ class Submit < Formular::Element::Bootstrap4::Submit
41
+ self.html_context = :wrapped
42
+
43
+ html(:wrapped) do |input|
44
+ input.wrapper do |wrapper|
45
+ wrapper.input_column_wrapper(
46
+ class: input.builder.class.column_classes[:left_offset],
47
+ content: input.to_html(context: :default)
48
+ )
49
+ end
50
+ end
51
+ end # class Submit
52
+
53
+ class Checkbox < Formular::Element::Bootstrap4::StackedCheckbox
54
+ include Formular::Element::Bootstrap3::Horizontal::WrappedCheckableControl
55
+ end # class Checkbox
56
+
57
+ class CustomStackedCheckbox < Formular::Element::Bootstrap4::CustomStackedCheckbox
58
+ include Formular::Element::Bootstrap3::Horizontal::WrappedCheckableControl
59
+ end # class CustomStackedCheckbox
60
+
61
+ class Radio < Formular::Element::Bootstrap4::StackedRadio
62
+ include Formular::Element::Bootstrap3::Horizontal::WrappedCheckableControl
63
+ end # class Radio
64
+
65
+ class CustomStackedRadio < Formular::Element::Bootstrap4::CustomStackedRadio
66
+ include Formular::Element::Bootstrap3::Horizontal::WrappedCheckableControl
67
+ end # class CustomStackedRadio
68
+
69
+ class InlineCheckbox < Formular::Element::Bootstrap4::InlineCheckbox
70
+ include Formular::Element::Bootstrap3::Horizontal::WrappedCheckableControl
71
+ end # class InlineCheckbox
72
+
73
+ class CustomCheckbox < Formular::Element::Bootstrap4::Inline::CustomCheckbox
74
+ include Formular::Element::Bootstrap3::Horizontal::WrappedCheckableControl
75
+ end # class CustomCheckbox
76
+
77
+ class InlineRadio < Formular::Element::Bootstrap4::InlineRadio
78
+ include Formular::Element::Bootstrap3::Horizontal::WrappedCheckableControl
79
+ end # class InlineRadio
80
+
81
+ class CustomRadio < Formular::Element::Bootstrap4::Inline::CustomRadio
82
+ include Formular::Element::Bootstrap3::Horizontal::WrappedCheckableControl
83
+ end # class CustomRadio
84
+ end # module Horizontal
85
+ end # module Bootstrap4
86
+ end # class Element
87
+ end # module Formular
@@ -0,0 +1,69 @@
1
+ require 'formular/elements'
2
+ require 'formular/element/modules/wrapped_control'
3
+ require 'formular/element/module'
4
+ require 'formular/element/foundation6/checkable_control'
5
+ require 'formular/element/foundation6/wrapped_control'
6
+ module Formular
7
+ class Element
8
+ module Foundation6
9
+ include CheckableControl
10
+
11
+ class ErrorNotification < Formular::Element::ErrorNotification
12
+ set_default :class, ['callout alert']
13
+ end
14
+
15
+ module InputWithErrors
16
+ include Formular::Element::Module
17
+ set_default :class, ['is-invalid-input'], if: :has_errors?
18
+ end # module InputWithErrors
19
+
20
+ class Submit < Formular::Element::Button
21
+ set_default :class, ['success', 'button']
22
+ set_default :type, 'submit'
23
+ end # class Submit
24
+
25
+ class LabelWithError < Formular::Element::Label
26
+ set_default :class, ['is-invalid-label']
27
+ end # class LabelWithError
28
+
29
+ class Error < Formular::Element::Error
30
+ tag :span
31
+ set_default :class, ['form-error', 'is-visible']
32
+ end # class Error
33
+
34
+ class Hint < Formular::Element::P
35
+ set_default :class, ['help-text']
36
+ end # class Hint
37
+
38
+ class Input < Formular::Element::Input
39
+ include WrappedControl
40
+ include InputWithErrors
41
+ end # class Input
42
+
43
+ class File < Input
44
+ set_default :type, 'file'
45
+ set_default :class, ['show-for-sr']
46
+ set_default :label_options, { class: ['button'] }
47
+
48
+ self.html_context = :wrapped
49
+
50
+ html(:wrapped) do |input|
51
+ concat input.label
52
+ concat input.to_html(context: :default)
53
+ concat input.hint
54
+ concat input.error
55
+ end
56
+ end # class File
57
+
58
+ class Select < Formular::Element::Select
59
+ include WrappedControl
60
+ include InputWithErrors
61
+ end # class Select
62
+
63
+ class Textarea < Formular::Element::Textarea
64
+ include WrappedControl
65
+ include InputWithErrors
66
+ end # class Textarea
67
+ end # module Foundation6
68
+ end # class Element
69
+ end # module Formular
@@ -0,0 +1,72 @@
1
+ require 'formular/elements'
2
+ require 'formular/element/module'
3
+ require 'formular/element/foundation6/wrapped_control'
4
+ module Formular
5
+ class Element
6
+ module Foundation6
7
+ module CheckableControl
8
+ module Checkable
9
+ include Formular::Element::Module
10
+ include Formular::Element::Foundation6::WrappedControl
11
+
12
+ set_default :label_options, { class: ['is-invalid-label'] }, if: :has_errors?
13
+ set_default :control_label_options, { class: ['is-invalid-label'] }, if: :has_errors?
14
+
15
+ html(:wrapped) do |input|
16
+ input.wrapper do
17
+ concat input.group_label
18
+ concat input.hidden_tag unless input.collection?
19
+ concat input.to_html(context: :collection)
20
+ concat input.hidden_tag if input.collection?
21
+ concat input.hint
22
+ concat input.error
23
+ end
24
+ end
25
+
26
+ module InstanceMethods
27
+ def wrapper(&block)
28
+ builder.fieldset(Attributes[options[:wrapper_options]], &block)
29
+ end
30
+ end
31
+ end # class Checkable
32
+
33
+ module StackedCheckable
34
+ include Formular::Element::Module
35
+ include Checkable
36
+
37
+ html(:collection) do |input|
38
+ input.collection.map { |control|
39
+ input.builder.div(content: control.to_html(context: :checkable_label))
40
+ }.join('')
41
+ end
42
+ end # class StackedCheckable
43
+
44
+ class Checkbox < Formular::Element::Checkbox
45
+ include Checkable
46
+
47
+ set_default :value, '1' # instead of reader value
48
+ set_default :label_options, { class: ['is-invalid-label'] }, if: :has_errors?
49
+ set_default :control_label_options, { class: ['is-invalid-label'] }, if: :has_errors?
50
+
51
+ html { closed_start_tag }
52
+ end # class Checkbox
53
+
54
+ class Radio < Formular::Element::Radio
55
+ include Checkable
56
+
57
+ def hidden_tag
58
+ ''
59
+ end
60
+ end # class Radio
61
+
62
+ class StackedRadio < Radio
63
+ include StackedCheckable
64
+ end # class StackedRadio
65
+
66
+ class StackedCheckbox < Checkbox
67
+ include StackedCheckable
68
+ end # class StackedCheckbox
69
+ end # module CheckableControls
70
+ end #module Foundation6
71
+ end # class Element
72
+ end # module Formular
@@ -0,0 +1,88 @@
1
+ require 'formular/elements'
2
+ require 'formular/element/modules/container'
3
+ require 'formular/element/modules/wrapped_control'
4
+ module Formular
5
+ class Element
6
+ module Foundation6
7
+ class InputGroup < Formular::Element::Input
8
+ module WrappedGroup
9
+ include Formular::Element::Module
10
+ include Formular::Element::Modules::WrappedControl
11
+
12
+ def wrapper(&block)
13
+ builder.fieldset(Attributes[options[:wrapper_options]], &block)
14
+ end
15
+ end # module WrappedGroup
16
+
17
+
18
+ class Wrapper < Formular::Element::Div
19
+ set_default :class, ['input-group']
20
+ end # class Wrapper
21
+
22
+ class Label < Formular::Element::Span
23
+ set_default :class, ['input-group-label']
24
+ end # class Label
25
+
26
+ class Button < Formular::Element::Div
27
+ set_default :class, ['input-group-button']
28
+ end # class Button
29
+
30
+ include WrappedGroup
31
+ include Formular::Element::Modules::Container
32
+
33
+ set_default :class, :input_class # we need to do classes better...
34
+
35
+ add_option_keys :left_label, :right_label, :left_button, :right_button
36
+
37
+ html(:raw_input) { closed_start_tag }
38
+
39
+ html do |input|
40
+ content = input.has_content? ? input.content : input.to_html(context: :with_options)
41
+ Wrapper.(content: content)
42
+ end
43
+
44
+ def group_label(content = nil, option_key: nil)
45
+ return '' unless content || option_key
46
+ addon_content = content || options[option_key]
47
+ return '' unless addon_content
48
+ label_options = { content: addon_content }
49
+ label_options[:class] = ['is-invalid-label'] if has_errors?
50
+
51
+ Label.(label_options)
52
+ end
53
+
54
+ def group_button(content = nil, option_key: nil)
55
+ return '' unless content || option_key
56
+ addon_content = content || options[option_key]
57
+ return '' unless addon_content
58
+
59
+ Button.(content: addon_content)
60
+ end
61
+
62
+ def group_input
63
+ to_html(context: :raw_input)
64
+ end
65
+
66
+ def input_class
67
+ return ['input-group-field'] unless has_errors?
68
+
69
+ ['input-group-field', 'is-invalid-input']
70
+ end
71
+
72
+ def label_options
73
+ return super unless has_errors?
74
+
75
+ @label_options ||= Attributes[options[:label_options]].merge(class: ['is-invalid-label'])
76
+ end
77
+
78
+ html(:with_options) do |input|
79
+ concat input.group_label(option_key: :left_label)
80
+ concat input.group_button(option_key: :left_button)
81
+ concat input.group_input
82
+ concat input.group_label(option_key: :right_label)
83
+ concat input.group_button(option_key: :right_button)
84
+ end
85
+ end # class InputGroup
86
+ end #module Foundation6
87
+ end #class Element
88
+ end # module Foundation
@@ -0,0 +1,21 @@
1
+ require 'formular/element/module'
2
+ require 'formular/element/modules/wrapped_control'
3
+ module Formular
4
+ class Element
5
+ module Foundation6
6
+ module WrappedControl
7
+ include Formular::Element::Module
8
+ include Formular::Element::Modules::WrappedControl
9
+
10
+ html(:wrapped) do |input|
11
+ input.wrapper do
12
+ concat input.label_text
13
+ concat input.to_html(context: :default)
14
+ concat input.hint
15
+ concat input.error
16
+ end.to_s
17
+ end
18
+ end # module WrappedControl
19
+ end # module Foundation6
20
+ end # class Element
21
+ end # module Formular
@@ -0,0 +1,35 @@
1
+ # Include this in every module that gets further included.
2
+ # This is literally a copy and past from reform. Could we dry this up ??
3
+ # https://github.com/apotonick/reform/blob/master/lib/reform/form/module.rb
4
+
5
+ # require 'declarative/heritage'
6
+ module Formular
7
+ class Element
8
+ module Module
9
+ # DISCUSS: could this be part of Declarative?
10
+ def self.included(base)
11
+ base.extend ClassMethods
12
+ base.extend Declarative::Heritage::DSL # ::heritage
13
+ # base.extend Declarative::Heritage::Included # ::included
14
+ base.extend Included
15
+ end
16
+
17
+ module Included
18
+ # Gets imported into your module and will be run when including it.
19
+ def included(includer)
20
+ super
21
+ # first, replay all declaratives like ::property on includer.
22
+ heritage.(includer) # this normally happens via Heritage::Included.
23
+ # then, include optional accessors.
24
+ includer.send(:include, self::InstanceMethods) if const_defined?(:InstanceMethods)
25
+ end
26
+ end # module Included
27
+
28
+ module ClassMethods
29
+ def method_missing(method, *args, &block)
30
+ heritage.record(method, *args, &block)
31
+ end
32
+ end # module ClassMethods
33
+ end # module Module
34
+ end # class Element
35
+ end # module Formular
@@ -0,0 +1,96 @@
1
+ require 'formular/element/module'
2
+ require 'formular/element/modules/collection'
3
+ require 'formular/element/modules/label'
4
+
5
+ module Formular
6
+ class Element
7
+ module Modules
8
+ # this module is used to correctly set the :checked attribute
9
+ # based on the reader value.
10
+ # It also provides an API to assist in generating checkable html (e.g. checkable labels)
11
+ # and create collections of checkable controls from an enumerable variable.
12
+ module Checkable
13
+ include Formular::Element::Module
14
+ include Collection
15
+ include Label
16
+
17
+ add_option_keys :control_label_options
18
+
19
+ set_default :checked, 'checked', if: :is_checked?
20
+
21
+ html(:checkable_label) do |input|
22
+ Formular::Element::Label.(input.label_options) do
23
+ if has_label?
24
+ concat input.to_html(context: :default)
25
+ concat " #{input.label_text}"
26
+ else
27
+ to_html(context: :default)
28
+ end
29
+ end
30
+ end
31
+
32
+ html(:collection) do |input|
33
+ input.collection.map { |item|
34
+ item.to_html(context: :checkable_label)
35
+ }.join('')
36
+ end
37
+
38
+ module InstanceMethods
39
+ def group_label
40
+ return '' unless has_group_label?
41
+ label_options[:content] = label_text
42
+ builder.checkable_group_label(label_options).to_s
43
+ end
44
+
45
+ def has_group_label?
46
+ collection.size > 1 && has_label?
47
+ end
48
+
49
+ def collection
50
+ unless collection?
51
+ options[:label_options] = options[:control_label_options]
52
+ return [self]
53
+ end
54
+
55
+ base_options = collection_base_options
56
+
57
+ @collection ||= options[:collection].map do |item|
58
+ opts = base_options.dup
59
+ opts[:value] = item.send(options[:value_method])
60
+ opts[:label] = item.send(options[:label_method])
61
+
62
+ opts[:id] = if attributes[:id]
63
+ "#{attributes[:id]}_#{opts[:value]}"
64
+ else
65
+ "#{attribute_name || attributes[:name].gsub('[]', '')}_#{opts[:value]}"
66
+ end
67
+
68
+ self.class.(opts)
69
+ end
70
+ end
71
+
72
+ def collection?
73
+ options[:collection]
74
+ end
75
+
76
+ private
77
+
78
+ # we can't access other defaults
79
+ def is_checked?
80
+ !options[:checked].nil? || reader_value == attributes[:value]
81
+ end
82
+
83
+ def collection_base_options
84
+ opts = attributes.select { |k, v| ![:name, :id, :checked, :class].include?(k) } #FIXME due to class merging, we'll end up with duplicate classes...
85
+ opts[:attribute_name] = attribute_name if attribute_name
86
+ opts[:builder] = builder if builder
87
+ opts[:label_options] = options[:control_label_options] if options[:control_label_options]
88
+ opts[:name] = attributes[:name] if attributes[:name]
89
+
90
+ opts
91
+ end
92
+ end
93
+ end
94
+ end # module Modules
95
+ end # class Element
96
+ end # module Formular