better_ui 0.3.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/better_ui/application/main/component.html.erb +1 -1
  3. data/app/components/better_ui/application/sidebar/component.html.erb +25 -3
  4. data/app/components/better_ui/application/sidebar/component.rb +62 -5
  5. data/app/components/better_ui/general/button/component.html.erb +8 -8
  6. data/app/components/better_ui/general/button/component.rb +11 -11
  7. data/app/components/better_ui/general/dropdown/component.html.erb +7 -4
  8. data/app/components/better_ui/general/dropdown/component.rb +23 -1
  9. data/app/components/better_ui/general/field/component.html.erb +3 -3
  10. data/app/components/better_ui/general/field/component.rb +3 -3
  11. data/app/components/better_ui/general/grid/cell_component.html.erb +3 -0
  12. data/app/components/better_ui/general/grid/cell_component.rb +390 -0
  13. data/app/components/better_ui/general/grid/component.html.erb +3 -0
  14. data/app/components/better_ui/general/grid/component.rb +301 -0
  15. data/app/components/better_ui/general/heading/component.html.erb +1 -1
  16. data/app/components/better_ui/general/icon/component.rb +2 -1
  17. data/app/components/better_ui/general/input/checkbox/component.rb +10 -10
  18. data/app/components/better_ui/general/input/pin/component.html.erb +1 -0
  19. data/app/components/better_ui/general/input/pin/component.rb +201 -0
  20. data/app/components/better_ui/general/input/radio/component.rb +10 -10
  21. data/app/components/better_ui/general/input/rating/component.html.erb +4 -0
  22. data/app/components/better_ui/general/input/rating/component.rb +272 -0
  23. data/app/components/better_ui/general/input/select/component.html.erb +76 -14
  24. data/app/components/better_ui/general/input/select/component.rb +166 -101
  25. data/app/components/better_ui/general/input/toggle/component.html.erb +5 -0
  26. data/app/components/better_ui/general/input/toggle/component.rb +242 -0
  27. data/app/components/better_ui/general/link/component.rb +1 -1
  28. data/app/components/better_ui/general/text/component.html.erb +1 -0
  29. data/app/components/better_ui/general/text/component.rb +194 -0
  30. data/app/helpers/better_ui/application_helper.rb +7 -0
  31. data/app/helpers/better_ui/general/components/button/button_helper.rb +6 -6
  32. data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +9 -0
  33. data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +13 -7
  34. data/app/helpers/better_ui/general/components/field/field_helper.rb +4 -4
  35. data/app/helpers/better_ui/general/components/grid/grid_helper.rb +145 -0
  36. data/app/helpers/better_ui/general/components/input/pin/pin_helper.rb +76 -0
  37. data/app/helpers/better_ui/general/components/input/rating/rating_helper.rb +70 -0
  38. data/app/helpers/better_ui/general/components/input/select/select_helper.rb +47 -31
  39. data/app/helpers/better_ui/general/components/input/toggle/toggle_helper.rb +77 -0
  40. data/app/helpers/better_ui/general/components/text/text_helper.rb +83 -0
  41. data/lib/better_ui/version.rb +1 -1
  42. data/lib/better_ui.rb +1 -0
  43. metadata +19 -4
  44. data/app/helpers/better_ui/general/components/accordion.rb +0 -11
  45. data/app/helpers/better_ui/general/components/modal.rb +0 -11
