better_ui_tmp 0.5.1

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 (143) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +211 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/builds/application.js +1 -0
  6. data/app/assets/builds/better_ui.css +1 -0
  7. data/app/assets/stylesheets/better_ui.scss +3 -0
  8. data/app/components/better_ui/application/main/component.html.erb +5 -0
  9. data/app/components/better_ui/application/main/component.rb +99 -0
  10. data/app/components/better_ui/application/navbar/component.html.erb +219 -0
  11. data/app/components/better_ui/application/navbar/component.rb +148 -0
  12. data/app/components/better_ui/application/sidebar/component.html.erb +184 -0
  13. data/app/components/better_ui/application/sidebar/component.rb +129 -0
  14. data/app/components/better_ui/general/alert/component.html.erb +32 -0
  15. data/app/components/better_ui/general/alert/component.rb +242 -0
  16. data/app/components/better_ui/general/avatar/component.html.erb +20 -0
  17. data/app/components/better_ui/general/avatar/component.rb +301 -0
  18. data/app/components/better_ui/general/badge/component.html.erb +23 -0
  19. data/app/components/better_ui/general/badge/component.rb +248 -0
  20. data/app/components/better_ui/general/breadcrumb/component.html.erb +15 -0
  21. data/app/components/better_ui/general/breadcrumb/component.rb +186 -0
  22. data/app/components/better_ui/general/button/component.html.erb +34 -0
  23. data/app/components/better_ui/general/button/component.rb +214 -0
  24. data/app/components/better_ui/general/card/component.html.erb +21 -0
  25. data/app/components/better_ui/general/card/component.rb +37 -0
  26. data/app/components/better_ui/general/container/component.html.erb +8 -0
  27. data/app/components/better_ui/general/container/component.rb +158 -0
  28. data/app/components/better_ui/general/divider/component.html.erb +10 -0
  29. data/app/components/better_ui/general/divider/component.rb +226 -0
  30. data/app/components/better_ui/general/heading/component.html.erb +22 -0
  31. data/app/components/better_ui/general/heading/component.rb +257 -0
  32. data/app/components/better_ui/general/icon/component.html.erb +1 -0
  33. data/app/components/better_ui/general/icon/component.rb +222 -0
  34. data/app/components/better_ui/general/link/component.html.erb +18 -0
  35. data/app/components/better_ui/general/link/component.rb +255 -0
  36. data/app/components/better_ui/general/panel/component.html.erb +28 -0
  37. data/app/components/better_ui/general/panel/component.rb +249 -0
  38. data/app/components/better_ui/general/progress/component.html.erb +11 -0
  39. data/app/components/better_ui/general/progress/component.rb +160 -0
  40. data/app/components/better_ui/general/spinner/component.html.erb +35 -0
  41. data/app/components/better_ui/general/spinner/component.rb +93 -0
  42. data/app/components/better_ui/general/table/component.html.erb +5 -0
  43. data/app/components/better_ui/general/table/component.rb +217 -0
  44. data/app/components/better_ui/general/table/tbody_component.html.erb +3 -0
  45. data/app/components/better_ui/general/table/tbody_component.rb +30 -0
  46. data/app/components/better_ui/general/table/td_component.html.erb +3 -0
  47. data/app/components/better_ui/general/table/td_component.rb +44 -0
  48. data/app/components/better_ui/general/table/tfoot_component.html.erb +3 -0
  49. data/app/components/better_ui/general/table/tfoot_component.rb +28 -0
  50. data/app/components/better_ui/general/table/th_component.html.erb +6 -0
  51. data/app/components/better_ui/general/table/th_component.rb +51 -0
  52. data/app/components/better_ui/general/table/thead_component.html.erb +3 -0
  53. data/app/components/better_ui/general/table/thead_component.rb +28 -0
  54. data/app/components/better_ui/general/table/tr_component.html.erb +3 -0
  55. data/app/components/better_ui/general/table/tr_component.rb +30 -0
  56. data/app/components/better_ui/general/tag/component.html.erb +3 -0
  57. data/app/components/better_ui/general/tag/component.rb +104 -0
  58. data/app/components/better_ui/general/tooltip/component.html.erb +7 -0
  59. data/app/components/better_ui/general/tooltip/component.rb +239 -0
  60. data/app/controllers/better_ui/application_controller.rb +5 -0
  61. data/app/helpers/better_ui/application/components/main/main_helper.rb +42 -0
  62. data/app/helpers/better_ui/application/components/main.rb +13 -0
  63. data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +51 -0
  64. data/app/helpers/better_ui/application/components/navbar.rb +13 -0
  65. data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +51 -0
  66. data/app/helpers/better_ui/application/components/sidebar.rb +13 -0
  67. data/app/helpers/better_ui/application_helper.rb +10 -0
  68. data/app/helpers/better_ui/form_helper.rb +5 -0
  69. data/app/helpers/better_ui/general/components/alert/alert_helper.rb +29 -0
  70. data/app/helpers/better_ui/general/components/alert.rb +13 -0
  71. data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +29 -0
  72. data/app/helpers/better_ui/general/components/avatar.rb +13 -0
  73. data/app/helpers/better_ui/general/components/badge/badge_helper.rb +53 -0
  74. data/app/helpers/better_ui/general/components/badge.rb +13 -0
  75. data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +37 -0
  76. data/app/helpers/better_ui/general/components/breadcrumb.rb +13 -0
  77. data/app/helpers/better_ui/general/components/button/button_helper.rb +65 -0
  78. data/app/helpers/better_ui/general/components/button.rb +13 -0
  79. data/app/helpers/better_ui/general/components/card/card_helper.rb +37 -0
  80. data/app/helpers/better_ui/general/components/card.rb +13 -0
  81. data/app/helpers/better_ui/general/components/container/container_helper.rb +60 -0
  82. data/app/helpers/better_ui/general/components/container.rb +13 -0
  83. data/app/helpers/better_ui/general/components/divider/divider_helper.rb +63 -0
  84. data/app/helpers/better_ui/general/components/divider.rb +13 -0
  85. data/app/helpers/better_ui/general/components/heading/heading_helper.rb +72 -0
  86. data/app/helpers/better_ui/general/components/heading.rb +13 -0
  87. data/app/helpers/better_ui/general/components/icon/icon_helper.rb +16 -0
  88. data/app/helpers/better_ui/general/components/icon.rb +13 -0
  89. data/app/helpers/better_ui/general/components/link/link_helper.rb +89 -0
  90. data/app/helpers/better_ui/general/components/link.rb +13 -0
  91. data/app/helpers/better_ui/general/components/panel/panel_helper.rb +83 -0
  92. data/app/helpers/better_ui/general/components/panel.rb +13 -0
  93. data/app/helpers/better_ui/general/components/progress/progress_helper.rb +53 -0
  94. data/app/helpers/better_ui/general/components/progress.rb +11 -0
  95. data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +17 -0
  96. data/app/helpers/better_ui/general/components/spinner.rb +10 -0
  97. data/app/helpers/better_ui/general/components/table/table_helper.rb +13 -0
  98. data/app/helpers/better_ui/general/components/table/tbody_helper.rb +13 -0
  99. data/app/helpers/better_ui/general/components/table/td_helper.rb +19 -0
  100. data/app/helpers/better_ui/general/components/table/tfoot_helper.rb +13 -0
  101. data/app/helpers/better_ui/general/components/table/th_helper.rb +19 -0
  102. data/app/helpers/better_ui/general/components/table/thead_helper.rb +13 -0
  103. data/app/helpers/better_ui/general/components/table/tr_helper.rb +13 -0
  104. data/app/helpers/better_ui/general/components/table.rb +25 -0
  105. data/app/helpers/better_ui/general/components/tag/tag_helper.rb +26 -0
  106. data/app/helpers/better_ui/general/components/tag.rb +15 -0
  107. data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +60 -0
  108. data/app/helpers/better_ui/general/components/tooltip.rb +13 -0
  109. data/app/helpers/better_ui/general_helper.rb +24 -0
  110. data/app/helpers/better_ui_helper.rb +16 -0
  111. data/app/javascript/application.js +1 -0
  112. data/app/jobs/better_ui/application_job.rb +4 -0
  113. data/app/mailers/better_ui/application_mailer.rb +6 -0
  114. data/app/models/better_ui/application_record.rb +5 -0
  115. data/app/views/components/better_ui/general/table/_custom_body_row.html.erb +17 -0
  116. data/app/views/components/better_ui/general/table/_custom_footer_rows.html.erb +17 -0
  117. data/app/views/components/better_ui/general/table/_custom_header_rows.html.erb +12 -0
  118. data/app/views/layouts/component_preview.html.erb +32 -0
  119. data/config/initializers/lookbook.rb +23 -0
  120. data/config/routes.rb +3 -0
  121. data/lib/better_ui/engine.rb +109 -0
  122. data/lib/better_ui/version.rb +3 -0
  123. data/lib/better_ui.rb +37 -0
  124. data/lib/generators/better_ui/install_generator.rb +103 -0
  125. data/lib/generators/better_ui/stylesheet_generator.rb +159 -0
  126. data/lib/generators/better_ui/templates/components/_avatar.scss +200 -0
  127. data/lib/generators/better_ui/templates/components/_badge.scss +154 -0
  128. data/lib/generators/better_ui/templates/components/_breadcrumb.scss +106 -0
  129. data/lib/generators/better_ui/templates/components/_button.scss +109 -0
  130. data/lib/generators/better_ui/templates/components/_card.scss +60 -0
  131. data/lib/generators/better_ui/templates/components/_heading.scss +81 -0
  132. data/lib/generators/better_ui/templates/components/_icon.scss +134 -0
  133. data/lib/generators/better_ui/templates/components/_index.scss +17 -0
  134. data/lib/generators/better_ui/templates/components/_link.scss +100 -0
  135. data/lib/generators/better_ui/templates/components/_panel.scss +104 -0
  136. data/lib/generators/better_ui/templates/components/_spinner.scss +129 -0
  137. data/lib/generators/better_ui/templates/components/_table.scss +156 -0
  138. data/lib/generators/better_ui/templates/components/_variables.scss +0 -0
  139. data/lib/generators/better_ui/templates/components_stylesheet.scss +35 -0
  140. data/lib/generators/better_ui/templates/index.scss +18 -0
  141. data/lib/generators/better_ui/templates/initializer.rb +41 -0
  142. data/lib/tasks/better_ui_tasks.rake +4 -0
  143. metadata +260 -0
