better_ui 0.3.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.

Potentially problematic release.


This version of better_ui might be problematic. Click here for more details.

Files changed (206) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +227 -209
  4. data/app/assets/javascripts/better_ui/controllers/navbar_controller.js +138 -0
  5. data/app/assets/javascripts/better_ui/controllers/sidebar_controller.js +211 -0
  6. data/app/assets/javascripts/better_ui/controllers/toast_controller.js +161 -0
  7. data/app/assets/javascripts/better_ui/index.js +159 -0
  8. data/app/assets/javascripts/better_ui/toast_manager.js +77 -0
  9. data/app/assets/stylesheets/better_ui/application.css +30 -0
  10. data/app/components/better_ui/application/alert_component.html.erb +27 -0
  11. data/app/components/better_ui/application/alert_component.rb +202 -0
  12. data/app/components/better_ui/application/card_component.html.erb +24 -0
  13. data/app/components/better_ui/application/card_component.rb +53 -0
  14. data/app/components/better_ui/application/card_container_component.html.erb +8 -0
  15. data/app/components/better_ui/application/card_container_component.rb +14 -0
  16. data/app/components/better_ui/application/header_component.html.erb +88 -0
  17. data/app/components/better_ui/application/header_component.rb +188 -0
  18. data/app/components/better_ui/application/navbar_component.html.erb +294 -0
  19. data/app/components/better_ui/application/navbar_component.rb +249 -0
  20. data/app/components/better_ui/application/sidebar_component.html.erb +207 -0
  21. data/app/components/better_ui/application/sidebar_component.rb +318 -0
  22. data/app/components/better_ui/application/toast_component.html.erb +35 -0
  23. data/app/components/better_ui/application/toast_component.rb +223 -0
  24. data/app/components/better_ui/general/avatar_component.html.erb +19 -0
  25. data/app/components/better_ui/general/avatar_component.rb +171 -0
  26. data/app/components/better_ui/general/badge_component.html.erb +19 -0
  27. data/app/components/better_ui/general/badge_component.rb +123 -0
  28. data/app/components/better_ui/general/{breadcrumb/component.html.erb → breadcrumb_component.html.erb} +4 -4
  29. data/app/components/better_ui/general/breadcrumb_component.rb +130 -0
  30. data/app/components/better_ui/general/{button/component.html.erb → button_component.html.erb} +7 -7
  31. data/app/components/better_ui/general/button_component.rb +162 -0
  32. data/app/components/better_ui/general/heading_component.html.erb +25 -0
  33. data/app/components/better_ui/general/heading_component.rb +148 -0
  34. data/app/components/better_ui/general/icon_component.html.erb +2 -0
  35. data/app/components/better_ui/general/icon_component.rb +100 -0
  36. data/app/components/better_ui/general/link_component.html.erb +17 -0
  37. data/app/components/better_ui/general/link_component.rb +132 -0
  38. data/app/components/better_ui/general/panel_component.html.erb +27 -0
  39. data/app/components/better_ui/general/panel_component.rb +103 -0
  40. data/app/components/better_ui/general/spinner_component.html.erb +15 -0
  41. data/app/components/better_ui/general/spinner_component.rb +79 -0
  42. data/app/components/better_ui/general/table_component.html.erb +73 -0
  43. data/app/components/better_ui/general/table_component.rb +167 -0
  44. data/app/components/better_ui/theme_helper.rb +171 -0
  45. data/app/controllers/better_ui/application_controller.rb +1 -0
  46. data/app/controllers/better_ui/docs_controller.rb +34 -0
  47. data/app/views/components/better_ui/general/table/_custom_body_row.html.erb +17 -0
  48. data/app/views/components/better_ui/general/table/_custom_footer_rows.html.erb +17 -0
  49. data/app/views/components/better_ui/general/table/_custom_header_rows.html.erb +12 -0
  50. data/app/views/layouts/component_preview.html.erb +32 -0
  51. data/config/initializers/lookbook.rb +12 -12
  52. data/config/routes.rb +13 -0
  53. data/lib/better_ui/engine.rb +36 -5
  54. data/lib/better_ui/version.rb +1 -1
  55. data/lib/better_ui.rb +24 -4
  56. data/lib/generators/better_ui/stylesheet_generator.rb +96 -0
  57. data/lib/generators/better_ui/templates/README +56 -0
  58. data/lib/generators/better_ui/templates/components/_avatar.scss +151 -0
  59. data/lib/generators/better_ui/templates/components/_badge.scss +142 -0
  60. data/lib/generators/better_ui/templates/components/_breadcrumb.scss +107 -0
  61. data/lib/generators/better_ui/templates/components/_button.scss +106 -0
  62. data/lib/generators/better_ui/templates/components/_card.scss +69 -0
  63. data/lib/generators/better_ui/templates/components/_heading.scss +180 -0
  64. data/lib/generators/better_ui/templates/components/_icon.scss +90 -0
  65. data/lib/generators/better_ui/templates/components/_link.scss +130 -0
  66. data/lib/generators/better_ui/templates/components/_panel.scss +144 -0
  67. data/lib/generators/better_ui/templates/components/_spinner.scss +132 -0
  68. data/lib/generators/better_ui/templates/components/_table.scss +105 -0
  69. data/lib/generators/better_ui/templates/components/_variables.scss +33 -0
  70. data/lib/generators/better_ui/templates/components_stylesheet.scss +66 -0
  71. metadata +145 -156
  72. data/app/components/better_ui/application/card/component.html.erb +0 -20
  73. data/app/components/better_ui/application/card/component.rb +0 -214
  74. data/app/components/better_ui/application/main/component.html.erb +0 -9
  75. data/app/components/better_ui/application/main/component.rb +0 -123
  76. data/app/components/better_ui/application/navbar/component.html.erb +0 -92
  77. data/app/components/better_ui/application/navbar/component.rb +0 -136
  78. data/app/components/better_ui/application/sidebar/component.html.erb +0 -227
  79. data/app/components/better_ui/application/sidebar/component.rb +0 -130
  80. data/app/components/better_ui/general/accordion/component.html.erb +0 -5
  81. data/app/components/better_ui/general/accordion/component.rb +0 -92
  82. data/app/components/better_ui/general/accordion/item_component.html.erb +0 -12
  83. data/app/components/better_ui/general/accordion/item_component.rb +0 -176
  84. data/app/components/better_ui/general/alert/component.html.erb +0 -32
  85. data/app/components/better_ui/general/alert/component.rb +0 -242
  86. data/app/components/better_ui/general/avatar/component.html.erb +0 -20
  87. data/app/components/better_ui/general/avatar/component.rb +0 -301
  88. data/app/components/better_ui/general/badge/component.html.erb +0 -23
  89. data/app/components/better_ui/general/badge/component.rb +0 -248
  90. data/app/components/better_ui/general/breadcrumb/component.rb +0 -187
  91. data/app/components/better_ui/general/button/component.rb +0 -214
  92. data/app/components/better_ui/general/divider/component.html.erb +0 -10
  93. data/app/components/better_ui/general/divider/component.rb +0 -226
  94. data/app/components/better_ui/general/dropdown/component.html.erb +0 -25
  95. data/app/components/better_ui/general/dropdown/component.rb +0 -170
  96. data/app/components/better_ui/general/dropdown/divider_component.html.erb +0 -1
  97. data/app/components/better_ui/general/dropdown/divider_component.rb +0 -41
  98. data/app/components/better_ui/general/dropdown/item_component.html.erb +0 -6
  99. data/app/components/better_ui/general/dropdown/item_component.rb +0 -119
  100. data/app/components/better_ui/general/field/component.html.erb +0 -27
  101. data/app/components/better_ui/general/field/component.rb +0 -37
  102. data/app/components/better_ui/general/heading/component.html.erb +0 -22
  103. data/app/components/better_ui/general/heading/component.rb +0 -257
  104. data/app/components/better_ui/general/icon/component.html.erb +0 -7
  105. data/app/components/better_ui/general/icon/component.rb +0 -239
  106. data/app/components/better_ui/general/input/checkbox/component.html.erb +0 -5
  107. data/app/components/better_ui/general/input/checkbox/component.rb +0 -238
  108. data/app/components/better_ui/general/input/datetime/component.html.erb +0 -5
  109. data/app/components/better_ui/general/input/datetime/component.rb +0 -223
  110. data/app/components/better_ui/general/input/radio/component.html.erb +0 -5
  111. data/app/components/better_ui/general/input/radio/component.rb +0 -230
  112. data/app/components/better_ui/general/input/select/component.html.erb +0 -16
  113. data/app/components/better_ui/general/input/select/component.rb +0 -184
  114. data/app/components/better_ui/general/input/select/select_component.html.erb +0 -5
  115. data/app/components/better_ui/general/input/select/select_component.rb +0 -37
  116. data/app/components/better_ui/general/input/text/component.html.erb +0 -5
  117. data/app/components/better_ui/general/input/text/component.rb +0 -171
  118. data/app/components/better_ui/general/input/textarea/component.html.erb +0 -5
  119. data/app/components/better_ui/general/input/textarea/component.rb +0 -166
  120. data/app/components/better_ui/general/link/component.html.erb +0 -18
  121. data/app/components/better_ui/general/link/component.rb +0 -258
  122. data/app/components/better_ui/general/modal/component.html.erb +0 -5
  123. data/app/components/better_ui/general/modal/component.rb +0 -47
  124. data/app/components/better_ui/general/modal/modal_component.html.erb +0 -52
  125. data/app/components/better_ui/general/modal/modal_component.rb +0 -160
  126. data/app/components/better_ui/general/pagination/component.html.erb +0 -85
  127. data/app/components/better_ui/general/pagination/component.rb +0 -216
  128. data/app/components/better_ui/general/panel/component.html.erb +0 -28
  129. data/app/components/better_ui/general/panel/component.rb +0 -249
  130. data/app/components/better_ui/general/progress/component.html.erb +0 -11
  131. data/app/components/better_ui/general/progress/component.rb +0 -160
  132. data/app/components/better_ui/general/spinner/component.html.erb +0 -35
  133. data/app/components/better_ui/general/spinner/component.rb +0 -93
  134. data/app/components/better_ui/general/table/component.html.erb +0 -5
  135. data/app/components/better_ui/general/table/component.rb +0 -217
  136. data/app/components/better_ui/general/table/tbody_component.html.erb +0 -3
  137. data/app/components/better_ui/general/table/tbody_component.rb +0 -30
  138. data/app/components/better_ui/general/table/td_component.html.erb +0 -3
  139. data/app/components/better_ui/general/table/td_component.rb +0 -44
  140. data/app/components/better_ui/general/table/tfoot_component.html.erb +0 -3
  141. data/app/components/better_ui/general/table/tfoot_component.rb +0 -28
  142. data/app/components/better_ui/general/table/th_component.html.erb +0 -6
  143. data/app/components/better_ui/general/table/th_component.rb +0 -51
  144. data/app/components/better_ui/general/table/thead_component.html.erb +0 -3
  145. data/app/components/better_ui/general/table/thead_component.rb +0 -28
  146. data/app/components/better_ui/general/table/tr_component.html.erb +0 -3
  147. data/app/components/better_ui/general/table/tr_component.rb +0 -30
  148. data/app/components/better_ui/general/tabs/component.html.erb +0 -11
  149. data/app/components/better_ui/general/tabs/component.rb +0 -120
  150. data/app/components/better_ui/general/tabs/panel_component.html.erb +0 -3
  151. data/app/components/better_ui/general/tabs/panel_component.rb +0 -37
  152. data/app/components/better_ui/general/tabs/tab_component.html.erb +0 -13
  153. data/app/components/better_ui/general/tabs/tab_component.rb +0 -111
  154. data/app/components/better_ui/general/tag/component.html.erb +0 -3
  155. data/app/components/better_ui/general/tag/component.rb +0 -104
  156. data/app/components/better_ui/general/tooltip/component.html.erb +0 -7
  157. data/app/components/better_ui/general/tooltip/component.rb +0 -239
  158. data/app/helpers/better_ui/application/components/card/card_helper.rb +0 -96
  159. data/app/helpers/better_ui/application/components/card.rb +0 -11
  160. data/app/helpers/better_ui/application/components/main/main_helper.rb +0 -64
  161. data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +0 -77
  162. data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +0 -51
  163. data/app/helpers/better_ui/application_helper.rb +0 -55
  164. data/app/helpers/better_ui/general/components/accordion/accordion_helper.rb +0 -73
  165. data/app/helpers/better_ui/general/components/accordion.rb +0 -11
  166. data/app/helpers/better_ui/general/components/alert/alert_helper.rb +0 -57
  167. data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +0 -29
  168. data/app/helpers/better_ui/general/components/badge/badge_helper.rb +0 -53
  169. data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +0 -37
  170. data/app/helpers/better_ui/general/components/button/button_helper.rb +0 -65
  171. data/app/helpers/better_ui/general/components/container/container_helper.rb +0 -60
  172. data/app/helpers/better_ui/general/components/divider/divider_helper.rb +0 -63
  173. data/app/helpers/better_ui/general/components/dropdown/divider_helper.rb +0 -32
  174. data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +0 -79
  175. data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +0 -62
  176. data/app/helpers/better_ui/general/components/field/field_helper.rb +0 -26
  177. data/app/helpers/better_ui/general/components/heading/heading_helper.rb +0 -72
  178. data/app/helpers/better_ui/general/components/icon/icon_helper.rb +0 -16
  179. data/app/helpers/better_ui/general/components/input/checkbox/checkbox_helper.rb +0 -81
  180. data/app/helpers/better_ui/general/components/input/datetime/datetime_helper.rb +0 -91
  181. data/app/helpers/better_ui/general/components/input/radio/radio_helper.rb +0 -79
  182. data/app/helpers/better_ui/general/components/input/radio_group/radio_group_helper.rb +0 -124
  183. data/app/helpers/better_ui/general/components/input/select/select_helper.rb +0 -70
  184. data/app/helpers/better_ui/general/components/input/text/text_helper.rb +0 -138
  185. data/app/helpers/better_ui/general/components/input/textarea/textarea_helper.rb +0 -73
  186. data/app/helpers/better_ui/general/components/link/link_helper.rb +0 -89
  187. data/app/helpers/better_ui/general/components/modal/modal_helper.rb +0 -85
  188. data/app/helpers/better_ui/general/components/modal.rb +0 -11
  189. data/app/helpers/better_ui/general/components/pagination/pagination_helper.rb +0 -82
  190. data/app/helpers/better_ui/general/components/panel/panel_helper.rb +0 -83
  191. data/app/helpers/better_ui/general/components/progress/progress_helper.rb +0 -53
  192. data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +0 -19
  193. data/app/helpers/better_ui/general/components/table/table_helper.rb +0 -53
  194. data/app/helpers/better_ui/general/components/table/tbody_helper.rb +0 -13
  195. data/app/helpers/better_ui/general/components/table/td_helper.rb +0 -19
  196. data/app/helpers/better_ui/general/components/table/tfoot_helper.rb +0 -13
  197. data/app/helpers/better_ui/general/components/table/th_helper.rb +0 -19
  198. data/app/helpers/better_ui/general/components/table/thead_helper.rb +0 -13
  199. data/app/helpers/better_ui/general/components/table/tr_helper.rb +0 -13
  200. data/app/helpers/better_ui/general/components/tabs/panel_helper.rb +0 -62
  201. data/app/helpers/better_ui/general/components/tabs/tab_helper.rb +0 -55
  202. data/app/helpers/better_ui/general/components/tabs/tabs_helper.rb +0 -95
  203. data/app/helpers/better_ui/general/components/tag/tag_helper.rb +0 -26
  204. data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +0 -60
  205. data/app/views/layouts/better_ui/application.html.erb +0 -17
  206. data/lib/better_ui/railtie.rb +0 -20
