better_ui 0.2.0 → 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 (27) 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 +18 -7
  10. data/app/components/better_ui/general/dropdown/component.rb +8 -57
  11. data/app/components/better_ui/general/dropdown/item_component.rb +2 -1
  12. data/app/components/better_ui/general/modal/component.html.erb +5 -42
  13. data/app/components/better_ui/general/modal/component.rb +22 -140
  14. data/app/components/better_ui/general/modal/modal_component.html.erb +52 -0
  15. data/app/components/better_ui/general/modal/modal_component.rb +160 -0
  16. data/app/components/better_ui/general/tabs/component.html.erb +10 -2
  17. data/app/components/better_ui/general/tabs/component.rb +26 -8
  18. data/app/components/better_ui/general/tabs/panel_component.rb +1 -1
  19. data/app/components/better_ui/general/tabs/tab_component.rb +1 -1
  20. data/app/helpers/better_ui/application_helper.rb +4 -4
  21. data/app/helpers/better_ui/general/components/accordion/accordion_helper.rb +73 -0
  22. data/app/helpers/better_ui/general/components/accordion.rb +11 -0
  23. data/app/helpers/better_ui/general/components/modal/modal_helper.rb +34 -44
  24. data/app/helpers/better_ui/general/components/modal.rb +11 -0
  25. data/app/helpers/better_ui/general/components/tabs/tabs_helper.rb +59 -26
  26. data/lib/better_ui/version.rb +1 -1
  27. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b6dbaeb8dede2be21e4bcff93ba5a7793a08f9e84521c2b46bd8e879be6d072
4
- data.tar.gz: b9f58c90c95a748e694018c85aff6ceeb23ecee0f155b8cb45d9b64dc78da2e1
3
+ metadata.gz: 9f8b92fdba76f5e84f09fb6a3bf9879b209e0e7dfd8f864186784db24b808c59
4
+ data.tar.gz: 4d21e971baeecb10c7a3c5952041ac7e6911342e0efe4d0015efb92f9f214652
5
5
  SHA512:
6
- metadata.gz: 7bba346a1d310e3869a232212c8708bbff5ef18f048d9361acc895efa78709b6de1e78bfee44dc3b30db77daf3cf380093331d1443b90d0bb6c18a7ad5d9e2e3
7
- data.tar.gz: be0e614d6d37d86946d7908e8f2ae0ff83a452dcfc7fc1dd29c42560f77a368bab654e556e767e2d06dcf26236281b9f39987221f0bbee451ff7a16d1125cf39
6
+ metadata.gz: 778ee8d067130b37947a227ccf1322e8cf5b82d19c9b3f7a0371cc16b62071daaeb5eb6a191898ef2b06419486fe656bc22bea4314fbf98884fdf16259ac8409
7
+ data.tar.gz: 1df545b428bdb281fb6f78b7be2e58e455bd6bd67f0202684f51b9e756c9ccc3e928a283f97d98a924185e1bb859b63f8cf2a91857ef49478cf6bfc3f254629d
@@ -1,10 +1,21 @@
1
- <aside class="<%= container_classes %>">
1
+ <div data-controller="bui-sidebar"
2
+ data-bui-sidebar-width-value="<%= @width == :md ? 256 : 320 %>"
3
+ data-bui-sidebar-min-width-value="200"
4
+ data-bui-sidebar-max-width-value="400"
5
+ data-bui-sidebar-pinned-value="true">
6
+
7
+ <!-- Mobile Overlay -->
8
+ <div data-bui-sidebar-target="overlay" class="fixed inset-0 bg-black bg-opacity-50 z-40 hidden md:hidden"></div>
9
+
10
+ <!-- Sidebar Container -->
11
+ <aside data-bui-sidebar-target="container" class="<%= container_classes %>">
2
12
  <!-- Header Section -->
3
13
  <% if has_header? %>
4
14
  <div class="px-6 py-4 border-b border-gray-200">
