better_ui 0.1.1 → 0.3.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/better_ui/application/sidebar/component.html.erb +53 -16
  3. data/app/components/better_ui/application/sidebar/component.rb +3 -2
  4. data/app/components/better_ui/general/accordion/component.html.erb +5 -0
  5. data/app/components/better_ui/general/accordion/component.rb +92 -0
  6. data/app/components/better_ui/general/accordion/item_component.html.erb +12 -0
  7. data/app/components/better_ui/general/accordion/item_component.rb +176 -0
  8. data/app/components/better_ui/general/button/component.html.erb +4 -4
  9. data/app/components/better_ui/general/dropdown/component.html.erb +25 -0
  10. data/app/components/better_ui/general/dropdown/component.rb +170 -0
  11. data/app/components/better_ui/general/dropdown/divider_component.html.erb +1 -0
  12. data/app/components/better_ui/general/dropdown/divider_component.rb +41 -0
  13. data/app/components/better_ui/general/dropdown/item_component.html.erb +6 -0
  14. data/app/components/better_ui/general/dropdown/item_component.rb +119 -0
  15. data/app/components/better_ui/general/modal/component.html.erb +5 -0
  16. data/app/components/better_ui/general/modal/component.rb +47 -0
  17. data/app/components/better_ui/general/modal/modal_component.html.erb +52 -0
  18. data/app/components/better_ui/general/modal/modal_component.rb +160 -0
  19. data/app/components/better_ui/general/pagination/component.html.erb +85 -0
  20. data/app/components/better_ui/general/pagination/component.rb +216 -0
  21. data/app/components/better_ui/general/tabs/component.html.erb +11 -0
  22. data/app/components/better_ui/general/tabs/component.rb +120 -0
  23. data/app/components/better_ui/general/tabs/panel_component.html.erb +3 -0
  24. data/app/components/better_ui/general/tabs/panel_component.rb +37 -0
  25. data/app/components/better_ui/general/tabs/tab_component.html.erb +13 -0
  26. data/app/components/better_ui/general/tabs/tab_component.rb +111 -0
  27. data/app/helpers/better_ui/application_helper.rb +12 -3
  28. data/app/helpers/better_ui/general/components/accordion/accordion_helper.rb +73 -0
  29. data/app/helpers/better_ui/general/components/accordion.rb +11 -0
  30. data/app/helpers/better_ui/general/components/dropdown/divider_helper.rb +32 -0
  31. data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +79 -0
  32. data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +62 -0
  33. data/app/helpers/better_ui/general/components/modal/modal_helper.rb +85 -0
  34. data/app/helpers/better_ui/general/components/modal.rb +11 -0
  35. data/app/helpers/better_ui/general/components/pagination/pagination_helper.rb +82 -0
  36. data/app/helpers/better_ui/general/components/tabs/panel_helper.rb +62 -0
  37. data/app/helpers/better_ui/general/components/tabs/tab_helper.rb +55 -0
  38. data/app/helpers/better_ui/general/components/tabs/tabs_helper.rb +95 -0
  39. data/lib/better_ui/version.rb +1 -1
  40. metadata +35 -2
