bootstrap4_helper 0.0.0 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,106 @@
1
+ module Bootstrap4Helper
2
+ # This super class is meant to contain commonly used methods that
3
+ # all sub classes can leverage.
4
+ #
5
+ # @note Every component that inherits from this class, needs to call the parent
6
+ # initialization method! In order to properly render erb blocks within the
7
+ # proper context, we need the template. The only way to get this, is to pass
8
+ # in the template.
9
+ #
10
+ # @note the `context` mentioned above, refers to the context of `@template` and
11
+ # not to be confused with `@context` that can be found in the sub classes.
12
+ # `@context` refers to the Bootstrap class context of the component.
13
+ #
14
+ class Component
15
+ # Used to ensure that the helpers always have the propert context for
16
+ # rendering and bindings.
17
+ #
18
+ # @param [Class] template
19
+ #
20
+ def initialize(template)
21
+ @template = template
22
+ end
23
+
24
+ # Used to pass all context of content_tag to the template. This ensures
25
+ # proper template binding of variables and methods!
26
+ #
27
+ # @param [String] name
28
+ # @param [Hash|NilClass] content_or_options_with_block
29
+ # @param [Hash|NilClass] options
30
+ # @param [Boolean] escape
31
+ # @return [String]
32
+ #
33
+ def content_tag(
34
+ name,
35
+ content_or_options_with_block = nil,
36
+ options = nil,
37
+ escape = true,
38
+ &block
39
+ )
40
+ @template.content_tag(
41
+ name,
42
+ content_or_options_with_block,
43
+ options,
44
+ escape,
45
+ &block
46
+ )
47
+ end
48
+
49
+ # Used to pass all context of the capture tag to then template. This ensures
50
+ # proper template binding of variables and methods!
51
+ #
52
+ # @param [Mixed] args
53
+ # @return [String]
54
+ #
55
+ def capture(*args)
56
+ @template.capture(*args)
57
+ end
58
+
59
+ # Used to pass all concat references to the template. This ensures proper
60
+ # binding. Concat adds a String to the template Output buffer. Useful when
61
+ # trying to add a String with no block.
62
+ #
63
+ # @param [String] text
64
+ # @return [String]
65
+ #
66
+ def concat(text)
67
+ @template.concat(text)
68
+ end
69
+
70
+ # Used to parse method arguments. If the first argument is
71
+ # a Hash, then it is assumed that the user left off the bootstrap
72
+ # contectual class. So we will assign it to `secondary` and
73
+ # return the Hash to be used as options.
74
+ #
75
+ # @param [Hash|NilClass|String|Symbol] args
76
+ # @return [Array]
77
+ #
78
+ def parse_arguments(*args)
79
+ first, second = *args
80
+ case first
81
+ when Hash, NilClass
82
+ ['secondary', first || second]
83
+ when Symbol, String
84
+ [first, second]
85
+ end
86
+ end
87
+
88
+ # Used to generate a (hopefully) unique ID for DOM elements. Used as a
89
+ # fallback if the user doesn't specify one.
90
+ #
91
+ # @return [String]
92
+ #
93
+ def uuid
94
+ (0...10).map { rand(65..90).chr }.join
95
+ end
96
+
97
+ # Used to get config settings inside of components quicker.
98
+ #
99
+ # @param [Symbol] setting
100
+ # @return [Mixed]
101
+ #
102
+ def config(setting, fallback)
103
+ Bootstrap4Helper.config.send(setting) || fallback
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,34 @@
1
+ module Bootstrap4Helper
2
+ # Simple configuration object for setting options for the gem.
3
+ #
4
+ # @todo Build a better, more comprehensive system.
5
+ #
6
+ class Configuration
7
+ DEFAULT_SETTINGS = {
8
+ autoload_in_views: true,
9
+ card_title: :h5,
10
+ card_text: :p,
11
+ accordion_header: :h5,
12
+ badge: :span
13
+ }.freeze
14
+
15
+ attr_accessor(*DEFAULT_SETTINGS.keys)
16
+
17
+ # Class constructor
18
+ #
19
+ # @param [Hash] _args
20
+ # @return [ClassName]
21
+ #
22
+ def initialize(_args = {})
23
+ DEFAULT_SETTINGS.each { |key, value| instance_variable_set("@#{key}", value) }
24
+ end
25
+
26
+ # Simple predicate method
27
+ #
28
+ # @return [Boolean]
29
+ #
30
+ def autoload_in_views?
31
+ @autoload_in_views
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,27 @@
1
+ module Bootstrap4Helper
2
+ # Simple class for storing constants
3
+ #
4
+ #
5
+ class Constants
6
+ COMPONENTS = %i[
7
+ component
8
+ alert
9
+ accordion
10
+ accordion_group
11
+ badge
12
+ card
13
+ card_grouping
14
+ card_group
15
+ card_deck
16
+ card_column
17
+ configuration
18
+ dropdown
19
+ dropdown/menu
20
+ modal
21
+ nav
22
+ spinner
23
+ tab
24
+ tab/content
25
+ ].freeze
26
+ end
27
+ end
@@ -0,0 +1,95 @@
1
+ module Bootstrap4Helper
2
+ # Builds a Dropdown component that can be used in other components.
3
+ #
4
+ #
5
+ class Dropdown < Component
6
+ # Class constructor
7
+ #
8
+ # @param [ActionView] template
9
+ # @param [Symbol|String] type
10
+ # @param [Hash] opts
11
+ # @option opts [String] :id
12
+ # @option opts [String] :class
13
+ # @option opts [Hash] :data
14
+ #
15
+ def initialize(template, type = :dropdown, opts = {}, &block)
16
+ super(template)
17
+
18
+ @type = type
19
+ @id = opts.fetch(:id, uuid)
20
+ @class = opts.fetch(:class, '')
21
+ @data = opts.fetch(:data, {})
22
+ @content = block || proc { '' }
23
+ end
24
+
25
+ # Used to generate a button for the dropdown. The buttons default as just
26
+ # a button that opens the coresponding dropdown menu. The `split: true` option
27
+ # make the button just the arrow indicator that open the menu.
28
+ #
29
+ # @param [Symbol] context
30
+ # @param [Hash] opts
31
+ # @option opts [Boolean] :split
32
+ # @option opts [String] :id
33
+ # @option opts [String] :class
34
+ # @option opts [Hash] :data
35
+ # @return [String]
36
+ #
37
+ def button(context = :primary, opts = {})
38
+ split = opts.fetch(:split, false)
39
+ id = opts.fetch(:id, nil)
40
+ klass = opts.fetch(:class, '')
41
+ data = opts.fetch(:data, {}).merge(toggle: 'dropdown')
42
+ extra = split ? 'dropdown-toggle-split' : ''
43
+
44
+ content_tag(
45
+ :button,
46
+ id: id,
47
+ type: 'button',
48
+ class: "dropdown-toggle btn btn-#{context} #{klass} #{extra}",
49
+ data: data,
50
+ aria: { haspopup: true, expanded: false }
51
+ ) do
52
+ split ? content_tag(:span, 'Toggle Dropdwon', class: 'sr-only') : yield
53
+ end
54
+ end
55
+
56
+ # Used to create a new `Dropdown::Menu`
57
+ #
58
+ # @param [Hash] opts
59
+ # @option opts [String] :id
60
+ # @option opts [String] :class
61
+ # @option opts [Hash] :data
62
+ # @return [Dropdown::Menu]
63
+ #
64
+ def menu(opts = {}, &block)
65
+ Menu.new(@template, opts, &block)
66
+ end
67
+
68
+ # String reprentation of the object.
69
+ #
70
+ # @return [String]
71
+ #
72
+ def to_s
73
+ content_tag :div, id: @id, class: "#{container_class} #{@class}", data: @data do
74
+ @content.call(self)
75
+ end
76
+ end
77
+
78
+ private
79
+
80
+ # Returns the container class for the dropdown component.
81
+ #
82
+ # @return [String]
83
+ #
84
+ def container_class
85
+ case @type
86
+ when :dropdown
87
+ 'dropdown'
88
+ when :group
89
+ 'btn-group'
90
+ else
91
+ ''
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,140 @@
1
+ module Bootstrap4Helper
2
+ class Dropdown
3
+ # Builds a menu component for use in dropdowns.
4
+ #
5
+ #
6
+ class Menu < Component
7
+ # Class constructor
8
+ #
9
+ # @param [ActionView] template
10
+ # @param [Hash] opts
11
+ # @option opts [String] :id
12
+ # @option opts [String] :class
13
+ # @option opts [Hash] :data
14
+ #
15
+ def initialize(template, opts = {}, &block)
16
+ super(template)
17
+
18
+ @id = opts.fetch(:id, uuid)
19
+ @class = opts.fetch(:class, '')
20
+ @data = opts.fetch(:data, {})
21
+ @content = block || proc { '' }
22
+ end
23
+
24
+ # Use this method when the `item`, `link` in the item in the menu is nothing
25
+ # more than a hyperlink.
26
+ #
27
+ # @param [String] name
28
+ # @param [Hash] options
29
+ # @param [Hash] html_options
30
+ # @return [String]
31
+ #
32
+ def link(name = nil, options = nil, html_options = nil, &block)
33
+ html_options = (html_options || {}).merge(class: 'dropdown-item')
34
+
35
+ @template.link_to(name, options, html_options, &block)
36
+ end
37
+
38
+ # Use this method when you are using the item in the menu as trigger for tab
39
+ # content.
40
+ #
41
+ # @param [Symbol|String] target
42
+ # @param [Hash] opts
43
+ # @option opts [String] :id
44
+ # @option opts [String] :class
45
+ # @option opts [Hash] :data
46
+ # @option opts [Hash] :aria
47
+ # @return [String]
48
+ #
49
+ def item(target, opts = {})
50
+ id = opts.fetch(:id, nil)
51
+ klass = opts.fetch(:class, '')
52
+ data = opts.fetch(:data, {}).merge(toggle: 'tab')
53
+ aria = opts.fetch(:aria, {})
54
+
55
+ content_tag(
56
+ :a,
57
+ id: id,
58
+ class: "dropdown-item #{klass}",
59
+ href: "##{target}",
60
+ aria: aria,
61
+ data: data
62
+ ) do
63
+ block_given? ? yield : target.to_s.titleize
64
+ end
65
+ end
66
+
67
+ # Builds a Text component
68
+ #
69
+ # @param [Symbol|String] text
70
+ # @param [Hash] opts
71
+ # @option opts [String] :id
72
+ # @option opts [String] :class
73
+ # @option opts [Hash] :data
74
+ # @return [String]
75
+ #
76
+ def text(text, opts = {}, &block)
77
+ build_sub_component :span, text, 'item-text', opts, &block
78
+ end
79
+
80
+ # Builds a Header component
81
+ #
82
+ # @param [Symbol|String] text
83
+ # @param [Hash] opts
84
+ # @option opts [String] :id
85
+ # @option opts [String] :class
86
+ # @option opts [Hash] :data
87
+ # @return [String]
88
+ #
89
+ def header(text, opts = {}, &block)
90
+ build_sub_component :h6, text, 'header', opts, &block
91
+ end
92
+
93
+ # Builds a divider element
94
+ #
95
+ # @return [String]
96
+ #
97
+ def divider
98
+ content_tag :div, '', class: 'dropdown-divider'
99
+ end
100
+
101
+ # String representation of the object.
102
+ #
103
+ # @return [String]
104
+ #
105
+ def to_s
106
+ content_tag :div, id: @id, class: "dropdown-menu #{@class}", data: @data do
107
+ @content.call(self)
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ # Used to build specific components.
114
+ #
115
+ # @param [Symbol] tag
116
+ # @param [Symbol|String] text
117
+ # @param [Symbol|String] type
118
+ # @param [Hash] opts
119
+ # @option opts [String] :id
120
+ # @option opts [String] :class
121
+ # @option opts [Hash] :data
122
+ # @return [String]
123
+ #
124
+ def build_sub_component(tag, text, type, opts)
125
+ id = opts.fetch(:id, nil)
126
+ klass = opts.fetch(:class, '')
127
+ data = opts.fetch(:data, {})
128
+
129
+ content_tag(
130
+ tag,
131
+ id: id,
132
+ class: "dropdown-#{type} #{klass}",
133
+ data: data
134
+ ) do
135
+ block_given? ? yield : text || ''
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,19 @@
1
+ module Bootstrap4Helper
2
+ # Naming convention used as to not pollute views where the module is
3
+ # included. @config is a common instance variable name. We don't want
4
+ # to risk overriding another developers variable.
5
+ #
6
+ @_bs4h_config = Configuration.new
7
+
8
+ class << self
9
+ # Simple interface for exposing the configuration object.
10
+ #
11
+ # @return [Bootstrap4Helper::Configuration]
12
+ #
13
+ def config
14
+ yield @_bs4h_config if block_given?
15
+
16
+ @_bs4h_config
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,167 @@
1
+ module Bootstrap4Helper
2
+ # Builds a Modal window component.
3
+ #
4
+ #
5
+ class Modal < Component
6
+ # Class constructor
7
+ #
8
+ # @param [ActionView] template
9
+ # @param [Hash] opts
10
+ # @option opts [String] :id
11
+ # @option opts [String] :class
12
+ # @option opts [Hash] :data
13
+ # @option opts [Boolean] :scrollable
14
+ # @option opts [Boolean] :vcentered
15
+ #
16
+ def initialize(template, opts = {}, &block)
17
+ super(template)
18
+
19
+ @id = opts.fetch(:id, uuid)
20
+ @class = opts.fetch(:class, '')
21
+ @data = opts.fetch(:data, {})
22
+ @scrollable = opts.fetch(:scrollable, false)
23
+ @vcentered = opts.fetch(:vcentered, false)
24
+ @content = block || proc { '' }
25
+ end
26
+
27
+ # Build the header component for the modal.
28
+ #
29
+ # @param [Hash] opts
30
+ # @option opts [String] :id
31
+ # @option opts [String] :class
32
+ # @option opts [Hash] :data
33
+ # @return [String]
34
+ #
35
+ def header(opts = {}, &block)
36
+ build_main_component :header, opts, &block
37
+ end
38
+
39
+ # Builds the body component.
40
+ #
41
+ # @param [Hash] opts
42
+ # @option opts [String] :id
43
+ # @option opts [String] :class
44
+ # @option opts [Hash] :data
45
+ # @return [String]
46
+ #
47
+ def body(opts = {}, &block)
48
+ build_main_component :body, opts, &block
49
+ end
50
+
51
+ # Builds the footer component.
52
+ #
53
+ # @param [Hash] opts
54
+ # @option opts [String] :id
55
+ # @option opts [String] :class
56
+ # @option opts [Hash] :data
57
+ # @return [String]
58
+ #
59
+ def footer(opts = {}, &block)
60
+ build_main_component :footer, opts, &block
61
+ end
62
+
63
+ # Builds a title component.
64
+ #
65
+ # @param [Hash] opts
66
+ # @option opts [String] :id
67
+ # @option opts [String] :class
68
+ # @option opts [Hash] :data
69
+ # @return [String]
70
+ #
71
+ def title(opts = {}, &block)
72
+ build_sub_component :h5, :title, opts, &block
73
+ end
74
+
75
+ # Builds a close button component.
76
+ #
77
+ # @param [Hash] opts
78
+ # @option opts [String] :class
79
+ # @return [String]
80
+ #
81
+ def close_button(opts = {})
82
+ klass = opts.fetch(:class, '')
83
+
84
+ content_tag(
85
+ :button,
86
+ type: 'button',
87
+ class: block_given? ? klass : 'close',
88
+ data: { dismiss: 'modal' },
89
+ aria: { label: 'Close' }
90
+ ) do
91
+ block_given? ? yield : xbutton
92
+ end
93
+ end
94
+
95
+ # String representation of the object.
96
+ #
97
+ # @return [String]
98
+ #
99
+ def to_s
100
+ content_tag :div, id: @id, class: "modal #{@class}", tabindex: -1, role: 'dialog', data: @data do
101
+ content_tag :div, class: "modal-dialog #{scrollable} #{vcentered}", role: 'document' do
102
+ content_tag(:div, class: 'modal-content') { @content.call(self) }
103
+ end
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ # Used to build main components, usually divs.
110
+ #
111
+ # @param [Symbol|String] type
112
+ # @param [Hash] opts
113
+ # @return [String]
114
+ #
115
+ def build_main_component(type, opts = {}, &block)
116
+ build_sub_component :div, type, opts, &block
117
+ end
118
+
119
+ # Used to build more specific components.
120
+ #
121
+ # @param [Symbol] tag
122
+ # @param [Symbol|String] type
123
+ # @param [Hash] opts
124
+ # @option opts [String] :id
125
+ # @option opts [String] :class
126
+ # @option opts [Hash] :data
127
+ # @return [String]
128
+ #
129
+ def build_sub_component(tag, type, opts = {}, &block)
130
+ id = opts.fetch(:id, nil)
131
+ klass = opts.fetch(:class, '')
132
+ data = opts.fetch(:data, {})
133
+
134
+ content_tag(
135
+ tag,
136
+ id: id,
137
+ class: "modal-#{type} #{klass}",
138
+ data: data,
139
+ &block
140
+ )
141
+ end
142
+
143
+ # Builds the `x` button normally used in the header.
144
+ #
145
+ # @return [String]
146
+ #
147
+ def xbutton
148
+ content_tag :span, '&times;'.html_safe, aria: { hidden: true }
149
+ end
150
+
151
+ # Gets the scrollable CSS class.
152
+ #
153
+ # @return [String]
154
+ #
155
+ def scrollable
156
+ @scrollable ? 'modal-dialog-scrollable' : ''
157
+ end
158
+
159
+ # Gets the vertical-center CSS class.
160
+ #
161
+ # @return [String]
162
+ #
163
+ def vcentered
164
+ @vcentered ? 'modal-dialog-centered' : ''
165
+ end
166
+ end
167
+ end