better_ui 0.1.0 → 0.5.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +199 -75
  3. data/app/assets/javascripts/better_ui/controllers/navbar_controller.js +138 -0
  4. data/app/assets/javascripts/better_ui/controllers/sidebar_controller.js +211 -0
  5. data/app/assets/javascripts/better_ui/controllers/toast_controller.js +161 -0
  6. data/app/assets/javascripts/better_ui/index.js +159 -0
  7. data/app/assets/javascripts/better_ui/toast_manager.js +77 -0
  8. data/app/assets/stylesheets/better_ui/application.css +25 -351
  9. data/app/components/better_ui/application/alert_component.html.erb +27 -0
  10. data/app/components/better_ui/application/alert_component.rb +202 -0
  11. data/app/components/better_ui/application/card_component.html.erb +24 -0
  12. data/app/components/better_ui/application/card_component.rb +53 -0
  13. data/app/components/better_ui/application/card_container_component.html.erb +8 -0
  14. data/app/components/better_ui/application/card_container_component.rb +14 -0
  15. data/app/components/better_ui/application/header_component.html.erb +88 -0
  16. data/app/components/better_ui/application/header_component.rb +188 -0
  17. data/app/components/better_ui/application/navbar_component.html.erb +294 -0
  18. data/app/components/better_ui/application/navbar_component.rb +249 -0
  19. data/app/components/better_ui/application/sidebar_component.html.erb +207 -0
  20. data/app/components/better_ui/application/sidebar_component.rb +318 -0
  21. data/app/components/better_ui/application/toast_component.html.erb +35 -0
  22. data/app/components/better_ui/application/toast_component.rb +223 -0
  23. data/app/components/better_ui/general/avatar_component.html.erb +19 -0
  24. data/app/components/better_ui/general/avatar_component.rb +171 -0
  25. data/app/components/better_ui/general/badge_component.html.erb +19 -0
  26. data/app/components/better_ui/general/badge_component.rb +123 -0
  27. data/app/components/better_ui/general/breadcrumb_component.html.erb +15 -0
  28. data/app/components/better_ui/general/breadcrumb_component.rb +130 -0
  29. data/app/components/better_ui/general/button_component.html.erb +34 -0
  30. data/app/components/better_ui/general/button_component.rb +162 -0
  31. data/app/components/better_ui/general/heading_component.html.erb +25 -0
  32. data/app/components/better_ui/general/heading_component.rb +148 -0
  33. data/app/components/better_ui/general/icon_component.html.erb +2 -0
  34. data/app/components/better_ui/general/icon_component.rb +100 -0
  35. data/app/components/better_ui/general/link_component.html.erb +17 -0
  36. data/app/components/better_ui/general/link_component.rb +132 -0
  37. data/app/components/better_ui/general/panel_component.html.erb +27 -0
  38. data/app/components/better_ui/general/panel_component.rb +103 -0
  39. data/app/components/better_ui/general/spinner_component.html.erb +15 -0
  40. data/app/components/better_ui/general/spinner_component.rb +79 -0
  41. data/app/components/better_ui/general/table_component.html.erb +73 -0
  42. data/app/components/better_ui/general/table_component.rb +167 -0
  43. data/app/components/better_ui/theme_helper.rb +171 -0
  44. data/app/controllers/better_ui/application_controller.rb +1 -0
  45. data/app/controllers/better_ui/docs_controller.rb +18 -25
  46. data/app/views/components/better_ui/general/table/_custom_body_row.html.erb +17 -0
  47. data/app/views/components/better_ui/general/table/_custom_footer_rows.html.erb +17 -0
  48. data/app/views/components/better_ui/general/table/_custom_header_rows.html.erb +12 -0
  49. data/app/views/layouts/component_preview.html.erb +32 -0
  50. data/config/initializers/lookbook.rb +23 -0
  51. data/config/routes.rb +6 -1
  52. data/lib/better_ui/engine.rb +18 -1
  53. data/lib/better_ui/version.rb +1 -1
  54. data/lib/better_ui.rb +4 -0
  55. data/lib/generators/better_ui/stylesheet_generator.rb +96 -0
  56. data/lib/generators/better_ui/templates/README +56 -0
  57. data/lib/generators/better_ui/templates/components/_avatar.scss +151 -0
  58. data/lib/generators/better_ui/templates/components/_badge.scss +142 -0
  59. data/lib/generators/better_ui/templates/components/_breadcrumb.scss +107 -0
  60. data/lib/generators/better_ui/templates/components/_button.scss +106 -0
  61. data/lib/generators/better_ui/templates/components/_card.scss +69 -0
  62. data/lib/generators/better_ui/templates/components/_heading.scss +180 -0
  63. data/lib/generators/better_ui/templates/components/_icon.scss +90 -0
  64. data/lib/generators/better_ui/templates/components/_link.scss +130 -0
  65. data/lib/generators/better_ui/templates/components/_panel.scss +144 -0
  66. data/lib/generators/better_ui/templates/components/_spinner.scss +132 -0
  67. data/lib/generators/better_ui/templates/components/_table.scss +105 -0
  68. data/lib/generators/better_ui/templates/components/_variables.scss +33 -0
  69. data/lib/generators/better_ui/templates/components_stylesheet.scss +66 -0
  70. metadata +135 -10
  71. data/app/helpers/better_ui/application_helper.rb +0 -183
  72. data/app/views/better_ui/docs/component.html.erb +0 -365
  73. data/app/views/better_ui/docs/index.html.erb +0 -100
  74. data/app/views/better_ui/docs/show.html.erb +0 -60
  75. data/app/views/layouts/better_ui/application.html.erb +0 -135
