better_ui 0.1.1 → 0.2.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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/better_ui/general/dropdown/component.html.erb +14 -0
  3. data/app/components/better_ui/general/dropdown/component.rb +219 -0
  4. data/app/components/better_ui/general/dropdown/divider_component.html.erb +1 -0
  5. data/app/components/better_ui/general/dropdown/divider_component.rb +41 -0
  6. data/app/components/better_ui/general/dropdown/item_component.html.erb +6 -0
  7. data/app/components/better_ui/general/dropdown/item_component.rb +118 -0
  8. data/app/components/better_ui/general/modal/component.html.erb +42 -0
  9. data/app/components/better_ui/general/modal/component.rb +165 -0
  10. data/app/components/better_ui/general/pagination/component.html.erb +85 -0
  11. data/app/components/better_ui/general/pagination/component.rb +216 -0
  12. data/app/components/better_ui/general/tabs/component.html.erb +3 -0
  13. data/app/components/better_ui/general/tabs/component.rb +102 -0
  14. data/app/components/better_ui/general/tabs/panel_component.html.erb +3 -0
  15. data/app/components/better_ui/general/tabs/panel_component.rb +37 -0
  16. data/app/components/better_ui/general/tabs/tab_component.html.erb +13 -0
  17. data/app/components/better_ui/general/tabs/tab_component.rb +111 -0
  18. data/app/helpers/better_ui/application_helper.rb +9 -0
  19. data/app/helpers/better_ui/general/components/dropdown/divider_helper.rb +32 -0
  20. data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +79 -0
  21. data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +62 -0
  22. data/app/helpers/better_ui/general/components/modal/modal_helper.rb +95 -0
  23. data/app/helpers/better_ui/general/components/pagination/pagination_helper.rb +82 -0
  24. data/app/helpers/better_ui/general/components/tabs/panel_helper.rb +62 -0
  25. data/app/helpers/better_ui/general/components/tabs/tab_helper.rb +55 -0
  26. data/app/helpers/better_ui/general/components/tabs/tabs_helper.rb +62 -0
  27. data/lib/better_ui/version.rb +1 -1
  28. metadata +25 -1