@@ -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,11 @@
1
+ <div <%= tag.attributes(wrapper_attributes) %>>
2
+ <!-- Navigation Tabs -->
3
+ <div <%= tag.attributes(navigation_attributes) %>>
4
+ <%= navigation %>
5
+ </div>
6
+
7
+ <!-- Tab Panels -->
8
+ <div <%= tag.attributes(panels_attributes) %>>
9
+ <%= panels %>
10
+ </div>
11
+ </div>
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Tabs
6
+ class Component < ViewComponent::Base
7
+ renders_one :navigation
8
+ renders_one :panels
9
+ TABS_VARIANT = {
10
+ pills: 'bg-gray-100 rounded-lg p-1',
11
+ underline: 'border-b border-gray-200',
12
+ bordered: 'border border-gray-200 rounded-lg',
13
+ minimal: ''
14
+ }.freeze
15
+
16
+ TABS_THEME_DEFAULT = {
17
+ default: 'text-gray-600',
18
+ blue: 'text-blue-600',
19
+ red: 'text-red-600',
20
+ green: 'text-green-600',
21
+ yellow: 'text-yellow-600',
22
+ violet: 'text-violet-600',
23
+ orange: 'text-orange-600',
24
+ rose: 'text-rose-600',
25
+ white: 'text-white'
26
+ }.freeze
27
+
28
+ TABS_SIZE = {
29
+ small: 'text-sm',
30
+ medium: 'text-base',
31
+ large: 'text-lg'
32
+ }.freeze
33
+
34
+ TABS_ORIENTATION = {
35
+ horizontal: 'flex-row',
36
+ vertical: 'flex-col'
37
+ }.freeze
38
+
39
+ def initialize(variant: :pills, theme: :default, size: :medium, orientation: :horizontal,
40
+ navigation_classes: '', panels_classes: 'mt-4', **options)
41
+ @variant = variant
42
+ @theme = theme
43
+ @size = size
44
+ @orientation = orientation
45
+ @navigation_classes = navigation_classes
46
+ @panels_classes = panels_classes
47
+ @options = options
48
+
49
+ validate_params
50
+ end
51
+
52
+ private
53
+
54
+ attr_reader :variant, :theme, :size, :orientation, :navigation_classes, :panels_classes, :options
55
+
56
+ def validate_params
57
+ validate_variant
58
+ validate_theme
59
+ validate_size
60
+ validate_orientation
61
+ end
62
+
63
+ def validate_variant
64
+ return if TABS_VARIANT.key?(variant)
65
+
66
+ raise ArgumentError, "Invalid variant: #{variant}. Must be one of #{TABS_VARIANT.keys}"
67
+ end
68
+
69
+ def validate_theme
70
+ return if TABS_THEME_DEFAULT.key?(theme)
71
+
72
+ raise ArgumentError, "Invalid theme: #{theme}. Must be one of #{TABS_THEME_DEFAULT.keys}"
73
+ end
74
+
75
+ def validate_size
76
+ return if TABS_SIZE.key?(size)
77
+
78
+ raise ArgumentError, "Invalid size: #{size}. Must be one of #{TABS_SIZE.keys}"
79
+ end
80
+
81
+ def validate_orientation
82
+ return if TABS_ORIENTATION.key?(orientation)
83
+
84
+ raise ArgumentError, "Invalid orientation: #{orientation}. Must be one of #{TABS_ORIENTATION.keys}"
85
+ end
86
+
87
+ # Attributi per il wrapper principale (con data-controller)
88
+ def wrapper_attributes
89
+ {
90
+ 'data-controller': 'bui-tabs'
91
+ }.merge(options)
92
+ end
93
+
94
+ # Attributi per il container della navigazione tabs
95
+ def navigation_attributes
96
+ base_classes = [
97
+ 'flex',
98
+ TABS_ORIENTATION[orientation],
99
+ TABS_VARIANT[variant],
100
+ TABS_THEME_DEFAULT[theme],
101
+ TABS_SIZE[size],
102
+ navigation_classes
103
+ ].compact.join(' ')
104
+
105
+ {
106
+ class: base_classes,
107
+ role: 'tablist'
108
+ }
109
+ end
110
+
111
+ # Attributi per il container dei panel
112
+ def panels_attributes
113
+ {
114
+ class: panels_classes
115
+ }
116
+ end
117
+ end
118
+ end
119
+ end
120
+ 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
+ 'hidden', # Sempre nascosto inizialmente, JavaScript gestisce la visibilità
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 keydown->bui-tabs#keydown',
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
@@ -4,18 +4,26 @@ module BetterUi
4
4
  # Questo assicura che tutti gli helper specifici dei componenti siano disponibili.
5
5
 
6
6
  # General Components
7
+ include General::Components::Accordion::AccordionHelper
7
8
  include General::Components::Alert::AlertHelper
8
9
  include General::Components::Avatar::AvatarHelper
9
10
  include General::Components::Badge::BadgeHelper
10
11
  include General::Components::Breadcrumb::BreadcrumbHelper
11
12
  include General::Components::Button::ButtonHelper
12
13
  include General::Components::Divider::DividerHelper
14
+ include General::Components::Dropdown::DropdownHelper
15
+ include General::Components::Dropdown::ItemHelper
16
+ include General::Components::Dropdown::DividerHelper
13
17
  include General::Components::Heading::HeadingHelper
14
18
  include General::Components::Icon::IconHelper
15
19
  include General::Components::Link::LinkHelper
20
+ include General::Components::Modal::ModalHelper
21
+ include General::Components::Pagination::PaginationHelper
16
22
  include General::Components::Panel::PanelHelper
