formular 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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