5
- <% if header[:logo].present? %>
6
- <div class="flex items-center">
7
- <div class="flex-shrink-0">
15
+ <div class="flex items-center justify-between">
16
+ <% if header[:logo].present? %>
17
+ <div class="flex items-center">
18
+ <div class="flex-shrink-0">
8
19
  <% if header[:logo].is_a?(Hash) %>
9
20
  <%= bui_avatar(**header[:logo]) %>
10
21
  <% else %>
@@ -20,19 +31,30 @@
20
31
  </div>
21
32
  <% end %>
22
33
  </div>
23
- <% elsif header[:title].present? %>
24
- <div>
25
- <h2 class="text-lg font-semibold text-gray-900"><%= header[:title] %></h2>
26
- <% if header[:subtitle].present? %>
27
- <p class="text-sm text-gray-500"><%= header[:subtitle] %></p>
28
- <% end %>
29
- </div>
30
- <% end %>
34
+ <% elsif header[:title].present? %>
35
+ <div>
36
+ <h2 class="text-lg font-semibold text-gray-900"><%= header[:title] %></h2>
37
+ <% if header[:subtitle].present? %>
38
+ <p class="text-sm text-gray-500"><%= header[:subtitle] %></p>
39
+ <% end %>
40
+ </div>
41
+ <% end %>
42
+
43
+ <!-- Collapse Button (solo se collapsible) -->
44
+ <% if collapsible %>
45
+ <%= bui_button(
46
+ icon: "arrow-left",
47
+ type: :white,
48
+ size: :small,
49
+ title: "Comprimi sidebar"
50
+ ) %>
51
+ <% end %>
52
+ </div>
31
53
  </div>
32
54
  <% end %>
33
55
 
34
56
  <!-- Navigation Section -->
35
- <nav class="flex-1 px-4 py-6 space-y-6">
57
+ <nav class="flex-1 px-4 py-6 space-y-6 overflow-y-auto">
36
58
  <% navigation_sections.each do |section| %>
37
59
  <div class="space-y-2">
38
60
  <!-- Section Title -->
@@ -57,7 +79,9 @@
57
79
  <button
58
80
  type="button"
59
81
  class="group flex items-center justify-between w-full px-3 py-2 text-sm font-medium text-gray-700 rounded-md hover:bg-gray-50 hover:text-gray-900 transition-colors duration-150"
60
- data-collapse-target="#<%= item[:id] %>"
82
+ data-bui-sidebar-target="sectionTrigger"
83
+ data-bui-sidebar-section-id="<%= item[:id] %>"
84
+ data-action="click->bui-sidebar#toggleSection"
61
85
  aria-expanded="<%= item[:expanded] || false %>"
62
86
  >
63
87
  <div class="flex items-center">
@@ -68,13 +92,14 @@
68
92
  <% end %>
69
93
  <span><%= item[:label] %></span>
70
94
  </div>
71
- <span class="ml-3 transform transition-transform duration-150 <%= 'rotate-90' if item[:expanded] %>">
95
+ <span class="ml-3 transform transition-transform duration-150 <%= 'rotate-90' if item[:expanded] %>" data-bui-sidebar-chevron>
72
96
  <%= bui_icon("chevron-right", size: :small) %>
73
97
  </span>
74
98
  </button>
75
99
 
76
100
  <div
77
- id="<%= item[:id] %>"
101
+ data-bui-sidebar-target="sectionContent"
102
+ data-bui-sidebar-section-id="<%= item[:id] %>"
78
103
  class="<%= item[:expanded] ? 'block' : 'hidden' %> mt-1 space-y-1"
79
104
  >
80
105
  <% (item[:children] || []).each do |child| %>
@@ -187,4 +212,16 @@
187
212
  <% end %>
188
213
  </div>
189
214
  <% end %>
215
+
216
+ <!-- Resize Handle (solo se NON collapsible) -->
217
+ <% unless collapsible %>
218
+ <div data-bui-sidebar-target="resizeHandle"
219
+ class="absolute top-0 right-0 w-1 h-full bg-transparent hover:bg-blue-500 cursor-col-resize transition-colors duration-150 group">
220
+ <div class="absolute inset-y-0 -right-1 w-3 flex items-center justify-center">
221
+ <div class="w-0.5 h-8 bg-gray-300 group-hover:bg-blue-500 transition-colors duration-150"></div>
222
+ </div>
223
+ </div>
224
+ <% end %>
190
225
  </aside>
