bootstrap5_helper 1.0.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.
@@ -0,0 +1,157 @@
1
+ module Bootstrap5Helper
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 ||= {}
34
+ html_options[:class] = (html_options[:class] || '') << ' dropdown-item'
35
+
36
+ @template.link_to(name, options, html_options, &block)
37
+ end
38
+
39
+ # Use this method when you are using the item in the menu as trigger for tab
40
+ # content.
41
+ #
42
+ # @param [Symbol|String] target
43
+ # @param [Hash] opts
44
+ # @option opts [String] :id
45
+ # @option opts [String] :class
46
+ # @option opts [Hash] :data
47
+ # @option opts [Hash] :aria
48
+ # @return [String]
49
+ #
50
+ def item(target, opts = {})
51
+ id = opts.fetch(:id, nil)
52
+ klass = opts.fetch(:class, '')
53
+ data = opts.fetch(:data, {}).merge('bs-toggle' => 'tab')
54
+ aria = opts.fetch(:aria, {})
55
+
56
+ content_tag(
57
+ :a,
58
+ id: id,
59
+ class: "dropdown-item #{klass}",
60
+ href: "##{target}",
61
+ aria: aria,
62
+ data: data
63
+ ) do
64
+ block_given? ? yield : target.to_s.titleize
65
+ end
66
+ end
67
+
68
+ # Builds a Text component
69
+ #
70
+ # @param [Symbol|String] text
71
+ # @param [Hash] opts
72
+ # @option opts [String] :id
73
+ # @option opts [String] :class
74
+ # @option opts [Hash] :data
75
+ # @return [String]
76
+ #
77
+ def text(text, opts = {}, &block)
78
+ build_sub_component(
79
+ config({ dropdown_menus: :text }, :span),
80
+ text,
81
+ 'item-text',
82
+ opts,
83
+ &block
84
+ )
85
+ end
86
+
87
+ # Builds a Header component
88
+ #
89
+ # @param [Symbol|String] text
90
+ # @param [Hash] opts
91
+ # @option opts [String] :id
92
+ # @option opts [String] :class
93
+ # @option opts [Hash] :data
94
+ # @return [String]
95
+ #
96
+ def header(text, opts = {}, &block)
97
+ build_sub_component(
98
+ config({ dropdown_menus: :header }, :h6),
99
+ text,
100
+ 'header',
101
+ opts,
102
+ &block
103
+ )
104
+ end
105
+
106
+ # Builds a divider element
107
+ #
108
+ # @return [String]
109
+ #
110
+ def divider
111
+ content_tag(
112
+ config({ dropdown_menus: :divider }, :div),
113
+ '',
114
+ class: 'dropdown-divider'
115
+ )
116
+ end
117
+
118
+ # String representation of the object.
119
+ #
120
+ # @return [String]
121
+ #
122
+ def to_s
123
+ content_tag :div, id: @id, class: "dropdown-menu #{@class}", data: @data do
124
+ @content.call(self)
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ # Used to build specific components.
131
+ #
132
+ # @param [Symbol] tag
133
+ # @param [Symbol|String] text
134
+ # @param [Symbol|String] type
135
+ # @param [Hash] opts
136
+ # @option opts [String] :id
137
+ # @option opts [String] :class
138
+ # @option opts [Hash] :data
139
+ # @return [String]
140
+ #
141
+ def build_sub_component(tag, text, type, opts)
142
+ id = opts.fetch(:id, nil)
143
+ klass = opts.fetch(:class, '')
144
+ data = opts.fetch(:data, {})
145
+
146
+ content_tag(
147
+ tag,
148
+ id: id,
149
+ class: "dropdown-#{type} #{klass}",
150
+ data: data
151
+ ) do
152
+ block_given? ? yield : text || ''
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,121 @@
1
+ module Bootstrap5Helper
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
+ # @option opts [Boolea] :split
15
+ # @option opts [Boolean] :centered
16
+ #
17
+ def initialize(template, type = :dropdown, opts = {}, &block)
18
+ super(template)
19
+
20
+ @type = type
21
+ @split = opts.fetch(:split, false)
22
+ @centered = opts.fetch(:centered, false)
23
+ @id = opts.fetch(:id, uuid)
24
+ @class = opts.fetch(:class, '')
25
+ @data = opts.fetch(:data, {})
26
+ @content = block || proc { '' }
27
+ end
28
+
29
+ # Used to generate a button for the dropdown. The buttons default as just
30
+ # a button that opens the coresponding dropdown menu. The `split: true` option
31
+ # make the button just the arrow indicator that open the menu.
32
+ #
33
+ # @param [Symbol] context
34
+ # @param [Hash] opts
35
+ # @option opts [String] :id
36
+ # @option opts [String] :class
37
+ # @option opts [Hash] :data
38
+ # @option opts [Boolean] :split
39
+ # @return [String]
40
+ #
41
+ def button(context = :primary, opts = {})
42
+ id = opts.fetch(:id, nil)
43
+ klass = opts.fetch(:class, '')
44
+ split = opts.fetch(:split, false)
45
+ data = opts.fetch(:data, {}).merge('bs-toggle' => 'dropdown')
46
+ extra = @split ? 'dropdown-toggle-split' : ''
47
+
48
+ content_tag(
49
+ :button,
50
+ id: id,
51
+ type: 'button',
52
+ class: "dropdown-toggle btn btn-#{context} #{klass} #{extra}",
53
+ data: data,
54
+ aria: { haspopup: true, expanded: false }
55
+ ) do
56
+ split ? content_tag(:span, 'Toggle Dropdown', class: 'visually-hidden') : yield
57
+ end
58
+ end
59
+
60
+ # Used to create a new `Dropdown::Menu`
61
+ #
62
+ # @param [Hash] opts
63
+ # @option opts [String] :id
64
+ # @option opts [String] :class
65
+ # @option opts [Hash] :data
66
+ # @return [Dropdown::Menu]
67
+ #
68
+ def menu(opts = {}, &block)
69
+ Menu.new(@template, opts, &block)
70
+ end
71
+
72
+ # String reprentation of the object.
73
+ #
74
+ # @return [String]
75
+ #
76
+ def to_s
77
+ content_tag(
78
+ :div,
79
+ id: @id,
80
+ class: "#{element_type_class} #{alignment_type_class} #{@class}",
81
+ data: @data
82
+ ) do
83
+ @content.call(self)
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ # Returns the container class for the dropdown component.
90
+ #
91
+ # @return [String]
92
+ #
93
+ def element_type_class
94
+ case @type
95
+ when :dropdown
96
+ 'dropdown'
97
+ when :dropup
98
+ 'dropup'
99
+ when :dropstart
100
+ 'dropstart'
101
+ when :dropend
102
+ 'dropend'
103
+ else
104
+ ''
105
+ end
106
+ end
107
+
108
+ # Returns the alignment class for the dropdown component.
109
+ #
110
+ # @return [String]
111
+ #
112
+ def alignment_type_class
113
+ case @type
114
+ when :dropdown, :dropup
115
+ @centered ? "#{element_type_class}-center" : ''
116
+ else
117
+ ''
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,19 @@
1
+ module Bootstrap5Helper # :nodoc:
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
+ @_bs5h_config = Configuration.new
7
+
8
+ class << self
9
+ # Simple interface for exposing the configuration object.
10
+ #
11
+ # @return [Bootstrap5Helper::Configuration]
12
+ #
13
+ def config
14
+ yield @_bs5h_config if block_given?
15
+
16
+ @_bs5h_config
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,70 @@
1
+ module Bootstrap5Helper
2
+ # The InputGroup helper is meant to help you rapidly build Bootstrap input
3
+ # group components quickly and easily.
4
+ #
5
+ class InputGroup < Component
6
+ # Class constructor
7
+ #
8
+ # @param [Class] template - Template in which your are binding too.
9
+ # @param [Symbol|Hash] context_or_options
10
+ # @param [Hash] opts
11
+ # @option opts [String] :id
12
+ # @option opts [String] :class
13
+ # @option opts [Hash] :data
14
+ # @return [InputGroup]
15
+ #
16
+ def initialize(template, context_or_options = nil, opts = {}, &block)
17
+ super(template)
18
+
19
+ @context, args = parse_context_or_options(context_or_options, opts)
20
+
21
+ @id = args.fetch(:id, nil)
22
+ @class = args.fetch(:class, '')
23
+ @data = args.fetch(:data, {})
24
+ @content = block || proc { '' }
25
+ end
26
+
27
+ # This is the element that actually houses the icon or text used
28
+ # in the input group.
29
+ #
30
+ # @param [Hash] opts
31
+ # @return [String]
32
+ #
33
+ def text(opts = {}, &block)
34
+ opts[:class] = (opts[:class] || '') << ' input-group-text'
35
+ content_tag :span, opts, &block
36
+ end
37
+
38
+ # Used to render out the InputGroup component.
39
+ #
40
+ # @return [String]
41
+ #
42
+ def to_s
43
+ content_tag(
44
+ :div,
45
+ id: @id,
46
+ class: "input-group #{size_class} #{@class}",
47
+ data: @data
48
+ ) do
49
+ @content.call(self)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # Used to get the size of the input group.
56
+ #
57
+ # @return [String]
58
+ #
59
+ def size_class
60
+ case @context
61
+ when :sm
62
+ 'input-group-sm'
63
+ when :lg
64
+ 'input-group-lg'
65
+ else
66
+ ''
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,224 @@
1
+ module Bootstrap5Helper
2
+ # rubocop:disable Metrics/ClassLength
3
+
4
+ # Builds a Modal window component.
5
+ #
6
+ #
7
+ class Modal < Component
8
+ # Class constructor
9
+ #
10
+ # @param [ActionView] template
11
+ # @param [Hash] opts
12
+ # @option opts [String] :id
13
+ # @option opts [String] :class
14
+ # @option opts [Hash] :data
15
+ # @option opts [Boolean] :scrollable
16
+ # @option opts [Boolean] :vcentered
17
+ # @option opts [Boolean] :static
18
+ # @option opts [Boolean|Symbol] :fullscreen
19
+ # @option opts [Symbol] :size
20
+ #
21
+ def initialize(template, opts = {}, &block)
22
+ super(template)
23
+
24
+ @id = opts.fetch(:id, uuid)
25
+ @class = opts.fetch(:class, '')
26
+ @data = opts.fetch(:data, {})
27
+ @scrollable = opts.fetch(:scrollable, false)
28
+ @vcentered = opts.fetch(:vcentered, false)
29
+ @static = opts.fetch(:static, false)
30
+ @fullscreen = opts.fetch(:fullscreen, true)
31
+ @size = opts.fetch(:size, nil)
32
+ @content = block || proc { '' }
33
+ end
34
+
35
+ # Build the header component for the modal.
36
+ #
37
+ # @param [Hash] opts
38
+ # @option opts [String] :id
39
+ # @option opts [String] :class
40
+ # @option opts [Hash] :data
41
+ # @return [String]
42
+ #
43
+ def header(opts = {}, &block)
44
+ build_main_component :header, opts, &block
45
+ end
46
+
47
+ # Builds the body component.
48
+ #
49
+ # @param [Hash] opts
50
+ # @option opts [String] :id
51
+ # @option opts [String] :class
52
+ # @option opts [Hash] :data
53
+ # @return [String]
54
+ #
55
+ def body(opts = {}, &block)
56
+ build_main_component :body, opts, &block
57
+ end
58
+
59
+ # Builds the footer component.
60
+ #
61
+ # @param [Hash] opts
62
+ # @option opts [String] :id
63
+ # @option opts [String] :class
64
+ # @option opts [Hash] :data
65
+ # @return [String]
66
+ #
67
+ def footer(opts = {}, &block)
68
+ build_main_component :footer, opts, &block
69
+ end
70
+
71
+ # Builds a title component.
72
+ #
73
+ # @param [Hash] opts
74
+ # @option opts [String] :id
75
+ # @option opts [String] :class
76
+ # @option opts [Hash] :data
77
+ # @return [String]
78
+ #
79
+ def title(opts = {}, &block)
80
+ build_sub_component config({ modals: :title }, :h5), :title, opts, &block
81
+ end
82
+
83
+ # Builds a close button component.
84
+ #
85
+ # @param [Hash] opts
86
+ # @option opts [String] :class
87
+ # @return [String]
88
+ #
89
+ def close_button(opts = {})
90
+ klass = opts.fetch(:class, '')
91
+
92
+ content_tag(
93
+ :button,
94
+ type: 'button',
95
+ class: block_given? ? klass : 'btn-close',
96
+ data: { 'bs-dismiss': 'modal' },
97
+ aria: { label: 'Close' }
98
+ ) do
99
+ block_given? ? yield : xbutton
100
+ end
101
+ end
102
+
103
+ # rubocop:disable Metrics/MethodLength
104
+
105
+ # String representation of the object.
106
+ #
107
+ # @return [String]
108
+ #
109
+ def to_s
110
+ @data.merge!('bs-backdrop' => 'static', 'bs-keyboard' => false) if @static
111
+
112
+ content_tag(
113
+ :div,
114
+ id: @id,
115
+ class: "modal #{@class}",
116
+ tabindex: -1,
117
+ role: 'dialog',
118
+ data: @data
119
+ ) do
120
+ content_tag(
121
+ :div,
122
+ class: "modal-dialog #{size} #{scrollable} #{vcentered} #{fullscreen}",
123
+ role: 'document'
124
+ ) do
125
+ content_tag(:div, class: 'modal-content') { @content.call(self) }
126
+ end
127
+ end
128
+ end
129
+ # rubocop:enable Metrics/MethodLength
130
+
131
+ private
132
+
133
+ # Used to build main components, usually divs.
134
+ #
135
+ # @param [Symbol|String] type
136
+ # @param [Hash] opts
137
+ # @return [String]
138
+ #
139
+ def build_main_component(type, opts = {}, &block)
140
+ build_sub_component :div, type, opts, &block
141
+ end
142
+
143
+ # Used to build more specific components.
144
+ #
145
+ # @param [Symbol] tag
146
+ # @param [Symbol|String] type
147
+ # @param [Hash] opts
148
+ # @option opts [String] :id
149
+ # @option opts [String] :class
150
+ # @option opts [Hash] :data
151
+ # @return [String]
152
+ #
153
+ def build_sub_component(tag, type, opts = {}, &block)
154
+ id = opts.fetch(:id, nil)
155
+ klass = opts.fetch(:class, '')
156
+ data = opts.fetch(:data, {})
157
+
158
+ content_tag(
159
+ tag,
160
+ id: id,
161
+ class: "modal-#{type} #{klass}",
162
+ data: data,
163
+ &block
164
+ )
165
+ end
166
+
167
+ # Builds the `x` button normally used in the header.
168
+ #
169
+ # @return [String]
170
+ #
171
+ def xbutton
172
+ content_tag :span, '&times;'.html_safe, class: 'visually-hidden', aria: { hidden: true }
173
+ end
174
+
175
+ # Gets the scrollable CSS class.
176
+ #
177
+ # @return [String]
178
+ #
179
+ def scrollable
180
+ @scrollable ? 'modal-dialog-scrollable' : ''
181
+ end
182
+
183
+ # Gets the vertical-center CSS class.
184
+ #
185
+ # @return [String]
186
+ #
187
+ def vcentered
188
+ @vcentered ? 'modal-dialog-centered' : ''
189
+ end
190
+
191
+ # Gets the fullscreen class.
192
+ #
193
+ # @return [String]
194
+ #
195
+ def fullscreen
196
+ case @fullscreen
197
+ when TrueClass
198
+ 'modal-fullscreen'
199
+ when String, Symbol
200
+ "modal-fullscreen-#{@fullscreen}-down "
201
+ else
202
+ ''
203
+ end
204
+ end
205
+
206
+ # Gets the size of the modal window.
207
+ #
208
+ # @return [String]
209
+ #
210
+ def size
211
+ case @size
212
+ when :xlarge
213
+ 'modal-xl'
214
+ when :large
215
+ 'modal-lg'
216
+ when :small
217
+ 'modal-sm'
218
+ else
219
+ ''
220
+ end
221
+ end
222
+ end
223
+ # rubocop:enable Metrics/ClassLength
224
+ end