bootstrap5_helper 1.2.0.pre3 → 2.0.0.beta

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.
@@ -1,7 +1,7 @@
1
1
  module Bootstrap5Helper
2
+ # rubocop:disable Metrics/ClassLength
3
+
2
4
  # Builds a Nav Component that can be used in other components.
3
- #
4
- #
5
5
  class Nav < Component
6
6
  # Class constructor
7
7
  #
@@ -11,8 +11,7 @@ module Bootstrap5Helper
11
11
  # @option opts [String] :id
12
12
  # @option opts [String] :class
13
13
  # @option opts [Hash] :data
14
- # @option opts [Array] :dropdown
15
- # @option opts []
14
+ # @option opts [Hash] :bs_attrs
16
15
  #
17
16
  def initialize(template, *tag_or_options, &block)
18
17
  super(template)
@@ -20,17 +19,16 @@ module Bootstrap5Helper
20
19
  @tag, args = parse_tag_or_options(*tag_or_options, {})
21
20
  @tag ||= config({ navs: :base }, :ul)
22
21
 
23
- @id = args.fetch(:id, uuid)
24
- @class = args.fetch(:class, '')
25
- @data = args.fetch(:data, {})
26
- @child = args.fetch(:child, {})
27
- @content = block || proc { '' }
22
+ @attrs = args
23
+ @id = @attrs.delete(:id) { uuid }
24
+ @class = @attrs.delete(:class) { '' }
25
+ @data = @attrs.delete(:data) { {} }
26
+ @bs_attrs = @attrs.delete(:bs_attrs) { {} }
28
27
  @dropdown = Dropdown.new(@template)
28
+ @content = block || proc { '' }
29
29
  end
30
30
 
31
- # rubocop:disable Metrics/MethodLength
32
-
33
- # Adds an nav-item to the nav component. this method gets used when the nav-item
31
+ # Adds an nav-item to the nav component. This method gets used when the nav-item
34
32
  # links to content in a tab or something.
35
33
  #
36
34
  # @param [Symbol|String] target
@@ -39,28 +37,17 @@ module Bootstrap5Helper
39
37
  # @option opts [String] :class
40
38
  # @option opts [Hash] :data
41
39
  # @option opts [Hash] :aria
40
+ # @option opts [Hash] :child
42
41
  # @return [String]
43
42
  #
44
- def item(target, opts = {})
45
- id = opts.fetch(:id, nil)
46
- klass = opts.fetch(:class, '')
47
- data = opts.fetch(:data, {})
48
- aria = opts.fetch(:aria, {})
49
-
50
- nav_item_wrapper id: id, data: data do
51
- content_tag(
52
- :a,
53
- class: "nav-link #{klass}",
54
- href: "##{target}",
55
- tabindex: -1,
56
- data: @child[:data],
57
- aria: aria
58
- ) do
59
- block_given? ? yield : target.to_s.titleize
60
- end
43
+ def item(target, opts = {}, &block)
44
+ case @tag
45
+ when :nav
46
+ nav_item_without_wrapper(target, opts, &block)
47
+ when :ul
48
+ nav_item_with_wrapper(target, opts, &block)
61
49
  end
62
50
  end
63
- # rubocop:enable Metrics/MethodLength
64
51
 
65
52
  # Use this when the nav item is nothing more than a hyperlink.
66
53
  #
@@ -88,6 +75,7 @@ module Bootstrap5Helper
88
75
  # @option opts [String] :class
89
76
  # @option opts [Hash] :data
90
77
  # @option opts [Hash] :aria
78
+ # @option opts [Hash] :child
91
79
  # @return [String]
92
80
  #
93
81
  def dropdown(name, opts = {}, &block)
@@ -96,9 +84,14 @@ module Bootstrap5Helper
96
84
  data = opts.fetch(:data, {}).merge('bs-toggle' => 'dropdown')
97
85
  aria = opts.fetch(:aria, {}).merge(haspopup: true, expanded: false)
98
86
 