226
+
227
+ </div>
@@ -4,9 +4,10 @@ module BetterUi
4
4
  module Application
5
5
  module Sidebar
6
6
  class Component < ViewComponent::Base
7
- # Include degli helper per utilizzare bui_icon e bui_avatar
7
+ # Include degli helper per utilizzare bui_icon, bui_avatar e bui_button
8
8
  include BetterUi::General::Components::Icon::IconHelper
9
9
  include BetterUi::General::Components::Avatar::AvatarHelper
10
+ include BetterUi::General::Components::Button::ButtonHelper
10
11
  attr_reader :width, :position, :theme, :shadow, :border, :header, :footer, :navigation_sections, :collapsible, :classes
11
12
 
12
13
  # Larghezze sidebar con classi Tailwind dirette
@@ -74,7 +75,7 @@ module BetterUi
74
75
  end
75
76
 
76
77
  def container_classes
77
- base_classes = %w[fixed inset-y-0 z-50 flex flex-col overflow-y-auto]
78
+ base_classes = %w[fixed inset-y-0 h-screen z-50 flex flex-col]
78
79
 
79
80
  # Posizione
80
81
  base_classes << (position == :right ? "right-0" : "left-0")
@@ -0,0 +1,5 @@
1
+ <div <%= tag.attributes(wrapper_attributes) %>>
2
+ <% items.each do |item| %>
3
+ <%= item %>
4
+ <% end %>
5
+ </div>
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Accordion
6
+ class Component < ViewComponent::Base
7
+ renders_many :items, "BetterUi::General::Accordion::ItemComponent"
8
+
9
+ ACCORDION_THEME = {
10
+ default: 'border-gray-200',
11
+ white: 'border-gray-100 bg-white',
12
+ blue: 'border-blue-200',
13
+ red: 'border-red-200',
14
+ green: 'border-green-200',
15
+ yellow: 'border-yellow-200',
16
+ violet: 'border-violet-200',
17
+ orange: 'border-orange-200',
18
+ rose: 'border-rose-200'
19
+ }.freeze
20
+
21
+ ACCORDION_VARIANT = {
22
+ minimal: '',
23
+ bordered: 'border rounded-lg',
24
+ separated: 'space-y-2'
25
+ }.freeze
26
+
27
+ ACCORDION_SIZE = {
28
+ small: 'text-sm',
29
+ medium: 'text-base',
30
+ large: 'text-lg'
31
+ }.freeze
32
+
33
+ def initialize(multiple: false, theme: :default, variant: :bordered, size: :medium,
34
+ classes: '', **options)
35
+ @multiple = multiple
36
+ @theme = theme
37
+ @variant = variant
38
+ @size = size
39
+ @classes = classes
40
+ @options = options
41
+
42
+ validate_params
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :multiple, :theme, :variant, :size, :classes, :options
48
+
49
+ def validate_params
50
+ validate_theme
51
+ validate_variant
52
+ validate_size
53
+ end
54
+
55
+ def validate_theme
56
+ return if ACCORDION_THEME.key?(theme)
57
+
58
+ raise ArgumentError, "Invalid theme: #{theme}. Must be one of #{ACCORDION_THEME.keys}"
59
+ end
60
+
61
+ def validate_variant
62
+ return if ACCORDION_VARIANT.key?(variant)
63
+
64
+ raise ArgumentError, "Invalid variant: #{variant}. Must be one of #{ACCORDION_VARIANT.keys}"
65
+ end
66
+
67
+ def validate_size
68
+ return if ACCORDION_SIZE.key?(size)
69
+
70
+ raise ArgumentError, "Invalid size: #{size}. Must be one of #{ACCORDION_SIZE.keys}"
71
+ end
72
+
73
+ # Attributi per il wrapper principale
74
+ def wrapper_attributes
75
+ base_classes = [
76
+ 'bui-accordion',
77
+ ACCORDION_SIZE[size],
78
+ ACCORDION_VARIANT[variant],
79
+ ACCORDION_THEME[theme],
80
+ classes
81
+ ].compact.join(' ')
82
+
83
+ {
84
+ class: base_classes,
85
+ 'data-controller': 'bui-accordion',
86
+ 'data-bui-accordion-multiple-value': multiple
87
+ }.merge(options)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,12 @@
1
+ <div <%= tag.attributes(item_attributes) %>>
2
+ <!-- Header -->
3
+ <button <%= tag.attributes(header_attributes) %>>
4
+ <span><%= title %></span>
5
+ <i class="fas fa-<%= icon %> <%= icon_attributes[:class] %>" <%= tag.attributes(icon_attributes.except(:class)) %>></i>
6
+ </button>
7
+
8
+ <!-- Content -->
9
+ <div <%= tag.attributes(content_attributes) %>>
10
+ <%= content %>
11
+ </div>
12
+ </div>
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Accordion
6
+ class ItemComponent < ViewComponent::Base
7
+ ITEM_THEME = {
8
+ default: 'border-gray-200 bg-white',
9
+ white: 'border-gray-100 bg-white',
10
+ blue: 'border-blue-200 bg-blue-50',
11
+ red: 'border-red-200 bg-red-50',
12
+ green: 'border-green-200 bg-green-50',
13
+ yellow: 'border-yellow-200 bg-yellow-50',
14
+ violet: 'border-violet-200 bg-violet-50',
15
+ orange: 'border-orange-200 bg-orange-50',
16
+ rose: 'border-rose-200 bg-rose-50'
17
+ }.freeze
18
+
19
+ ITEM_HEADER_THEME = {
20
+ default: 'text-gray-900 hover:bg-gray-50',
21
+ white: 'text-gray-900 hover:bg-gray-50',
22
+ blue: 'text-blue-900 hover:bg-blue-100',
23
+ red: 'text-red-900 hover:bg-red-100',
24
+ green: 'text-green-900 hover:bg-green-100',
25
+ yellow: 'text-yellow-900 hover:bg-yellow-100',
26
+ violet: 'text-violet-900 hover:bg-violet-100',
27
+ orange: 'text-orange-900 hover:bg-orange-100',
28
+ rose: 'text-rose-900 hover:bg-rose-100'
29
+ }.freeze
30
+
31
+ ITEM_SIZE_HEADER = {
32
+ small: 'text-sm px-4 py-3',
33
+ medium: 'text-base px-5 py-4',
34
+ large: 'text-lg px-6 py-5'
35
+ }.freeze
36
+
37
+ ITEM_SIZE_CONTENT = {
38
+ small: 'px-4 py-3',
39
+ medium: 'px-5 py-4',
40
+ large: 'px-6 py-5'
41
+ }.freeze
42
+
43
+ ITEM_SIZE_ICON = {
44
+ small: 'w-4 h-4',
45
+ medium: 'w-5 h-5',
46
+ large: 'w-6 h-6'
47
+ }.freeze
48
+
49
+ def initialize(title:, expanded: false, disabled: false, icon: 'chevron-down',
50
+ theme: :default, size: :medium, classes: '', **options)
51
+ @title = title
52
+ @expanded = expanded
53
+ @disabled = disabled
54
+ @icon = icon
55
+ @theme = theme
56
+ @size = size
57
+ @classes = classes
58
+ @options = options
59
+
60
+ validate_params
61
+ end
62
+
63
+ private
64
+
65
+ attr_reader :title, :expanded, :disabled, :icon, :theme, :size, :classes, :options
66
+
67
+ def validate_params
68
+ validate_theme
69
+ validate_size
70
+
71
+ raise ArgumentError, 'title cannot be blank' if title.blank?
72
+ end
73
+
74
+ def validate_theme
75
+ return if ITEM_THEME.key?(theme)
76
+
77
+ raise ArgumentError, "Invalid theme: #{theme}. Must be one of #{ITEM_THEME.keys}"
78
+ end
79
+
80
+ def validate_size
81
+ return if ITEM_SIZE_HEADER.key?(size)
82
+
83
+ raise ArgumentError, "Invalid size: #{size}. Must be one of #{ITEM_SIZE_HEADER.keys}"
84
+ end
85
+
86
+ def unique_id
87
+ @unique_id ||= "accordion-item-#{SecureRandom.hex(4)}"
88
+ end
89
+
90
+ def content_id
91
+ "#{unique_id}-content"
92
+ end
93
+
94
+ def header_id
95
+ "#{unique_id}-header"
96
+ end
97
+
98
+ # Attributi per il wrapper dell'item
99
+ def item_attributes
100
+ base_classes = [
101
+ 'bui-accordion-item',
102
+ ITEM_THEME[theme],
103
+ classes
104
+ ].compact.join(' ')
105
+
106
+ {
107
+ class: base_classes,
108
+ 'data-bui-accordion-target': 'item'
109
+ }.merge(options)
110
+ end
111
+
112
+ # Attributi per il button header
113
+ def header_attributes
114
+ base_classes = [
115
+ 'bui-accordion-header',
116
+ 'w-full flex items-center justify-between text-left font-medium transition-colors duration-200',
117
+ ITEM_SIZE_HEADER[size],
118
+ ITEM_HEADER_THEME[theme],
119
+ disabled? ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'
120
+ ].compact.join(' ')
121
+
122
+ {
123
+ class: base_classes,
124
+ type: 'button',
125
+ id: header_id,
126
+ 'aria-expanded': expanded,
127
+ 'aria-controls': content_id,
128
+ 'data-action': 'click->bui-accordion#toggle',
129
+ 'data-bui-accordion-target': 'trigger',
130
+ disabled: disabled
131
+ }
132
+ end
133
+
134
+ # Attributi per il contenuto
135
+ def content_attributes
136
+ base_classes = [
137
+ 'bui-accordion-content',
138
+ 'transition-all duration-300 ease-in-out overflow-hidden',
139
+ ITEM_SIZE_CONTENT[size],
140
+ expanded? ? 'block' : 'hidden'
141
+ ].compact.join(' ')
142
+
143
+ {
144
+ class: base_classes,
145
+ id: content_id,
146
+ 'aria-labelledby': header_id,
147
+ 'data-bui-accordion-target': 'content'
148
+ }
149
+ end
150
+
151
+ # Attributi per l'icona
152
+ def icon_attributes
153
+ base_classes = [
154
+ 'bui-accordion-icon',
155
+ 'transition-transform duration-200',
156
+ ITEM_SIZE_ICON[size],
157
+ expanded? ? 'rotate-180' : ''
158
+ ].compact.join(' ')
159
+
160
+ {
161
+ class: base_classes,
162
+ 'data-bui-accordion-target': 'icon'
163
+ }
164
+ end
165
+
166
+ def disabled?
167
+ disabled
168
+ end
169
+
170
+ def expanded?
171
+ expanded
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -2,7 +2,7 @@
2
2
  <% if link? %>
