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.
- 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
|