99
- data.merge!('bs-display' => 'static') if @child[:data]&.key?('bs-display')
100
-
101
- nav_item_wrapper id: id, class: 'dropdown', data: data do
87
+ nav_item_wrapper(
88
+ id: id,
89
+ class: 'dropdown',
90
+ data: (@bs_attrs[:data] || {}).merge(
91
+ 'bs-toggle' => 'dropdown',
92
+ 'bs-display' => 'static'
93
+ )
94
+ ) do
102
95
  content_tag(
103
96
  :a,
104
97
  name,
@@ -117,34 +110,114 @@ module Bootstrap5Helper
117
110
  # @return [String]
118
111
  #
119
112
  def to_s
120
- content_tag(@tag, id: @id, class: "nav #{@class}", data: @data) do
113
+ content_tag(
114
+ @tag,
115
+ {
116
+ id: @id,
117
+ class: "nav #{@class}",
118
+ data: @data
119
+ }.merge(@attrs)
120
+ ) do
121
121
  @content.call(self)
122
122
  end
123
123
  end
124
124
 
125
125
  private
126
126
 
127
+ # rubocop:disable Metrics/AbcSize
128
+
129
+ # @note This is one of the few places where things deviate
130
+ # a little from the pattern of child elements requiring
131
+ # the :child option class needing to be set in order to
132
+ # pass on the class. The reason being that the default DOM
133
+ # elements built for navs are UL, which means that the
134
+ # majority of them will have children elements. Just wanted
135
+ # an easy process of setting the active nav, without having
136
+ # to remember if you are using :nav or :ul components.
137
+ #
138
+ # @example Nav built with UL(default config) would require the
139
+ # the following for setting the active nav:
140
+ #
141
+ # ```ruby
142
+ # <%= nav_helper do |nav| %>
143
+ # <%= nav.item(child: { class: 'active' }) do
144
+ # <% end %>
145
+ # <% end %>
146
+ #
147
+ # @example With the deviation:
148
+ #
149
+ # <%= nav_helper do |nav| %>
150
+ # <%= nav.item(class: 'active') do
151
+ # <% end %>
152
+ # <% end %>
153
+ # ```
154
+ #
155
+ # @param [Symbol|String] target
156
+ # @param [Hash] opts
157
+ # @option opts [String] :id
158
+ # @option opts [String] :class
159
+ # @option opts [Hash] :data
160
+ # @option opts [Hash] :aria
161
+ # @option opts [Hash] :child
162
+ # @return [String]
163
+ #
164
+ def nav_item_with_wrapper(target, opts = {}, &block)
165
+ attrs = opts
166
+ child = attrs.delete(:child) { {} }.merge(@bs_attrs)
167
+ child[:class] = (child[:class] || '') << ' nav-link'
168
+
169
+ if attrs.fetch(:class, '').match?('active')
170
+ attrs[:class].gsub!('active', '')
171
+ child[:class] << ' active'
172
+ end
173
+
174
+ nav_item_wrapper(attrs) do
175
+ content_tag(
176
+ :a,
177
+ { href: "##{target}", tabindex: -1 }.merge(child)
178
+ ) do
179
+ block_given? ? block.call : target.to_s.titleize
180
+ end
181
+ end
182
+ end
183
+ # rubocop:enable Metrics/AbcSize
184
+
185
+ # @param [Symbol|String] target
186
+ # @param [Hash] opts
187
+ # @option opts [String] :id
188
+ # @option opts [String] :class
189
+ # @option opts [Hash] :data
190
+ # @option opts [Hash] :aria
191
+ # @return [String]
192
+ #
193
+ def nav_item_without_wrapper(target, opts = {}, &block)
194
+ attrs = opts.merge(@bs_attrs)
195
+ attrs[:class] = (attrs[:class] || '') << ' nav-link'
196
+
197
+ nav_item_wrapper do
198
+ content_tag(
199
+ :a,
200
+ { href: "##{target}", tabindex: -1 }.merge(attrs)
201
+ ) do
202
+ block_given? ? block.call : target.to_s.titleize
203
+ end
204
+ end
205
+ end
206
+
127
207
  # Decorator for elements requiring a wrapper component.
128
208
  #
129
209
  # @return [String]
130
210
  #
131
211
  def nav_item_wrapper(opts = {}, &block)
132
- id = opts.fetch(:id, '')
133
- klass = opts.fetch(:class, '')
134
- data = opts.fetch(:data, {})
212
+ opts[:class] = (opts[:class] || '') << ' nav-item'
135
213
 
136
214
  case @tag
137
215
  when :nav
138
216
  block.call
139
217
  when :ul
140
- content_tag(
141
- :li,
142
- id: id,
143
- class: "nav-item #{klass}",
144
- data: data,
145
- &block
146
- )
218
+ content_tag(:li, opts, &block)
147
219
  end
148
220
  end
149
221
  end
222
+ # rubocop:enable Metrics/ClassLength
150
223
  end
@@ -4,111 +4,112 @@ module Bootstrap5Helper
4
4
  #
5
5
  #
6
6
  class Content < Component
7
- # Constructor description...
8
- #
9
- #
10
7
  # @param [Hash] opts
11
8
  # @return [ClassName]
12
9
  #
13
10
  def initialize(template, opts = {}, &block)
14
11
  super(template)
15
12
 
16
- @id = opts.fetch(:id, uuid)
17
- @class = opts.fetch(:class, '')
18
- @data = opts.fetch(:data, {})
19
- @aria = opts.fetch(:aria, {})
20
- @scrollable = opts.fetch(:scrollable, false)
21
- @position = opts.fetch(:position, 'start')
22
- @backdrop = opts.fetch(:backdrop, true)
13
+ @attrs = opts
14
+ @id = @attrs.delete(:id) { uuid }
15
+ @class = @attrs.delete(:class) { '' }
16
+ @data = @attrs.delete(:data) { {} }
17
+ @aria = @attrs.delete(:aria) { {} }
18
+ @scrollable = @attrs.delete(:scrollable) { false }
19
+ @position = @attrs.delete(:position) { 'start' }
20
+ @backdrop = @attrs.delete(:backdrop) { true }
23
21
  @content = block || proc { '' }
24
22
  end
25
23
 
26
24
  # @todo
27
- #
28
- #
29
25
  def header(opts = {}, &block)
30
- id = opts.fetch(:id, nil)
31
- klass = opts.fetch(:class, '')
32
- data = opts.fetch(:data, {})
26
+ attrs = opts
27
+ id = attrs.delete(:id) { nil }
28
+ klass = attrs.delete(:class) { '' }
29
+ data = attrs.delete(:data) { {} }
33
30
 
34
31
  content_tag(
35
32
  config({ offcanvas: :header }, :div),
36
- id: id,
37
- class: "offcanvas-header #{klass}",
38
- data: data,
33
+ {
34
+ id: id,
35
+ class: "offcanvas-header #{klass}",
36
+ data: data
37
+ }.merge(attrs),
39
38
  &block
40
39
  )
41
40
  end
42
41
 
43
42
  # @todo
44
- #
45
- #
46
43
  def title(text_or_options = nil, opts = {}, &block)
47
- text, args = parse_text_or_options(text_or_options, opts)
44
+ text, attrs = parse_text_or_options(text_or_options, opts)
48
45
 
49
- id = args.fetch(:id, nil)
50
- klass = args.fetch(:class, '')
51
- data = args.fetch(:data, {})
46
+ id = attrs.delete(:id) { nil }
47
+ klass = attrs.delete(:class) { '' }
48
+ data = attrs.delete(:data) { {} }
52
49
 
53
50
  content_tag(
54
51
  config({ offcanvas: :title }, :h6),
55
52
  text,
56
- id: id,
57
- class: "offcanvas-title #{klass}",
58
- data: data,
53
+ {
54
+ id: id,
55
+ class: "offcanvas-title #{klass}",
56
+ data: data
57
+ }.merge(attrs),
59
58
  &block
60
59
  )
61
60
  end
62
61
 
63
62
  # @todo
64
- #
65
- #
66
63
  def close_button(opts = {})
67
- klass = opts.fetch(:class, '')
64
+ attrs = opts
65
+ klass = attrs.delete(:class) { '' }
66
+ data = attrs.delete(:data) { {} }
67
+ aria = attrs.delete(:aria) { {} }
68
68
 
69
69
  content_tag(
70
70
  config({ offcanvas: :close }, :button),
71
71
  class: block_given? ? klass : 'btn-close',
72
- data: { 'bs-dismiss': 'offcanvas' },
73
- aria: { label: 'Close' }
72
+ data: data.merge('bs-dismiss' => 'offcanvas'),
73
+ aria: aria.merge(label: 'Close')
74
74
  ) do
75
75
  block_given? ? yield : xbutton
76
76
  end
77
77
  end
78
78
 
79
79
  # @todo
80
- #
81
- #
82
80
  def body(opts = {}, &block)
83
- id = opts.fetch(:id, nil)
84
- klass = opts.fetch(:class, '')
85
- data = opts.fetch(:data, {})
86
- aria = opts.fetch(:aria, {})
81
+ attrs = opts
82
+ id = attrs.delete(:id) { nil }
83
+ klass = attrs.delete(:class) { '' }
84
+ data = attrs.delete(:data) { {} }
85
+ aria = attrs.delete(:aria) { {} }
87
86
 
88
87
  content_tag(
89
88
  :div,
90
- id: id,
91
- class: "offcanvas-body #{klass}",
92
- data: data,
93
- aria: aria,
89
+ {
90
+ id: id,
91
+ class: "offcanvas-body #{klass}",
92
+ data: data,
93
+ aria: aria
94
+ }.merge(attrs),
94
95
  &block
95
96
  )
96
97
  end
97
98
 
98
99
  # @todo
99
- #
100
- #
101
100
  def to_s
102
- @data.merge!('bs-scroll': @scrollable)
103
- @data.merge!('bs-backdrop': @backdrop)
104
-
105
101
  content_tag(
106
102
  :div,
107
- id: @id,
108
- class: "offcanvas offcanvas-#{@position} #{@class}",
109
- tabindex: -1,
110
- data: @data,
111
- aria: { labelledby: 'offcanvasExampleLabel' }
103
+ {
104
+ id: @id,
105
+ class: "offcanvas offcanvas-#{@position} #{@class}",
106
+ tabindex: -1,
107
+ data: @data.merge!(
108
+ 'bs-scroll' => @scrollable,
109
+ 'bs-backdrop' => @backdrop
110
+ ),
111
+ aria: @aria.merge!(labelledby: 'offcanvasExampleLabel')
112
+ }.merge(@attrs)
112
113
  ) do
113
114
  @content.call(self)
114
115
  end
@@ -18,14 +18,14 @@ module Bootstrap5Helper
18
18
  #
19
19
  def initialize(template, position_or_options = nil, opts = {}, &block)
20
20
  super(template)
21
- @pos, args = parse_position_or_options(position_or_options, opts)
22
- @id = args.fetch(:id, uuid)
23
- @class = args.fetch(:class, '')
24
- @data = args.fetch(:data, {})
25
- @aria = args.fetch(:aria, {})
26
- @scrollable = args.fetch(:scrollable, false)
27
- @backdrop = args.fetch(:backdrop, true)
28
- @content = block || proc { '' }
21
+ @pos, @attrs = parse_position_or_options(position_or_options, opts)
22
+ @id = @attrs.delete(:id) { uuid }
23
+ @class = @attrs.delete(:class) { '' }
24
+ @data = @attrs.delete(:data) { {} }
25
+ @aria = @attrs.delete(:aria) { {} }
26
+ @scrollable = @attrs.delete(:scrollable) { false }
27
+ @backdrop = @attrs.delete(:backdrop) { true }
28
+ @content = block || proc { '' }
29
29
  end
30
30
  # rubocop:disable Metrics/MethodLength
31
31
 
@@ -139,7 +139,7 @@ module Bootstrap5Helper
139
139
  scrollable: @scrollable,
140
140
  backdrop: @backdrop,
141
141
  position: @pos
142
- },
142
+ }.merge(@attrs),
143
143
  &block