3
3
  <%= link_to @href, **link_attributes do %>
4
4
  <% if @icon && @icon_position == :left %>
5
- <span class="flex-shrink-0 mr-2"><%= render_icon(@icon) %></span>
5
+ <span class="flex-shrink-0<%= @label.present? ? ' mr-2' : '' %>"><%= render_icon(@icon) %></span>
6
6
  <% end %>
7
7
 
8
8
  <% if @label %>
@@ -10,7 +10,7 @@
10
10
  <% end %>
11
11
 
12
12
  <% if @icon && @icon_position == :right %>
13
- <span class="flex-shrink-0 ml-2"><%= render_icon(@icon) %></span>
13
+ <span class="flex-shrink-0<%= @label.present? ? ' ml-2' : '' %>"><%= render_icon(@icon) %></span>
14
14
  <% end %>
15
15
 
16
16
  <%= content %>
@@ -18,7 +18,7 @@
18
18
  <% else %>
19
19
  <%= tag.button(**button_attributes) do %>
20
20
  <% if @icon && @icon_position == :left %>
21
- <span class="flex-shrink-0 mr-2"><%= render_icon(@icon) %></span>
21
+ <span class="flex-shrink-0<%= @label.present? ? ' mr-2' : '' %>"><%= render_icon(@icon) %></span>
22
22
  <% end %>