@@ -0,0 +1,242 @@
1
+ module BetterUi
2
+ module General
3
+ module Alert
4
+ class Component < ViewComponent::Base
5
+ # Classi base sempre presenti
6
+ ALERT_BASE_CLASSES = "flex p-4 mb-4 border"
7
+
8
+ # Classi per elementi interni
9
+ ALERT_ICON_CLASSES = "flex-shrink-0 mr-3 w-5 h-5"
10
+ ALERT_ICON_RIGHT_CLASSES = "flex-shrink-0 ml-3 mr-0"
11
+ ALERT_CONTENT_CLASSES = "flex-1"
12
+ ALERT_TITLE_CLASSES = "font-medium mb-1"
13
+ ALERT_MESSAGE_CLASSES = "text-sm"
14
+ ALERT_CLOSE_CLASSES = "ml-auto -my-1.5 -mr-1.5 p-1.5 rounded-md focus:ring-2 focus:ring-offset-2 hover:bg-gray-100"
15
+
16
+ # Temi di alert con classi Tailwind dirette
17
+ ALERT_THEME_CLASSES = {
18
+ default: "bg-black text-white border-gray-900",
19
+ white: "bg-white text-black border-gray-200",
20
+ red: "bg-red-500 text-white border-red-600",
21
+ rose: "bg-rose-500 text-white border-rose-600",
22
+ orange: "bg-orange-500 text-white border-orange-600",
23
+ green: "bg-green-500 text-white border-green-600",
24
+ blue: "bg-blue-500 text-white border-blue-600",
25
+ yellow: "bg-yellow-500 text-black border-yellow-600",
26
+ violet: "bg-violet-500 text-white border-violet-600"
27
+ }
28
+
29
+ # Colori icone per ogni tema
30
+ ALERT_ICON_THEME_CLASSES = {
31
+ default: "text-white",
32
+ white: "text-black",
33
+ red: "text-white",
34
+ rose: "text-white",
35
+ orange: "text-white",
36
+ green: "text-white",
37
+ blue: "text-white",
38
+ yellow: "text-black",
39
+ violet: "text-white"
40
+ }
41
+
42
+ # Colori close button per ogni tema
43
+ ALERT_CLOSE_THEME_CLASSES = {
44
+ default: "text-white focus:ring-gray-600",
45
+ white: "text-black focus:ring-gray-400",
46
+ red: "text-white focus:ring-red-400",
47
+ rose: "text-white focus:ring-rose-400",
48
+ orange: "text-white focus:ring-orange-400",
49
+ green: "text-white focus:ring-green-400",
50
+ blue: "text-white focus:ring-blue-400",
51
+ yellow: "text-black focus:ring-yellow-400",
52
+ violet: "text-white focus:ring-violet-400"
53
+ }
54
+
55
+ # Border radius con classi Tailwind dirette
56
+ ALERT_RADIUS_CLASSES = {
57
+ none: "rounded-none",
58
+ small: "rounded-sm",
59
+ medium: "rounded-md",
60
+ large: "rounded-lg",
61
+ full: "rounded-full"
62
+ }
63
+
64
+ # Classi per layout quando icon è a destra
65
+ ALERT_ICON_RIGHT_LAYOUT_CLASSES = "flex-row-reverse"
66
+ ALERT_CONTENT_RIGHT_CLASSES = "text-right"
67
+
68
+ # Classi per alert dismissible
69
+ ALERT_DISMISSIBLE_CLASSES = "pr-12 relative"
70
+ ALERT_CLOSE_POSITION_CLASSES = "absolute right-4 top-4"
71
+
72
+ # Livelli di importanza con attributi ARIA
73
+ IMPORTANCE_LEVELS = {
74
+ high: { role: "alert", "aria-live": "assertive" },
75
+ medium: { role: "status", "aria-live": "polite" },
76
+ low: { role: "status", "aria-live": "polite" }
77
+ }
78
+
79
+ # @param title [String] titolo dell'alert (opzionale)
80
+ # @param message [String] contenuto dell'alert
81
+ # @param theme [Symbol] :default, :white, :red, :rose, :orange, :green, :blue, :yellow, :violet
82
+ # @param icon [String] nome dell'icona (opzionale)
83
+ # @param icon_position [Symbol] :left, :right posizione dell'icona
84
+ # @param dismissible [Boolean] se l'alert può essere chiuso
85
+ # @param rounded [Symbol] :none, :small, :medium, :large, :full arrotondamento degli angoli
86
+ # @param importance [Symbol] :high, :medium, :low livello di importanza per accessibilità
87
+ # @param html_content [Boolean] se il messaggio contiene HTML
88
+ # @param classes [String] classi CSS aggiuntive
89
+ # @param html_options [Hash] opzioni HTML per il container
90
+ def initialize(
91
+ title: nil,
92
+ message: nil,
93
+ theme: :white,
94
+ icon: nil,
95
+ icon_position: :left,
96
+ dismissible: false,
97
+ rounded: :medium,
98
+ importance: :medium,
99
+ html_content: false,
100
+ classes: nil,
101
+ **html_options
102
+ )
103
+ @title = title
104
+ @message = message
105
+ @theme = theme.to_sym
106
+ @icon = icon
107
+ @icon_position = icon_position.to_sym
108
+ @dismissible = dismissible
109
+ @rounded = rounded.to_sym
110
+ @importance = importance.to_sym
111
+ @html_content = html_content
112
+ @classes = classes
113
+ @html_options = html_options
114
+
115
+ validate_params
116
+ end
117
+
118
+ def default_icon
119
+ case @theme
120
+ when :default then "bell"
121
+ when :white then "information-circle"
122
+ when :red, :rose then "exclamation-circle"
123
+ when :orange then "bell"
124
+ when :green then "check-circle"
125
+ when :blue then "information-circle"
126
+ when :yellow then "exclamation-triangle"
127
+ when :violet then "shield-exclamation"
128
+ else "information-circle"
129
+ end
130
+ end
131
+
132
+ # Combina tutte le classi
133
+ def combined_classes
134
+ [
135
+ ALERT_BASE_CLASSES,
136
+ get_theme_class,
137
+ get_border_radius_class,
138
+ get_icon_position_class,
139
+ @dismissible ? ALERT_DISMISSIBLE_CLASSES : nil,
140
+ @classes,
141
+ @html_options[:class]
142
+ ].compact.join(" ")
143
+ end
144
+
145
+ # Restituisce gli attributi per l'alert
146
+ def alert_attributes
147
+ attrs = {
148
+ class: combined_classes
149
+ }
150
+
151
+ # Aggiungi attributi ARIA in base al livello di importanza
152
+ importance_attrs = IMPORTANCE_LEVELS[@importance] || IMPORTANCE_LEVELS[:medium]
153
+ importance_attrs.each do |key, value|
154
+ attrs[key] = value
155
+ end
156
+
157
+ # Aggiungi altri attributi HTML se presenti
158
+ @html_options.except(:class).each do |key, value|
159
+ attrs[key] = value
160
+ end
161
+
162
+ attrs
163
+ end
164
+
165
+ # Genera le classi del tema
166
+ def get_theme_class
167
+ ALERT_THEME_CLASSES[@theme] || ALERT_THEME_CLASSES[:white]
168
+ end
169
+
170
+ # Genera le classi per il border radius
171
+ def get_border_radius_class
172
+ ALERT_RADIUS_CLASSES[@rounded] || ALERT_RADIUS_CLASSES[:medium]
173
+ end
174
+
175
+ # Genera la classe per la posizione dell'icona (layout)
176
+ def get_icon_position_class
177
+ @icon_position == :right ? ALERT_ICON_RIGHT_LAYOUT_CLASSES : nil
178
+ end
179
+
180
+ # Genera le classi per l'icona
181
+ def get_icon_classes
182
+ if @icon_position == :right
183
+ ALERT_ICON_RIGHT_CLASSES
184
+ else
185
+ ALERT_ICON_CLASSES
186
+ end
187
+ end
188
+
189
+ # Genera le classi per il contenuto
190
+ def get_content_classes
191
+ if @icon_position == :right
192
+ [ALERT_CONTENT_CLASSES, ALERT_CONTENT_RIGHT_CLASSES].join(" ")
193
+ else
194
+ ALERT_CONTENT_CLASSES
195
+ end
196
+ end
197
+
198
+ # Genera le classi per l'icona del tema
199
+ def get_icon_theme_class
200
+ ALERT_ICON_THEME_CLASSES[@theme] || ALERT_ICON_THEME_CLASSES[:white]
201
+ end
202
+
203
+ # Genera le classi per il close button
204
+ def get_close_classes
205
+ base_classes = ALERT_CLOSE_CLASSES
206
+ theme_classes = ALERT_CLOSE_THEME_CLASSES[@theme] || ALERT_CLOSE_THEME_CLASSES[:white]
207
+ position_classes = @dismissible ? ALERT_CLOSE_POSITION_CLASSES : nil
208
+
209
+ [base_classes, theme_classes, position_classes].compact.join(" ")
210
+ end
211
+
212
+ private
213
+
214
+ def validate_params
215
+ # Validazione tema
216
+ valid_themes = ALERT_THEME_CLASSES.keys
217
+ unless valid_themes.include?(@theme)
218
+ raise ArgumentError, "Il tema deve essere uno tra: #{valid_themes.join(', ')}"
219
+ end
220
+
221
+ # Validazione border radius
222
+ valid_radius = ALERT_RADIUS_CLASSES.keys
223
+ unless valid_radius.include?(@rounded)
224
+ raise ArgumentError, "Il border radius deve essere uno tra: #{valid_radius.join(', ')}"
225
+ end
226
+
227
+ # Validazione posizione icona
228
+ valid_positions = [:left, :right]
229
+ unless valid_positions.include?(@icon_position)
230
+ raise ArgumentError, "La posizione dell'icona deve essere una tra: #{valid_positions.join(', ')}"
231
+ end
232
+
233
+ # Validazione livello di importanza
234
+ valid_importance = IMPORTANCE_LEVELS.keys
235
+ unless valid_importance.include?(@importance)
236
+ raise ArgumentError, "Il livello di importanza deve essere uno tra: #{valid_importance.join(', ')}"
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,20 @@
1
+ <%# Template per l'avatar %>
2
+ <%= tag.div(**avatar_attributes) do %>
3
+ <% if show_image? %>
4
+ <%= tag.img(
5
+ src: @src,
6
+ alt: @name || 'Avatar',
7
+ class: img_classes,
8
+ width: pixel_size,
9
+ height: pixel_size
10
+ ) %>
11
+ <% else %>
12
+ <%= tag.div(class: placeholder_classes) do %>
13
+ <%= initials %>
14
+ <% end %>
15
+ <% end %>
16
+
17
+ <% if show_status? %>
18
+ <%= tag.span(class: status_indicator_classes) %>
19
+ <% end %>
20
+ <% end %>
@@ -0,0 +1,301 @@
1
+ module BetterUi
2
+ module General
3
+ module Avatar
4
+ class Component < ViewComponent::Base
5
+ attr_reader :name, :src, :size, :shape, :status, :status_position, :theme, :style, :classes, :id
6
+
7
+ # Classi base sempre presenti
8
+ AVATAR_BASE_CLASSES = "relative inline-flex items-center justify-center flex-shrink-0 overflow-hidden"
9
+
10
+ # Classi per elementi interni
11
+ AVATAR_IMG_CLASSES = "h-full w-full object-cover"
12
+ AVATAR_PLACEHOLDER_BASE_CLASSES = "flex items-center justify-center h-full w-full font-medium"
13
+ AVATAR_STATUS_BASE_CLASSES = "absolute rounded-full border-2 border-white"
14
+
15
+ # Dimensioni container con classi Tailwind dirette
16
+ AVATAR_SIZE_CLASSES = {
17
+ xxsmall: "h-5 w-5",
18
+ xsmall: "h-6 w-6",
19
+ small: "h-8 w-8",
20
+ medium: "h-10 w-10",
21
+ large: "h-12 w-12",
22
+ xlarge: "h-16 w-16",
23
+ xxlarge: "h-24 w-24"
24
+ }
25
+
26
+ # Dimensioni testo placeholder
27
+ AVATAR_PLACEHOLDER_SIZE_CLASSES = {
28
+ xxsmall: "text-xs",
29
+ xsmall: "text-xs",
30
+ small: "text-sm",
31
+ medium: "text-base",
32
+ large: "text-lg",
33
+ xlarge: "text-xl",
34
+ xxlarge: "text-2xl"
35
+ }
36
+
37
+ # Forme con classi Tailwind dirette
38
+ AVATAR_SHAPE_CLASSES = {
39
+ circle: "rounded-full",
40
+ square: "rounded-none",
41
+ rounded: "rounded-lg"
42
+ }
43
+
44
+ # Temi placeholder con classi Tailwind dirette
45
+ AVATAR_PLACEHOLDER_THEME_CLASSES = {
46
+ default: "bg-black text-white",
47
+ white: "bg-white text-black border border-gray-300",
48
+ red: "bg-red-500 text-white",
49
+ rose: "bg-rose-500 text-white",
50
+ orange: "bg-orange-500 text-white",
51
+ green: "bg-green-500 text-white",
52
+ blue: "bg-blue-500 text-white",
53
+ yellow: "bg-yellow-500 text-black",
54
+ violet: "bg-violet-500 text-white"
55
+ }
56
+
57
+ # Stati online con classi Tailwind dirette
58
+ AVATAR_STATUS_THEME_CLASSES = {
59
+ online: "bg-green-400",
60
+ offline: "bg-gray-400",
61
+ busy: "bg-red-400",
62
+ away: "bg-yellow-400"
63
+ }
64
+
65
+ # Dimensioni indicatore stato
66
+ AVATAR_STATUS_SIZE_CLASSES = {
67
+ xxsmall: "h-1 w-1",
68
+ xsmall: "h-1.5 w-1.5",
69
+ small: "h-2 w-2",
70
+ medium: "h-2.5 w-2.5",
71
+ large: "h-3 w-3",
72
+ xlarge: "h-3.5 w-3.5",
73
+ xxlarge: "h-4 w-4"
74
+ }
75
+
76
+ # Posizioni dell'indicatore di stato
77
+ AVATAR_STATUS_POSITION_CLASSES = {
78
+ bottom_right: "bottom-0 right-0",
79
+ bottom_left: "bottom-0 left-0",
80
+ top_right: "top-0 right-0",
81
+ top_left: "top-0 left-0"
82
+ }
83
+
84
+ # Stili disponibili (mantenuto per compatibilità)
85
+ AVATAR_STYLE_CLASSES = {
86
+ filled: "",
87
+ outline: "border-2 border-gray-300",
88
+ light: "opacity-75"
89
+ }
90
+
91
+ def initialize(
92
+ name: nil,
93
+ src: nil,
94
+ size: :medium,
95
+ shape: :circle,
96
+ status: nil,
97
+ status_position: :bottom_right,
98
+ theme: :white,
99
+ style: :filled,
100
+ classes: nil,
101
+ id: nil,
102
+ **html_options
103
+ )
104
+ @name = name
105
+ @src = src
106
+ @size = size.to_sym
107
+ @shape = shape.to_sym
108
+ @status = status.present? ? status.to_sym : nil
109
+ @status_position = status_position.to_sym
110
+ @theme = theme.to_sym
111
+ @style = style.to_sym
112
+ @classes = classes
113
+ @id = id
114
+ @html_options = html_options
115
+
116
+ validate_params
117
+ end
118
+
119
+ # Combina tutte le classi per il container
120
+ def combined_classes
121
+ [
122
+ AVATAR_BASE_CLASSES,
123
+ get_size_class,
124
+ get_shape_class,
125
+ get_style_class,
126
+ @classes,
127
+ @html_options[:class]
128
+ ].compact.join(" ")
129
+ end
130
+
131
+ # Classi per il placeholder
132
+ def placeholder_classes
133
+ [
134
+ AVATAR_PLACEHOLDER_BASE_CLASSES,
135
+ get_placeholder_theme_class,
136
+ get_placeholder_size_class,
137
+ get_shape_class
138
+ ].compact.join(" ")
139
+ end
140
+
141
+ # Classi per l'immagine
142
+ def img_classes
143
+ [
144
+ AVATAR_IMG_CLASSES,
145
+ get_shape_class
146
+ ].compact.join(" ")
147
+ end
148
+
149
+ # Classi per l'indicatore di stato
150
+ def status_indicator_classes
151
+ [
152
+ AVATAR_STATUS_BASE_CLASSES,
153
+ get_status_theme_class,
154
+ get_status_size_class,
155
+ get_status_position_class
156
+ ].compact.join(" ")
157
+ end
158
+
159
+ def get_size_class
160
+ AVATAR_SIZE_CLASSES[@size] || AVATAR_SIZE_CLASSES[:medium]
161
+ end
162
+
163
+ def get_shape_class
164
+ AVATAR_SHAPE_CLASSES[@shape] || AVATAR_SHAPE_CLASSES[:circle]
165
+ end
166
+
167
+ def get_style_class
168
+ AVATAR_STYLE_CLASSES[@style] || AVATAR_STYLE_CLASSES[:filled]
169
+ end
170
+
171
+ def get_placeholder_theme_class
172
+ AVATAR_PLACEHOLDER_THEME_CLASSES[@theme] || AVATAR_PLACEHOLDER_THEME_CLASSES[:white]
173
+ end
174
+
175
+ def get_placeholder_size_class
176
+ AVATAR_PLACEHOLDER_SIZE_CLASSES[@size] || AVATAR_PLACEHOLDER_SIZE_CLASSES[:medium]
177
+ end
178
+
179
+ def get_status_theme_class
180
+ AVATAR_STATUS_THEME_CLASSES[@status] || ""
181
+ end
182
+
183
+ def get_status_size_class
184
+ AVATAR_STATUS_SIZE_CLASSES[@size] || AVATAR_STATUS_SIZE_CLASSES[:medium]
185
+ end
186
+
187
+ def get_status_position_class
188
+ AVATAR_STATUS_POSITION_CLASSES[@status_position] || AVATAR_STATUS_POSITION_CLASSES[:bottom_right]
189
+ end
190
+
191
+ # Restituisce gli attributi per l'avatar
192
+ def avatar_attributes
193
+ attrs = {
194
+ class: combined_classes,
195
+ id: @id
196
+ }
197
+
198
+ # Aggiungi altri attributi HTML se presenti
199
+ @html_options.except(:class).each do |key, value|
200
+ attrs[key] = value
201
+ end
202
+
203
+ attrs
204
+ end
205
+
206
+ # Determina se mostrare l'indicatore di stato
207
+ def show_status?
208
+ @status.present? && AVATAR_STATUS_THEME_CLASSES.key?(@status)
209
+ end
210
+
211
+ # Ottiene le iniziali dal nome
212
+ def initials
213
+ return "" unless @name.present?
214
+
215
+ words = @name.strip.split(/\s+/)
216
+ if words.size >= 2
217
+ "#{words[0][0]}#{words[1][0]}".upcase
218
+ else
219
+ @name[0..1].upcase
220
+ end
221
+ end
222
+
223
+ # Determina se mostrare l'immagine
224
+ def show_image?
225
+ @src.present?
226
+ end
227
+
228
+ # Ottiene le dimensioni dell'avatar in pixel (per attributi width/height img)
229
+ def pixel_size
230
+ case @size
231
+ when :xxsmall
232
+ 20
233
+ when :xsmall
234
+ 24
235
+ when :small
236
+ 32
237
+ when :medium
238
+ 40
239
+ when :large
240
+ 48
241
+ when :xlarge
242
+ 64
243
+ when :xxlarge
244
+ 96
245
+ else
246
+ 40
247
+ end
248
+ end
249
+
250
+ private
251
+
252
+ def validate_params
253
+ validate_size
254
+ validate_shape
255
+ validate_theme
256
+ validate_style
257
+ validate_status
258
+ validate_status_position
259
+ end
260
+
261
+ def validate_size
262
+ unless AVATAR_SIZE_CLASSES.keys.include?(@size)
263
+ raise ArgumentError, "La dimensione deve essere una tra: #{AVATAR_SIZE_CLASSES.keys.join(', ')}"
264
+ end
265
+ end
266
+
267
+ def validate_shape
268
+ unless AVATAR_SHAPE_CLASSES.keys.include?(@shape)
269
+ raise ArgumentError, "La forma deve essere una tra: #{AVATAR_SHAPE_CLASSES.keys.join(', ')}"
270
+ end
271
+ end
272
+
273
+ def validate_theme
274
+ unless AVATAR_PLACEHOLDER_THEME_CLASSES.keys.include?(@theme)
275
+ raise ArgumentError, "Il tema deve essere uno tra: #{AVATAR_PLACEHOLDER_THEME_CLASSES.keys.join(', ')}"
276
+ end
277
+ end
278
+
279
+ def validate_style
280
+ unless AVATAR_STYLE_CLASSES.keys.include?(@style)
281
+ raise ArgumentError, "Lo stile deve essere uno tra: #{AVATAR_STYLE_CLASSES.keys.join(', ')}"
282
+ end
283
+ end
284
+
285
+ def validate_status
286
+ return if @status.nil?
287
+
288
+ unless AVATAR_STATUS_THEME_CLASSES.keys.include?(@status)
289
+ raise ArgumentError, "Lo stato deve essere uno tra: #{AVATAR_STATUS_THEME_CLASSES.keys.join(', ')}"
290
+ end
291
+ end
292
+
293
+ def validate_status_position
294
+ unless AVATAR_STATUS_POSITION_CLASSES.keys.include?(@status_position)
295
+ raise ArgumentError, "La posizione dello stato deve essere una tra: #{AVATAR_STATUS_POSITION_CLASSES.keys.join(', ')}"
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
@@ -0,0 +1,23 @@
1
+ <span <%= tag.attributes(badge_attributes) %>>
2
+ <% if @variant == :dot %>
3
+ <span class="<%= dot_classes %>"></span>
4
+ <% end %>
5
+
6
+ <% if @icon && @icon_position == :left %>
7
+ <span class="<%= icon_classes %>">
8
+ <%= render_icon(@icon) %>
9
+ </span>
10
+ <% end %>
11
+
12
+ <% if @label.present? %>
13
+ <span class="<%= text_classes %>"><%= @label %></span>
14
+ <% end %>
15
+
16
+ <% if @icon && @icon_position == :right %>
17
+ <span class="<%= icon_classes %>">
18
+ <%= render_icon(@icon) %>
19
+ </span>
20
+ <% end %>
21
+
22
+ <%= content %>
23
+ </span>