@@ -0,0 +1,390 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Grid
6
+ class CellComponent < ViewComponent::Base
7
+ attr_reader :col, :row, :col_start, :col_end, :row_start, :row_end,
8
+ :justify_self, :align_self, :classes, :id, :html_options
9
+
10
+ # Validazione valori supportati
11
+ VALID_COL_SPANS = (1..12).to_a + [:auto, :full].freeze
12
+ VALID_ROW_SPANS = (1..6).to_a + [:auto, :full].freeze
13
+ VALID_COL_POSITIONS = (1..13).to_a + [:auto].freeze
14
+ VALID_ROW_POSITIONS = (1..7).to_a + [:auto].freeze
15
+ VALID_BREAKPOINTS = [:sm, :md, :lg, :xl].freeze
16
+
17
+ # Justify self options
18
+ GRID_CELL_JUSTIFY_SELF = {
19
+ auto: 'justify-self-auto', start: 'justify-self-start',
20
+ center: 'justify-self-center', end: 'justify-self-end',
21
+ stretch: 'justify-self-stretch'
22
+ }.freeze
23
+
24
+ # Align self options
25
+ GRID_CELL_ALIGN_SELF = {
26
+ auto: 'self-auto', start: 'self-start',
27
+ center: 'self-center', end: 'self-end',
28
+ stretch: 'self-stretch'
29
+ }.freeze
30
+
31
+ def initialize(
32
+ col: 1,
33
+ row: nil,
34
+ col_start: nil,
35
+ col_end: nil,
36
+ row_start: nil,
37
+ row_end: nil,
38
+ justify_self: nil,
39
+ align_self: nil,
40
+ classes: '',
41
+ id: nil,
42
+ **html_options
43
+ )
44
+ @col = normalize_cell_col_with_defaults(col)
45
+ @row = normalize_cell_row_with_defaults(row) if row
46
+ @col_start = normalize_cell_position_with_defaults(col_start) if col_start
47
+ @col_end = normalize_cell_position_with_defaults(col_end) if col_end
48
+ @row_start = normalize_cell_position_with_defaults(row_start) if row_start
49
+ @row_end = normalize_cell_position_with_defaults(row_end) if row_end
50
+ @justify_self = justify_self&.to_sym
51
+ @align_self = align_self&.to_sym
52
+ @classes = classes
53
+ @id = id
54
+ @html_options = html_options
55
+
56
+ validate_cell_params
57
+ end
58
+
59
+ def combined_classes
60
+ [
61
+ generate_col_classes,
62
+ generate_row_classes,
63
+ generate_col_start_classes,
64
+ generate_col_end_classes,
65
+ generate_row_start_classes,
66
+ generate_row_end_classes,
67
+ @justify_self ? GRID_CELL_JUSTIFY_SELF[@justify_self] : nil,
68
+ @align_self ? GRID_CELL_ALIGN_SELF[@align_self] : nil,
69
+ @classes,
70
+ @html_options[:class]
71
+ ].compact.join(" ")
72
+ end
73
+
74
+ def cell_attributes
75
+ attrs = {
76
+ class: combined_classes,
77
+ id: @id
78
+ }
79
+
80
+ @html_options.except(:class).each do |key, value|
81
+ attrs[key] = value
82
+ end
83
+
84
+ attrs
85
+ end
86
+
87
+ private
88
+
89
+ # Genera classi CSS dinamicamente invece di usare costanti massive
90
+ def generate_col_span_class(span, breakpoint = nil)
91
+ prefix = breakpoint ? "#{breakpoint}:" : ""
92
+ "#{prefix}col-span-#{span}"
93
+ end
94
+
95
+ def generate_row_span_class(span, breakpoint = nil)
96
+ prefix = breakpoint ? "#{breakpoint}:" : ""
97
+ "#{prefix}row-span-#{span}"
98
+ end
99
+
100
+ def generate_col_start_class(start, breakpoint = nil)
101
+ prefix = breakpoint ? "#{breakpoint}:" : ""
102
+ "#{prefix}col-start-#{start}"
103
+ end
104
+
105
+ def generate_col_end_class(end_val, breakpoint = nil)
106
+ prefix = breakpoint ? "#{breakpoint}:" : ""
107
+ "#{prefix}col-end-#{end_val}"
108
+ end
109
+
110
+ def generate_row_start_class(start, breakpoint = nil)
111
+ prefix = breakpoint ? "#{breakpoint}:" : ""
112
+ "#{prefix}row-start-#{start}"
113
+ end
114
+
115
+ def generate_row_end_class(end_val, breakpoint = nil)
116
+ prefix = breakpoint ? "#{breakpoint}:" : ""
117
+ "#{prefix}row-end-#{end_val}"
118
+ end
119
+
120
+ # Normalizza col con default intelligenti per tutti i breakpoint
121
+ def normalize_cell_col_with_defaults(col)
122
+ # Se è un valore semplice, lo usiamo per tutti i breakpoint
123
+ return col unless col.is_a?(Hash)
124
+
125
+ # Se è un hash, riempiamo i breakpoint mancanti con fallback intelligente
126
+ normalized = {}
127
+ last_value = 1 # Default base
128
+
129
+ # Base (senza breakpoint)
130
+ normalized[:base] = 1
131
+
132
+ # Processa i breakpoint in ordine
133
+ VALID_BREAKPOINTS.each do |breakpoint|
134
+ if col.key?(breakpoint)
135
+ last_value = col[breakpoint]
136
+ end
137
+ normalized[breakpoint] = last_value
138
+ end
139
+
140
+ normalized
141
+ end
142
+
143
+ # Normalizza row con default intelligenti (solo se specificato)
144
+ def normalize_cell_row_with_defaults(row)
145
+ return row unless row.is_a?(Hash)
146
+
147
+ normalized = {}
148
+ last_value = nil
149
+
150
+ VALID_BREAKPOINTS.each do |breakpoint|
151
+ if row.key?(breakpoint)
152
+ last_value = row[breakpoint]
153
+ end
154
+ normalized[breakpoint] = last_value if last_value
155
+ end
156
+
157
+ normalized
158
+ end
159
+
160
+ # Normalizza posizioni (start/end) con fallback intelligente
161
+ def normalize_cell_position_with_defaults(position)
162
+ return position unless position.is_a?(Hash)
163
+
164
+ normalized = {}
165
+ last_value = nil
166
+
167
+ VALID_BREAKPOINTS.each do |breakpoint|
168
+ if position.key?(breakpoint)
169
+ last_value = position[breakpoint]
170
+ end
171
+ normalized[breakpoint] = last_value if last_value
172
+ end
173
+
174
+ normalized
175
+ end
176
+
177
+ def generate_col_classes
178
+ return nil unless @col
179
+
180
+ if @col.is_a?(Hash)
181
+ classes = []
182
+
183
+ # Base class (senza breakpoint)
184
+ if @col[:base]
185
+ classes << generate_col_span_class(@col[:base])
186
+ end
187
+
188
+ # Responsive classes
189
+ VALID_BREAKPOINTS.each do |breakpoint|
190
+ if @col[breakpoint]
191
+ classes << generate_col_span_class(@col[breakpoint], breakpoint)
192
+ end
193
+ end
194
+
195
+ classes.join(" ")
196
+ else
197
+ generate_col_span_class(@col)
198
+ end
199
+ end
200
+
201
+ def generate_row_classes
202
+ return nil unless @row
203
+
204
+ if @row.is_a?(Hash)
205
+ @row.map do |breakpoint, row_value|
206
+ generate_row_span_class(row_value, breakpoint)
207
+ end.compact.join(" ")
208
+ else
209
+ generate_row_span_class(@row)
210
+ end
211
+ end
212
+
213
+ def generate_col_start_classes
214
+ return nil unless @col_start
215
+
216
+ if @col_start.is_a?(Hash)
217
+ @col_start.map do |breakpoint, start_value|
218
+ generate_col_start_class(start_value, breakpoint)
219
+ end.compact.join(" ")
220
+ else
221
+ generate_col_start_class(@col_start)
222
+ end
223
+ end
224
+
225
+ def generate_col_end_classes
226
+ return nil unless @col_end
227
+
228
+ if @col_end.is_a?(Hash)
229
+ @col_end.map do |breakpoint, end_value|
230
+ generate_col_end_class(end_value, breakpoint)
231
+ end.compact.join(" ")
232
+ else
233
+ generate_col_end_class(@col_end)
234
+ end
235
+ end
236
+
237
+ def generate_row_start_classes
238
+ return nil unless @row_start
239
+
240
+ if @row_start.is_a?(Hash)
241
+ @row_start.map do |breakpoint, start_value|
242
+ generate_row_start_class(start_value, breakpoint)
243
+ end.compact.join(" ")
244
+ else
245
+ generate_row_start_class(@row_start)
246
+ end
247
+ end
248
+
249
+ def generate_row_end_classes
250
+ return nil unless @row_end
251
+
252
+ if @row_end.is_a?(Hash)
253
+ @row_end.map do |breakpoint, end_value|
254
+ generate_row_end_class(end_value, breakpoint)
255
+ end.compact.join(" ")
256
+ else
257
+ generate_row_end_class(@row_end)
258
+ end
259
+ end
260
+
261
+ def validate_cell_params
262
+ validate_cell_col if @col
263
+ validate_cell_row if @row
264
+ validate_cell_col_start if @col_start
265
+ validate_cell_col_end if @col_end
266
+ validate_cell_row_start if @row_start
267
+ validate_cell_row_end if @row_end
268
+ validate_cell_justify_self if @justify_self
269
+ validate_cell_align_self if @align_self
270
+ end
271
+
272
+ def validate_cell_col
273
+ if @col.is_a?(Hash)
274
+ @col.each do |breakpoint, col_value|
275
+ next if breakpoint == :base # Skip validation for base
276
+
277
+ unless VALID_BREAKPOINTS.include?(breakpoint)
278
+ raise ArgumentError, "Breakpoint #{breakpoint} non supportato per col"
279
+ end
280
+ unless VALID_COL_SPANS.include?(col_value)
281
+ raise ArgumentError, "Valore col #{col_value} non supportato per breakpoint #{breakpoint}"
282
+ end
283
+ end
284
+ else
285
+ unless VALID_COL_SPANS.include?(@col)
286
+ raise ArgumentError, "col deve essere uno tra: #{VALID_COL_SPANS.join(', ')}"
287
+ end
288
+ end
289
+ end
290
+
291
+ def validate_cell_row
292
+ if @row.is_a?(Hash)
293
+ @row.each do |breakpoint, row_value|
294
+ unless VALID_BREAKPOINTS.include?(breakpoint)
295
+ raise ArgumentError, "Breakpoint #{breakpoint} non supportato per row"
296
+ end
297
+ unless VALID_ROW_SPANS.include?(row_value)
298
+ raise ArgumentError, "Valore row #{row_value} non supportato per breakpoint #{breakpoint}"
299
+ end
300
+ end
301
+ else
302
+ unless VALID_ROW_SPANS.include?(@row)
303
+ raise ArgumentError, "row deve essere uno tra: #{VALID_ROW_SPANS.join(', ')}"
304
+ end
305
+ end
306
+ end
307
+
308
+ def validate_cell_col_start
309
+ if @col_start.is_a?(Hash)
310
+ @col_start.each do |breakpoint, start_value|
311
+ unless VALID_BREAKPOINTS.include?(breakpoint)
312
+ raise ArgumentError, "Breakpoint #{breakpoint} non supportato per col_start"
313
+ end
314
+ unless VALID_COL_POSITIONS.include?(start_value)
315
+ raise ArgumentError, "Valore col_start #{start_value} non supportato per breakpoint #{breakpoint}"
316
+ end
317
+ end
318
+ else
319
+ unless VALID_COL_POSITIONS.include?(@col_start)
320
+ raise ArgumentError, "col_start deve essere uno tra: #{VALID_COL_POSITIONS.join(', ')}"
321
+ end
322
+ end
323
+ end
324
+
325
+ def validate_cell_col_end
326
+ if @col_end.is_a?(Hash)
327
+ @col_end.each do |breakpoint, end_value|
328
+ unless VALID_BREAKPOINTS.include?(breakpoint)
329
+ raise ArgumentError, "Breakpoint #{breakpoint} non supportato per col_end"
330
+ end
331
+ unless VALID_COL_POSITIONS.include?(end_value)
332
+ raise ArgumentError, "Valore col_end #{end_value} non supportato per breakpoint #{breakpoint}"
333
+ end
334
+ end
335
+ else
336
+ unless VALID_COL_POSITIONS.include?(@col_end)
337
+ raise ArgumentError, "col_end deve essere uno tra: #{VALID_COL_POSITIONS.join(', ')}"
338
+ end
339
+ end
340
+ end
341
+
342
+ def validate_cell_row_start
343
+ if @row_start.is_a?(Hash)
344
+ @row_start.each do |breakpoint, start_value|
345
+ unless VALID_BREAKPOINTS.include?(breakpoint)
346
+ raise ArgumentError, "Breakpoint #{breakpoint} non supportato per row_start"
347
+ end
348
+ unless VALID_ROW_POSITIONS.include?(start_value)
349
+ raise ArgumentError, "Valore row_start #{start_value} non supportato per breakpoint #{breakpoint}"
350
+ end
351
+ end
352
+ else
353
+ unless VALID_ROW_POSITIONS.include?(@row_start)
354
+ raise ArgumentError, "row_start deve essere uno tra: #{VALID_ROW_POSITIONS.join(', ')}"
355
+ end
356
+ end
357
+ end
358
+
359
+ def validate_cell_row_end
360
+ if @row_end.is_a?(Hash)
361
+ @row_end.each do |breakpoint, end_value|
362
+ unless VALID_BREAKPOINTS.include?(breakpoint)
363
+ raise ArgumentError, "Breakpoint #{breakpoint} non supportato per row_end"
364
+ end
365
+ unless VALID_ROW_POSITIONS.include?(end_value)
366
+ raise ArgumentError, "Valore row_end #{end_value} non supportato per breakpoint #{breakpoint}"
367
+ end
368
+ end
369
+ else
370
+ unless VALID_ROW_POSITIONS.include?(@row_end)
371
+ raise ArgumentError, "row_end deve essere uno tra: #{VALID_ROW_POSITIONS.join(', ')}"
372
+ end
373
+ end
374
+ end
375
+
376
+ def validate_cell_justify_self
377
+ unless GRID_CELL_JUSTIFY_SELF.keys.include?(@justify_self)
378
+ raise ArgumentError, "justify_self deve essere uno tra: #{GRID_CELL_JUSTIFY_SELF.keys.join(', ')}"
379
+ end
380
+ end
381
+
382
+ def validate_cell_align_self
383
+ unless GRID_CELL_ALIGN_SELF.keys.include?(@align_self)
384
+ raise ArgumentError, "align_self deve essere uno tra: #{GRID_CELL_ALIGN_SELF.keys.join(', ')}"
385
+ end
386
+ end
387
+ end
388
+ end
389
+ end
390
+ end
@@ -0,0 +1,3 @@
1
+ <div class="<%= combined_classes %>"<% if @id %> id="<%= @id %>"<% end %><% @html_options.except(:class).each do |attr, value| %> <%= attr %>="<%= value %>"<% end %>>
2
+ <%= content %>
3
+ </div>
@@ -0,0 +1,301 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module General
5
+ module Grid
6
+ class Component < ViewComponent::Base
7
+ attr_reader :cols, :rows, :gap, :flow, :align_items, :justify_items, :classes, :id, :html_options
8
+
9
+ # Classi base grid
10
+ GRID_BASE_CLASSES = "grid"
11
+
12
+ # Valori supportati per validazione
13
+ VALID_GRID_COLS = (1..12).to_a + [:auto, :none].freeze
14
+ VALID_GRID_ROWS = (1..6).to_a + [:auto, :none].freeze
15
+ VALID_BREAKPOINTS = [:sm, :md, :lg, :xl].freeze
16
+
17
+ # Gap (spaziatura)
18
+ GRID_GAP = {
19
+ none: 'gap-0', small: 'gap-2', medium: 'gap-4', large: 'gap-6'
20
+ }.freeze
21
+
22
+ # Flow (direzione)
23
+ GRID_FLOW = {
24
+ row: 'grid-flow-row', col: 'grid-flow-col',
25
+ row_dense: 'grid-flow-row-dense', col_dense: 'grid-flow-col-dense'
26
+ }.freeze
27
+
28
+ # Align items
29
+ GRID_ALIGN_ITEMS = {
30
+ start: 'items-start', center: 'items-center',
31
+ end: 'items-end', stretch: 'items-stretch'
32
+ }.freeze
33
+
34
+ # Justify items
35
+ GRID_JUSTIFY_ITEMS = {
36
+ start: 'justify-items-start', center: 'justify-items-center',
37
+ end: 'justify-items-end', stretch: 'justify-items-stretch'
38
+ }.freeze
39
+
40
+ def initialize(
41
+ cols: 1,
42
+ rows: nil,
43
+ gap: :medium,
44
+ flow: :row,
45
+ align_items: nil,
46
+ justify_items: nil,
47
+ classes: '',
48
+ id: nil,
49
+ **html_options
50
+ )
51
+ @cols = normalize_grid_cols_with_defaults(cols)
52
+ @rows = normalize_grid_rows_with_defaults(rows) if rows
53
+ @gap = normalize_grid_gap_with_defaults(gap)
54
+ @flow = flow.to_sym
55
+ @align_items = align_items&.to_sym
56
+ @justify_items = justify_items&.to_sym
57
+ @classes = classes
58
+ @id = id
59
+ @html_options = html_options
60
+
61
+ validate_grid_params
62
+ end
63
+
64
+ def combined_classes
65
+ [
66
+ GRID_BASE_CLASSES,
67
+ generate_cols_classes,
68
+ generate_rows_classes,
69
+ generate_gap_classes,
70
+ GRID_FLOW[@flow],
71
+ @align_items ? GRID_ALIGN_ITEMS[@align_items] : nil,
72
+ @justify_items ? GRID_JUSTIFY_ITEMS[@justify_items] : nil,
73
+ @classes,
74
+ @html_options[:class]
75
+ ].compact.join(" ")
76
+ end
77
+
78
+ def grid_attributes
79
+ attrs = {
80
+ class: combined_classes,
81
+ id: @id
82
+ }
83
+
84
+ @html_options.except(:class).each do |key, value|
85
+ attrs[key] = value
86
+ end
87
+
88
+ attrs
89
+ end
90
+
91
+ private
92
+
93
+ # Genera classi CSS dinamicamente invece di usare costanti massive
94
+ def generate_grid_cols_class(cols, breakpoint = nil)
95
+ prefix = breakpoint ? "#{breakpoint}:" : ""
96
+ "#{prefix}grid-cols-#{cols}"
97
+ end
98
+
99
+ def generate_grid_rows_class(rows, breakpoint = nil)
100
+ prefix = breakpoint ? "#{breakpoint}:" : ""
101
+ "#{prefix}grid-rows-#{rows}"
102
+ end
103
+
104
+ def generate_grid_gap_class(gap, breakpoint = nil)
105
+ prefix = breakpoint ? "#{breakpoint}:" : ""
106
+ gap_class = GRID_GAP[gap.to_sym]
107
+ return nil unless gap_class
108
+
109
+ if breakpoint
110
+ "#{breakpoint}:#{gap_class.sub(/^gap-/, 'gap-')}"
111
+ else
112
+ gap_class
113
+ end
114
+ end
115
+
116
+ # Normalizza cols con default intelligenti per tutti i breakpoint
117
+ def normalize_grid_cols_with_defaults(cols)
118
+ # Se è un valore semplice, lo usiamo per tutti i breakpoint
119
+ return cols unless cols.is_a?(Hash)
120
+
121
+ # Se è un hash, riempiamo i breakpoint mancanti con fallback intelligente
122
+ normalized = {}
123
+ last_value = 1 # Default base
124
+
125
+ # Base (senza breakpoint)
126
+ normalized[:base] = 1
127
+
128
+ # Processa i breakpoint in ordine
129
+ VALID_BREAKPOINTS.each do |breakpoint|
130
+ if cols.key?(breakpoint)
131
+ last_value = cols[breakpoint]
132
+ end
133
+ normalized[breakpoint] = last_value
134
+ end
135
+
136
+ normalized
137
+ end
138
+
139
+ # Normalizza rows con default intelligenti (solo se specificato)
140
+ def normalize_grid_rows_with_defaults(rows)
141
+ return rows unless rows.is_a?(Hash)
142
+
143
+ normalized = {}
144
+ last_value = nil
145
+
146
+ VALID_BREAKPOINTS.each do |breakpoint|
147
+ if rows.key?(breakpoint)
148
+ last_value = rows[breakpoint]
149
+ end
150
+ normalized[breakpoint] = last_value if last_value
151
+ end
152
+
153
+ normalized
154
+ end
155
+
156
+ # Normalizza gap con default intelligenti
157
+ def normalize_grid_gap_with_defaults(gap)
158
+ return gap unless gap.is_a?(Hash)
159
+
160
+ normalized = {}
161
+ last_value = :medium # Default base
162
+
163
+ VALID_BREAKPOINTS.each do |breakpoint|
164
+ if gap.key?(breakpoint)
165
+ last_value = gap[breakpoint]
166
+ end
167
+ normalized[breakpoint] = last_value
168
+ end
169
+
170
+ normalized
171
+ end
172
+
173
+ def generate_cols_classes
174
+ return nil unless @cols
175
+
176
+ if @cols.is_a?(Hash)
177
+ classes = []
178
+
179
+ # Base class (senza breakpoint)
180
+ if @cols[:base]
181
+ classes << generate_grid_cols_class(@cols[:base])
182
+ end
183
+
184
+ # Responsive classes
185
+ VALID_BREAKPOINTS.each do |breakpoint|
186
+ if @cols[breakpoint]
187
+ classes << generate_grid_cols_class(@cols[breakpoint], breakpoint)
188
+ end
189
+ end
190
+
191
+ classes.join(" ")
192
+ else
193
+ generate_grid_cols_class(@cols)
194
+ end
195
+ end
196
+
197
+ def generate_rows_classes
198
+ return nil unless @rows
199
+
200
+ if @rows.is_a?(Hash)
201
+ @rows.map do |breakpoint, rows_value|
202
+ generate_grid_rows_class(rows_value, breakpoint)
203
+ end.compact.join(" ")
204
+ else
205
+ generate_grid_rows_class(@rows)
206
+ end
207
+ end
208
+
209
+ def generate_gap_classes
210
+ if @gap.is_a?(Hash)
211
+ @gap.map do |breakpoint, gap_value|
212
+ generate_grid_gap_class(gap_value, breakpoint)
213
+ end.compact.join(" ")
214
+ else
215
+ GRID_GAP[@gap.to_sym]
216
+ end
217
+ end
218
+
219
+ def validate_grid_params
220
+ validate_grid_cols
221
+ validate_grid_rows if @rows
222
+ validate_grid_gap
223
+ validate_grid_flow
224
+ validate_grid_align_items if @align_items
225
+ validate_grid_justify_items if @justify_items
226
+ end
227
+
228
+ def validate_grid_cols
229
+ if @cols.is_a?(Hash)
230
+ @cols.each do |breakpoint, cols_value|
231
+ next if breakpoint == :base # Skip validation for base
232
+
233
+ unless VALID_BREAKPOINTS.include?(breakpoint)
234
+ raise ArgumentError, "Breakpoint #{breakpoint} non supportato per cols"
235
+ end
236
+ unless VALID_GRID_COLS.include?(cols_value)
237
+ raise ArgumentError, "Valore cols #{cols_value} non supportato per breakpoint #{breakpoint}"
238
+ end
239
+ end
240
+ else
241
+ unless VALID_GRID_COLS.include?(@cols)
242
+ raise ArgumentError, "cols deve essere uno tra: #{VALID_GRID_COLS.join(', ')}"
243
+ end
244
+ end
245
+ end
246
+
247
+ def validate_grid_rows
248
+ if @rows.is_a?(Hash)
249
+ @rows.each do |breakpoint, rows_value|
250
+ unless VALID_BREAKPOINTS.include?(breakpoint)
251
+ raise ArgumentError, "Breakpoint #{breakpoint} non supportato per rows"
252
+ end
253
+ unless VALID_GRID_ROWS.include?(rows_value)
254
+ raise ArgumentError, "Valore rows #{rows_value} non supportato per breakpoint #{breakpoint}"
255
+ end
256
+ end
257
+ else
258
+ unless VALID_GRID_ROWS.include?(@rows)
259
+ raise ArgumentError, "rows deve essere uno tra: #{VALID_GRID_ROWS.join(', ')}"
260
+ end
261
+ end
262
+ end
263
+
264
+ def validate_grid_gap
265
+ if @gap.is_a?(Hash)
266
+ @gap.each do |breakpoint, gap_value|
267
+ unless VALID_BREAKPOINTS.include?(breakpoint)
268
+ raise ArgumentError, "Breakpoint #{breakpoint} non supportato per gap"
269
+ end
270
+ unless GRID_GAP.keys.include?(gap_value.to_sym)
271
+ raise ArgumentError, "Valore gap #{gap_value} non supportato per breakpoint #{breakpoint}"
272
+ end
273
+ end
274
+ else
275
+ unless GRID_GAP.keys.include?(@gap.to_sym)
276
+ raise ArgumentError, "gap deve essere uno tra: #{GRID_GAP.keys.join(', ')}"
277
+ end
278
+ end
279
+ end
280
+
281
+ def validate_grid_flow
282
+ unless GRID_FLOW.keys.include?(@flow)
283
+ raise ArgumentError, "flow deve essere uno tra: #{GRID_FLOW.keys.join(', ')}"
284
+ end
285
+ end
286
+
287
+ def validate_grid_align_items
288
+ unless GRID_ALIGN_ITEMS.keys.include?(@align_items)
289
+ raise ArgumentError, "align_items deve essere uno tra: #{GRID_ALIGN_ITEMS.keys.join(', ')}"
290
+ end
291
+ end
292
+
293
+ def validate_grid_justify_items
294
+ unless GRID_JUSTIFY_ITEMS.keys.include?(@justify_items)
295
+ raise ArgumentError, "justify_items deve essere uno tra: #{GRID_JUSTIFY_ITEMS.keys.join(', ')}"
296
+ end
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
@@ -3,7 +3,7 @@
3
3
  <%= tag.public_send(heading_tag, **heading_attributes) do %>
4
4
  <% if show_icon? %>
5
5
  <span class="<%= icon_classes %>">
6
- <%= render BetterUi::General::IconComponent.new(name: @icon) %>
6
+ <%= render BetterUi::General::Icon::Component.new(name: @icon) %>
7
7
  </span>
8
8
  <% end %>
9
9