@@ -0,0 +1,77 @@
1
+ // Gestore per l'impilamento dei toast in diverse posizioni
2
+ class ToastManager {
3
+ constructor() {
4
+ // Mantiene traccia dei toast attivi per posizione
5
+ this.toasts = {
6
+ 'top-right': [],
7
+ 'top-left': [],
8
+ 'bottom-right': [],
9
+ 'bottom-left': [],
10
+ 'top-center': [],
11
+ 'bottom-center': []
12
+ }
13
+
14
+ // Offset di base per ogni posizione (in px)
15
+ this.baseOffset = 16; // equivalente a Tailwind top-4 (16px)
16
+
17
+ // Spaziatura tra i toast (in px)
18
+ this.spacing = 12;
19
+ }
20
+
21
+ // Registra un nuovo toast e calcola la sua posizione
22
+ registerToast(toast, position) {
23
+ if (!this.toasts[position]) {
24
+ console.warn(`Posizione toast non valida: ${position}. Utilizzo 'top-right' come predefinita.`);
25
+ position = 'top-right';
26
+ }
27
+
28
+ // Aggiungi il toast all'array della posizione specifica
29
+ this.toasts[position].push(toast);
30
+
31
+ // Ricalcola le posizioni per tutti i toast in questa posizione
32
+ this.updatePositions(position);
33
+
34
+ return () => this.removeToast(toast, position);
35
+ }
36
+
37
+ // Rimuove un toast e ricalcola le posizioni
38
+ removeToast(toast, position) {
39
+ const index = this.toasts[position].indexOf(toast);
40
+ if (index !== -1) {
41
+ this.toasts[position].splice(index, 1);
42
+ this.updatePositions(position);
43
+ }
44
+ }
45
+
46
+ // Aggiorna le posizioni di tutti i toast in una data posizione
47
+ updatePositions(position) {
48
+ const isTopPosition = position.startsWith('top-');
49
+ const toastArray = this.toasts[position];
50
+
51
+ let currentOffset = this.baseOffset;
52
+
53
+ toastArray.forEach((toast, index) => {
54
+ // Applica la posizione corretta
55
+ if (isTopPosition) {
56
+ toast.style.top = `${currentOffset}px`;
57
+ } else {
58
+ toast.style.bottom = `${currentOffset}px`;
59
+ }
60
+
61
+ // Aggiorna l'offset per il prossimo toast
62
+ const height = toast.offsetHeight;
63
+ currentOffset += height + this.spacing;
64
+ });
65
+ }
66
+
67
+ // Metodo statico per accedere all'istanza singleton
68
+ static getInstance() {
69
+ if (!ToastManager.instance) {
70
+ ToastManager.instance = new ToastManager();
71
+ }
72
+ return ToastManager.instance;
73
+ }
74
+ }
75
+
76
+ // Esporta il gestore come singleton
77
+ export default ToastManager.getInstance();
@@ -12,4 +12,34 @@
12
12
  *
