better_ui 0.1.0 → 0.1.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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +225 -119
  4. data/app/assets/stylesheets/better_ui/application.css +0 -356
  5. data/app/components/better_ui/application/card/component.html.erb +20 -0
  6. data/app/components/better_ui/application/card/component.rb +214 -0
  7. data/app/components/better_ui/application/main/component.html.erb +9 -0
  8. data/app/components/better_ui/application/main/component.rb +123 -0
  9. data/app/components/better_ui/application/navbar/component.html.erb +92 -0
  10. data/app/components/better_ui/application/navbar/component.rb +136 -0
  11. data/app/components/better_ui/application/sidebar/component.html.erb +190 -0
  12. data/app/components/better_ui/application/sidebar/component.rb +129 -0
  13. data/app/components/better_ui/general/alert/component.html.erb +32 -0
  14. data/app/components/better_ui/general/alert/component.rb +242 -0
  15. data/app/components/better_ui/general/avatar/component.html.erb +20 -0
  16. data/app/components/better_ui/general/avatar/component.rb +301 -0
  17. data/app/components/better_ui/general/badge/component.html.erb +23 -0
  18. data/app/components/better_ui/general/badge/component.rb +248 -0
  19. data/app/components/better_ui/general/breadcrumb/component.html.erb +15 -0
  20. data/app/components/better_ui/general/breadcrumb/component.rb +187 -0
  21. data/app/components/better_ui/general/button/component.html.erb +34 -0
  22. data/app/components/better_ui/general/button/component.rb +214 -0
  23. data/app/components/better_ui/general/divider/component.html.erb +10 -0
  24. data/app/components/better_ui/general/divider/component.rb +226 -0
  25. data/app/components/better_ui/general/field/component.html.erb +27 -0
  26. data/app/components/better_ui/general/field/component.rb +37 -0
  27. data/app/components/better_ui/general/heading/component.html.erb +22 -0
  28. data/app/components/better_ui/general/heading/component.rb +257 -0
  29. data/app/components/better_ui/general/icon/component.html.erb +7 -0
  30. data/app/components/better_ui/general/icon/component.rb +239 -0
  31. data/app/components/better_ui/general/input/checkbox/component.html.erb +5 -0
  32. data/app/components/better_ui/general/input/checkbox/component.rb +238 -0
  33. data/app/components/better_ui/general/input/datetime/component.html.erb +5 -0
  34. data/app/components/better_ui/general/input/datetime/component.rb +223 -0
  35. data/app/components/better_ui/general/input/radio/component.html.erb +5 -0
  36. data/app/components/better_ui/general/input/radio/component.rb +230 -0
  37. data/app/components/better_ui/general/input/select/component.html.erb +16 -0
  38. data/app/components/better_ui/general/input/select/component.rb +184 -0
  39. data/app/components/better_ui/general/input/select/select_component.html.erb +5 -0
  40. data/app/components/better_ui/general/input/select/select_component.rb +37 -0
  41. data/app/components/better_ui/general/input/text/component.html.erb +5 -0
  42. data/app/components/better_ui/general/input/text/component.rb +171 -0
  43. data/app/components/better_ui/general/input/textarea/component.html.erb +5 -0
  44. data/app/components/better_ui/general/input/textarea/component.rb +166 -0
  45. data/app/components/better_ui/general/link/component.html.erb +18 -0
  46. data/app/components/better_ui/general/link/component.rb +258 -0
  47. data/app/components/better_ui/general/panel/component.html.erb +28 -0
  48. data/app/components/better_ui/general/panel/component.rb +249 -0
  49. data/app/components/better_ui/general/progress/component.html.erb +11 -0
  50. data/app/components/better_ui/general/progress/component.rb +160 -0
  51. data/app/components/better_ui/general/spinner/component.html.erb +35 -0
  52. data/app/components/better_ui/general/spinner/component.rb +93 -0
  53. data/app/components/better_ui/general/table/component.html.erb +5 -0
  54. data/app/components/better_ui/general/table/component.rb +217 -0
  55. data/app/components/better_ui/general/table/tbody_component.html.erb +3 -0
  56. data/app/components/better_ui/general/table/tbody_component.rb +30 -0
  57. data/app/components/better_ui/general/table/td_component.html.erb +3 -0
  58. data/app/components/better_ui/general/table/td_component.rb +44 -0
  59. data/app/components/better_ui/general/table/tfoot_component.html.erb +3 -0
  60. data/app/components/better_ui/general/table/tfoot_component.rb +28 -0
  61. data/app/components/better_ui/general/table/th_component.html.erb +6 -0
  62. data/app/components/better_ui/general/table/th_component.rb +51 -0
  63. data/app/components/better_ui/general/table/thead_component.html.erb +3 -0
  64. data/app/components/better_ui/general/table/thead_component.rb +28 -0
  65. data/app/components/better_ui/general/table/tr_component.html.erb +3 -0
  66. data/app/components/better_ui/general/table/tr_component.rb +30 -0
  67. data/app/components/better_ui/general/tag/component.html.erb +3 -0
  68. data/app/components/better_ui/general/tag/component.rb +104 -0
  69. data/app/components/better_ui/general/tooltip/component.html.erb +7 -0
  70. data/app/components/better_ui/general/tooltip/component.rb +239 -0
  71. data/app/helpers/better_ui/application/components/card/card_helper.rb +96 -0
  72. data/app/helpers/better_ui/application/components/card.rb +11 -0
  73. data/app/helpers/better_ui/application/components/main/main_helper.rb +64 -0
  74. data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +77 -0
  75. data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +51 -0
  76. data/app/helpers/better_ui/application_helper.rb +42 -179
  77. data/app/helpers/better_ui/general/components/alert/alert_helper.rb +57 -0
  78. data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +29 -0
  79. data/app/helpers/better_ui/general/components/badge/badge_helper.rb +53 -0
  80. data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +37 -0
  81. data/app/helpers/better_ui/general/components/button/button_helper.rb +65 -0
  82. data/app/helpers/better_ui/general/components/container/container_helper.rb +60 -0
  83. data/app/helpers/better_ui/general/components/divider/divider_helper.rb +63 -0
  84. data/app/helpers/better_ui/general/components/field/field_helper.rb +26 -0
  85. data/app/helpers/better_ui/general/components/heading/heading_helper.rb +72 -0
  86. data/app/helpers/better_ui/general/components/icon/icon_helper.rb +16 -0
  87. data/app/helpers/better_ui/general/components/input/checkbox/checkbox_helper.rb +81 -0
  88. data/app/helpers/better_ui/general/components/input/datetime/datetime_helper.rb +91 -0
  89. data/app/helpers/better_ui/general/components/input/radio/radio_helper.rb +79 -0
  90. data/app/helpers/better_ui/general/components/input/radio_group/radio_group_helper.rb +124 -0
  91. data/app/helpers/better_ui/general/components/input/select/select_helper.rb +70 -0
  92. data/app/helpers/better_ui/general/components/input/text/text_helper.rb +138 -0
  93. data/app/helpers/better_ui/general/components/input/textarea/textarea_helper.rb +73 -0
  94. data/app/helpers/better_ui/general/components/link/link_helper.rb +89 -0
  95. data/app/helpers/better_ui/general/components/panel/panel_helper.rb +83 -0
  96. data/app/helpers/better_ui/general/components/progress/progress_helper.rb +53 -0
  97. data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +19 -0
  98. data/app/helpers/better_ui/general/components/table/table_helper.rb +53 -0
  99. data/app/helpers/better_ui/general/components/table/tbody_helper.rb +13 -0
  100. data/app/helpers/better_ui/general/components/table/td_helper.rb +19 -0
  101. data/app/helpers/better_ui/general/components/table/tfoot_helper.rb +13 -0
  102. data/app/helpers/better_ui/general/components/table/th_helper.rb +19 -0
  103. data/app/helpers/better_ui/general/components/table/thead_helper.rb +13 -0
  104. data/app/helpers/better_ui/general/components/table/tr_helper.rb +13 -0
  105. data/app/helpers/better_ui/general/components/tag/tag_helper.rb +26 -0
  106. data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +60 -0
  107. data/app/views/layouts/better_ui/application.html.erb +6 -124
  108. data/config/initializers/lookbook.rb +23 -0
  109. data/config/routes.rb +0 -8
  110. data/lib/better_ui/engine.rb +5 -19
  111. data/lib/better_ui/railtie.rb +20 -0
  112. data/lib/better_ui/version.rb +1 -1
  113. data/lib/better_ui.rb +4 -20
  114. metadata +131 -28
  115. data/app/controllers/better_ui/docs_controller.rb +0 -41
  116. data/app/views/better_ui/docs/component.html.erb +0 -365
  117. data/app/views/better_ui/docs/index.html.erb +0 -100
  118. data/app/views/better_ui/docs/show.html.erb +0 -60