144
144
  )
145
145
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: false
2
+
1
3
  module Bootstrap5Helper
2
4
  class Overlay
5
+ # rubocop:disable Metrics/ClassLength
3
6
  # Builds a menu component for use in dropdowns.
4
- #
5
- #
6
7
  class Menu < Component
7
8
  # Class constructor
8
9
  #
@@ -17,9 +18,10 @@ module Bootstrap5Helper
17
18
  super(template)
18
19
 
19
20
  @tag, opts = parse_tag_or_options(*tag_or_options, {})
20
- @id = opts.fetch(:id, uuid)
21
- @class = opts.fetch(:class, '')
22
- @data = opts.fetch(:data, {})
21
+ @attrs = opts
22
+ @id = @attrs.delete(:id) { uuid }
23
+ @class = @attrs.delete(:class) { '' }
24
+ @data = @attrs.delete(:data) { {} }
23
25
  @content = block || proc { '' }
24
26
  end
25
27
 
@@ -45,8 +47,6 @@ module Bootstrap5Helper
45
47
  end
46
48
  end
47
49
 
48
- # rubocop:disable Metrics/MethodLength
49
-
50
50
  # Use this method when you are using the item in the menu as trigger for
51
51
  # something like tab content.
52
52
  #
@@ -59,25 +59,23 @@ module Bootstrap5Helper
59
59
  # @return [String]