@@ -0,0 +1,85 @@
1
+ <% if should_show_pagination? %>
2
+ <nav aria-label="Pagination" class="flex items-center justify-between">
3
+
4
+ <!-- Info testo opzionale -->
5
+ <% if show_info %>
6
+ <div class="hidden sm:block">
7
+ <p class="text-sm text-gray-700"><%= info_text %></p>
8
+ </div>
9
+ <% end %>
10
+
11
+ <!-- Controlli paginazione -->
12
+ <div class="<%= container_classes %>">
13
+
14
+ <!-- Pulsante Previous -->
15
+ <% if previous_page %>
16
+ <a href="<%= build_url(previous_page) %>"
17
+ class="<%= page_link_classes(previous_page) %> rounded-l-md"
18
+ aria-label="Pagina precedente">
19
+ <span class="sr-only">Precedente</span>
20
+ <%= bui_icon(name: "chevron-left", size: :medium) %>
21
+ </a>
22
+ <% else %>
23
+ <span class="<%= disabled_classes %> rounded-l-md" aria-label="Pagina precedente">
24
+ <span class="sr-only">Precedente</span>
25
+ <%= bui_icon(name: "chevron-left", size: :medium) %>
26
+ </span>
27
+ <% end %>
28
+
29
+ <!-- Prima pagina se necessaria -->
30
+ <% if show_left_ellipsis? %>
31
+ <a href="<%= build_url(1) %>" class="<%= page_link_classes(1) %>">1</a>
32
+ <span class="<%= disabled_classes %>">...</span>
33
+ <% end %>
34
+
35
+ <!-- Range pagine -->
36
+ <% page_range.each do |page_num| %>
37
+ <% if page_num == current_page %>
38
+ <span aria-current="page" class="<%= page_link_classes(page_num) %>">
39
+ <%= page_num %>
40
+ </span>
41
+ <% else %>
42
+ <a href="<%= build_url(page_num) %>"
43
+ class="<%= page_link_classes(page_num) %>"
44
+ aria-label="Vai alla pagina <%= page_num %>">
45
+ <%= page_num %>
46
+ </a>
47
+ <% end %>
48
+ <% end %>
49
+
50
+ <!-- Ultima pagina se necessaria -->
51
+ <% if show_right_ellipsis? %>
52
+ <span class="<%= disabled_classes %>">...</span>
53
+ <a href="<%= build_url(total_pages) %>" class="<%= page_link_classes(total_pages) %>">
54
+ <%= total_pages %>
55
+ </a>
56
+ <% end %>
57
+
58
+ <!-- Pulsante Next -->
59
+ <% if next_page %>
60
+ <a href="<%= build_url(next_page) %>"
61
+ class="<%= page_link_classes(next_page) %> rounded-r-md"
62
+ aria-label="Pagina successiva">
63
+ <span class="sr-only">Successiva</span>
64
+ <%= bui_icon(name: "chevron-right", size: :medium) %>
65
+ </a>
66
+ <% else %>
67
+ <span class="<%= disabled_classes %> rounded-r-md" aria-label="Pagina successiva">
68
+ <span class="sr-only">Successiva</span>
69
+ <%= bui_icon(name: "chevron-right", size: :medium) %>
70
+ </span>
71
+ <% end %>
72
+
73
+ </div>
74
+
75
+ <!-- Info mobile -->
76
+ <% if show_info %>
77
+ <div class="flex flex-1 justify-between sm:hidden">
78
+ <span class="text-sm text-gray-700">
79
+ Pagina <%= current_page %> di <%= total_pages %>
80
+ </span>
81
+ </div>
82
+ <% end %>
83
+
84
+ </nav>
85
+ <% end %>
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Pagination
6
+ class Component < ViewComponent::Base
7
+ include BetterUi::Engine.helpers
8
+
9
+ # Costanti per temi
10
+ PAGINATION_THEME = {
11
+ default: {
12
+ container: 'border-gray-300',
13
+ page: 'border-gray-300 text-gray-500 hover:bg-gray-50 hover:text-gray-700',
14
+ current: 'border-blue-500 bg-blue-50 text-blue-600',
15
+ disabled: 'border-gray-300 text-gray-300 cursor-not-allowed'
16
+ },
17
+ blue: {
18
+ container: 'border-blue-300',
19
+ page: 'border-blue-300 text-blue-600 hover:bg-blue-50 hover:text-blue-700',
20
+ current: 'border-blue-500 bg-blue-100 text-blue-700',
21
+ disabled: 'border-blue-200 text-blue-300 cursor-not-allowed'
22
+ },
23
+ red: {
24
+ container: 'border-red-300',
25
+ page: 'border-red-300 text-red-600 hover:bg-red-50 hover:text-red-700',
26
+ current: 'border-red-500 bg-red-100 text-red-700',
27
+ disabled: 'border-red-200 text-red-300 cursor-not-allowed'
28
+ },
29
+ green: {
30
+ container: 'border-green-300',
31
+ page: 'border-green-300 text-green-600 hover:bg-green-50 hover:text-green-700',
32
+ current: 'border-green-500 bg-green-100 text-green-700',
33
+ disabled: 'border-green-200 text-green-300 cursor-not-allowed'
34
+ },
35
+ yellow: {
36
+ container: 'border-yellow-300',
37
+ page: 'border-yellow-300 text-yellow-600 hover:bg-yellow-50 hover:text-yellow-700',
38
+ current: 'border-yellow-500 bg-yellow-100 text-yellow-700',
39
+ disabled: 'border-yellow-200 text-yellow-300 cursor-not-allowed'
40
+ },
41
+ violet: {
42
+ container: 'border-violet-300',
43
+ page: 'border-violet-300 text-violet-600 hover:bg-violet-50 hover:text-violet-700',
44
+ current: 'border-violet-500 bg-violet-100 text-violet-700',
45
+ disabled: 'border-violet-200 text-violet-300 cursor-not-allowed'
46
+ },
47
+ orange: {
48
+ container: 'border-orange-300',
49
+ page: 'border-orange-300 text-orange-600 hover:bg-orange-50 hover:text-orange-700',
50
+ current: 'border-orange-500 bg-orange-100 text-orange-700',
51
+ disabled: 'border-orange-200 text-orange-300 cursor-not-allowed'
52
+ },
53
+ rose: {
54
+ container: 'border-rose-300',
55
+ page: 'border-rose-300 text-rose-600 hover:bg-rose-50 hover:text-rose-700',
56
+ current: 'border-rose-500 bg-rose-100 text-rose-700',
57
+ disabled: 'border-rose-200 text-rose-300 cursor-not-allowed'
58
+ },
59
+ white: {
60
+ container: 'border-white',
61
+ page: 'border-white text-gray-700 hover:bg-white hover:text-gray-900',
62
+ current: 'border-white bg-white text-gray-900',
63
+ disabled: 'border-white text-gray-400 cursor-not-allowed'
64
+ }
65
+ }.freeze
66
+
67
+ # Costanti per dimensioni
68
+ PAGINATION_SIZE = {
69
+ small: 'px-2 py-1 text-sm',
70
+ medium: 'px-3 py-2 text-base',
71
+ large: 'px-4 py-3 text-lg'
72
+ }.freeze
73
+
74
+ def initialize(current_page:, total_pages:, path:, theme: :default, size: :medium,
75
+ window: 2, show_info: false, per_page: nil, classes: '', **options)
76
+ @current_page = current_page.to_i
77
+ @total_pages = total_pages.to_i
78
+ @path = path
79
+ @theme = theme
80
+ @size = size
81
+ @window = window.to_i
82
+ @show_info = show_info
83
+ @per_page = per_page
84
+ @classes = classes
85
+ @options = options
86
+
87
+ validate_params
88
+ end
89
+
90
+ private
91
+
92
+ attr_reader :current_page, :total_pages, :path, :theme, :size, :window,
93
+ :show_info, :per_page, :classes, :options
94
+
95
+ def validate_params
96
+ validate_theme
97
+ validate_size
98
+ validate_pages
99
+ end
100
+
101
+ def validate_theme
102
+ return if PAGINATION_THEME.key?(theme)
103
+
104
+ raise ArgumentError,
105
+ "Invalid theme: #{theme}. Valid themes are: #{PAGINATION_THEME.keys.join(', ')}"
106
+ end
107
+
108
+ def validate_size
109
+ return if PAGINATION_SIZE.key?(size)
110
+
111
+ raise ArgumentError,
112
+ "Invalid size: #{size}. Valid sizes are: #{PAGINATION_SIZE.keys.join(', ')}"
113
+ end
114
+
115
+ def validate_pages
116
+ if current_page < 1 || current_page > total_pages
117
+ raise ArgumentError, "current_page must be between 1 and #{total_pages}"
118
+ end
119
+
120
+ if total_pages < 1
121
+ raise ArgumentError, "total_pages must be at least 1"
122
+ end
123
+ end
124
+
125
+ def theme_classes
126
+ PAGINATION_THEME[theme]
127
+ end
128
+
129
+ def size_classes
130
+ PAGINATION_SIZE[size]
131
+ end
132
+
133
+ def container_classes
134
+ "inline-flex -space-x-px rounded-md shadow-sm #{theme_classes[:container]} #{classes}".strip
135
+ end
136
+
137
+ def page_link_classes(page_num)
138
+ base_classes = "relative inline-flex items-center border #{size_classes} font-medium"
139
+
140
+ if page_num == current_page
141
+ "#{base_classes} #{theme_classes[:current]}"
142
+ else
143
+ "#{base_classes} #{theme_classes[:page]} focus:z-10 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
144
+ end
145
+ end
146
+
147
+ def disabled_classes
148
+ "relative inline-flex items-center border #{size_classes} font-medium #{theme_classes[:disabled]}"
149
+ end
150
+
151
+ def page_range
152
+ return [1] if total_pages == 1
153
+
154
+ start_page = [current_page - window, 1].max
155
+ end_page = [current_page + window, total_pages].min
156
+
157
+ # Espandi il range se possibile
158
+ if end_page - start_page < (window * 2)
159
+ if start_page == 1
160
+ end_page = [start_page + (window * 2), total_pages].min
161
+ elsif end_page == total_pages
162
+ start_page = [end_page - (window * 2), 1].max
163
+ end
164
+ end
165
+
166
+ (start_page..end_page).to_a
167
+ end
168
+
169
+ def show_left_ellipsis?
170
+ page_range.first > 2
171
+ end
172
+
173
+ def show_right_ellipsis?
174
+ page_range.last < total_pages - 1
175
+ end
176
+
177
+ def build_url(page_num)
178
+ return '#' if page_num == current_page
179
+
180
+ uri = URI.parse(path)
181
+ params = URI.decode_www_form(uri.query || '')
182
+ params = params.reject { |k, _v| k == 'page' }
183
+ params << ['page', page_num] if page_num > 1
184
+
185
+ uri.query = params.empty? ? nil : URI.encode_www_form(params)
186
+ uri.to_s
187
+ end
188
+
189
+ def previous_page
190
+ current_page > 1 ? current_page - 1 : nil
191
+ end
192
+
193
+ def next_page
194
+ current_page < total_pages ? current_page + 1 : nil
195
+ end
196
+
197
+ def info_text
198
+ return '' unless show_info && per_page
199
+
200
+ start_item = ((current_page - 1) * per_page) + 1
201
+ end_item = [current_page * per_page, total_items].min
202
+
203
+ "Mostrando #{start_item}-#{end_item} di #{total_items} risultati"
204
+ end
205
+
206
+ def total_items
207
+ total_pages * per_page
208
+ end
209
+
210
+ def should_show_pagination?
211
+ total_pages > 1
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,3 @@
1
+ <div <%= tag.attributes(container_attributes) %>>
2
+ <%= content %>
3
+ </div>
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Tabs
6
+ class Component < ViewComponent::Base
7
+ TABS_VARIANT = {
8
+ pills: 'bg-gray-100 rounded-lg p-1',
9
+ underline: 'border-b border-gray-200',
10
+ bordered: 'border border-gray-200 rounded-lg',
11
+ minimal: ''
12
+ }.freeze
13
+
14
+ TABS_THEME_DEFAULT = {
15
+ default: 'text-gray-600',
16
+ blue: 'text-blue-600',
17
+ red: 'text-red-600',
18
+ green: 'text-green-600',
19
+ yellow: 'text-yellow-600',
20
+ violet: 'text-violet-600',
21
+ orange: 'text-orange-600',
22
+ rose: 'text-rose-600',
23
+ white: 'text-white'
24
+ }.freeze
25
+
26
+ TABS_SIZE = {
27
+ small: 'text-sm',
28
+ medium: 'text-base',
29
+ large: 'text-lg'
30
+ }.freeze
31
+
32
+ TABS_ORIENTATION = {
33
+ horizontal: 'flex-row',
34
+ vertical: 'flex-col'
35
+ }.freeze
36
+
37
+ def initialize(variant: :pills, theme: :default, size: :medium, orientation: :horizontal, classes: '', **options)
38
+ @variant = variant
39
+ @theme = theme
40
+ @size = size
41
+ @orientation = orientation
42
+ @classes = classes
43
+ @options = options
44
+
45
+ validate_params
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :variant, :theme, :size, :orientation, :classes, :options
51
+
52
+ def validate_params
53
+ validate_variant
54
+ validate_theme
55
+ validate_size
56
+ validate_orientation
57
+ end
58
+
59
+ def validate_variant
60
+ return if TABS_VARIANT.key?(variant)
61
+
62
+ raise ArgumentError, "Invalid variant: #{variant}. Must be one of #{TABS_VARIANT.keys}"
63
+ end
64
+
65
+ def validate_theme
66
+ return if TABS_THEME_DEFAULT.key?(theme)
67
+
68
+ raise ArgumentError, "Invalid theme: #{theme}. Must be one of #{TABS_THEME_DEFAULT.keys}"
69
+ end
70
+
71
+ def validate_size
72
+ return if TABS_SIZE.key?(size)
73
+
74
+ raise ArgumentError, "Invalid size: #{size}. Must be one of #{TABS_SIZE.keys}"
75
+ end
76
+
77
+ def validate_orientation
78
+ return if TABS_ORIENTATION.key?(orientation)
79
+
80
+ raise ArgumentError, "Invalid orientation: #{orientation}. Must be one of #{TABS_ORIENTATION.keys}"
81
+ end
82
+
83
+ def container_attributes
84
+ base_classes = [
85
+ 'flex',
86
+ TABS_ORIENTATION[orientation],
87
+ TABS_VARIANT[variant],
88
+ TABS_THEME_DEFAULT[theme],
89
+ TABS_SIZE[size],
90
+ classes
91
+ ].compact.join(' ')
92
+
93
+ {
94
+ class: base_classes,
95
+ role: 'tablist',
96
+ 'data-controller': 'bui-tabs'
97
+ }.merge(options)
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,3 @@
1
+ <div <%= tag.attributes(panel_attributes) %>>
2
+ <%= content %>
3
+ </div>
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Tabs
6
+ class PanelComponent < ViewComponent::Base
7
+ def initialize(id:, active: false, classes: '', **options)
8
+ @id = id
9
+ @active = active
10
+ @classes = classes
11
+ @options = options
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :id, :active, :classes, :options
17
+
18
+ def panel_attributes
19
+ base_classes = [
20
+ 'focus:outline-none',
21
+ active ? 'block' : 'hidden',
22
+ classes
23
+ ].compact.join(' ')
24
+
25
+ {
26
+ class: base_classes,
27
+ role: 'tabpanel',
28
+ id: id,
29
+ 'aria-labelledby': "tab-#{id}",
30
+ 'data-bui-tabs-target': 'panel',
31
+ tabindex: '0'
32
+ }.merge(options)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ <button <%= tag.attributes(tab_attributes) %>>
2
+ <% if has_icon? %>
3
+ <%= bui_icon(name: icon, classes: 'w-5 h-5') %>
4
+ <% end %>
5
+
6
+ <span><%= text %></span>
7
+
8
+ <% if has_badge? %>
9
+ <span class="ml-2 bg-red-100 text-red-800 text-xs font-medium px-2 py-0.5 rounded-full">
10
+ <%= badge %>
11
+ </span>
12
+ <% end %>
13
+ </button>
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Tabs
6
+ class TabComponent < ViewComponent::Base
7
+ include BetterUi::General::Components::Icon::IconHelper
8
+
9
+ TAB_THEME_ACTIVE = {
10
+ default: 'bg-white text-gray-900 shadow-sm',
11
+ blue: 'bg-blue-600 text-white',
12
+ red: 'bg-red-600 text-white',
13
+ green: 'bg-green-600 text-white',
14
+ yellow: 'bg-yellow-600 text-white',
15
+ violet: 'bg-violet-600 text-white',
16
+ orange: 'bg-orange-600 text-white',
17
+ rose: 'bg-rose-600 text-white',
18
+ white: 'bg-white text-gray-900'
19
+ }.freeze
20
+
21
+ TAB_THEME_INACTIVE = {
22
+ default: 'text-gray-500 hover:text-gray-700',
23
+ blue: 'text-blue-600 hover:text-blue-700',
24
+ red: 'text-red-600 hover:text-red-700',
25
+ green: 'text-green-600 hover:text-green-700',
26
+ yellow: 'text-yellow-600 hover:text-yellow-700',
27
+ violet: 'text-violet-600 hover:text-violet-700',
28
+ orange: 'text-orange-600 hover:text-orange-700',
29
+ rose: 'text-rose-600 hover:text-rose-700',
30
+ white: 'text-gray-600 hover:text-gray-700'
31
+ }.freeze
32
+
33
+ TAB_SIZE = {
34
+ small: 'px-3 py-1.5 text-sm',
35
+ medium: 'px-4 py-2 text-base',
36
+ large: 'px-6 py-3 text-lg'
37
+ }.freeze
38
+
39
+ def initialize(text:, target:, active: false, icon: nil, disabled: false, badge: nil,
40
+ theme: :default, size: :medium, classes: '', **options)
41
+ @text = text
42
+ @target = target
43
+ @active = active
44
+ @icon = icon
45
+ @disabled = disabled
46
+ @badge = badge
47
+ @theme = theme
48
+ @size = size
49
+ @classes = classes
50
+ @options = options
51
+
52
+ validate_params
53
+ end
54
+
55
+ private
56
+
57
+ attr_reader :text, :target, :active, :icon, :disabled, :badge, :theme, :size, :classes, :options
58
+
59
+ def validate_params
60
+ validate_theme
61
+ validate_size
62
+ end
63
+
64
+ def validate_theme
65
+ return if TAB_THEME_ACTIVE.key?(theme)
66
+
67
+ raise ArgumentError, "Invalid theme: #{theme}. Must be one of #{TAB_THEME_ACTIVE.keys}"
68
+ end
69
+
70
+ def validate_size
71
+ return if TAB_SIZE.key?(size)
72
+
73
+ raise ArgumentError, "Invalid size: #{size}. Must be one of #{TAB_SIZE.keys}"
74
+ end
75
+
76
+ def tab_attributes
77
+ theme_classes = active ? TAB_THEME_ACTIVE[theme] : TAB_THEME_INACTIVE[theme]
78
+
79
+ base_classes = [
80
+ 'inline-flex items-center justify-center gap-2 font-medium rounded-md transition-colors',
81
+ 'focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500',
82
+ TAB_SIZE[size],
83
+ theme_classes,
84
+ disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer',
85
+ classes
86
+ ].compact.join(' ')
87
+
88
+ {
89
+ class: base_classes,
90
+ role: 'tab',
91
+ 'aria-selected': active.to_s,
92
+ 'aria-controls': target,
93
+ 'data-bui-tabs-target': 'tab',
94
+ 'data-target': target,
95
+ 'data-action': disabled ? '' : 'click->bui-tabs#switchTab',
96
+ tabindex: active ? '0' : '-1',
97
+ id: "tab-#{target}"
98
+ }.merge(options)
99
+ end
100
+
101
+ def has_icon?
102
+ icon.present?
103
+ end
104
+
105
+ def has_badge?
106
+ badge.present?
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -10,9 +10,14 @@ module BetterUi
10
10
  include General::Components::Breadcrumb::BreadcrumbHelper
