bs5 0.0.23 → 0.0.28
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 +4 -4
- data/app/components/bs5/alert_component.html.erb +1 -1
- data/app/components/bs5/alert_component.rb +1 -0
- data/app/components/bs5/button_tag_component.rb +10 -1
- data/app/components/bs5/carousel/caption_component.html.erb +3 -0
- data/app/components/bs5/carousel/caption_component.rb +8 -0
- data/app/components/bs5/carousel/item_component.html.erb +4 -0
- data/app/components/bs5/carousel/item_component.rb +74 -0
- data/app/components/bs5/carousel_component.html.erb +30 -0
- data/app/components/bs5/carousel_component.rb +60 -0
- data/app/components/bs5/close_button_component.rb +14 -6
- data/app/components/bs5/dropdown/item_component.html.erb +1 -0
- data/app/components/bs5/dropdown/item_component.rb +39 -0
- data/app/components/bs5/dropdown_component.html.erb +22 -0
- data/app/components/bs5/dropdown_component.rb +154 -0
- data/app/components/bs5/modal/body_component.html.erb +3 -0
- data/app/components/bs5/modal/body_component.rb +8 -0
- data/app/components/bs5/modal/controller_component.html.erb +1 -0
- data/app/components/bs5/modal/controller_component.rb +40 -0
- data/app/components/bs5/modal/footer_component.html.erb +4 -0
- data/app/components/bs5/modal/footer_component.rb +8 -0
- data/app/components/bs5/modal/header_component.html.erb +4 -0
- data/app/components/bs5/modal/header_component.rb +18 -0
- data/app/components/bs5/modal_component.html.erb +11 -0
- data/app/components/bs5/modal_component.rb +80 -0
- data/app/components/bs5/toast/body_component.html.erb +3 -0
- data/app/components/bs5/toast/body_component.rb +8 -0
- data/app/components/bs5/toast/header_component.html.erb +4 -0
- data/app/components/bs5/toast/header_component.rb +9 -0
- data/app/components/bs5/toast_component.html.erb +8 -0
- data/app/components/bs5/toast_component.rb +72 -0
- data/app/components/bs5/toast_container_component.html.erb +3 -0
- data/app/components/bs5/toast_container_component.rb +19 -0
- data/app/helpers/bs5/components_helper.rb +3 -2
- data/app/views/bs5/examples/alert/color/snippet.html.erb +8 -24
- data/app/views/bs5/examples/alert/default/snippet.html.erb +1 -3
- data/app/views/bs5/examples/carousel/_dark_variant.html.erb +2 -0
- data/app/views/bs5/examples/carousel/_examples.html.erb +13 -0
- data/app/views/bs5/examples/carousel/dark_variant/snippet1.html.erb +25 -0
- data/app/views/bs5/examples/carousel/examples/snippet1.html.erb +13 -0
- data/app/views/bs5/examples/carousel/examples/snippet2.html.erb +13 -0
- data/app/views/bs5/examples/carousel/examples/snippet3.html.erb +13 -0
- data/app/views/bs5/examples/carousel/examples/snippet4.html.erb +25 -0
- data/app/views/bs5/examples/carousel/examples/snippet5.html.erb +13 -0
- data/app/views/bs5/examples/carousel/examples/snippet6.html.erb +13 -0
- data/app/views/bs5/examples/dropdowns/dark/_example.html.erb +2 -0
- data/app/views/bs5/examples/dropdowns/dark/snippet.html.erb +7 -0
- data/app/views/bs5/examples/dropdowns/directions/_example.html.erb +7 -0
- data/app/views/bs5/examples/dropdowns/directions/snippet1.html.erb +17 -0
- data/app/views/bs5/examples/dropdowns/directions/snippet2.html.erb +17 -0
- data/app/views/bs5/examples/dropdowns/directions/snippet3.html.erb +17 -0
- data/app/views/bs5/examples/dropdowns/menu_alignment/_example.html.erb +5 -0
- data/app/views/bs5/examples/dropdowns/menu_alignment/snippet1.html.erb +5 -0
- data/app/views/bs5/examples/dropdowns/menu_alignment/snippet2.html.erb +7 -0
- data/app/views/bs5/examples/dropdowns/menu_alignment/snippet3.html.erb +7 -0
- data/app/views/bs5/examples/dropdowns/menu_content/_example.html.erb +9 -0
- data/app/views/bs5/examples/dropdowns/menu_content/snippet1.html.erb +5 -0
- data/app/views/bs5/examples/dropdowns/menu_content/snippet2.html.erb +7 -0
- data/app/views/bs5/examples/dropdowns/menu_content/snippet3.html.erb +10 -0
- data/app/views/bs5/examples/dropdowns/menu_content/snippet4.html.erb +24 -0
- data/app/views/bs5/examples/dropdowns/menu_items/_example.html.erb +7 -0
- data/app/views/bs5/examples/dropdowns/menu_items/snippet1.html.erb +5 -0
- data/app/views/bs5/examples/dropdowns/menu_items/snippet2.html.erb +6 -0
- data/app/views/bs5/examples/dropdowns/menu_items/snippet3.html.erb +5 -0
- data/app/views/bs5/examples/dropdowns/menu_items/snippet4.html.erb +5 -0
- data/app/views/bs5/examples/dropdowns/single/_example.html.erb +4 -0
- data/app/views/bs5/examples/dropdowns/single/snippet1.html.erb +5 -0
- data/app/views/bs5/examples/dropdowns/single/snippet2.html.erb +47 -0
- data/app/views/bs5/examples/dropdowns/single/snippet3.html.erb +47 -0
- data/app/views/bs5/examples/dropdowns/sizing/_example.html.erb +3 -0
- data/app/views/bs5/examples/dropdowns/sizing/snippet1.html.erb +17 -0
- data/app/views/bs5/examples/dropdowns/sizing/snippet2.html.erb +17 -0
- data/app/views/bs5/examples/dropdowns/split/_example.html.erb +3 -0
- data/app/views/bs5/examples/dropdowns/split/snippet1.html.erb +35 -0
- data/app/views/bs5/examples/dropdowns/split/snippet2.html.erb +47 -0
- data/app/views/bs5/examples/list_group/active/snippet.html.erb +5 -5
- data/app/views/bs5/examples/list_group/default/snippet.html.erb +5 -5
- data/app/views/bs5/examples/list_group/disabled/snippet.html.erb +5 -5
- data/app/views/bs5/examples/list_group/flush/snippet.html.erb +5 -5
- data/app/views/bs5/examples/list_group/horizontal/snippet.html.erb +18 -18
- data/app/views/bs5/examples/list_group/style/default.html.erb +8 -8
- data/app/views/bs5/examples/modal/_examples.html.erb +9 -0
- data/app/views/bs5/examples/modal/_fullscreen.html.erb +2 -0
- data/app/views/bs5/examples/modal/_optional_sizes.html.erb +2 -0
- data/app/views/bs5/examples/modal/examples/snippet1.html.erb +12 -0
- data/app/views/bs5/examples/modal/examples/snippet2.html.erb +12 -0
- data/app/views/bs5/examples/modal/examples/snippet3.html.erb +14 -0
- data/app/views/bs5/examples/modal/examples/snippet4.html.erb +12 -0
- data/app/views/bs5/examples/modal/fullscreen/snippet1.html.erb +55 -0
- data/app/views/bs5/examples/modal/optional_sizes/snippet1.html.erb +23 -0
- data/app/views/bs5/examples/toasts/color_schemes/_example.html.erb +2 -0
- data/app/views/bs5/examples/toasts/color_schemes/snippet.html.erb +33 -0
- data/app/views/bs5/examples/toasts/custom_content/_example.html.erb +3 -0
- data/app/views/bs5/examples/toasts/custom_content/snippet1.html.erb +3 -0
- data/app/views/bs5/examples/toasts/custom_content/snippet2.html.erb +9 -0
- data/app/views/bs5/examples/toasts/default/_example.html.erb +2 -0
- data/app/views/bs5/examples/toasts/default/snippet.html.erb +17 -0
- data/app/views/bs5/examples/toasts/js_options/_example.html.erb +2 -0
- data/app/views/bs5/examples/toasts/js_options/snippet.html.erb +23 -0
- data/app/views/bs5/examples/toasts/placement/_example.html.erb +3 -0
- data/app/views/bs5/examples/toasts/placement/snippet1.html.erb +44 -0
- data/app/views/bs5/examples/toasts/placement/snippet2.html.erb +24 -0
- data/app/views/bs5/examples/toasts/stacking/_example.html.erb +2 -0
- data/app/views/bs5/examples/toasts/stacking/snippet.html.erb +37 -0
- data/app/views/bs5/pages/carousel.html.erb +3 -0
- data/app/views/bs5/pages/dropdowns.html.erb +10 -0
- data/app/views/bs5/pages/modal.html.erb +4 -0
- data/app/views/bs5/pages/toasts.html.erb +7 -0
- data/app/views/layouts/bs5/pages.html.erb +4 -0
- data/lib/bs5/version.rb +1 -1
- data/lib/generators/bs5/install/templates/bs5.js +24 -13
- data/lib/tasks/rubocop.rake +2 -0
- metadata +96 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 215200d1f6820e7e163cbd6b214f9952645e225f535c45a34254ef53f445480a
|
|
4
|
+
data.tar.gz: cc2f9daf0107719ee613670620ddb42e42a08b995bb32a7348003f9b74761584
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8361ad156b1c5aa5a2e8d2e85482a891792fe28693aff0ab0de36a7acc258d1818934512a7b9c76d4be17de2e96f0c6207affa1f5765add53cff6bc83e999b74
|
|
7
|
+
data.tar.gz: 978c438199e216476b87a1eef92dcab0b9b8d828daca289c4eb7cc8b97fa802f75ef4620c06541870caeca578845303eb874fde87988036bea462918e5d0fc97
|
|
@@ -47,6 +47,7 @@ module Bs5
|
|
|
47
47
|
extract_color
|
|
48
48
|
extract_outline
|
|
49
49
|
extract_size
|
|
50
|
+
extract_dismiss
|
|
50
51
|
end
|
|
51
52
|
|
|
52
53
|
def extract_color
|
|
@@ -61,6 +62,10 @@ module Bs5
|
|
|
61
62
|
@size = @options.delete(:size)
|
|
62
63
|
end
|
|
63
64
|
|
|
65
|
+
def extract_dismiss
|
|
66
|
+
@dismiss = @options.delete(:dismiss)
|
|
67
|
+
end
|
|
68
|
+
|
|
64
69
|
def merge_default_options
|
|
65
70
|
@options.deep_merge!(default_options) do |_key, this_val, other_val|
|
|
66
71
|
[this_val, other_val].join(' ').strip
|
|
@@ -68,7 +73,11 @@ module Bs5
|
|
|
68
73
|
end
|
|
69
74
|
|
|
70
75
|
def default_options
|
|
71
|
-
{ class: button_class }
|
|
76
|
+
default_options = { class: button_class }
|
|
77
|
+
|
|
78
|
+
default_options[:data] = { 'bs-dismiss': @dismiss } if @dismiss
|
|
79
|
+
|
|
80
|
+
default_options
|
|
72
81
|
end
|
|
73
82
|
|
|
74
83
|
def button_class
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bs5
|
|
4
|
+
module Carousel
|
|
5
|
+
class ItemComponent < ViewComponent::Base
|
|
6
|
+
include ViewComponent::SlotableV2
|
|
7
|
+
using HashRefinement
|
|
8
|
+
|
|
9
|
+
COMPONENT_OPTIONS = %i[interval].freeze
|
|
10
|
+
|
|
11
|
+
renders_one :caption, Bs5::Carousel::CaptionComponent
|
|
12
|
+
|
|
13
|
+
attr_accessor :active
|
|
14
|
+
|
|
15
|
+
def initialize(options = {})
|
|
16
|
+
@options = options.symbolize_keys
|
|
17
|
+
extract_options
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def active?
|
|
21
|
+
active
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def extract_options
|
|
27
|
+
extract_component_options
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def extract_component_options
|
|
31
|
+
@component_options = @options.extract!(*COMPONENT_OPTIONS)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def component_attributes
|
|
35
|
+
{ class: component_classes,
|
|
36
|
+
data: @component_options.prefix_keys_with_bs }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def component_classes
|
|
40
|
+
class_names = %w[carousel-item]
|
|
41
|
+
class_names << 'active' if active?
|
|
42
|
+
|
|
43
|
+
class_names
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def content
|
|
47
|
+
set_element_class_names
|
|
48
|
+
element.to_html.html_safe # rubocop:disable Rails/OutputSafety
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def set_element_class_names
|
|
52
|
+
class_names = Array(element[:class])
|
|
53
|
+
class_names << element_classes
|
|
54
|
+
element[:class] = class_names.join(' ')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def element_classes
|
|
58
|
+
%w[d-block w-100]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def element
|
|
62
|
+
@element ||= begin
|
|
63
|
+
if (elements = Nokogiri::HTML::DocumentFragment.parse(@content).elements).one?
|
|
64
|
+
elements.first
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def element?
|
|
70
|
+
!!element
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<%= tag.div(component_attributes) do %>
|
|
2
|
+
<% items.each_with_index do |item, index| %>
|
|
3
|
+
<% item.active = index.zero? %>
|
|
4
|
+
<% end %>
|
|
5
|
+
|
|
6
|
+
<% if indicators? %>
|
|
7
|
+
<ol class="carousel-indicators">
|
|
8
|
+
<% items.each_with_index do |item, index| %>
|
|
9
|
+
<li data-bs-target="#<%= id %>" data-bs-slide-to="<%= index %>" class="<%= item.active? ? 'active' : '' %>"></li>
|
|
10
|
+
<% end %>
|
|
11
|
+
</ol>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
14
|
+
<div class="carousel-inner">
|
|
15
|
+
<% items.each_with_index do |item, index| %>
|
|
16
|
+
<%= item %>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<% if controls? %>
|
|
21
|
+
<a class="carousel-control-prev" href="#<%= id %>" role="button" data-bs-slide="prev">
|
|
22
|
+
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
|
|
23
|
+
<span class="visually-hidden">Previous</span>
|
|
24
|
+
</a>
|
|
25
|
+
<a class="carousel-control-next" href="#<%= id %>" role="button" data-bs-slide="next">
|
|
26
|
+
<span class="carousel-control-next-icon" aria-hidden="true"></span>
|
|
27
|
+
<span class="visually-hidden">Next</span>
|
|
28
|
+
</a>
|
|
29
|
+
<% end %>
|
|
30
|
+
<% end %>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bs5
|
|
4
|
+
class CarouselComponent < ViewComponent::Base
|
|
5
|
+
include ComponentsHelper
|
|
6
|
+
include ViewComponent::SlotableV2
|
|
7
|
+
using HashRefinement
|
|
8
|
+
|
|
9
|
+
CAROUSEL_OPTIONS = %i[interval keyboard pause slide wrap touch].freeze
|
|
10
|
+
|
|
11
|
+
renders_many :items, Bs5::Carousel::ItemComponent
|
|
12
|
+
|
|
13
|
+
def initialize(options = {})
|
|
14
|
+
@options = options.symbolize_keys
|
|
15
|
+
extract_options
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def extract_options
|
|
21
|
+
@controls = @options.delete(:controls)
|
|
22
|
+
@indicators = @options.delete(:indicators)
|
|
23
|
+
@crossfade = @options.delete(:crossfade)
|
|
24
|
+
@dark = @options.delete(:dark)
|
|
25
|
+
|
|
26
|
+
extract_carousel_options
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def extract_carousel_options
|
|
30
|
+
@carousel_options = @options.extract!(*CAROUSEL_OPTIONS)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def id
|
|
34
|
+
"carousel-#{object_id}"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def items_count
|
|
38
|
+
items.size
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def component_attributes
|
|
42
|
+
{ class: component_class,
|
|
43
|
+
id: id,
|
|
44
|
+
data: @carousel_options.merge(ride: :carousel).prefix_keys_with_bs }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def component_class
|
|
48
|
+
class_names = %w[carousel slide]
|
|
49
|
+
class_names << %w[carousel-fade] if crossfade?
|
|
50
|
+
class_names << %w[carousel-dark] if dark?
|
|
51
|
+
class_names.join(' ')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
%i[controls indicators crossfade dark].each do |name|
|
|
55
|
+
define_method("#{name}?") do
|
|
56
|
+
!instance_variable_get("@#{name}").nil?
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -2,12 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
module Bs5
|
|
4
4
|
class CloseButtonComponent < ViewComponent::Base
|
|
5
|
-
|
|
5
|
+
def initialize(options = {})
|
|
6
|
+
@options = options.symbolize_keys
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
@
|
|
9
|
-
@
|
|
10
|
-
@data = data
|
|
8
|
+
@disabled = options.delete(:disabled)
|
|
9
|
+
@white = options.delete(:white)
|
|
10
|
+
@dismiss = options.delete(:dismiss)
|
|
11
|
+
@data = options.fetch(:data, {})
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def data
|
|
15
|
+
@data['bs-dismiss'] = @dismiss if @dismiss
|
|
16
|
+
|
|
17
|
+
@data
|
|
11
18
|
end
|
|
12
19
|
|
|
13
20
|
private
|
|
@@ -21,7 +28,8 @@ module Bs5
|
|
|
21
28
|
end
|
|
22
29
|
|
|
23
30
|
def component_class
|
|
24
|
-
class_names = [
|
|
31
|
+
class_names = Array(@options[:class])
|
|
32
|
+
class_names << 'btn-close'
|
|
25
33
|
class_names << %w[btn-close-white] if white?
|
|
26
34
|
class_names.join(' ')
|
|
27
35
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<li><%= content || tag.hr(class: 'dropdown-divider') %></li>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bs5
|
|
4
|
+
module Dropdown
|
|
5
|
+
class ItemComponent < ViewComponent::Base
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def content
|
|
9
|
+
return nil if @content.blank?
|
|
10
|
+
|
|
11
|
+
if actionable_element?
|
|
12
|
+
set_actionable_element_class_names
|
|
13
|
+
actionable_element.to_html.html_safe # rubocop:disable Rails/OutputSafety
|
|
14
|
+
else
|
|
15
|
+
@content
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def set_actionable_element_class_names
|
|
20
|
+
class_names = Array(actionable_element[:class])
|
|
21
|
+
class_names << 'dropdown-item'
|
|
22
|
+
actionable_element[:class] = class_names.join(' ')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def actionable_element
|
|
26
|
+
@actionable_element ||= begin
|
|
27
|
+
if (elements = Nokogiri::HTML::DocumentFragment.parse(@content).elements).one? &&
|
|
28
|
+
(element = elements.first).name.in?(%w[a button])
|
|
29
|
+
element
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def actionable_element?
|
|
35
|
+
!!actionable_element
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% if split? %>
|
|
2
|
+
<% if dropstart? %>
|
|
3
|
+
<%= bs5_button_group do %>
|
|
4
|
+
<div class="<%= component_class %>">
|
|
5
|
+
<%= split_button_toggle %>
|
|
6
|
+
<%= menu_content %>
|
|
7
|
+
</div>
|
|
8
|
+
<%= split_button %>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% else %>
|
|
11
|
+
<div class="<%= component_class %>">
|
|
12
|
+
<%= split_button %>
|
|
13
|
+
<%= split_button_toggle %>
|
|
14
|
+
<%= menu_content %>
|
|
15
|
+
</div>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% else %>
|
|
18
|
+
<div class="<%= component_class %>">
|
|
19
|
+
<%= single_button %>
|
|
20
|
+
<%= menu_content %>
|
|
21
|
+
</div>
|
|
22
|
+
<% end %>
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bs5
|
|
4
|
+
class DropdownComponent < ViewComponent::Base # rubocop:disable Metrics/ClassLength
|
|
5
|
+
include ViewComponent::SlotableV2
|
|
6
|
+
include ComponentsHelper
|
|
7
|
+
using HashRefinement
|
|
8
|
+
|
|
9
|
+
CLASS_PREFIX = 'dropdown'
|
|
10
|
+
CLASS_NAMES = {
|
|
11
|
+
visually_hidden: 'visually-hidden',
|
|
12
|
+
toggle_split: "#{CLASS_PREFIX}-toggle-split",
|
|
13
|
+
toggle: "#{CLASS_PREFIX}-toggle",
|
|
14
|
+
menu: "#{CLASS_PREFIX}-menu",
|
|
15
|
+
menu_dark: "#{CLASS_PREFIX}-menu-dark",
|
|
16
|
+
menu_end: "#{CLASS_PREFIX}-menu-end"
|
|
17
|
+
}.freeze
|
|
18
|
+
DIRECTIONS = {
|
|
19
|
+
up: :dropup,
|
|
20
|
+
end: :dropend,
|
|
21
|
+
start: :dropstart
|
|
22
|
+
}.with_indifferent_access.freeze
|
|
23
|
+
DROPDOWN_OPTIONS = %i[offset flip boundary reference display].freeze
|
|
24
|
+
|
|
25
|
+
renders_many :items, Bs5::Dropdown::ItemComponent
|
|
26
|
+
attr_reader :title
|
|
27
|
+
|
|
28
|
+
def initialize(content_or_options = nil, options = {})
|
|
29
|
+
if content_or_options.is_a? Hash
|
|
30
|
+
@options = content_or_options.symbolize_keys
|
|
31
|
+
else
|
|
32
|
+
@title = content_or_options
|
|
33
|
+
@options = options.symbolize_keys
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
extract_options
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def extract_options
|
|
42
|
+
@split = @options.delete(:split)
|
|
43
|
+
@dark = @options.delete(:dark)
|
|
44
|
+
@direction = @options.delete(:direction)
|
|
45
|
+
@align = @options.delete(:align)
|
|
46
|
+
|
|
47
|
+
extract_dropdown_options
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def extract_dropdown_options
|
|
51
|
+
@dropdown_options = @options.extract!(*DROPDOWN_OPTIONS)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def split_button_toggle
|
|
55
|
+
bs5_button_tag(split_button_toggle_options) do
|
|
56
|
+
tag.span('Toggle Dropdown', class: CLASS_NAMES[:visually_hidden])
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def split_button
|
|
61
|
+
bs5_button_tag title, split_button_options
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def single_button
|
|
65
|
+
bs5_button_tag title, single_button_options
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def menu_content
|
|
69
|
+
if items.any?
|
|
70
|
+
tag.ul(class: dropdown_menu_classes) { items.map(&method(:concat)) }
|
|
71
|
+
else
|
|
72
|
+
tag.div(content, class: dropdown_menu_classes)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def single_button_options
|
|
77
|
+
@options.merge(default_options)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def component_class
|
|
81
|
+
class_names = split? ? ['btn-group'] : [CLASS_PREFIX]
|
|
82
|
+
class_names << DIRECTIONS[@direction] if direction?
|
|
83
|
+
|
|
84
|
+
class_names.join(' ')
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def split_button_options
|
|
88
|
+
@options.merge(default_button_options)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def split_button_toggle_options
|
|
92
|
+
single_button_options.dup.tap do |h|
|
|
93
|
+
h[:class] += " #{CLASS_NAMES[:toggle_split]}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
%i[split dark direction].each do |name|
|
|
98
|
+
define_method("#{name}?") do
|
|
99
|
+
!instance_variable_get("@#{name}").nil?
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def dropstart?
|
|
104
|
+
@direction == :start
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def default_button_options
|
|
108
|
+
{ type: :button }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def default_options
|
|
112
|
+
default_button_options.merge({
|
|
113
|
+
data: default_data_options.merge(@dropdown_options).prefix_keys_with_bs,
|
|
114
|
+
aria: { expanded: false },
|
|
115
|
+
class: CLASS_NAMES[:toggle]
|
|
116
|
+
})
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def default_data_options
|
|
120
|
+
{ toggle: :dropdown }.tap do |h|
|
|
121
|
+
h[:display] = 'static' if responsive_align?
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def dropdown_menu_classes
|
|
126
|
+
class_names = [CLASS_NAMES[:menu]]
|
|
127
|
+
class_names << CLASS_NAMES[:menu_dark] if dark?
|
|
128
|
+
class_names << align_classes
|
|
129
|
+
|
|
130
|
+
class_names.compact.join(' ')
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def align_classes
|
|
134
|
+
case @align
|
|
135
|
+
when Symbol
|
|
136
|
+
CLASS_NAMES[:menu_end]
|
|
137
|
+
when Hash
|
|
138
|
+
responsive_align_classes
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def responsive_align_classes
|
|
143
|
+
k, v = @align.first
|
|
144
|
+
class_names = ["#{CLASS_PREFIX}-menu-#{v}-#{k}"]
|
|
145
|
+
class_names << CLASS_NAMES[:menu_end] if @align.with_indifferent_access.key?(:start)
|
|
146
|
+
|
|
147
|
+
class_names
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def responsive_align?
|
|
151
|
+
@align.is_a? Hash
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|