23
23
 
24
24
  <% if @label %>
@@ -26,7 +26,7 @@
26
26
  <% end %>
27
27
 
28
28
  <% if @icon && @icon_position == :right %>
29
- <span class="flex-shrink-0 ml-2"><%= render_icon(@icon) %></span>
29
+ <span class="flex-shrink-0<%= @label.present? ? ' ml-2' : '' %>"><%= render_icon(@icon) %></span>
30
30
  <% end %>
31
31
 
32
32
  <%= content %>
@@ -1,13 +1,24 @@
1
- <div <%= tag.attributes(container_attributes) %>>
2
- <button <%= tag.attributes(trigger_attributes) %>>
1
+ <div class="relative inline-block <%= @classes %>"
2
+ data-controller="bui-dropdown"
3
+ data-bui-dropdown-open-value="false"
4
+ <%= tag.attributes(@html_options.except(:class)) %>>
5
+
6
+ <button type="button"
7
+ class="inline-flex items-center justify-center border font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors <%= dynamic_trigger_classes %>"
8
+ data-bui-dropdown-target="trigger"
9
+ data-action="click->bui-dropdown#toggle keydown->bui-dropdown#keydown"
10
+ aria-expanded="false"
11
+ aria-haspopup="true">
3
12
  <%= @trigger %>