@@ -0,0 +1,10 @@
1
+ <%# Template per il divider %>
2
+ <% if show_label? %>
3
+ <%= tag.div(**divider_attributes) do %>
4
+ <div class="<%= label_line_classes %>"></div>
5
+ <span class="<%= label_classes %>"><%= @label %></span>
6
+ <div class="<%= label_line_classes %>"></div>
7
+ <% end %>
8
+ <% else %>
9
+ <%= tag.div(**divider_attributes) %>
10
+ <% end %>
@@ -0,0 +1,226 @@
1
+ module BetterUi
2
+ module General
3
+ module Divider
4
+ class Component < ViewComponent::Base
5
+ attr_reader :theme, :orientation, :style, :size, :label, :height, :classes
6
+
7
+ # Classi base sempre presenti
8
+ DIVIDER_BASE_CLASSES = "my-4"
9
+
10
+ # Temi con classi Tailwind dirette - LOGICA CORRETTA
11
+ DIVIDER_THEME_CLASSES = {
12
+ default: "border-white", # Bordo bianco (per sfondi scuri)
13
+ white: "border-gray-300", # Bordo grigio (per sfondi chiari)
14
+ red: "border-red-500",
15
+ rose: "border-rose-500",
16
+ orange: "border-orange-500",
17
+ green: "border-green-500",
18
+ blue: "border-blue-500",
19
+ yellow: "border-yellow-500",
20
+ violet: "border-violet-500"
21
+ }
22
+
23
+ # Orientamento con classi Tailwind dirette
24
+ DIVIDER_ORIENTATION_CLASSES = {
25
+ horizontal: "w-full border-t",
26
+ vertical: "h-full border-l"
27
+ }
28
+
29
+ # Stili di linea con classi Tailwind dirette
30
+ DIVIDER_STYLE_CLASSES = {
31
+ solid: "border-solid",
32
+ dashed: "border-dashed",
33
+ dotted: "border-dotted",
34
+ double: "border-double"
35
+ }
36
+
37
+ # Dimensioni con classi Tailwind dirette
38
+ DIVIDER_SIZE_CLASSES = {
39
+ thin: {
40
+ horizontal: "border-t",
41
+ vertical: "border-l"
42
+ },
43
+ medium: {
44
+ horizontal: "border-t-2",
45
+ vertical: "border-l-2"
46
+ },
47
+ thick: {
48
+ horizontal: "border-t-4",
49
+ vertical: "border-l-4"
50
+ }
51
+ }
52
+
53
+ # Classi per label con classi Tailwind dirette - LOGICA CORRETTA
54
+ DIVIDER_LABEL_THEME_CLASSES = {
55
+ default: "text-white bg-transparent px-3", # Testo bianco trasparente (per sfondi scuri)
56
+ white: "text-gray-900 bg-white px-3", # Testo nero su bianco (per sfondi chiari)
57
+ red: "text-red-500 bg-white px-3",
58
+ rose: "text-rose-500 bg-white px-3",
59
+ orange: "text-orange-500 bg-white px-3",
60
+ green: "text-green-500 bg-white px-3",
61
+ blue: "text-blue-500 bg-white px-3",
62
+ yellow: "text-yellow-600 bg-white px-3",
63
+ violet: "text-violet-500 bg-white px-3"
64
+ }
65
+
66
+ # @param theme [Symbol] tema del divider (:default, :white, etc.)
67
+ # @param orientation [Symbol] orientamento del divider (:horizontal, :vertical)
68
+ # @param style [Symbol] stile della linea (:solid, :dashed, :dotted, :double)
69
+ # @param size [Symbol] dimensione della linea (:thin, :medium, :thick)
70
+ # @param label [String] testo opzionale da mostrare al centro del divider
71
+ # @param height [String] altezza per divider verticale (es. "100px", "100%")
72
+ # @param classes [String] classi CSS aggiuntive
73
+ # @param html_options [Hash] opzioni HTML per il container
74
+ def initialize(
75
+ theme: :white,
76
+ orientation: :horizontal,
77
+ style: :solid,
78
+ size: :medium,
79
+ label: nil,
80
+ height: nil,
81
+ classes: nil,
82
+ **html_options
83
+ )
84
+ @theme = theme.to_sym
85
+ @orientation = orientation.to_sym
86
+ @style = style.to_sym
87
+ @size = size.to_sym
88
+ @label = label
89
+ @height = height
90
+ @classes = classes
91
+ @html_options = html_options
92
+
93
+ validate_params
94
+ end
95
+
96
+ # Combina tutte le classi per il container
97
+ def combined_classes
98
+ base_classes = []
99
+
100
+ if @label.present? && @orientation == :horizontal
101
+ # Per divider con label orizzontale: flex layout
102
+ base_classes = [
103
+ "flex items-center text-center",
104
+ get_theme_class,
105
+ get_style_class
106
+ ]
107
+ else
108
+ # Per divider normale
109
+ base_classes = [
110
+ DIVIDER_BASE_CLASSES,
111
+ get_orientation_class,
112
+ get_theme_class,
113
+ get_style_class,
114
+ get_size_class
115
+ ]
116
+ end
117
+
118
+ [ *base_classes, @classes, @html_options[:class] ].compact.join(" ")
119
+ end
120
+
121
+ # Classi per il label
122
+ def label_classes
123
+ return "" unless @label.present?
124
+
125
+ [
126
+ "relative z-10 text-sm font-medium",
127
+ get_label_theme_class
128
+ ].compact.join(" ")
129
+ end
130
+
131
+ # Classi per le linee before/after quando c'è un label
132
+ def label_line_classes
133
+ return "" unless @label.present? && @orientation == :horizontal
134
+
135
+ [
136
+ "flex-1 h-px",
137
+ get_theme_class,
138
+ get_style_class
139
+ ].compact.join(" ")
140
+ end
141
+
142
+ # Genera gli attributi di stile inline necessari
143
+ def inline_styles
144
+ return nil unless @orientation == :vertical && @height.present?
145
+ "height: #{@height};"
146
+ end
147
+
148
+ # Restituisce gli attributi per il divider
149
+ def divider_attributes
150
+ attrs = {
151
+ class: combined_classes
152
+ }
153
+
154
+ # Aggiungi stile inline se presente
155
+ attrs[:style] = inline_styles if inline_styles.present?
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
+ # Determina se mostrare il label
166
+ def show_label?
167
+ @label.present? && @orientation == :horizontal
168
+ end
169
+
170
+ private
171
+
172
+ def get_theme_class
173
+ DIVIDER_THEME_CLASSES[@theme] || DIVIDER_THEME_CLASSES[:white]
174
+ end
175
+
176
+ def get_orientation_class
177
+ DIVIDER_ORIENTATION_CLASSES[@orientation] || DIVIDER_ORIENTATION_CLASSES[:horizontal]
178
+ end
179
+
180
+ def get_style_class
181
+ DIVIDER_STYLE_CLASSES[@style] || DIVIDER_STYLE_CLASSES[:solid]
182
+ end
183
+
184
+ def get_size_class
185
+ size_map = DIVIDER_SIZE_CLASSES[@size] || DIVIDER_SIZE_CLASSES[:medium]
186
+ size_map[@orientation] || size_map[:horizontal]
187
+ end
188
+
189
+ def get_label_theme_class
190
+ DIVIDER_LABEL_THEME_CLASSES[@theme] || DIVIDER_LABEL_THEME_CLASSES[:white]
191
+ end
192
+
193
+ def validate_params
194
+ validate_theme
195
+ validate_orientation
196
+ validate_style
197
+ validate_size
198
+ end
199
+
200
+ def validate_theme
201
+ unless DIVIDER_THEME_CLASSES.keys.include?(@theme)
202
+ raise ArgumentError, "Il tema deve essere uno tra: #{DIVIDER_THEME_CLASSES.keys.join(', ')}"
203
+ end
204
+ end
205
+
206
+ def validate_orientation
207
+ unless DIVIDER_ORIENTATION_CLASSES.keys.include?(@orientation)
208
+ raise ArgumentError, "L'orientamento deve essere uno tra: #{DIVIDER_ORIENTATION_CLASSES.keys.join(', ')}"
209
+ end
210
+ end
211
+
212
+ def validate_style
213
+ unless DIVIDER_STYLE_CLASSES.keys.include?(@style)
214
+ raise ArgumentError, "Lo stile deve essere uno tra: #{DIVIDER_STYLE_CLASSES.keys.join(', ')}"
215
+ end
216
+ end
217
+
218
+ def validate_size
219
+ unless DIVIDER_SIZE_CLASSES.keys.include?(@size)
220
+ raise ArgumentError, "La dimensione deve essere una tra: #{DIVIDER_SIZE_CLASSES.keys.join(', ')}"
221
+ end
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,27 @@
1
+ <%# Form field component template %>
2
+ <div class="<%= BASE_CLASSES %>">
3
+ <% if label.present? %>
4
+ <label for="<%= id %>" class="<%= LABEL_CLASSES %>">
5
+ <%= label %>
6
+ <% if required %>
7
+ <span class="<%= REQUIRED_CLASSES %>">*</span>
8
+ <% end %>
9
+ </label>
10
+ <% end %>
11
+
12
+ <div class="mt-1">
13
+ <%= content %>
14
+ </div>
15
+
16
+ <% if error.present? %>
17
+ <div class="<%= ERROR_CLASSES %>">
18
+ <%= error %>
19
+ </div>
20
+ <% end %>
21
+
22
+ <% if help_text.present? %>
23
+ <div class="<%= HELP_TEXT_CLASSES %>">
24
+ <%= help_text %>
25
+ </div>
26
+ <% end %>
27
+ </div>
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Field
6
+ class Component < ViewComponent::Base
7
+ attr_reader :label, :name, :required, :error, :help_text, :id
8
+
9
+ renders_one :input
10
+
11
+ BASE_CLASSES = "flex flex-col space-y-2"
12
+ LABEL_CLASSES = "text-sm font-medium text-gray-700"
13
+ REQUIRED_CLASSES = "text-red-500 ml-1"
14
+ ERROR_CLASSES = "text-sm text-red-600 mt-1"
15
+ HELP_TEXT_CLASSES = "text-sm text-gray-500 mt-1"
16
+
17
+ def initialize(label:, name:, required: false, error: nil, help_text: nil, id: nil)
18
+ @label = label
19
+ @name = name
20
+ @required = required
21
+ @error = error
22
+ @help_text = help_text
23
+ @id = id
24
+ super()
25
+ end
26
+
27
+ private
28
+
29
+ def field_classes
30
+ classes = [BASE_CLASSES]
31
+ classes << ERROR_CLASSES if @error.present?
32
+ classes.join(" ")
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ <%# Template per il heading %>
2
+ <div class="<%= container_classes %>">
3
+ <%= tag.public_send(heading_tag, **heading_attributes) do %>
4
+ <% if show_icon? %>
5
+ <span class="<%= icon_classes %>">
6
+ <%= render BetterUi::General::IconComponent.new(name: @icon) %>
7
+ </span>
8
+ <% end %>
9
+
10
+ <span><%= content %></span>
11
+ <% end %>
12
+
13
+ <% if show_subtitle? %>
14
+ <div class="<%= subtitle_classes %>">
15
+ <%= @subtitle %>
16
+ </div>
17
+ <% end %>
18
+
19
+ <% if show_divider? %>
20
+ <div class="<%= divider_classes %>"></div>
21
+ <% end %>
22
+ </div>
@@ -0,0 +1,257 @@
1
+ module BetterUi
2
+ module General
3
+ module Heading
4
+ class Component < ViewComponent::Base
5
+ attr_reader :level, :theme, :align, :size, :style, :icon, :subtitle, :with_divider
6
+
7
+ # Classi base sempre presenti
8
+ HEADING_BASE_CLASSES = "font-bold leading-tight"
9
+
10
+ # Temi con classi Tailwind dirette - LOGICA CORRETTA
11
+ HEADING_THEME_CLASSES = {
12
+ default: "text-white", # Testo bianco (per sfondi scuri)
13
+ white: "text-gray-900", # Testo nero (per sfondi chiari)
14
+ red: "text-red-500",
15
+ rose: "text-rose-500",
16
+ orange: "text-orange-500",
17
+ green: "text-green-500",
18
+ blue: "text-blue-500",
19
+ yellow: "text-yellow-600",
20
+ violet: "text-violet-500",
21
+ purple: "text-purple-500"
22
+ }
23
+
24
+ # Allineamenti con classi Tailwind dirette
25
+ HEADING_ALIGN_CLASSES = {
26
+ left: "text-left",
27
+ center: "text-center",
28
+ right: "text-right"
29
+ }
30
+
31
+ # Dimensioni base (verranno combinate con level)
32
+ HEADING_SIZE_CLASSES = {
33
+ small: {
34
+ 1 => "text-2xl sm:text-3xl",
35
+ 2 => "text-xl sm:text-2xl",
36
+ 3 => "text-lg sm:text-xl",
37
+ 4 => "text-base sm:text-lg",
38
+ 5 => "text-sm sm:text-base",
39
+ 6 => "text-xs sm:text-sm"
40
+ },
41
+ medium: {
42
+ 1 => "text-3xl sm:text-4xl",
43
+ 2 => "text-2xl sm:text-3xl",
44
+ 3 => "text-xl sm:text-2xl",
45
+ 4 => "text-lg sm:text-xl",
46
+ 5 => "text-base sm:text-lg",
47
+ 6 => "text-sm sm:text-base"
48
+ },
49
+ large: {
50
+ 1 => "text-4xl sm:text-5xl",
51
+ 2 => "text-3xl sm:text-4xl",
52
+ 3 => "text-2xl sm:text-3xl",
53
+ 4 => "text-xl sm:text-2xl",
54
+ 5 => "text-lg sm:text-xl",
55
+ 6 => "text-base sm:text-lg"
56
+ }
57
+ }
58
+
59
+ # Stili con classi Tailwind dirette
60
+ HEADING_STYLE_CLASSES = {
61
+ normal: "",
62
+ bold: "font-extrabold",
63
+ italic: "italic",
64
+ underline: "underline"
65
+ }
66
+
67
+ # Temi per subtitle - LOGICA CORRETTA
68
+ HEADING_SUBTITLE_THEME_CLASSES = {
69
+ default: "text-gray-300", # Testo grigio chiaro (per sfondi scuri)
70
+ white: "text-gray-600", # Testo grigio scuro (per sfondi chiari)
71
+ red: "text-red-400",
72
+ rose: "text-rose-400",
73
+ orange: "text-orange-400",
74
+ green: "text-green-400",
75
+ blue: "text-blue-400",
76
+ yellow: "text-yellow-500",
77
+ violet: "text-violet-400"
78
+ }
79
+
80
+ # Temi per divider - LOGICA CORRETTA
81
+ HEADING_DIVIDER_THEME_CLASSES = {
82
+ default: "border-gray-700", # Bordo grigio scuro (per sfondi scuri)
83
+ white: "border-gray-200", # Bordo grigio chiaro (per sfondi chiari)
84
+ red: "border-red-200",
85
+ rose: "border-rose-200",
86
+ orange: "border-orange-200",
87
+ green: "border-green-200",
88
+ blue: "border-blue-200",
89
+ yellow: "border-yellow-200",
90
+ violet: "border-violet-200"
91
+ }
92
+
93
+ # @param level [Integer] livello del heading (1-6)
94
+ # @param theme [Symbol] tema del colore (:default, :white, etc.)
95
+ # @param align [Symbol] allineamento (:left, :center, :right)
96
+ # @param size [Symbol] dimensione (:small, :medium, :large)
97
+ # @param style [Symbol] stile (:normal, :bold, :italic, :underline)
98
+ # @param icon [String] icona opzionale
99
+ # @param subtitle [String] sottotitolo opzionale
100
+ # @param with_divider [Boolean] mostra linea divisoria
101
+ # @param html_options [Hash] opzioni HTML per il container
102
+ def initialize(
103
+ level: 2,
104
+ theme: :white,
105
+ align: :left,
106
+ size: :medium,
107
+ style: :normal,
108
+ icon: nil,
109
+ subtitle: nil,
110
+ with_divider: false,
111
+ **html_options
112
+ )
113
+ @level = level.to_i.clamp(1, 6)
114
+ @theme = theme.to_sym
115
+ @align = align.to_sym
116
+ @size = size.to_sym
117
+ @style = style.to_sym
118
+ @icon = icon
119
+ @subtitle = subtitle
120
+ @with_divider = with_divider
121
+ @html_options = html_options
122
+
123
+ validate_params
124
+ end
125
+
126
+ # Combina tutte le classi per il heading
127
+ def heading_classes
128
+ [
129
+ HEADING_BASE_CLASSES,
130
+ get_theme_class,
131
+ get_align_class,
132
+ get_size_class,
133
+ get_style_class,
134
+ @html_options[:class]
135
+ ].compact.join(" ")
136
+ end
137
+
138
+ # Classi per il container principale
139
+ def container_classes
140
+ "mb-4"
141
+ end
142
+
143
+ # Classi per il subtitle
144
+ def subtitle_classes
145
+ return "" unless @subtitle.present?
146
+
147
+ [
148
+ "mt-1 text-sm",
149
+ get_subtitle_theme_class,
150
+ get_align_class
151
+ ].compact.join(" ")
152
+ end
153
+
154
+ # Classi per il divider
155
+ def divider_classes
156
+ return "" unless @with_divider
157
+
158
+ [
159
+ "mt-2 border-t",
160
+ get_divider_theme_class
161
+ ].compact.join(" ")
162
+ end
163
+
164
+ # Classi per l'icona
165
+ def icon_classes
166
+ return "" unless @icon.present?
167
+ "mr-2 inline-block"
168
+ end
169
+
170
+ # Restituisce gli attributi HTML per il heading
171
+ def heading_attributes
172
+ attrs = @html_options.except(:class)
173
+ attrs[:class] = heading_classes
174
+ attrs
175
+ end
176
+
177
+ # Tag del heading basato sul level
178
+ def heading_tag
179
+ "h#{@level}"
180
+ end
181
+
182
+ # Determina se mostrare l'icona
183
+ def show_icon?
184
+ @icon.present?
185
+ end
186
+
187
+ # Determina se mostrare il subtitle
188
+ def show_subtitle?
189
+ @subtitle.present?
190
+ end
191
+
192
+ # Determina se mostrare il divider
193
+ def show_divider?
194
+ @with_divider
195
+ end
196
+
197
+ private
198
+
199
+ def get_theme_class
200
+ HEADING_THEME_CLASSES[@theme] || HEADING_THEME_CLASSES[:white]
201
+ end
202
+
203
+ def get_align_class
204
+ HEADING_ALIGN_CLASSES[@align] || HEADING_ALIGN_CLASSES[:left]
205
+ end
206
+
207
+ def get_size_class
208
+ size_map = HEADING_SIZE_CLASSES[@size] || HEADING_SIZE_CLASSES[:medium]
209
+ size_map[@level] || size_map[2]
210
+ end
211
+
212
+ def get_style_class
213
+ HEADING_STYLE_CLASSES[@style] || HEADING_STYLE_CLASSES[:normal]
214
+ end
215
+
216
+ def get_subtitle_theme_class
217
+ HEADING_SUBTITLE_THEME_CLASSES[@theme] || HEADING_SUBTITLE_THEME_CLASSES[:white]
218
+ end
219
+
220
+ def get_divider_theme_class
221
+ HEADING_DIVIDER_THEME_CLASSES[@theme] || HEADING_DIVIDER_THEME_CLASSES[:white]
222
+ end
223
+
224
+ def validate_params
225
+ validate_theme
226
+ validate_align
227
+ validate_size
228
+ validate_style
229
+ end
230
+
231
+ def validate_theme
232
+ unless HEADING_THEME_CLASSES.keys.include?(@theme)
233
+ raise ArgumentError, "Il tema deve essere uno tra: #{HEADING_THEME_CLASSES.keys.join(', ')}"
234
+ end
235
+ end
236
+
237
+ def validate_align
238
+ unless HEADING_ALIGN_CLASSES.keys.include?(@align)
239
+ raise ArgumentError, "L'allineamento deve essere uno tra: #{HEADING_ALIGN_CLASSES.keys.join(', ')}"
240
+ end
241
+ end
242
+
243
+ def validate_size
244
+ unless HEADING_SIZE_CLASSES.keys.include?(@size)
245
+ raise ArgumentError, "La dimensione deve essere una tra: #{HEADING_SIZE_CLASSES.keys.join(', ')}"
246
+ end
247
+ end
248
+
249
+ def validate_style
250
+ unless HEADING_STYLE_CLASSES.keys.include?(@style)
251
+ raise ArgumentError, "Lo stile deve essere uno tra: #{HEADING_STYLE_CLASSES.keys.join(', ')}"
252
+ end
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,7 @@
1
+ <% if href.present? %>
2
+ <%= tag.a(**icon_attributes) do %>
3
+ <i class="<%= fa_class_name %> <%= icon_classes %>"></i>
4
+ <% end %>
5
+ <% else %>
6
+ <i class="<%= fa_class_name %> <%= icon_classes %>"></i>
7
+ <% end %>