60
60
  #
61
61
  def item(target, opts = {})
62
- id = opts.fetch(:id, nil)
63
- klass = opts.fetch(:class, '')
64
- data = opts.fetch(:data, {}).merge('bs-toggle' => 'tab')
65
- aria = opts.fetch(:aria, {})
62
+ attrs = opts
63
+ klass = attrs.delete(:class) { '' }
64
+ data = attrs.delete(:data) { {} }
66
65
 
67
66
  nav_item_wrapper do
68
67
  content_tag(
69
68
  :a,
70
- id: id,
71
- class: "dropdown-item #{klass}",
72
- href: "##{target}",
73
- aria: aria,
74
- data: data
69
+ {
70
+ class: "dropdown-item #{klass}",
71
+ href: "##{target}",
72
+ data: data.merge('bs-toggle' => 'tab')
73
+ }.merge(attrs)
75
74
  ) do
76
75
  block_given? ? yield : target.to_s.titleize
77
76
  end
78
77
  end
79
78
  end
80
- # rubocop:enable Metrics/MethodLength
81
79
 
82
80
  # Builds a Text component
83
81
  #
@@ -142,9 +140,11 @@ module Bootstrap5Helper
142
140
  def to_s
143
141
  content_tag(
144
142
  @tag || config({ overlay_menus: :base }, :div),
145
- id: @id,
146
- class: "dropdown-menu #{@class}",
147
- data: @data
143
+ {
144
+ id: @id,
145
+ class: "dropdown-menu #{@class}",
146
+ data: @data
147
+ }.merge(@attrs)
148
148
  ) do
