bootstrap4_helper 0.0.0 → 1.0.4

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