4
- <svg class="ml-2 -mr-1 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
5
- <path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
6
- </svg>
13
+ <%= bui_icon("chevron-down", size: :small, classes: "ml-2 -mr-1") %>
7
14
  </button>
8
15
 
9
- <div <%= tag.attributes(menu_attributes) %>>
10
- <div class="py-1" role="none">
16
+ <div class="absolute z-50 mt-2 origin-top-right bg-white border border-gray-200 shadow-lg focus:outline-none <%= dynamic_menu_classes %>"
17
+ data-bui-dropdown-target="menu"
18
+ role="menu"
19
+ aria-orientation="vertical"
20
+ style="display: none;">
21
+ <div class="py-1" role="none" data-action="click->bui-dropdown#itemClick">
11
22
  <%= content %>
12
23
  </div>
13
24
  </div>
@@ -4,16 +4,11 @@ module BetterUi
4
4
  module General
5
5
  module Dropdown
6
6
  class Component < ViewComponent::Base
7
+ include BetterUi::General::Components::Icon::IconHelper
8
+
7
9
  attr_reader :trigger, :position, :theme, :size, :rounded, :animation, :classes, :html_options
8
10
 
9
- # Classi base per il contenitore dropdown
10
- DROPDOWN_CONTAINER_CLASSES = "relative inline-block"
11
-
12
- # Classi base per il pulsante trigger
13
- DROPDOWN_TRIGGER_BASE_CLASSES = "inline-flex items-center justify-center border font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors"
14
-
15
- # Classi base per il menu dropdown
16
- DROPDOWN_MENU_BASE_CLASSES = "absolute z-50 mt-2 origin-top-right bg-white border border-gray-200 shadow-lg focus:outline-none"
11
+ # Classi base spostate nel template HTML per migliore leggibilità
17
12
 
18
13
  # Temi per il trigger del dropdown con classi Tailwind dirette