13
13
  *= require_tree .
14
14
  *= require_self
15
+ *= require font-awesome
15
16
  */
17
+
18
+ /*
19
+ * BetterUI - Utilizziamo Tailwind CSS per tutti gli stili
20
+ */
21
+
22
+ /* Stili per il syntax highlighting di CodeRay */
23
+ .syntax-ruby {
24
+ color: #333;
25
+ background-color: #f8f8f8;
26
+ font-family: Monaco, Consolas, 'Courier New', monospace;
27
+ line-height: 1.5;
28
+ }
29
+ .syntax-ruby .keyword { color: #0066CC; font-weight: bold; }
30
+ .syntax-ruby .string, .syntax-ruby .string-content { color: #008800; }
31
+ .syntax-ruby .comment { color: #888888; font-style: italic; }
32
+ .syntax-ruby .constant { color: #CC0000; }
33
+ .syntax-ruby .class { color: #0086B3; font-weight: bold; }
34
+ .syntax-ruby .module { color: #0086B3; font-weight: bold; }
35
+ .syntax-ruby .symbol { color: #AA6600; }
36
+ .syntax-ruby .function { color: #990000; }
37
+ .syntax-ruby .regexp { color: #AA6600; }
38
+ .syntax-ruby .integer, .syntax-ruby .float { color: #0000DD; }
39
+ .syntax-ruby .global-variable, .syntax-ruby .instance-variable { color: #336699; }
40
+ .syntax-ruby .predefined { color: #3377BB; font-weight: bold; }
41
+ .syntax-ruby .error { color: #FF0000; background-color: #FFAAAA; }
42
+ .syntax-ruby .delimiter { color: #555555; }
43
+ .syntax-ruby .method { color: #990000; font-weight: bold; }
44
+ .syntax-ruby .attribute-name { color: #994500; }
45
+ .syntax-ruby .value { color: #0066CC; }
@@ -0,0 +1,27 @@
1
+ <div role="alert" class="<%= container_classes %>" <%= @data&.map { |k, v| "data-#{k}=\"#{v}\"" }&.join(' ')&.html_safe %>>
2
+ <% if effective_icon.present? %>
3
+ <div class="<%= icon_classes %>">
4
+ <%= render BetterUi::General::IconComponent.new(name: effective_icon) %>
5
+ </div>
6
+ <% end %>
7
+
8
+ <div class="<%= content_classes %>">
9
+ <% if @title.present? %>
10
+ <div class="<%= title_classes %>"><%= @title %></div>
11
+ <% end %>
12
+
13
+ <% if @message.present? %>
14
+ <div class="<%= message_classes %>"><%= @message %></div>
15
+ <% end %>
16
+
17
+ <% if content.present? %>
18
+ <div class="<%= message_classes %>"><%= content %></div>
19
+ <% end %>
20
+ </div>
21
+
22
+ <% if @dismissible %>
23
+ <button type="button" class="<%= close_button_classes %>" data-dismiss-target="[role='alert']" aria-label="Chiudi">
24
+ <%= render BetterUi::General::IconComponent.new(name: "xmark") %>
25
+ </button>
26
+ <% end %>
27
+ </div>
@@ -0,0 +1,202 @@
1
+ module BetterUi
2
+ module Application
3
+ class AlertComponent < ViewComponent::Base
4
+ attr_reader :title, :message, :variant, :icon, :dismissible, :classes, :data, :icon_position, :outline, :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 le icone
96
+ ICON_POSITIONS = [:left, :top]
97
+
98
+ # Inizializzazione del componente
99
+ def initialize(
100
+ title: nil,
101
+ message: nil,
102
+ variant: :default,
103
+ icon: nil,
104
+ dismissible: false,
105
+ classes: nil,
106
+ data: {},
107
+ icon_position: :left,
108
+ outline: false,
109
+ rounded: :sm
110
+ )
111
+ @title = title
112
+ @message = message
113
+ @variant = variant.to_sym
114
+ @icon = icon
115
+ @dismissible = dismissible
116
+ @classes = classes
117
+ @data = data
118
+ @icon_position = icon_position.to_sym
119
+ @outline = outline
120
+ @rounded = rounded.to_sym
121
+ end
122
+
123
+ # Genera l'icona in base alla variante se non specificata
124
+ def effective_icon
125
+ return @icon if @icon.present?
126
+ DEFAULT_ICONS[@variant] || DEFAULT_ICONS[:default]
127
+ end
128
+
129
+ # Genera le classi per il container
130
+ def container_classes
131
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
132
+
133
+ base_classes = ["p-4 mb-4 flex border"]
134
+ base_classes << get_border_radius_class
135
+
136
+ [
137
+ *base_classes,
138
+ styles[:bg],
139
+ styles[:border],
140
+ @icon_position == :top ? "flex-col" : "items-start",
141
+ @classes
142
+ ].compact.join(" ")
143
+ end
144
+
145
+ # Genera il border-radius
146
+ def get_border_radius_class
147
+ ThemeHelper::BORDER_RADIUS[@rounded] || ThemeHelper::BORDER_RADIUS[:sm]
148
+ end
149
+
150
+ # Genera le classi per il titolo
151
+ def title_classes
152
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
153
+
154
+ [
155
+ "font-medium",
156
+ styles[:title]
157
+ ].compact.join(" ")
158
+ end
159
+
160
+ # Genera le classi per il messaggio
161
+ def message_classes
162
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
163
+
164
+ [
165
+ "mt-1",
166
+ styles[:text]
167
+ ].compact.join(" ")
168
+ end
169
+
170
+ # Genera le classi per l'icona
171
+ def icon_classes
172
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
173
+
174
+ [
175
+ "flex-shrink-0",
176
+ @icon_position == :left ? "mr-3 mt-0.5" : "mb-3",
177
+ styles[:icon]
178
+ ].compact.join(" ")
179
+ end
180
+
181
+ # Genera le classi per il pulsante di chiusura
182
+ def close_button_classes
183
+ styles = VARIANTS.fetch(@variant, VARIANTS[:default])
184
+
185
+ [
186
+ "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",
187
+ styles[:close]
188
+ ].compact.join(" ")
189
+ end
190
+
191
+ # Genera le classi per il contenuto
192
+ def content_classes
193
+ "flex-1"
194
+ end
195
+
196
+ # Verifica se il componente deve essere reso
197
+ def render?
198
+ @title.present? || @message.present? || content.present?
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,24 @@
1
+ <div class="bui-card-base <%= get_border_radius_class %> px-4 py-5 sm:p-6">
2
+ <dt class="bui-card-title text-gray-900"><%= title %></dt>
3
+ <dd class="bui-card-body mt-1 flex items-baseline justify-between md:block lg:flex">
4
+ <div class="flex items-baseline">
5
+ <span class="bui-card-value text-indigo-600"><%= value %></span>
6
+ <span class="bui-card-value-from ml-2">from <%= value_from %></span>
7
+ </div>
8
+
9
+ <div class="bui-card-trend <%= trend_classes %>">
10
+ <% if trend_type == :up %>
11
+ <svg class="mr-0.5 -ml-1 size-5 shrink-0 self-center <%= trend_icon_classes %>" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
12
+ <path fill-rule="evenodd" d="M10 17a.75.75 0 0 1-.75-.75V5.612L5.29 9.77a.75.75 0 0 1-1.08-1.04l5.25-5.5a.75.75 0 0 1 1.08 0l5.25 5.5a.75.75 0 1 1-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0 1 10 17Z" clip-rule="evenodd" />
13
+ </svg>
14
+ <span class="sr-only">Incremento di</span>
15
+ <% else %>
16
+ <svg class="mr-0.5 -ml-1 size-5 shrink-0 self-center <%= trend_icon_classes %>" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
17
+ <path fill-rule="evenodd" d="M10 3a.75.75 0 0 1 .75.75v10.638l3.96-4.158a.75.75 0 1 1 1.08 1.04l-5.25 5.5a.75.75 0 0 1-1.08 0l-5.25-5.5a.75.75 0 1 1 1.08-1.04l3.96 4.158V3.75A.75.75 0 0 1 10 3Z" clip-rule="evenodd" />
18
+ </svg>
19
+ <span class="sr-only">Decremento di</span>
20
+ <% end %>
21
+ <%= trend_value %>
22
+ </div>
23
+ </dd>
24
+ </div>
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module Application
5
+ class CardComponent < ViewComponent::Base
6
+ attr_reader :title, :value, :value_from, :trend_value, :trend_type, :rounded
7
+
8
+ # Opzioni di bordi arrotondati standardizzati
9
+ CARD_RADIUS = {
10
+ none: 'bui-card-radius-none',
11
+ small: 'bui-card-radius-small',
12
+ medium: 'bui-card-radius-medium',
13
+ large: 'bui-card-radius-large',
14
+ full: 'bui-card-radius-full'
15
+ }.freeze
16
+
17
+ # @param title [String] Titolo della card
18
+ # @param value [String] Valore principale da visualizzare
19
+ # @param value_from [String] Valore precedente di riferimento
20
+ # @param trend_value [String] Percentuale di variazione
21
+ # @param trend_type [Symbol] Tipo di variazione (:up o :down)
22
+ # @param rounded [Symbol] Tipo di border-radius (:none, :small, :medium, :large, :full), default :small
23
+ def initialize(title:, value:, value_from:, trend_value:, trend_type: :up, rounded: :small)
24
+ @title = title
25
+ @value = value
26
+ @value_from = value_from
27
+ @trend_value = trend_value
28
+ @trend_type = trend_type.to_sym
29
+ @rounded = rounded.to_sym
30
+ end
31
+
32
+ def trend_classes
33
+ if trend_type == :up
34
+ 'bg-green-500 text-white'
35
+ else
36
+ 'bg-black text-white'
37
+ end
38
+ end
39
+
40
+ def trend_icon_classes
41
+ if trend_type == :up
42
+ 'text-white'
43
+ else
44
+ 'text-white'
45
+ end
46
+ end
47
+
48
+ def get_border_radius_class
49
+ CARD_RADIUS[@rounded] || CARD_RADIUS[:small]
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,8 @@
1
+ <div>
2
+ <h3 class="text-base font-semibold text-gray-900"><%= @title %></h3>
3
+ <dl class="mt-5 grid grid-cols-1 divide-y divide-gray-200 overflow-hidden rounded-lg bg-white shadow-sm md:grid-cols-3 md:divide-x md:divide-y-0">
4
+ <% cards.each do |card| %>
5
+ <%= card %>
6
+ <% end %>
7
+ </dl>
8
+ </div>
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module Application
5
+ class CardContainerComponent < ViewComponent::Base
6
+ renders_many :cards, CardComponent
7
+
8
+ # @param title [String] Titolo del container delle card
9
+ def initialize(title:)
10
+ @title = title
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,88 @@
1
+ <header class="<%= header_classes %>" <%= @data&.map { |k, v| "data-#{k}=\"#{v}\"" }&.join(' ')&.html_safe %>>
2
+ <div class="<%= container_class %>">
3
+ <div class="md:flex md:justify-between md:items-start">
4
+ <div class="min-w-0 flex-1">
5
+ <% if has_breadcrumbs? %>
6
+ <nav class="<%= breadcrumb_container_classes %>" aria-label="Breadcrumb">
7
+ <ol class="flex items-center">
8
+ <% @breadcrumbs.each_with_index do |breadcrumb, index| %>
9
+ <li class="flex items-center">
10
+ <% if index > 0 %>
11
+ <span class="<%= breadcrumb_divider_classes %>" aria-hidden="true">
12
+ <% if variant == :modern %>
13
+ <svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
14
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
15
+ </svg>
16
+ <% else %>
17
+ /
18
+ <% end %>
19
+ </span>
20
+ <% end %>
21
+ <% is_last = index == @breadcrumbs.length - 1 %>
22
+ <% if is_last || !breadcrumb[:url].present? %>
23
+ <span class="<%= breadcrumb_link_classes(is_last) %>" <%= is_last ? 'aria-current="page"' : '' %>>
24
+ <%= breadcrumb[:text] %>
25
+ </span>
26
+ <% else %>
27
+ <a href="<%= breadcrumb[:url] %>" class="<%= breadcrumb_link_classes %>">
28
+ <%= breadcrumb[:text] %>
29
+ </a>
30
+ <% end %>
31
+ </li>
32
+ <% end %>
33
+ </ol>
34
+ </nav>
35
+ <% end %>
36
+
37
+ <div class="flex items-center">
38
+ <% if @title.is_a?(Hash) && @title[:icon].present? %>
39
+ <div class="mr-3 bg-gray-100 rounded-md p-2 flex-shrink-0">
40
+ <% if @title[:icon] == "document" %>
41
+ <svg class="h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
42
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
43
+ </svg>
44
+ <% elsif @title[:icon] == "users" %>
45
+ <svg class="h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
46
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" />
47
+ </svg>
48
+ <% elsif @title[:icon] == "settings" || @title[:icon] == "gear" %>
49
+ <svg class="h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
50
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
51
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
52
+ </svg>
53
+ <% elsif @title[:icon] == "dashboard" %>
54
+ <svg class="h-5 w-5 text-gray-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
55
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z" />
56
+ </svg>
57
+ <% else %>
58
+ <%= render BetterUi::General::IconComponent.new(name: @title[:icon], classes: "h-5 w-5 text-gray-500") %>
59
+ <% end %>
60
+ </div>
61
+ <h1 class="<%= title_classes %>"><%= @title[:text] %></h1>
62
+ <% else %>
63
+ <h1 class="<%= title_classes %>"><%= @title %></h1>
64
+ <% end %>
65
+ </div>
66
+ <% if @subtitle.present? %>
67
+ <p class="<%= subtitle_classes %>">
68
+ <%= @subtitle %>
69
+ </p>
70
+ <% end %>
71
+ </div>
72
+
73
+ <% if action_items.any? %>
74
+ <div class="<%= actions_container_classes %>">
75
+ <% action_items.each do |action| %>
76
+ <div class="ml-2">
77
+ <% if action.is_a?(Hash) && action[:content].present? %>
78
+ <%= action[:content].html_safe %>
79
+ <% elsif action.is_a?(String) %>
80
+ <%= action.html_safe %>
81
+ <% end %>
82
+ </div>
83
+ <% end %>
84
+ </div>
85
+ <% end %>
86
+ </div>
87
+ </div>
88
+ </header>