17
23
  include General::Components::Progress::ProgressHelper
18
24
  include General::Components::Spinner::SpinnerHelper
25
+ include General::Components::Tag::TagHelper
26
+ include General::Components::Tooltip::TooltipHelper
19
27
 
20
28
  include General::Components::Table::TableHelper
21
29
  include General::Components::Table::TbodyHelper
@@ -24,9 +32,10 @@ module BetterUi
24
32
  include General::Components::Table::ThHelper
25
33
  include General::Components::Table::TheadHelper
26
34
  include General::Components::Table::TrHelper
27
-
28
- include General::Components::Tag::TagHelper
29
- include General::Components::Tooltip::TooltipHelper
35
+
36
+ include General::Components::Tabs::TabsHelper
37
+ include General::Components::Tabs::TabHelper
38
+ include General::Components::Tabs::PanelHelper
30
39
 
31
40
  # General Form Components
32
41
  include General::Components::Input::Checkbox::CheckboxHelper
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Components
6
+ module Accordion
7
+ module AccordionHelper
8
+ # Rendering di un accordion con item collassabili/espandibili
9
+ #
10
+ # @param multiple [Boolean] se permettere multipli item aperti contemporaneamente
11
+ # @param theme [Symbol] tema di colore per l'accordion
12
+ # @param variant [Symbol] variante visuale dell'accordion
13
+ # @param size [Symbol] dimensione del testo e padding
14
+ # @param classes [String] classi CSS aggiuntive per il wrapper
15
+ # @param options [Hash] attributi HTML aggiuntivi da passare al componente
16
+ # @return [String] HTML renderizzato dell'accordion
17
+ #
18
+ # @example Uso base standalone
19
+ # <%= bui_accordion do |accordion| %>
20
+ # <%= accordion.with_item(title: "Domanda 1") do %>
21
+ # <p>Risposta alla prima domanda</p>
22
+ # <% end %>
23
+ # <% end %>
24
+ #
25
+ # @example Con tema e configurazioni
26
+ # <%= bui_accordion(multiple: true, theme: :blue, size: :large) do |accordion| %>
27
+ # <%= accordion.with_item(title: "FAQ 1", expanded: true) do %>
28
+ # <p>Contenuto della prima FAQ</p>
29
+ # <% end %>
30
+ # <%= accordion.with_item(title: "FAQ 2") do %>
31
+ # <p>Contenuto della seconda FAQ</p>
32
+ # <% end %>
33
+ # <% end %>
34
+ #
35
+ # @example Con varianti stilistiche
36
+ # <%= bui_accordion(variant: :separated, theme: :green) do |accordion| %>
37
+ # <%= accordion.with_item(title: "Sezione 1") do %>
38
+ # <div>Contenuto complesso con HTML</div>
39
+ # <% end %>
40
+ # <% end %>
41
+ #
42
+ # @example Con attributi HTML aggiuntivi
43
+ # <%= bui_accordion(id: "faq-accordion", "data-testid": "main-faq") do |accordion| %>
44
+ # <%= accordion.with_item(title: "Come funziona?") do %>
45
+ # <p>Spiegazione dettagliata...</p>
46
+ # <% end %>
47
+ # <% end %>
48
+ #
49
+ # @example Con item disabilitati e icone personalizzate
50
+ # <%= bui_accordion(theme: :violet, size: :small) do |accordion| %>
51
+ # <%= accordion.with_item(title: "Disponibile", icon: "check-circle") do %>
52
+ # <p>Questa sezione è disponibile</p>
53
+ # <% end %>
54
+ # <%= accordion.with_item(title: "Non disponibile", disabled: true) do %>
55
+ # <p>Questa sezione è disabilitata</p>
56
+ # <% end %>
57
+ # <% end %>
58
+ def bui_accordion(multiple: false, theme: :default, variant: :bordered, size: :medium,
59
+ classes: '', **options, &block)
60
+ render BetterUi::General::Accordion::Component.new(
61
+ multiple: multiple,
62
+ theme: theme,
63
+ variant: variant,
64
+ size: size,
65
+ classes: classes,
66
+ **options
67
+ ), &block
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Components
6
+ module Accordion
7
+ include BetterUi::General::Components::Accordion::AccordionHelper
8
+ end
9
+ end
10
+ end
11
+ end
@@ -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