11
11
  include General::Components::Button::ButtonHelper
12
12
  include General::Components::Divider::DividerHelper
13
+ include General::Components::Dropdown::DropdownHelper
14
+ include General::Components::Dropdown::ItemHelper
15
+ include General::Components::Dropdown::DividerHelper
13
16
  include General::Components::Heading::HeadingHelper
14
17
  include General::Components::Icon::IconHelper
15
18
  include General::Components::Link::LinkHelper
19
+ include General::Components::Modal::ModalHelper
20
+ include General::Components::Pagination::PaginationHelper
16
21
  include General::Components::Panel::PanelHelper
17
22
  include General::Components::Progress::ProgressHelper
18
23
  include General::Components::Spinner::SpinnerHelper
@@ -26,6 +31,10 @@ module BetterUi
26
31
  include General::Components::Table::TrHelper
27
32
 
28
33
  include General::Components::Tag::TagHelper
34
+ include General::Components::Tabs::TabsHelper
35
+ include General::Components::Tabs::TabHelper
36
+
37
+ include General::Components::Tabs::PanelHelper
29
38
  include General::Components::Tooltip::TooltipHelper
30
39
 
31
40
  # General Form Components
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Components
6
+ module Dropdown
7
+ module DividerHelper
8
+ ##
9
+ # Crea un divisore per separare gruppi di elementi nel menu dropdown.
10
+ #
11
+ # @param classes [String] Classi CSS aggiuntive
12
+ # @param options [Hash] Attributi HTML aggiuntivi
13
+ #
14
+ # @return [String] Il markup HTML del divisore dropdown
15
+ #
16
+ # @example Uso base
17
+ # <%= bui_dropdown_divider %>
18
+ #
19
+ # @example Con classi personalizzate
20
+ # <%= bui_dropdown_divider(classes: "my-4") %>
21
+ #
22
+ def bui_dropdown_divider(classes: '', **options)
23
+ render BetterUi::General::Dropdown::DividerComponent.new(
24
+ classes: classes,
25
+ **options
26
+ )
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end