formular 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +29 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +105 -0
- data/Rakefile +12 -0
- data/formular.gemspec +33 -0
- data/lib/formular.rb +8 -0
- data/lib/formular/attributes.rb +45 -0
- data/lib/formular/builder.rb +52 -0
- data/lib/formular/builders/basic.rb +64 -0
- data/lib/formular/builders/bootstrap3.rb +28 -0
- data/lib/formular/builders/bootstrap3_horizontal.rb +32 -0
- data/lib/formular/builders/bootstrap3_inline.rb +10 -0
- data/lib/formular/builders/bootstrap4.rb +36 -0
- data/lib/formular/builders/bootstrap4_horizontal.rb +39 -0
- data/lib/formular/builders/bootstrap4_inline.rb +15 -0
- data/lib/formular/builders/foundation6.rb +28 -0
- data/lib/formular/element.rb +135 -0
- data/lib/formular/element/bootstrap3.rb +70 -0
- data/lib/formular/element/bootstrap3/checkable_control.rb +105 -0
- data/lib/formular/element/bootstrap3/column_control.rb +45 -0
- data/lib/formular/element/bootstrap3/horizontal.rb +146 -0
- data/lib/formular/element/bootstrap3/input_group.rb +83 -0
- data/lib/formular/element/bootstrap4.rb +49 -0
- data/lib/formular/element/bootstrap4/checkable_control.rb +94 -0
- data/lib/formular/element/bootstrap4/custom_control.rb +120 -0
- data/lib/formular/element/bootstrap4/horizontal.rb +87 -0
- data/lib/formular/element/foundation6.rb +69 -0
- data/lib/formular/element/foundation6/checkable_control.rb +72 -0
- data/lib/formular/element/foundation6/input_group.rb +88 -0
- data/lib/formular/element/foundation6/wrapped_control.rb +21 -0
- data/lib/formular/element/module.rb +35 -0
- data/lib/formular/element/modules/checkable.rb +96 -0
- data/lib/formular/element/modules/collection.rb +17 -0
- data/lib/formular/element/modules/container.rb +60 -0
- data/lib/formular/element/modules/control.rb +42 -0
- data/lib/formular/element/modules/error.rb +48 -0
- data/lib/formular/element/modules/hint.rb +36 -0
- data/lib/formular/element/modules/label.rb +30 -0
- data/lib/formular/element/modules/wrapped_control.rb +73 -0
- data/lib/formular/elements.rb +295 -0
- data/lib/formular/helper.rb +53 -0
- data/lib/formular/html_block.rb +70 -0
- data/lib/formular/path.rb +30 -0
- data/lib/formular/version.rb +3 -0
- 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
|