@@ -0,0 +1,318 @@
1
+ module BetterUi
2
+ module Application
3
+ class SidebarComponent < ViewComponent::Base
4
+ attr_reader :title, :variant, :items, :footer, :classes, :data, :collapsible, :collapsed_default, :position, :width, :overlay_on_mobile
5
+
6
+ # Varianti di colore disponibili
7
+ VARIANTS = {
8
+ light: {
9
+ bg: "bg-white",
10
+ border: "border-gray-200",
11
+ text: "text-gray-700",
12
+ active_bg: "bg-gray-50",
13
+ active_text: "text-gray-900",
14
+ hover: "hover:bg-gray-50 hover:text-gray-900",
15
+ divider: "border-gray-200",
16
+ shadow: "shadow-sm",
17
+ icon: "text-gray-500",
18
+ chevron: "text-gray-400",
19
+ heading: "text-gray-500"
20
+ },
21
+ dark: {
22
+ bg: "bg-gray-800",
23
+ border: "border-gray-700",
24
+ text: "text-gray-200",
25
+ active_bg: "bg-gray-700",
26
+ active_text: "text-white",
27
+ hover: "hover:bg-gray-700 hover:text-white",
28
+ divider: "border-gray-700",
29
+ shadow: "shadow-lg",
30
+ icon: "text-gray-400",
31
+ chevron: "text-gray-500",
32
+ heading: "text-gray-400"
33
+ },
34
+ primary: {
35
+ bg: "bg-orange-600",
36
+ border: "border-orange-700",
37
+ text: "text-white",
38
+ active_bg: "bg-orange-700",
39
+ active_text: "text-white",
40
+ hover: "hover:bg-orange-700",
41
+ divider: "border-orange-500",
42
+ shadow: "shadow-lg",
43
+ icon: "text-orange-300",
44
+ chevron: "text-orange-400",
45
+ heading: "text-orange-200"
46
+ },
47
+ blue: {
48
+ bg: "bg-blue-800",
49
+ border: "border-blue-700",
50
+ text: "text-gray-100",
51
+ active_bg: "bg-blue-700",
52
+ active_text: "text-white",
53
+ hover: "hover:bg-blue-700 hover:text-white",
54
+ divider: "border-blue-700",
55
+ shadow: "shadow-lg",
56
+ icon: "text-blue-300",
57
+ chevron: "text-blue-400",
58
+ heading: "text-blue-200"
59
+ },
60
+ modern: {
61
+ bg: "bg-white",
62
+ border: "border-gray-200",
63
+ text: "text-gray-700",
64
+ active_bg: "bg-gray-100",
65
+ active_text: "text-gray-900",
66
+ hover: "hover:bg-gray-50",
67
+ divider: "border-gray-100",
68
+ shadow: "shadow-none",
69
+ icon: "text-gray-500",
70
+ chevron: "text-gray-400",
71
+ heading: "text-gray-500 uppercase text-xs font-medium"
72
+ }
73
+ }
74
+
75
+ # Dimensioni disponibili per la sidebar
76
+ WIDTHS = {
77
+ narrow: "w-60",
78
+ medium: "w-64",
79
+ wide: "w-72",
80
+ custom: "" # La larghezza custom viene specificata nelle classi aggiuntive
81
+ }
82
+
83
+ # Posizioni disponibili
84
+ POSITIONS = {
85
+ left: "left-0",
86
+ right: "right-0"
87
+ }
88
+
89
+ # Inizializzazione del componente
90
+ def initialize(
91
+ title: nil,
92
+ variant: :modern,
93
+ items: [],
94
+ footer: nil,
95
+ classes: nil,
96
+ data: {},
97
+ collapsible: true,
98
+ collapsed_default: false,
99
+ position: :left,
100
+ width: :narrow,
101
+ overlay_on_mobile: true
102
+ )
103
+ @title = title
104
+ @variant = variant.to_sym
105
+ @items = items || []
106
+ @footer = footer
107
+ @classes = classes
108
+ @data = data || {}
109
+ @collapsible = collapsible
110
+ @collapsed_default = collapsed_default
111
+ @position = position.to_sym
112
+ @width = width.to_sym
113
+ @overlay_on_mobile = overlay_on_mobile
114
+
115
+ # Aggiungiamo controller Stimulus per la gestione della sidebar
116
+ @data[:controller] = "sidebar" if @data[:controller].blank?
117
+ @data[:sidebar_position_value] = @position.to_s
118
+ @data[:sidebar_collapsed_value] = @collapsed_default.to_s
119
+ @data[:sidebar_overlay_on_mobile_value] = @overlay_on_mobile.to_s
120
+ end
121
+
122
+ # Genera le classi per il container della sidebar
123
+ def container_classes
124
+ styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
125
+ width_class = WIDTHS.fetch(@width, WIDTHS[:narrow])
126
+ position_class = POSITIONS.fetch(@position, POSITIONS[:left])
127
+
128
+ [
129
+ "h-screen relative z-40",
130
+ styles[:bg],
131
+ styles[:border],
132
+ @position == :left ? "border-r" : "border-l",
133
+ styles[:shadow],
134
+ width_class,
135
+ position_class,
136
+ @classes,
137
+ "transition-transform duration-300 ease-in-out transform",
138
+ "flex flex-col"
139
+ ].compact.join(" ")
140
+ end
141
+
142
+ # Genera le classi per l'overlay
143
+ def overlay_classes
144
+ [
145
+ "fixed inset-0 bg-black bg-opacity-50 z-30",
146
+ "transition-opacity duration-300 ease-in-out",
147
+ "hidden"
148
+ ].join(" ")
149
+ end
150
+
151
+ # Genera le classi per il pulsante del toggle
152
+ def toggle_button_classes
153
+ styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
154
+ position_class = @position == :left ? "right-0 -mr-3" : "left-0 -ml-3"
155
+
156
+ [
157
+ "absolute top-16",
158
+ position_class,
159
+ "z-50 flex items-center justify-center",
160
+ "w-6 h-12 rounded-lg",
161
+ styles[:bg],
162
+ styles[:border],
163
+ "cursor-pointer shadow-lg",
164
+ "transform transition-transform duration-300 ease-in-out"
165
+ ].compact.join(" ")
166
+ end
167
+
168
+ # Genera le classi per l'intestazione
169
+ def header_classes
170
+ styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
171
+
172
+ [
173
+ "px-4 py-4 flex items-center",
174
+ @variant == :modern ? "" : "border-b #{styles[:border]}"
175
+ ].compact.join(" ")
176
+ end
177
+
178
+ # Genera le classi per i link nel menu
179
+ def menu_item_classes(active = false, nested = false, has_children = false)
180
+ styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
181
+
182
+ padding_x = @variant == :modern ? "pl-5 pr-3" : "px-3"
183
+ padding_y = @variant == :modern ? "py-2.5" : "py-2"
184
+ rounded = @variant == :modern ? "" : "rounded-md"
185
+
186
+ base_classes = [
187
+ "flex items-center #{padding_y} #{padding_x} #{rounded} my-0.5 w-full",
188
+ ]
189
+
190
+ if active
191
+ active_classes = @variant == :modern ? "bg-gray-100 font-medium" : styles[:active_bg]
192
+ [
193
+ *base_classes,
194
+ active_classes,
195
+ styles[:active_text],
196
+ ].join(" ")
197
+ else
198
+ [
199
+ *base_classes,
200
+ styles[:text],
201
+ styles[:hover],
202
+ has_children ? "cursor-pointer" : ""
203
+ ].join(" ")
204
+ end
205
+ end
206
+
207
+ # Genera le classi per i separatori
208
+ def divider_classes
209
+ styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
210
+
211
+ [
212
+ "my-2 border-t",
213
+ styles[:divider]
214
+ ].join(" ")
215
+ end
216
+
217
+ # Genera le classi per il footer
218
+ def footer_classes
219
+ styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
220
+
221
+ [
222
+ "mt-auto px-4 py-3",
223
+ @variant == :modern ? "" : "border-t #{styles[:border]}"
224
+ ].join(" ")
225
+ end
226
+
227
+ # Genera le classi per le icone
228
+ def icon_classes
229
+ styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
230
+
231
+ [
232
+ "mr-3 flex-shrink-0",
233
+ styles[:icon]
234
+ ].join(" ")
235
+ end
236
+
237
+ # Genera le classi per la label della voce di menu
238
+ def item_label_classes
239
+ [
240
+ "flex-1 truncate"
241
+ ].join(" ")
242
+ end
243
+
244
+ # Genera le classi per il badge nella voce di menu
245
+ def badge_classes(type = :default)
246
+ badge_types = {
247
+ default: "bg-gray-200 text-gray-800",
248
+ primary: "bg-orange-100 text-orange-800",
249
+ success: "bg-green-100 text-green-800",
250
+ warning: "bg-yellow-100 text-yellow-800",
251
+ danger: "bg-red-100 text-red-800",
252
+ info: "bg-blue-100 text-blue-800"
253
+ }
254
+
255
+ [
256
+ "px-2 py-0.5 text-xs rounded-full",
257
+ badge_types.fetch(type.to_sym, badge_types[:default])
258
+ ].join(" ")
259
+ end
260
+
261
+ # Genera le classi per il titolo di sezione
262
+ def section_heading_classes
263
+ styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
264
+
265
+ if @variant == :modern
266
+ "px-5 py-2 text-xs font-medium text-gray-500"
267
+ else
268
+ [
269
+ "px-4 py-2 text-xs tracking-wider",
270
+ styles[:heading]
271
+ ].join(" ")
272
+ end
273
+ end
274
+
275
+ # Genera le classi per icona chevron
276
+ def chevron_classes
277
+ styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
278
+
279
+ [
280
+ "ml-auto",
281
+ styles[:chevron]
282
+ ].join(" ")
283
+ end
284
+
285
+ # Verifica se la sidebar deve essere resa
286
+ def render?
287
+ true
288
+ end
289
+
290
+ # Determina se un elemento dovrebbe essere considerato attivo
291
+ def active_item?(item)
292
+ item[:active] == true
293
+ end
294
+
295
+ # Verifica se un elemento ha figli
296
+ def has_children?(item)
297
+ item[:children].present? && item[:children].is_a?(Array) && item[:children].any?
298
+ end
299
+
300
+ # Verifica se un elemento ha un badge
301
+ def has_badge?(item)
302
+ item[:badge].present?
303
+ end
304
+
305
+ # Ottiene il tipo di badge per un elemento
306
+ def badge_type(item)
307
+ return :default unless item[:badge].is_a?(Hash)
308
+ (item[:badge][:type] || :default).to_sym
309
+ end
310
+
311
+ # Ottiene il testo del badge per un elemento
312
+ def badge_text(item)
313
+ return item[:badge].to_s unless item[:badge].is_a?(Hash)
314
+ item[:badge][:text].to_s
315
+ end
316
+ end
317
+ end
318
+ end
@@ -0,0 +1,35 @@
1
+ <div role="status" aria-live="polite" class="<%= container_classes %>"
2
+ <%= @data&.map { |k, v| "data-#{k}=\"#{v}\"" }&.join(' ')&.html_safe %>>
3
+
4
+ <% if effective_icon.present? %>
5
+ <div class="<%= icon_classes %>">
6
+ <%= render BetterUi::General::IconComponent.new(name: effective_icon) %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <div class="<%= content_classes %>">
11
+ <% if @title.present? %>
12
+ <div class="<%= title_classes %>"><%= @title %></div>
13
+ <% end %>
14
+
15
+ <% if @message.present? %>
16
+ <div class="<%= message_classes %>"><%= @message %></div>
17
+ <% end %>
18
+
19
+ <% if content.present? %>
20
+ <div class="<%= message_classes %>"><%= content %></div>
21
+ <% end %>
22
+
23
+ <% if @auto_hide %>
24
+ <div class="w-full bg-gray-200 h-1 mt-2 rounded overflow-hidden">
25
+ <div class="bg-current h-1 transition-all" data-toast-target="progressBar"></div>
26
+ </div>
27
+ <% end %>
28
+ </div>
29
+
30
+ <% if @dismissible %>
31
+ <button type="button" class="<%= close_button_classes %>" data-action="toast#hide" aria-label="Chiudi">
32
+ <%= render BetterUi::General::IconComponent.new(name: "xmark") %>
33
+ </button>
34
+ <% end %>
35
+ </div>
@@ -0,0 +1,223 @@
1
+ module BetterUi
2
+ module Application
3
+ class ToastComponent < ViewComponent::Base
4
+ attr_reader :title, :message, :variant, :icon, :dismissible, :classes, :data, :position, :duration, :auto_hide, :rounded
5
+
6
+ # Varianti di colore disponibili
7
+ VARIANTS = {
8
+ default: {
9
+ bg: "bg-black",
10
+ border: "border-gray-900",
11
+ title: "text-white",
12
+ text: "text-white",
13
+ icon: "text-white",
14
+ close: "text-white hover:bg-gray-800"
15
+ },
16
+ white: {
17
+ bg: "bg-white",
18
+ border: "border-gray-200",
19
+ title: "text-black",
20
+ text: "text-black",
21
+ icon: "text-black",
22
+ close: "text-black hover:bg-gray-100"
23
+ },
24
+ red: {
25
+ bg: "bg-red-500",
26
+ border: "border-red-600",
27
+ title: "text-white",
28
+ text: "text-white",
29
+ icon: "text-white",
30
+ close: "text-white hover:bg-red-600"
31
+ },
32
+ rose: {
33
+ bg: "bg-rose-500",
34
+ border: "border-rose-600",
35
+ title: "text-white",
36
+ text: "text-white",
37
+ icon: "text-white",
38
+ close: "text-white hover:bg-rose-600"
39
+ },
40
+ orange: {
41
+ bg: "bg-orange-500",
42
+ border: "border-orange-600",
43
+ title: "text-white",
44
+ text: "text-white",
45
+ icon: "text-white",
46
+ close: "text-white hover:bg-orange-600"
47
+ },
48
+ green: {
49
+ bg: "bg-green-500",
50
+ border: "border-green-600",
51
+ title: "text-white",
52
+ text: "text-white",
53
+ icon: "text-white",
54
+ close: "text-white hover:bg-green-600"
55
+ },
56
+ blue: {
57
+ bg: "bg-blue-500",
58
+ border: "border-blue-600",
59
+ title: "text-white",
60
+ text: "text-white",
61
+ icon: "text-white",
62
+ close: "text-white hover:bg-blue-600"
63
+ },
64
+ yellow: {
65
+ bg: "bg-yellow-500",
66
+ border: "border-yellow-600",
67
+ title: "text-black",
68
+ text: "text-black",
69
+ icon: "text-black",
70
+ close: "text-black hover:bg-yellow-600"
71
+ },
72
+ violet: {
73
+ bg: "bg-violet-500",
74
+ border: "border-violet-600",
75
+ title: "text-white",
76
+ text: "text-white",
77
+ icon: "text-white",
78
+ close: "text-white hover:bg-violet-600"
79
+ }
80
+ }
81
+
82
+ # Icone predefinite per ciascuna variante
83
+ DEFAULT_ICONS = {
84
+ default: "bell",
85
+ white: "info-circle",
86
+ red: "exclamation-circle",
87
+ rose: "exclamation-circle",
88
+ orange: "bell",
89
+ green: "check-circle",
90
+ blue: "info-circle",
91
+ yellow: "exclamation-triangle",
92
+ violet: "shield-exclamation"
93
+ }
94
+
95
+ # Posizioni possibili per i toast
96
+ POSITIONS = {
97
+ "top-right": "top-right",
98
+ "top-left": "top-left",
99
+ "bottom-right": "bottom-right",
100
+ "bottom-left": "bottom-left",
101
+ "top-center": "top-center",
102
+ "bottom-center": "bottom-center"
103
+ }
104
+
105
+ # Inizializzazione del componente
106
+ def initialize(
107
+ title: nil,
108
+ message: nil,
109
+ variant: :default,
110
+ icon: nil,
111
+ dismissible: true,
112
+ classes: nil,
113
+ data: {},
114
+ position: :"top-right",
115
+ duration: 5000,
116
+ auto_hide: true,
117
+ rounded: :sm
118
+ )
119
+ @title = title
120
+ @message = message
121
+ @variant = variant.to_sym
122
+ @icon = icon
123
+ @dismissible = dismissible
124
+ @classes = classes
125
+ @data = data || {}
126
+ @position = position.to_sym
127
+ @duration = duration
128
+ @auto_hide = auto_hide
129
+ @rounded = rounded.to_sym
130
+
131
+ # Aggiungiamo dati per il comportamento del toast
132
+ @data[:controller] = "toast" if @data[:controller].blank?
133
+ @data[:toast_duration_value] = @duration if @auto_hide
134
+ @data[:toast_auto_hide_value] = @auto_hide.to_s
135
+ @data[:toast_position_value] = @position.to_s
136
+ end
137
+
138
+ # Genera l'icona in base alla variante se non specificata
139
+ def effective_icon
140
+ return @icon if @icon.present?
141
+ DEFAULT_ICONS[@variant] || DEFAULT_ICONS[:default]
142
+ end
143
+
144
+ # Genera le classi per il container
145
+ def container_classes
146
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
147
+ position_class = POSITIONS.fetch(@position, POSITIONS[:"top-right"])
148
+
149
+ [
150
+ "fixed z-50 p-4 border shadow-lg",
151
+ get_border_radius_class,
152
+ "transform transition-transform duration-300",
153
+ "data-toast-enter-from-class='translate-y-2 opacity-0'",
154
+ "data-toast-enter-to-class='translate-y-0 opacity-100'",
155
+ "data-toast-leave-from-class='translate-y-0 opacity-100'",
156
+ "data-toast-leave-to-class='translate-y-2 opacity-0'",
157
+ styles[:bg],
158
+ styles[:border],
159
+ position_class,
160
+ "min-w-[20rem] max-w-sm",
161
+ "flex items-start",
162
+ @classes
163
+ ].compact.join(" ")
164
+ end
165
+
166
+ # Genera il border-radius
167
+ def get_border_radius_class
168
+ ThemeHelper::BORDER_RADIUS[@rounded] || ThemeHelper::BORDER_RADIUS[:sm]
169
+ end
170
+
171
+ # Genera le classi per il titolo
172
+ def title_classes
173
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
174
+
175
+ [
176
+ "font-medium",
177
+ styles[:title]
178
+ ].compact.join(" ")
179
+ end
180
+
181
+ # Genera le classi per il messaggio
182
+ def message_classes
183
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
184
+
185
+ [
186
+ "mt-1",
187
+ styles[:text]
188
+ ].compact.join(" ")
189
+ end
190
+
191
+ # Genera le classi per l'icona
192
+ def icon_classes
193
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
194
+
195
+ [
196
+ "flex-shrink-0",
197
+ "mr-3 mt-0.5",
198
+ styles[:icon]
199
+ ].compact.join(" ")
200
+ end
201
+
202
+ # Genera le classi per il pulsante di chiusura
203
+ def close_button_classes
204
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
205
+
206
+ [
207
+ "ml-auto -mr-1.5 -mt-1.5 inline-flex h-8 w-8 rounded-lg p-1.5 focus:ring-2 focus:ring-gray-400",
208
+ styles[:close]
209
+ ].compact.join(" ")
210
+ end
211
+
212
+ # Genera le classi per il contenuto
213
+ def content_classes
214
+ "flex-1"
215
+ end
216
+
217
+ # Verifica se il componente deve essere reso
218
+ def render?
219
+ @title.present? || @message.present? || content.present?
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,19 @@
1
+ <div <%= avatar_attributes.to_s.html_safe %>>
2
+ <% if show_image? %>
3
+ <img
4
+ src="<%= @src %>"
5
+ alt="<%= @name || 'Avatar' %>"
6
+ class="bui-avatar-image"
7
+ width="<%= pixel_size %>"
8
+ height="<%= pixel_size %>"
9
+ >
10
+ <% else %>
11
+ <div class="bui-avatar-initials">
12
+ <%= initials %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <% if show_status? %>
17
+ <span class="<%= status_indicator_classes %>"></span>
18
+ <% end %>
19
+ </div>