149
149
  @content.call(self)
150
150
  end
@@ -177,19 +177,23 @@ module Bootstrap5Helper
177
177
  # @return [String]
178
178
  #
179
179
  def build_sub_component(tag, text, type, opts)
180
- id = opts.fetch(:id, nil)
181
- klass = opts.fetch(:class, '')
182
- data = opts.fetch(:data, {})
180
+ attrs = opts
181
+ id = attrs.delete(:id) { nil }
182
+ klass = attrs.delete(:class) { '' }
183
+ data = attrs.delete(:data) { {} }
183
184
 
184
185
  content_tag(
185
186
  tag,
186
- id: id,
187
- class: "dropdown-#{type} #{klass}",
188
- data: data
187
+ {
188
+ id: id,
189
+ class: "dropdown-#{type} #{klass}",
190
+ data: data
191
+ }.merge(attrs)
189
192
  ) do
190
193
  block_given? ? yield : text || ''
191
194
  end
192
195
  end
193
196
  end
197
+ # rubocop:enable Metrics/ClassLength
194
198
  end
195
199
  end
@@ -28,15 +28,18 @@ module Bootstrap5Helper
28
28
  super(template)
29
29
 
30
30
  @tag, args = parse_tag_or_options(*tag_or_options, {})
31
- @split = args.fetch(:split, false)
32
- @centered = args.fetch(:centered, false)
33
- @id = args.fetch(:id, uuid)
34
- @class = args.fetch(:class, '')
35
- @data = args.fetch(:data, {})
31
+ @attrs = args
32
+ @split = @attrs.delete(:split) { false }
33
+ @centered = @attrs.delete(:centered) { false }
34
+ @id = @attrs.delete(:id) { uuid }
35
+ @class = @attrs.delete(:class) { '' }
36
+ @data = @attrs.delete(:data) { {} }
36
37
 
