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.
- checksums.yaml +4 -4
- data/app/components/better_ui/application/main/component.html.erb +1 -1
- data/app/components/better_ui/application/sidebar/component.html.erb +25 -3
- data/app/components/better_ui/application/sidebar/component.rb +62 -5
- data/app/components/better_ui/general/button/component.html.erb +8 -8
- data/app/components/better_ui/general/button/component.rb +11 -11
- data/app/components/better_ui/general/dropdown/component.html.erb +7 -4
- data/app/components/better_ui/general/dropdown/component.rb +23 -1
- data/app/components/better_ui/general/field/component.html.erb +3 -3
- data/app/components/better_ui/general/field/component.rb +3 -3
- data/app/components/better_ui/general/grid/cell_component.html.erb +3 -0
- data/app/components/better_ui/general/grid/cell_component.rb +390 -0
- data/app/components/better_ui/general/grid/component.html.erb +3 -0
- data/app/components/better_ui/general/grid/component.rb +301 -0
- data/app/components/better_ui/general/heading/component.html.erb +1 -1
- data/app/components/better_ui/general/icon/component.rb +2 -1
- data/app/components/better_ui/general/input/checkbox/component.rb +10 -10
- data/app/components/better_ui/general/input/pin/component.html.erb +1 -0
- data/app/components/better_ui/general/input/pin/component.rb +201 -0
- data/app/components/better_ui/general/input/radio/component.rb +10 -10
- data/app/components/better_ui/general/input/rating/component.html.erb +4 -0
- data/app/components/better_ui/general/input/rating/component.rb +272 -0
- data/app/components/better_ui/general/input/select/component.html.erb +76 -14
- data/app/components/better_ui/general/input/select/component.rb +166 -101
- data/app/components/better_ui/general/input/toggle/component.html.erb +5 -0
- data/app/components/better_ui/general/input/toggle/component.rb +242 -0
- data/app/components/better_ui/general/link/component.rb +1 -1
- data/app/components/better_ui/general/text/component.html.erb +1 -0
- data/app/components/better_ui/general/text/component.rb +194 -0
- data/app/helpers/better_ui/application_helper.rb +7 -0
- data/app/helpers/better_ui/general/components/button/button_helper.rb +6 -6
- data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +9 -0
- data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +13 -7
- data/app/helpers/better_ui/general/components/field/field_helper.rb +4 -4
- data/app/helpers/better_ui/general/components/grid/grid_helper.rb +145 -0
- data/app/helpers/better_ui/general/components/input/pin/pin_helper.rb +76 -0
- data/app/helpers/better_ui/general/components/input/rating/rating_helper.rb +70 -0
- data/app/helpers/better_ui/general/components/input/select/select_helper.rb +47 -31
- data/app/helpers/better_ui/general/components/input/toggle/toggle_helper.rb +77 -0
- data/app/helpers/better_ui/general/components/text/text_helper.rb +83 -0
- data/lib/better_ui/version.rb +1 -1
- data/lib/better_ui.rb +1 -0
- metadata +19 -4
- data/app/helpers/better_ui/general/components/accordion.rb +0 -11
- 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,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::
|
6
|
+
<%= render BetterUi::General::Icon::Component.new(name: @icon) %>
|
7
7
|
</span>
|
8
8
|
<% end %>
|
9
9
|
|