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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6522f1369534716a8fe823429de29dad169ad4218482f5b3862b55f4f540831
4
- data.tar.gz: 58ffe37cb7d9f769727b46430684b50d0ddbd1254e6b67838a3e5ba52d3a387a
3
+ metadata.gz: 9f8b92fdba76f5e84f09fb6a3bf9879b209e0e7dfd8f864186784db24b808c59
4
+ data.tar.gz: 4d21e971baeecb10c7a3c5952041ac7e6911342e0efe4d0015efb92f9f214652
5
5
  SHA512:
6
- metadata.gz: 9c3190b2940333413e85612676ab048b9c06a24a8138f99b202704cfc9799985386c03a37cb9485a7d57e2ca679ac1bd52535292d5ecaa36930667e5427404af
7
- data.tar.gz: f11573aa7341a77d293c2af1f1ff602cb9a7eaf494767175b93459b25cbdb4a0008eacff85f2d25d8c3bacedbb99800c6fb5ef74e28ec412ac8576c8b1ac184d
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 %>
@@ -0,0 +1,25 @@
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">
12
+ <%= @trigger %>
13
+ <%= bui_icon("chevron-down", size: :small, classes: "ml-2 -mr-1") %>
14
+ </button>
15
+
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">
22
+ <%= content %>
23
+ </div>
24
+ </div>
25
+ </div>
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Dropdown
6
+ class Component < ViewComponent::Base
7
+ include BetterUi::General::Components::Icon::IconHelper
8
+
9
+ attr_reader :trigger, :position, :theme, :size, :rounded, :animation, :classes, :html_options
10
+
11
+ # Classi base spostate nel template HTML per migliore leggibilità
12
+
13
+ # Temi per il trigger del dropdown con classi Tailwind dirette
14
+ DROPDOWN_TRIGGER_THEME = {
15
+ default: "bg-white border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-blue-500",
16
+ white: "bg-white border-gray-300 text-gray-900 hover:bg-gray-50 focus:ring-gray-500",
17
+ red: "bg-red-600 border-red-600 text-white hover:bg-red-700 focus:ring-red-500",
18
+ rose: "bg-rose-600 border-rose-600 text-white hover:bg-rose-700 focus:ring-rose-500",
19
+ orange: "bg-orange-600 border-orange-600 text-white hover:bg-orange-700 focus:ring-orange-500",
20
+ green: "bg-green-600 border-green-600 text-white hover:bg-green-700 focus:ring-green-500",
21
+ blue: "bg-blue-600 border-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
22
+ yellow: "bg-yellow-500 border-yellow-500 text-white hover:bg-yellow-600 focus:ring-yellow-500",
23
+ violet: "bg-violet-600 border-violet-600 text-white hover:bg-violet-700 focus:ring-violet-500"
24
+ }.freeze
25
+
26
+ # Dimensioni del trigger con classi Tailwind dirette
27
+ DROPDOWN_TRIGGER_SIZE = {
28
+ small: "px-3 py-1.5 text-sm",
29
+ medium: "px-4 py-2 text-sm",
30
+ large: "px-6 py-3 text-base"
31
+ }.freeze
32
+
33
+ # Border radius con classi Tailwind dirette
34
+ DROPDOWN_ROUNDED = {
35
+ none: "rounded-none",
36
+ small: "rounded-md",
37
+ medium: "rounded-lg",
38
+ large: "rounded-xl",
39
+ full: "rounded-full"
40
+ }.freeze
41
+
42
+ # Posizioni del menu dropdown
43
+ DROPDOWN_POSITION = {
44
+ bottom: "top-full left-0",
45
+ top: "bottom-full left-0",
46
+ left: "top-0 right-full mr-2",
47
+ right: "top-0 left-full ml-2"
48
+ }.freeze
49
+
50
+ # Animazioni del dropdown
51
+ DROPDOWN_ANIMATION = {
52
+ fade: "transition-opacity duration-150",
53
+ slide: "transition-all duration-150 transform",
54
+ none: ""
55
+ }.freeze
56
+
57
+ def initialize(
58
+ trigger:,
59
+ position: :bottom,
60
+ theme: :default,
61
+ size: :medium,
62
+ rounded: :medium,
63
+ animation: :fade,
64
+ classes: nil,
65
+ **html_options
66
+ )
67
+ @trigger = trigger
68
+ @position = position.to_sym
69
+ @theme = theme.to_sym
70
+ @size = size.to_sym
71
+ @rounded = rounded.to_sym
72
+ @animation = animation.to_sym
73
+ @classes = classes
74
+ @html_options = html_options
75
+
76
+ validate_params
77
+ end
78
+
79
+ # Restituisce solo le classi dinamiche per il trigger
80
+ def dynamic_trigger_classes
81
+ [
82
+ get_trigger_theme_classes,
83
+ get_trigger_size_classes,
84
+ get_trigger_rounded_classes
85
+ ].compact.join(" ")
86
+ end
87
+
88
+ # Restituisce solo le classi dinamiche per il menu
89
+ def dynamic_menu_classes
90
+ [
91
+ get_position_classes,
92
+ get_animation_classes,
93
+ get_menu_rounded_classes
94
+ ].compact.join(" ")
95
+ end
96
+
97
+ # Metodi per attributi rimossi - ora gestiti direttamente nel template HTML
98
+
99
+ # Verifica se rendere il componente
100
+ def render?
101
+ @trigger.present?
102
+ end
103
+
104
+ private
105
+
106
+ def get_trigger_theme_classes
107
+ DROPDOWN_TRIGGER_THEME[@theme] || DROPDOWN_TRIGGER_THEME[:default]
108
+ end
109
+
110
+ def get_trigger_size_classes
111
+ DROPDOWN_TRIGGER_SIZE[@size] || DROPDOWN_TRIGGER_SIZE[:medium]
112
+ end
113
+
114
+ def get_trigger_rounded_classes
115
+ DROPDOWN_ROUNDED[@rounded] || DROPDOWN_ROUNDED[:medium]
116
+ end
117
+
118
+ def get_menu_rounded_classes
119
+ DROPDOWN_ROUNDED[@rounded] || DROPDOWN_ROUNDED[:medium]
120
+ end
121
+
122
+ def get_position_classes
123
+ DROPDOWN_POSITION[@position] || DROPDOWN_POSITION[:bottom]
124
+ end
125
+
126
+ def get_animation_classes
127
+ DROPDOWN_ANIMATION[@animation] || DROPDOWN_ANIMATION[:fade]
128
+ end
129
+
130
+ def validate_params
131
+ validate_theme
132
+ validate_size
133
+ validate_rounded
134
+ validate_position
135
+ validate_animation
136
+ end
137
+
138
+ def validate_theme
139
+ unless DROPDOWN_TRIGGER_THEME.keys.include?(@theme)
140
+ raise ArgumentError, "Il tema deve essere uno tra: #{DROPDOWN_TRIGGER_THEME.keys.join(', ')}"
141
+ end
142
+ end
143
+
144
+ def validate_size
145
+ unless DROPDOWN_TRIGGER_SIZE.keys.include?(@size)
146
+ raise ArgumentError, "La dimensione deve essere una tra: #{DROPDOWN_TRIGGER_SIZE.keys.join(', ')}"
147
+ end
148
+ end
149
+
150
+ def validate_rounded
151
+ unless DROPDOWN_ROUNDED.keys.include?(@rounded)
152
+ raise ArgumentError, "Il border radius deve essere uno tra: #{DROPDOWN_ROUNDED.keys.join(', ')}"
153
+ end
154
+ end
155
+
156
+ def validate_position
157
+ unless DROPDOWN_POSITION.keys.include?(@position)
158
+ raise ArgumentError, "La posizione deve essere una tra: #{DROPDOWN_POSITION.keys.join(', ')}"
159
+ end
160
+ end
161
+
162
+ def validate_animation
163
+ unless DROPDOWN_ANIMATION.keys.include?(@animation)
164
+ raise ArgumentError, "L'animazione deve essere una tra: #{DROPDOWN_ANIMATION.keys.join(', ')}"
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1 @@
1
+ <div <%= tag.attributes(divider_attributes) %>></div>