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,248 @@
1
+ module BetterUi
2
+ module General
3
+ module Badge
4
+ class Component < ViewComponent::Base
5
+ attr_reader :label, :theme, :size, :shape, :style, :variant, :icon, :icon_position, :classes, :id
6
+
7
+ # Classi base sempre presenti
8
+ BADGE_BASE_CLASSES = "inline-flex items-center justify-center font-medium"
9
+
10
+ # Classi per elementi interni
11
+ BADGE_ICON_LEFT_CLASSES = "flex-shrink-0 -ml-0.5 mr-1.5"
12
+ BADGE_ICON_RIGHT_CLASSES = "flex-shrink-0 -mr-0.5 ml-1.5"
13
+ BADGE_TEXT_CLASSES = "whitespace-nowrap"
14
+ BADGE_DOT_CLASSES = "h-2 w-2 rounded-full mr-1.5"
15
+
16
+ # Temi di badge con stile FILLED - classi Tailwind dirette
17
+ BADGE_THEME_FILLED_CLASSES = {
18
+ default: "bg-black text-white",
19
+ white: "bg-white text-black",
20
+ red: "bg-red-500 text-white",
21
+ rose: "bg-rose-500 text-white",
22
+ orange: "bg-orange-500 text-white",
23
+ green: "bg-green-500 text-white",
24
+ blue: "bg-blue-500 text-white",
25
+ yellow: "bg-yellow-500 text-black",
26
+ violet: "bg-violet-500 text-white",
27
+ gray: "bg-gray-500 text-white"
28
+ }
29
+
30
+ # Temi di badge con stile OUTLINE - classi Tailwind dirette
31
+ BADGE_THEME_OUTLINE_CLASSES = {
32
+ default: "border border-black text-black bg-transparent",
33
+ white: "border border-gray-300 text-gray-700 bg-transparent",
34
+ red: "border border-red-500 text-red-500 bg-transparent",
35
+ rose: "border border-rose-500 text-rose-500 bg-transparent",
36
+ orange: "border border-orange-500 text-orange-500 bg-transparent",
37
+ green: "border border-green-500 text-green-500 bg-transparent",
38
+ blue: "border border-blue-500 text-blue-500 bg-transparent",
39
+ yellow: "border border-yellow-500 text-yellow-500 bg-transparent",
40
+ violet: "border border-violet-500 text-violet-500 bg-transparent",
41
+ gray: "border border-gray-500 text-gray-500 bg-transparent"
42
+ }
43
+
44
+ # Dimensioni con classi Tailwind dirette
45
+ BADGE_SIZE_CLASSES = {
46
+ small: "text-xs px-2 py-0.5",
47
+ medium: "text-sm px-2.5 py-0.5",
48
+ large: "text-sm px-3 py-1"
49
+ }
50
+
51
+ # Forme con classi Tailwind dirette
52
+ BADGE_SHAPE_CLASSES = {
53
+ square: "rounded-md",
54
+ rounded: "rounded-full"
55
+ }
56
+
57
+ # Colori dot per ogni tema
58
+ BADGE_DOT_COLOR_CLASSES = {
59
+ default: "bg-gray-500",
60
+ white: "bg-gray-400",
61
+ red: "bg-red-700",
62
+ rose: "bg-rose-700",
63
+ orange: "bg-orange-700",
64
+ green: "bg-green-700",
65
+ blue: "bg-blue-700",
66
+ yellow: "bg-yellow-700",
67
+ violet: "bg-violet-700",
68
+ gray: "bg-gray-700"
69
+ }
70
+
71
+ # @param label [String] Testo del badge
72
+ # @param theme [Symbol] default, white, red, rose, orange, green, blue, yellow, violet, gray
73
+ # @param size [Symbol] small, medium, large
74
+ # @param shape [Symbol] square, rounded
75
+ # @param style [Symbol] filled, outline
76
+ # @param variant [Symbol] nil, notification, counter, dot
77
+ # @param icon [String] Nome icona (opzionale)
78
+ # @param icon_position [Symbol] left, right
79
+ # @param html_options [Hash] Opzioni HTML aggiuntive
80
+ def initialize(
81
+ label: nil,
82
+ theme: :white,
83
+ size: :medium,
84
+ shape: :rounded,
85
+ style: :filled,
86
+ variant: nil,
87
+ icon: nil,
88
+ icon_position: :left,
89
+ classes: nil,
90
+ id: nil,
91
+ **html_options
92
+ )
93
+ @label = label
94
+ @theme = theme.to_sym
95
+ @size = size.to_sym
96
+ @shape = shape.to_sym
97
+ @style = style.to_sym
98
+ @variant = variant.present? ? variant.to_sym : nil
99
+ @icon = icon
100
+ @icon_position = icon_position.to_sym
101
+ @classes = classes
102
+ @id = id
103
+ @html_options = html_options
104
+
105
+ validate_params
106
+ end
107
+
108
+ # Combina tutte le classi
109
+ def combined_classes
110
+ [
111
+ BADGE_BASE_CLASSES,
112
+ get_theme_class,
113
+ get_size_class,
114
+ get_shape_class,
115
+ @classes,
116
+ @html_options[:class]
117
+ ].compact.join(" ")
118
+ end
119
+
120
+ def get_theme_class
121
+ if @style == :outline
122
+ BADGE_THEME_OUTLINE_CLASSES[@theme] || BADGE_THEME_OUTLINE_CLASSES[:white]
123
+ else
124
+ BADGE_THEME_FILLED_CLASSES[@theme] || BADGE_THEME_FILLED_CLASSES[:white]
125
+ end
126
+ end
127
+
128
+ def get_size_class
129
+ BADGE_SIZE_CLASSES[@size] || BADGE_SIZE_CLASSES[:medium]
130
+ end
131
+
132
+ def get_shape_class
133
+ BADGE_SHAPE_CLASSES[@shape] || BADGE_SHAPE_CLASSES[:rounded]
134
+ end
135
+
136
+ # Restituisce gli attributi per il badge
137
+ def badge_attributes
138
+ attrs = {
139
+ class: combined_classes,
140
+ id: @id
141
+ }
142
+
143
+ # Aggiungi altri attributi HTML se presenti
144
+ @html_options.except(:class).each do |key, value|
145
+ attrs[key] = value
146
+ end
147
+
148
+ attrs
149
+ end
150
+
151
+ def icon_classes
152
+ if @icon_position == :left
153
+ BADGE_ICON_LEFT_CLASSES
154
+ else
155
+ BADGE_ICON_RIGHT_CLASSES
156
+ end
157
+ end
158
+
159
+ def text_classes
160
+ BADGE_TEXT_CLASSES
161
+ end
162
+
163
+ def dot_classes
164
+ [
165
+ BADGE_DOT_CLASSES,
166
+ get_dot_color_class
167
+ ].compact.join(" ")
168
+ end
169
+
170
+ def get_dot_color_class
171
+ BADGE_DOT_COLOR_CLASSES[@theme] || BADGE_DOT_COLOR_CLASSES[:white]
172
+ end
173
+
174
+ # Helper per renderizzare le icone
175
+ def render_icon(icon_name)
176
+ # Mappa le dimensioni del badge alle dimensioni dell'icona
177
+ icon_size = case @size
178
+ when :large
179
+ :small
180
+ when :small
181
+ :tiny
182
+ else
183
+ :tiny
184
+ end
185
+
186
+ # Utilizziamo il componente Icon
187
+ render BetterUi::General::IconComponent.new(
188
+ name: icon_name,
189
+ size: icon_size,
190
+ fixed_width: true
191
+ )
192
+ end
193
+
194
+ private
195
+
196
+ def validate_params
197
+ validate_theme
198
+ validate_size
199
+ validate_shape
200
+ validate_style
201
+ validate_variant
202
+ validate_icon_position
203
+ end
204
+
205
+ def validate_theme
206
+ unless BADGE_THEME_FILLED_CLASSES.keys.include?(@theme)
207
+ raise ArgumentError, "Il tema deve essere uno tra: #{BADGE_THEME_FILLED_CLASSES.keys.join(', ')}"
208
+ end
209
+ end
210
+
211
+ def validate_size
212
+ unless BADGE_SIZE_CLASSES.keys.include?(@size)
213
+ raise ArgumentError, "La dimensione deve essere una tra: #{BADGE_SIZE_CLASSES.keys.join(', ')}"
214
+ end
215
+ end
216
+
217
+ def validate_shape
218
+ unless BADGE_SHAPE_CLASSES.keys.include?(@shape)
219
+ raise ArgumentError, "La forma deve essere una tra: #{BADGE_SHAPE_CLASSES.keys.join(', ')}"
220
+ end
221
+ end
222
+
223
+ def validate_style
224
+ valid_styles = [:filled, :outline]
225
+ unless valid_styles.include?(@style)
226
+ raise ArgumentError, "Lo stile deve essere uno tra: #{valid_styles.join(', ')}"
227
+ end
228
+ end
229
+
230
+ def validate_variant
231
+ return if @variant.nil?
232
+
233
+ valid_variants = [:notification, :counter, :dot]
234
+ unless valid_variants.include?(@variant)
235
+ raise ArgumentError, "La variante deve essere una tra: #{valid_variants.join(', ')}"
236
+ end
237
+ end
238
+
239
+ def validate_icon_position
240
+ valid_positions = [:left, :right]
241
+ unless valid_positions.include?(@icon_position)
242
+ raise ArgumentError, "La posizione dell'icona deve essere una tra: #{valid_positions.join(', ')}"
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,15 @@
1
+ <nav <%= tag.attributes(breadcrumb_attributes) %>>
2
+ <ol class="<%= BREADCRUMB_LIST_CLASSES %>">
3
+ <% @items.each_with_index do |item, index| %>
4
+ <li class="<%= BREADCRUMB_ITEM_CLASSES %>">
5
+ <%= render link_for_item(item, active: last_item?(index)) %>
6
+
7
+ <% unless last_item?(index) %>
8
+ <span class="<%= separator_classes %>" aria-hidden="true">
9
+ <%= separator_text %>
10
+ </span>
11
+ <% end %>
12
+ </li>
13
+ <% end %>
14
+ </ol>
15
+ </nav>
@@ -0,0 +1,186 @@
1
+ module BetterUi
2
+ module General
3
+ module Breadcrumb
4
+ class Component < ViewComponent::Base
5
+ attr_reader :items, :separator, :size, :theme, :classes, :html_options
6
+
7
+ # Classi base sempre presenti
8
+ BREADCRUMB_BASE_CLASSES = "flex items-center flex-wrap"
9
+
10
+ # Classi per lista e items
11
+ BREADCRUMB_LIST_CLASSES = "flex flex-wrap items-center"
12
+ BREADCRUMB_ITEM_CLASSES = "flex items-center"
13
+
14
+ # Temi di breadcrumb con classi Tailwind dirette
15
+ BREADCRUMB_THEME_CLASSES = {
16
+ default: "text-white",
17
+ white: "text-black",
18
+ red: "text-white",
19
+ rose: "text-white",
20
+ orange: "text-white",
21
+ green: "text-white",
22
+ blue: "text-white",
23
+ yellow: "text-black",
24
+ violet: "text-white",
25
+ gray: "text-gray-900"
26
+ }
27
+
28
+ # Classi per separatori con temi
29
+ BREADCRUMB_SEPARATOR_THEME_CLASSES = {
30
+ default: "text-gray-500",
31
+ white: "text-gray-400",
32
+ red: "text-red-300",
33
+ rose: "text-rose-300",
34
+ orange: "text-orange-300",
35
+ green: "text-green-300",
36
+ blue: "text-blue-300",
37
+ yellow: "text-yellow-600",
38
+ violet: "text-violet-300",
39
+ gray: "text-gray-500"
40
+ }
41
+
42
+ # Dimensioni con classi Tailwind dirette
43
+ BREADCRUMB_SIZE_CLASSES = {
44
+ small: "text-xs",
45
+ medium: "text-sm",
46
+ large: "text-base"
47
+ }
48
+
49
+ # Separatori predefiniti
50
+ BREADCRUMB_SEPARATOR_TYPES = {
51
+ slash: "/",
52
+ chevron: "›",
53
+ arrow: "→",
54
+ dot: "•",
55
+ pipe: "|"
56
+ }
57
+
58
+ # Inizializzazione del componente
59
+ def initialize(
60
+ items: [],
61
+ separator: :chevron,
62
+ theme: :white,
63
+ size: :medium,
64
+ classes: nil,
65
+ **html_options
66
+ )
67
+ @items = items || []
68
+ @separator = separator.to_sym
69
+ @theme = theme.to_sym
70
+ @size = size.to_sym
71
+ @classes = classes
72
+ @html_options = html_options
73
+
74
+ validate_params
75
+ end
76
+
77
+ # Restituisce il separatore come stringa
78
+ def separator_text
79
+ if BREADCRUMB_SEPARATOR_TYPES.key?(@separator)
80
+ BREADCRUMB_SEPARATOR_TYPES[@separator]
81
+ else
82
+ @separator.to_s
83
+ end
84
+ end
85
+
86
+ # Genera le classi per il container
87
+ def container_classes
88
+ [
89
+ BREADCRUMB_BASE_CLASSES,
90
+ get_theme_class,
91
+ get_size_class,
92
+ @classes,
93
+ @html_options[:class]
94
+ ].compact.join(" ")
95
+ end
96
+
97
+ # Verifica se un item è l'ultimo (attivo)
98
+ def last_item?(index)
99
+ index == @items.length - 1
100
+ end
101
+
102
+ # Crea un componente link per l'item
103
+ def link_for_item(item, active: false)
104
+ label = item.is_a?(Hash) ? item[:label] : item.to_s
105
+ href = item.is_a?(Hash) ? item[:url] : nil
106
+ icon = item.is_a?(Hash) ? item[:icon] : nil
107
+
108
+ BetterUi::General::Link::Component.new(
109
+ label: label,
110
+ href: href,
111
+ theme: @theme,
112
+ size: @size,
113
+ icon: icon,
114
+ active: active
115
+ )
116
+ end
117
+
118
+ # Restituisce gli attributi per il breadcrumb
119
+ def breadcrumb_attributes
120
+ attrs = {
121
+ "aria-label": "Breadcrumb",
122
+ class: container_classes
123
+ }
124
+
125
+ # Aggiungi altri attributi HTML se presenti
126
+ @html_options.except(:class).each do |key, value|
127
+ attrs[key] = value
128
+ end
129
+
130
+ attrs
131
+ end
132
+
133
+ # Restituisce le classi CSS per il separatore
134
+ def separator_classes
135
+ [
136
+ "mx-2",
137
+ get_separator_theme_class
138
+ ].compact.join(" ")
139
+ end
140
+
141
+ def get_separator_theme_class
142
+ BREADCRUMB_SEPARATOR_THEME_CLASSES[@theme] || BREADCRUMB_SEPARATOR_THEME_CLASSES[:white]
143
+ end
144
+
145
+ # Verifica se rendere il componente
146
+ def render?
147
+ @items.present? && @items.length > 0
148
+ end
149
+
150
+ private
151
+
152
+ def get_theme_class
153
+ BREADCRUMB_THEME_CLASSES[@theme] || BREADCRUMB_THEME_CLASSES[:white]
154
+ end
155
+
156
+ def get_size_class
157
+ BREADCRUMB_SIZE_CLASSES[@size] || BREADCRUMB_SIZE_CLASSES[:medium]
158
+ end
159
+
160
+ def validate_params
161
+ validate_theme
162
+ validate_size
163
+ validate_separator
164
+ end
165
+
166
+ def validate_theme
167
+ unless BREADCRUMB_THEME_CLASSES.keys.include?(@theme)
168
+ raise ArgumentError, "Il tema deve essere uno tra: #{BREADCRUMB_THEME_CLASSES.keys.join(', ')}"
169
+ end
170
+ end
171
+
172
+ def validate_size
173
+ unless BREADCRUMB_SIZE_CLASSES.keys.include?(@size)
174
+ raise ArgumentError, "La dimensione deve essere una tra: #{BREADCRUMB_SIZE_CLASSES.keys.join(', ')}"
175
+ end
176
+ end
177
+
178
+ def validate_separator
179
+ return if !@separator.is_a?(Symbol) || BREADCRUMB_SEPARATOR_TYPES.keys.include?(@separator)
180
+
181
+ raise ArgumentError, "Il separatore predefinito deve essere uno tra: #{BREADCRUMB_SEPARATOR_TYPES.keys.join(', ')}"
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,34 @@
1
+ <%# Template per il bottone %>
2
+ <% if link? %>
3
+ <%= link_to @href, **link_attributes do %>
4
+ <% if @icon && @icon_position == :left %>
5
+ <span class="flex-shrink-0 mr-2"><%= render_icon(@icon) %></span>
6
+ <% end %>
7
+
8
+ <% if @label %>
9
+ <span class="flex-grow"><%= @label %></span>
10
+ <% end %>
11
+
12
+ <% if @icon && @icon_position == :right %>
13
+ <span class="flex-shrink-0 ml-2"><%= render_icon(@icon) %></span>
14
+ <% end %>
15
+
16
+ <%= content %>
17
+ <% end %>
18
+ <% else %>
19
+ <%= tag.button(**button_attributes) do %>
20
+ <% if @icon && @icon_position == :left %>
21
+ <span class="flex-shrink-0 mr-2"><%= render_icon(@icon) %></span>
22
+ <% end %>
23
+
24
+ <% if @label %>
25
+ <span class="flex-grow"><%= @label %></span>
26
+ <% end %>
27
+
28
+ <% if @icon && @icon_position == :right %>
29
+ <span class="flex-shrink-0 ml-2"><%= render_icon(@icon) %></span>
30
+ <% end %>
31
+
32
+ <%= content %>
33
+ <% end %>
34
+ <% end %>
@@ -0,0 +1,214 @@
1
+ module BetterUi
2
+ module General
3
+ module Button
4
+ class Component < ViewComponent::Base
5
+ attr_reader :label, :type, :size, :full_width, :disabled,
6
+ :icon, :icon_position, :href, :method, :data, :classes, :id, :rounded, :button_type, :html_options
7
+
8
+ # Classi base sempre presenti
9
+ BUTTON_BASE_CLASSES = "inline-flex items-center justify-center font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2"
10
+
11
+ # Temi di bottoni con classi Tailwind dirette
12
+ BUTTON_THEME = {
13
+ default: "bg-black text-white hover:bg-gray-900 focus:ring-gray-900",
14
+ white: "bg-white text-black border border-gray-300 hover:bg-gray-50 focus:ring-gray-400",
15
+ red: "bg-red-500 text-white hover:bg-red-600 focus:ring-red-500",
16
+ rose: "bg-rose-500 text-white hover:bg-rose-600 focus:ring-rose-500",
17
+ orange: "bg-orange-500 text-white hover:bg-orange-600 focus:ring-orange-500",
18
+ green: "bg-green-500 text-white hover:bg-green-600 focus:ring-green-500",
19
+ blue: "bg-blue-500 text-white hover:bg-blue-600 focus:ring-blue-500",
20
+ yellow: "bg-yellow-500 text-black hover:bg-yellow-600 focus:ring-yellow-500",
21
+ violet: "bg-violet-500 text-white hover:bg-violet-600 focus:ring-violet-500",
22
+ purple: "bg-purple-500 text-white hover:bg-purple-600 focus:ring-purple-500",
23
+ }
24
+
25
+ # Dimensioni con classi Tailwind dirette
26
+ BUTTON_SIZES = {
27
+ small: "px-2.5 py-1.5 text-xs",
28
+ medium: "px-4 py-2 text-sm",
29
+ large: "px-6 py-3 text-base"
30
+ }
31
+
32
+ # Border radius con classi Tailwind dirette
33
+ BUTTON_RADIUS = {
34
+ none: "rounded-none",
35
+ small: "rounded-md",
36
+ medium: "rounded-lg",
37
+ large: "rounded-xl",
38
+ full: "rounded-full"
39
+ }
40
+
41
+ # Inizializzazione del componente
42
+ def initialize(
43
+ label: nil,
44
+ type: :white,
45
+ size: :medium,
46
+ full_width: false,
47
+ disabled: false,
48
+ icon: nil,
49
+ icon_position: :left,
50
+ href: nil,
51
+ method: nil,
52
+ data: {},
53
+ classes: nil,
54
+ id: nil,
55
+ rounded: :medium,
56
+ button_type: :button,
57
+ **html_options
58
+ )
59
+ @label = label
60
+ @type = type.to_sym
61
+ @size = size.to_sym
62
+ @full_width = full_width
63
+ @disabled = disabled
64
+ @icon = icon
65
+ @icon_position = icon_position.to_sym
66
+ @href = href
67
+ @method = method
68
+ @data = data
69
+ @classes = classes
70
+ @id = id
71
+ @rounded = rounded.to_sym
72
+ @button_type = button_type.to_sym
73
+ @html_options = html_options
74
+
75
+ validate_params
76
+ end
77
+
78
+ # Determina se il bottone è un link o un button
79
+ def link?
80
+ @href.present?
81
+ end
82
+
83
+ # Combina tutte le classi
84
+ def combined_classes
85
+ [
86
+ BUTTON_BASE_CLASSES,
87
+ get_button_type_classes,
88
+ get_button_size_classes,
89
+ get_border_radius_class,
90
+ @full_width ? "w-full" : nil,
91
+ @disabled ? "opacity-50 cursor-not-allowed" : nil,
92
+ @classes,
93
+ @html_options[:class]
94
+ ].compact.join(" ")
95
+ end
96
+
97
+ def get_button_type_classes
98
+ BUTTON_THEME[@type] || BUTTON_THEME[:white]
99
+ end
100
+
101
+ def get_border_radius_class
102
+ BUTTON_RADIUS[@rounded] || BUTTON_RADIUS[:medium]
103
+ end
104
+
105
+ def get_button_size_classes
106
+ BUTTON_SIZES[@size] || BUTTON_SIZES[:medium]
107
+ end
108
+
109
+ # Restituisce gli attributi per il bottone
110
+ def button_attributes
111
+ attrs = {
112
+ class: combined_classes,
113
+ type: button_type,
114
+ id: @id
115
+ }
116
+
117
+ attrs[:disabled] = true if @disabled
118
+ attrs[:data] = @data if @data.present?
119
+
120
+ # Aggiungi altri attributi HTML se presenti
121
+ @html_options.except(:class).each do |key, value|
122
+ attrs[key] = value
123
+ end
124
+
125
+ attrs
126
+ end
127
+
128
+ # Restituisce gli attributi per il link
129
+ def link_attributes
130
+ attrs = {
131
+ class: combined_classes,
132
+ id: @id
133
+ }
134
+
135
+ attrs[:data] = @data.merge(turbo_method: @method) if @method.present?
136
+ attrs[:data] = @data if @data.present? && !@method.present?
137
+ attrs[:href] = @disabled ? nil : @href
138
+ attrs[:role] = "button"
139
+ attrs[:tabindex] = @disabled ? "-1" : "0"
140
+ attrs[:aria] = { disabled: @disabled } if @disabled
141
+
142
+ # Aggiungi altri attributi HTML se presenti
143
+ @html_options.except(:class).each do |key, value|
144
+ attrs[key] = value
145
+ end
146
+
147
+ attrs
148
+ end
149
+
150
+ def button_type
151
+ @button_type || 'button'
152
+ end
153
+
154
+ # Helper per renderizzare le icone
155
+ def render_icon(icon_name)
156
+ # Mappa le dimensioni del bottone alle dimensioni dell'icona
157
+ icon_size = case @size
158
+ when :large
159
+ :large
160
+ when :small
161
+ :small
162
+ else
163
+ :medium
164
+ end
165
+
166
+ # Utilizziamo il componente Icon
167
+ render BetterUi::General::Icon::Component.new(
168
+ name: icon_name,
169
+ size: icon_size,
170
+ fixed_width: true
171
+ )
172
+ end
173
+
174
+ # Verifica se rendere il componente
175
+ def render?
176
+ @label.present? || @icon.present? || content.present?
177
+ end
178
+
179
+ private
180
+
181
+ def validate_params
182
+ validate_type
183
+ validate_size
184
+ validate_icon_position
185
+ validate_rounded
186
+ end
187
+
188
+ def validate_type
189
+ unless BUTTON_THEME.keys.include?(@type)
190
+ raise ArgumentError, "Il tipo deve essere uno tra: #{BUTTON_THEME.keys.join(', ')}"
191
+ end
192
+ end
193
+
194
+ def validate_size
195
+ unless BUTTON_SIZES.keys.include?(@size)
196
+ raise ArgumentError, "La dimensione deve essere una tra: #{BUTTON_SIZES.keys.join(', ')}"
197
+ end
198
+ end
199
+
200
+ def validate_icon_position
201
+ unless [:left, :right].include?(@icon_position)
202
+ raise ArgumentError, "La posizione dell'icona deve essere :left o :right"
203
+ end
204
+ end
205
+
206
+ def validate_rounded
207
+ unless BUTTON_RADIUS.keys.include?(@rounded)
208
+ raise ArgumentError, "Il bordo deve essere uno tra: #{BUTTON_RADIUS.keys.join(', ')}"
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end