37
38
  @content = block || proc { '' }
38
39
  end
39
40
 
41
+ # rubocop:disable Metrics/MethodLength
42
+
40
43
  # Used to generate a button for the dropdown. This button just
41
44
  # opens the coresponding dropdown menu.
42
45
  #
@@ -48,23 +51,29 @@ module Bootstrap5Helper
48
51
  # @return [String]
49
52
  #
50
53
  def button(context = :primary, opts = {}, &block)
51
- id = opts.fetch(:id, nil)
52
- klass = opts.fetch(:class, '')
53
- data = opts.fetch(:data, {}).merge(
54
- 'bs-toggle' => 'dropdown',
55
- 'bs-display' => 'static'
56
- )
54
+ attrs = opts
55
+ klass = attrs.delete(:class) { '' }
56
+ data = attrs.delete(:data) { {} }
57
+ aria = attrs.delete(:aria) { {} }
57
58
 
58
59
  content_tag(
59
60
  :button,
60
- id: id,
61
- type: 'button',
62
- class: "dropdown-toggle btn btn-#{context} #{klass}",
63
- data: data,
64
- aria: { haspopup: true, expanded: false },
61
+ {
62
+ type: 'button',
63
+ class: "dropdown-toggle btn btn-#{context} #{klass}",
64
+ data: data.merge(
65
+ 'bs-toggle' => 'dropdown',
66
+ 'bs-display' => 'static'
67
+ ),
68
+ aria: aria.merge(
69
+ haspopup: true,
70
+ expanded: false
71
+ )
72
+ }.merge(attrs),
65
73
  &block
66
74
  )
67
75
  end
76
+ # rubocop:enable Metrics/MethodLength
68
77
 
69
78
  # Used to generate a button with just the caret, to open the dropdown.
70
79
  #
@@ -112,9 +121,11 @@ module Bootstrap5Helper
112
121
  def to_s
113
122
  content_tag(
114
123
  @tag || :div,
115
- id: @id,
116
- class: "#{@type} #{alignment_type_class} #{@class}",
117
- data: @data
124
+ {
125
+ id: @id,
126
+ class: "#{@type} #{alignment_type_class} #{@class}",
127
+ data: @data
128
+ }.merge(@attrs)
118
129
  ) do
119
130
  @content.call(self)
120
131
  end
@@ -15,12 +15,12 @@ module Bootstrap5Helper
15
15
  def initialize(template, tag_or_options = nil, opts = {}, &block)
16
16
  super(template)
17
17
 
18
- @tag, args = parse_tag_or_options(tag_or_options, opts)
18
+ @tag, @attrs = parse_tag_or_options(tag_or_options, opts)
19
19
  @tag ||= config(:page_header, :h1)
20
20
 
21
- @id = args.fetch(:id, uuid)
22
- @class = args.fetch(:class, '')
23
- @data = args.fetch(:data, {})
21
+ @id = @attrs.delete(:id) { uuid }
22
+ @class = @attrs.delete(:class) { '' }
23
+ @data = @attrs.delete(:data) { {} }
24
24
  @content = block || proc { '' }
25
25
  end
26
26
 
@@ -31,9 +31,11 @@ module Bootstrap5Helper
31
31
  def to_s
32
32
  content_tag(
33
33
  @tag,
34
- id: @id,
35
- class: "pb-2 mt-4 mb-2 border-bottom #{@class}",
36
- data: @data
34
+ {
35
+ id: @id,
36
+ class: "pb-2 mt-4 mb-2 border-bottom #{@class}",
37
+ data: @data
38
+ }.merge(@attrs)
37
39
  ) do
38
40
  @content.call(self)
39
41
  end