19
14
  DROPDOWN_TRIGGER_THEME = {
@@ -81,69 +76,25 @@ module BetterUi
81
76
  validate_params
82
77
  end
83
78
 
84
- # Combina tutte le classi per il contenitore
85
- def container_classes
79
+ # Restituisce solo le classi dinamiche per il trigger
80
+ def dynamic_trigger_classes
86
81
  [
87
- DROPDOWN_CONTAINER_CLASSES,
88
- @classes
89
- ].compact.join(" ")
90
- end
91
-
92
- # Combina tutte le classi per il trigger
93
- def trigger_classes
94
- [
95
- DROPDOWN_TRIGGER_BASE_CLASSES,
96
82
  get_trigger_theme_classes,
97
83
  get_trigger_size_classes,
98
84
  get_trigger_rounded_classes
99
85
  ].compact.join(" ")
100
86
  end
101
87
 
102
- # Combina tutte le classi per il menu
103
- def menu_classes
88
+ # Restituisce solo le classi dinamiche per il menu
89
+ def dynamic_menu_classes
104
90
  [
105
- DROPDOWN_MENU_BASE_CLASSES,
106
91
  get_position_classes,
107
92
  get_animation_classes,
108
93
  get_menu_rounded_classes
109
94
  ].compact.join(" ")
110
95
  end
111
96
 
112
- # Restituisce gli attributi per il contenitore
113
- def container_attributes
114
- attrs = {
115
- class: container_classes,
116
- "data-dropdown": true
117
- }
118
-
119
- @html_options.except(:class).each do |key, value|
120
- attrs[key] = value
121
- end
122
-
123
- attrs
124
- end
125
-
126
- # Restituisce gli attributi per il trigger
127
- def trigger_attributes
128
- {
129
- type: "button",
130
- class: trigger_classes,
131
- "data-dropdown-trigger": true,
132
- "aria-expanded": "false",
133
- "aria-haspopup": "true"
134
- }
135
- end
136
-
137
- # Restituisce gli attributi per il menu
138
- def menu_attributes
139
- {
140
- class: menu_classes,
141
- "data-dropdown-menu": true,
142
- role: "menu",
143
- "aria-orientation": "vertical",
144
- style: "display: none;"
145
- }
146
- end
97
+ # Metodi per attributi rimossi - ora gestiti direttamente nel template HTML
147
98
 
148
99
  # Verifica se rendere il componente
149
100
  def render?
@@ -68,7 +68,8 @@ module BetterUi
68
68
  def item_attributes
69
69
  attrs = {
70
70
  class: item_classes,
71
- role: "menuitem"
71
+ role: "menuitem",
72
+ "data-bui-dropdown-target": "item"
72
73
  }
73
74
 
74
75
  if @href.present? && !@disabled
@@ -1,42 +1,5 @@
1
- <%# Template per il modal %>
2
- <% if @backdrop %>
3
- <%= tag.div **backdrop_attributes do %>
4
- <%= tag.div **container_attributes do %>
5
- <%# Header del modal %>
6
- <%= tag.div **header_attributes do %>
7
- <h3 class="text-lg font-semibold" id="modal-title"><%= @title %></h3>
8
- <% if @closable %>
9
- <button type="button" class="text-gray-400 hover:text-gray-600 focus:outline-none focus:text-gray-600 transition-colors duration-200" aria-label="Chiudi modal">
10
- <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
11
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
12
- </svg>
13
- </button>
14
- <% end %>
15
- <% end %>
16
-
17
- <%# Body del modal %>
18
- <div class="p-6">
19
- <%= content %>
20
- </div>
21
- <% end %>
22
- <% end %>
23
- <% else %>
24
- <%= tag.div **container_attributes do %>
25
- <%# Header del modal %>
26
- <%= tag.div **header_attributes do %>
27
- <h3 class="text-lg font-semibold" id="modal-title"><%= @title %></h3>
28
- <% if @closable %>
29
- <button type="button" class="text-gray-400 hover:text-gray-600 focus:outline-none focus:text-gray-600 transition-colors duration-200" aria-label="Chiudi modal">
30
- <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
31
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
32
- </svg>
33
- </button>
34
- <% end %>
35
- <% end %>
36
-
37
- <%# Body del modal %>
38
- <div class="p-6">
39
- <%= content %>
40
- </div>
41
- <% end %>
42
- <% end %>
1
+ <%# Wrapper component con controller Stimulus e slots %>
2
+ <div <%= tag.attributes(wrapper_attributes) %>>
3
+ <%= trigger %>
4
+ <%= modal %>
5
+ </div>