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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +225 -119
- data/app/assets/stylesheets/better_ui/application.css +0 -356
- data/app/components/better_ui/application/card/component.html.erb +20 -0
- data/app/components/better_ui/application/card/component.rb +214 -0
- data/app/components/better_ui/application/main/component.html.erb +9 -0
- data/app/components/better_ui/application/main/component.rb +123 -0
- data/app/components/better_ui/application/navbar/component.html.erb +92 -0
- data/app/components/better_ui/application/navbar/component.rb +136 -0
- data/app/components/better_ui/application/sidebar/component.html.erb +190 -0
- data/app/components/better_ui/application/sidebar/component.rb +129 -0
- data/app/components/better_ui/general/alert/component.html.erb +32 -0
- data/app/components/better_ui/general/alert/component.rb +242 -0
- data/app/components/better_ui/general/avatar/component.html.erb +20 -0
- data/app/components/better_ui/general/avatar/component.rb +301 -0
- data/app/components/better_ui/general/badge/component.html.erb +23 -0
- data/app/components/better_ui/general/badge/component.rb +248 -0
- data/app/components/better_ui/general/breadcrumb/component.html.erb +15 -0
- data/app/components/better_ui/general/breadcrumb/component.rb +187 -0
- data/app/components/better_ui/general/button/component.html.erb +34 -0
- data/app/components/better_ui/general/button/component.rb +214 -0
- data/app/components/better_ui/general/divider/component.html.erb +10 -0
- data/app/components/better_ui/general/divider/component.rb +226 -0
- data/app/components/better_ui/general/field/component.html.erb +27 -0
- data/app/components/better_ui/general/field/component.rb +37 -0
- data/app/components/better_ui/general/heading/component.html.erb +22 -0
- data/app/components/better_ui/general/heading/component.rb +257 -0
- data/app/components/better_ui/general/icon/component.html.erb +7 -0
- data/app/components/better_ui/general/icon/component.rb +239 -0
- data/app/components/better_ui/general/input/checkbox/component.html.erb +5 -0
- data/app/components/better_ui/general/input/checkbox/component.rb +238 -0
- data/app/components/better_ui/general/input/datetime/component.html.erb +5 -0
- data/app/components/better_ui/general/input/datetime/component.rb +223 -0
- data/app/components/better_ui/general/input/radio/component.html.erb +5 -0
- data/app/components/better_ui/general/input/radio/component.rb +230 -0
- data/app/components/better_ui/general/input/select/component.html.erb +16 -0
- data/app/components/better_ui/general/input/select/component.rb +184 -0
- data/app/components/better_ui/general/input/select/select_component.html.erb +5 -0
- data/app/components/better_ui/general/input/select/select_component.rb +37 -0
- data/app/components/better_ui/general/input/text/component.html.erb +5 -0
- data/app/components/better_ui/general/input/text/component.rb +171 -0
- data/app/components/better_ui/general/input/textarea/component.html.erb +5 -0
- data/app/components/better_ui/general/input/textarea/component.rb +166 -0
- data/app/components/better_ui/general/link/component.html.erb +18 -0
- data/app/components/better_ui/general/link/component.rb +258 -0
- data/app/components/better_ui/general/panel/component.html.erb +28 -0
- data/app/components/better_ui/general/panel/component.rb +249 -0
- data/app/components/better_ui/general/progress/component.html.erb +11 -0
- data/app/components/better_ui/general/progress/component.rb +160 -0
- data/app/components/better_ui/general/spinner/component.html.erb +35 -0
- data/app/components/better_ui/general/spinner/component.rb +93 -0
- data/app/components/better_ui/general/table/component.html.erb +5 -0
- data/app/components/better_ui/general/table/component.rb +217 -0
- data/app/components/better_ui/general/table/tbody_component.html.erb +3 -0
- data/app/components/better_ui/general/table/tbody_component.rb +30 -0
- data/app/components/better_ui/general/table/td_component.html.erb +3 -0
- data/app/components/better_ui/general/table/td_component.rb +44 -0
- data/app/components/better_ui/general/table/tfoot_component.html.erb +3 -0
- data/app/components/better_ui/general/table/tfoot_component.rb +28 -0
- data/app/components/better_ui/general/table/th_component.html.erb +6 -0
- data/app/components/better_ui/general/table/th_component.rb +51 -0
- data/app/components/better_ui/general/table/thead_component.html.erb +3 -0
- data/app/components/better_ui/general/table/thead_component.rb +28 -0
- data/app/components/better_ui/general/table/tr_component.html.erb +3 -0
- data/app/components/better_ui/general/table/tr_component.rb +30 -0
- data/app/components/better_ui/general/tag/component.html.erb +3 -0
- data/app/components/better_ui/general/tag/component.rb +104 -0
- data/app/components/better_ui/general/tooltip/component.html.erb +7 -0
- data/app/components/better_ui/general/tooltip/component.rb +239 -0
- data/app/helpers/better_ui/application/components/card/card_helper.rb +96 -0
- data/app/helpers/better_ui/application/components/card.rb +11 -0
- data/app/helpers/better_ui/application/components/main/main_helper.rb +64 -0
- data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +77 -0
- data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +51 -0
- data/app/helpers/better_ui/application_helper.rb +42 -179
- data/app/helpers/better_ui/general/components/alert/alert_helper.rb +57 -0
- data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +29 -0
- data/app/helpers/better_ui/general/components/badge/badge_helper.rb +53 -0
- data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +37 -0
- data/app/helpers/better_ui/general/components/button/button_helper.rb +65 -0
- data/app/helpers/better_ui/general/components/container/container_helper.rb +60 -0
- data/app/helpers/better_ui/general/components/divider/divider_helper.rb +63 -0
- data/app/helpers/better_ui/general/components/field/field_helper.rb +26 -0
- data/app/helpers/better_ui/general/components/heading/heading_helper.rb +72 -0
- data/app/helpers/better_ui/general/components/icon/icon_helper.rb +16 -0
- data/app/helpers/better_ui/general/components/input/checkbox/checkbox_helper.rb +81 -0
- data/app/helpers/better_ui/general/components/input/datetime/datetime_helper.rb +91 -0
- data/app/helpers/better_ui/general/components/input/radio/radio_helper.rb +79 -0
- data/app/helpers/better_ui/general/components/input/radio_group/radio_group_helper.rb +124 -0
- data/app/helpers/better_ui/general/components/input/select/select_helper.rb +70 -0
- data/app/helpers/better_ui/general/components/input/text/text_helper.rb +138 -0
- data/app/helpers/better_ui/general/components/input/textarea/textarea_helper.rb +73 -0
- data/app/helpers/better_ui/general/components/link/link_helper.rb +89 -0
- data/app/helpers/better_ui/general/components/panel/panel_helper.rb +83 -0
- data/app/helpers/better_ui/general/components/progress/progress_helper.rb +53 -0
- data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +19 -0
- data/app/helpers/better_ui/general/components/table/table_helper.rb +53 -0
- data/app/helpers/better_ui/general/components/table/tbody_helper.rb +13 -0
- data/app/helpers/better_ui/general/components/table/td_helper.rb +19 -0
- data/app/helpers/better_ui/general/components/table/tfoot_helper.rb +13 -0
- data/app/helpers/better_ui/general/components/table/th_helper.rb +19 -0
- data/app/helpers/better_ui/general/components/table/thead_helper.rb +13 -0
- data/app/helpers/better_ui/general/components/table/tr_helper.rb +13 -0
- data/app/helpers/better_ui/general/components/tag/tag_helper.rb +26 -0
- data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +60 -0
- data/app/views/layouts/better_ui/application.html.erb +6 -124
- data/config/initializers/lookbook.rb +23 -0
- data/config/routes.rb +0 -8
- data/lib/better_ui/engine.rb +5 -19
- data/lib/better_ui/railtie.rb +20 -0
- data/lib/better_ui/version.rb +1 -1
- data/lib/better_ui.rb +4 -20
- metadata +131 -28
- data/app/controllers/better_ui/docs_controller.rb +0 -41
- data/app/views/better_ui/docs/component.html.erb +0 -365
- data/app/views/better_ui/docs/index.html.erb +0 -100
- data/app/views/better_ui/docs/show.html.erb +0 -60
@@ -0,0 +1,239 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module General
|
3
|
+
module Icon
|
4
|
+
class Component < ViewComponent::Base
|
5
|
+
# Classi base per l'icona con nomenclatura BEM
|
6
|
+
ICON_BASE_CLASSES = "bui-icon inline-flex items-center justify-center"
|
7
|
+
|
8
|
+
# Dimensioni dell'icona (standardizzate: small, medium, large)
|
9
|
+
ICON_SIZE_CLASSES = {
|
10
|
+
small: "bui-icon--small w-4 h-4 text-sm",
|
11
|
+
medium: "bui-icon--medium w-5 h-5 text-base",
|
12
|
+
large: "bui-icon--large w-6 h-6 text-lg"
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
# Temi dell'icona con colori coerenti
|
16
|
+
ICON_THEME_CLASSES = {
|
17
|
+
default: "bui-icon--default text-gray-600",
|
18
|
+
white: "bui-icon--white text-white",
|
19
|
+
red: "bui-icon--red text-red-600",
|
20
|
+
rose: "bui-icon--rose text-rose-600",
|
21
|
+
orange: "bui-icon--orange text-orange-600",
|
22
|
+
green: "bui-icon--green text-green-600",
|
23
|
+
blue: "bui-icon--blue text-blue-600",
|
24
|
+
yellow: "bui-icon--yellow text-yellow-600",
|
25
|
+
violet: "bui-icon--violet text-violet-600",
|
26
|
+
purple: "bui-icon--purple text-purple-600"
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
# Animazioni disponibili
|
30
|
+
ICON_ANIMATION_CLASSES = {
|
31
|
+
spin: "bui-icon--spin animate-spin",
|
32
|
+
pulse: "bui-icon--pulse animate-pulse"
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
# Trasformazioni disponibili
|
36
|
+
ICON_ROTATION_CLASSES = {
|
37
|
+
90 => "bui-icon--rotate-90 transform rotate-90",
|
38
|
+
180 => "bui-icon--rotate-180 transform rotate-180",
|
39
|
+
270 => "bui-icon--rotate-270 transform rotate-270"
|
40
|
+
}.freeze
|
41
|
+
|
42
|
+
ICON_FLIP_CLASSES = {
|
43
|
+
horizontal: "bui-icon--flip-h transform scale-x-[-1]",
|
44
|
+
vertical: "bui-icon--flip-v transform scale-y-[-1]",
|
45
|
+
both: "bui-icon--flip-both transform scale-[-1]"
|
46
|
+
}.freeze
|
47
|
+
|
48
|
+
# Bordo e larghezza fissa
|
49
|
+
ICON_BORDER_CLASSES = "bui-icon--border border border-current rounded-full p-1"
|
50
|
+
ICON_FIXED_WIDTH_CLASSES = "bui-icon--fixed-width w-5"
|
51
|
+
|
52
|
+
attr_reader :name, :style, :size, :theme, :spin, :pulse, :border, :fixed_width,
|
53
|
+
:rotation, :flip, :classes, :id, :href, :method, :target, :html_options
|
54
|
+
|
55
|
+
# @param name [String] Nome dell'icona (richiesto)
|
56
|
+
# @param style [Symbol] Stile dell'icona (:solid, :regular, :brands)
|
57
|
+
# @param size [Symbol] Dimensione (:small, :medium, :large)
|
58
|
+
# @param theme [Symbol] Tema colore (:default, :white, :red, :rose, :orange, :green, :blue, :yellow, :violet)
|
59
|
+
# @param spin [Boolean] Rotazione continua
|
60
|
+
# @param pulse [Boolean] Animazione pulsazione
|
61
|
+
# @param border [Boolean] Mostra bordo attorno all'icona
|
62
|
+
# @param fixed_width [Boolean] Larghezza fissa per allineamento
|
63
|
+
# @param rotation [Integer] Rotazione in gradi (0, 90, 180, 270)
|
64
|
+
# @param flip [Symbol] Tipo di flip (:horizontal, :vertical, :both)
|
65
|
+
# @param classes [String] Classi CSS aggiuntive
|
66
|
+
# @param id [String] ID HTML
|
67
|
+
# @param href [String] URL per l'icona
|
68
|
+
# @param html_options [Hash] Attributi HTML aggiuntivi
|
69
|
+
def initialize(
|
70
|
+
name:,
|
71
|
+
style: :solid,
|
72
|
+
size: :medium,
|
73
|
+
theme: :default,
|
74
|
+
spin: false,
|
75
|
+
pulse: false,
|
76
|
+
border: false,
|
77
|
+
fixed_width: false,
|
78
|
+
rotation: nil,
|
79
|
+
flip: nil,
|
80
|
+
classes: nil,
|
81
|
+
id: nil,
|
82
|
+
href: nil,
|
83
|
+
method: nil,
|
84
|
+
target: nil,
|
85
|
+
**html_options
|
86
|
+
)
|
87
|
+
@name = name
|
88
|
+
@style = style.to_sym
|
89
|
+
@size = size.to_sym
|
90
|
+
@theme = theme.to_sym
|
91
|
+
@spin = spin
|
92
|
+
@pulse = pulse
|
93
|
+
@border = border
|
94
|
+
@fixed_width = fixed_width
|
95
|
+
@rotation = rotation&.to_i
|
96
|
+
@flip = flip&.to_sym
|
97
|
+
@classes = classes
|
98
|
+
@id = id
|
99
|
+
@href = href
|
100
|
+
@method = method
|
101
|
+
@target = target
|
102
|
+
@html_options = html_options
|
103
|
+
|
104
|
+
validate_params!
|
105
|
+
end
|
106
|
+
|
107
|
+
# Genera le classi CSS complete per l'icona
|
108
|
+
def icon_classes
|
109
|
+
classes = [
|
110
|
+
ICON_BASE_CLASSES,
|
111
|
+
size_classes,
|
112
|
+
theme_classes,
|
113
|
+
animation_classes,
|
114
|
+
transformation_classes,
|
115
|
+
border_classes,
|
116
|
+
fixed_width_classes,
|
117
|
+
@classes
|
118
|
+
].compact.join(" ")
|
119
|
+
end
|
120
|
+
|
121
|
+
# Attributi HTML per l'elemento icona
|
122
|
+
def icon_attributes
|
123
|
+
attrs = {
|
124
|
+
class: icon_classes,
|
125
|
+
id: @id,
|
126
|
+
href: @href,
|
127
|
+
target: @target,
|
128
|
+
**@html_options
|
129
|
+
}.compact
|
130
|
+
|
131
|
+
# Handle method for Turbo
|
132
|
+
if @method.present?
|
133
|
+
attrs[:data] ||= {}
|
134
|
+
attrs[:data][:turbo_method] = @method
|
135
|
+
end
|
136
|
+
|
137
|
+
attrs
|
138
|
+
end
|
139
|
+
|
140
|
+
# Nome completo della classe FontAwesome basato su stile
|
141
|
+
def fa_class_name
|
142
|
+
prefix = case @style
|
143
|
+
when :solid then "fas"
|
144
|
+
when :regular then "far"
|
145
|
+
when :brands then "fab"
|
146
|
+
else "fas"
|
147
|
+
end
|
148
|
+
|
149
|
+
"#{prefix} fa-#{@name}"
|
150
|
+
end
|
151
|
+
|
152
|
+
# Verifica se il componente deve essere renderizzato
|
153
|
+
def render?
|
154
|
+
@name.present?
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def validate_params!
|
160
|
+
validate_name!
|
161
|
+
validate_style!
|
162
|
+
validate_size!
|
163
|
+
validate_theme!
|
164
|
+
validate_rotation!
|
165
|
+
validate_flip!
|
166
|
+
end
|
167
|
+
|
168
|
+
def validate_name!
|
169
|
+
raise ArgumentError, "Il nome dell'icona è richiesto" if @name.blank?
|
170
|
+
end
|
171
|
+
|
172
|
+
def validate_style!
|
173
|
+
valid_styles = [ :solid, :regular, :brands ]
|
174
|
+
unless valid_styles.include?(@style)
|
175
|
+
raise ArgumentError, "Lo stile deve essere uno tra: #{valid_styles.join(', ')}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def validate_size!
|
180
|
+
unless ICON_SIZE_CLASSES.key?(@size)
|
181
|
+
valid_sizes = ICON_SIZE_CLASSES.keys
|
182
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{valid_sizes.join(', ')}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def validate_theme!
|
187
|
+
unless ICON_THEME_CLASSES.key?(@theme)
|
188
|
+
valid_themes = ICON_THEME_CLASSES.keys
|
189
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{valid_themes.join(', ')}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def validate_rotation!
|
194
|
+
if @rotation && !ICON_ROTATION_CLASSES.key?(@rotation)
|
195
|
+
valid_rotations = ICON_ROTATION_CLASSES.keys
|
196
|
+
raise ArgumentError, "La rotazione deve essere una tra: #{valid_rotations.join(', ')}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def validate_flip!
|
201
|
+
if @flip && !ICON_FLIP_CLASSES.key?(@flip)
|
202
|
+
valid_flips = ICON_FLIP_CLASSES.keys
|
203
|
+
raise ArgumentError, "Il flip deve essere uno tra: #{valid_flips.join(', ')}"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def size_classes
|
208
|
+
ICON_SIZE_CLASSES[@size]
|
209
|
+
end
|
210
|
+
|
211
|
+
def theme_classes
|
212
|
+
ICON_THEME_CLASSES[@theme]
|
213
|
+
end
|
214
|
+
|
215
|
+
def animation_classes
|
216
|
+
animations = []
|
217
|
+
animations << ICON_ANIMATION_CLASSES[:spin] if @spin
|
218
|
+
animations << ICON_ANIMATION_CLASSES[:pulse] if @pulse
|
219
|
+
animations.join(" ")
|
220
|
+
end
|
221
|
+
|
222
|
+
def transformation_classes
|
223
|
+
transformations = []
|
224
|
+
transformations << ICON_ROTATION_CLASSES[@rotation] if @rotation
|
225
|
+
transformations << ICON_FLIP_CLASSES[@flip] if @flip
|
226
|
+
transformations.join(" ")
|
227
|
+
end
|
228
|
+
|
229
|
+
def border_classes
|
230
|
+
@border ? ICON_BORDER_CLASSES : nil
|
231
|
+
end
|
232
|
+
|
233
|
+
def fixed_width_classes
|
234
|
+
@fixed_width ? ICON_FIXED_WIDTH_CLASSES : nil
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Input
|
6
|
+
module Checkbox
|
7
|
+
class Component < ViewComponent::Base
|
8
|
+
# Costanti con classi Tailwind dirette
|
9
|
+
CHECKBOX_THEME = {
|
10
|
+
default: 'border-gray-300 text-blue-600 focus:border-blue-500 focus:ring-blue-500 checked:bg-blue-600 checked:border-blue-600',
|
11
|
+
white: 'border-gray-300 text-gray-900 focus:border-gray-500 focus:ring-gray-500 checked:bg-white checked:border-gray-900 checked:text-gray-900',
|
12
|
+
red: 'border-gray-300 text-red-600 focus:border-red-500 focus:ring-red-500 checked:bg-red-600 checked:border-red-600',
|
13
|
+
rose: 'border-gray-300 text-rose-600 focus:border-rose-500 focus:ring-rose-500 checked:bg-rose-600 checked:border-rose-600',
|
14
|
+
orange: 'border-gray-300 text-orange-600 focus:border-orange-500 focus:ring-orange-500 checked:bg-orange-600 checked:border-orange-600',
|
15
|
+
green: 'border-gray-300 text-green-600 focus:border-green-500 focus:ring-green-500 checked:bg-green-600 checked:border-green-600',
|
16
|
+
blue: 'border-gray-300 text-blue-600 focus:border-blue-500 focus:ring-blue-500 checked:bg-blue-600 checked:border-blue-600',
|
17
|
+
yellow: 'border-gray-300 text-yellow-600 focus:border-yellow-500 focus:ring-yellow-500 checked:bg-yellow-600 checked:border-yellow-600',
|
18
|
+
violet: 'border-gray-300 text-violet-600 focus:border-violet-500 focus:ring-violet-500 checked:bg-violet-600 checked:border-violet-600'
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
CHECKBOX_SIZE = {
|
22
|
+
small: 'h-4 w-4',
|
23
|
+
medium: 'h-5 w-5',
|
24
|
+
large: 'h-6 w-6'
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
CHECKBOX_ROUNDED = {
|
28
|
+
none: 'rounded-none',
|
29
|
+
small: 'rounded-sm',
|
30
|
+
medium: 'rounded',
|
31
|
+
large: 'rounded-lg',
|
32
|
+
full: 'rounded-full'
|
33
|
+
}.freeze
|
34
|
+
|
35
|
+
CHECKBOX_BASE_CLASSES = 'appearance-none border-2 focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors duration-200 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50'.freeze
|
36
|
+
|
37
|
+
CHECKBOX_LABEL_GAP = {
|
38
|
+
small: 'gap-2',
|
39
|
+
medium: 'gap-2.5',
|
40
|
+
large: 'gap-3'
|
41
|
+
}.freeze
|
42
|
+
|
43
|
+
CHECKBOX_LABEL_TEXT = {
|
44
|
+
small: 'text-sm',
|
45
|
+
medium: 'text-base',
|
46
|
+
large: 'text-lg'
|
47
|
+
}.freeze
|
48
|
+
|
49
|
+
attr_reader :name, :value, :checked, :required, :disabled, :indeterminate,
|
50
|
+
:label, :label_position, :theme, :size, :rounded, :classes, :form, :options
|
51
|
+
|
52
|
+
# @param name [String] Nome del campo checkbox (obbligatorio)
|
53
|
+
# @param value [String] Valore del checkbox (default: "1")
|
54
|
+
# @param checked [Boolean] Se il checkbox è selezionato
|
55
|
+
# @param required [Boolean] Se il campo è obbligatorio
|
56
|
+
# @param disabled [Boolean] Se il campo è disabilitato
|
57
|
+
# @param indeterminate [Boolean] Se il checkbox è in stato indeterminate
|
58
|
+
# @param label [String, nil] Testo della label associata al checkbox
|
59
|
+
# @param label_position [Symbol] Posizione della label (:left, :right)
|
60
|
+
# @param theme [Symbol] Tema del componente (:default, :white, :red, :rose, :orange, :green, :blue, :yellow, :violet)
|
61
|
+
# @param size [Symbol] Dimensione del componente (:small, :medium, :large)
|
62
|
+
# @param rounded [Symbol] Border radius (:none, :small, :medium, :large, :full)
|
63
|
+
# @param classes [String] Classi CSS aggiuntive
|
64
|
+
# @param form [ActionView::Helpers::FormBuilder, nil] Form builder Rails opzionale
|
65
|
+
# @param options [Hash] Opzioni aggiuntive per l'input (es. data attributes, aria attributes)
|
66
|
+
def initialize(name:, value: "1", checked: false, required: false, disabled: false,
|
67
|
+
indeterminate: false, label: nil, label_position: :right, theme: :default,
|
68
|
+
size: :medium, rounded: :medium, classes: '', form: nil, **options)
|
69
|
+
@name = name
|
70
|
+
@value = value
|
71
|
+
@checked = checked
|
72
|
+
@required = required
|
73
|
+
@disabled = disabled
|
74
|
+
@indeterminate = indeterminate
|
75
|
+
@label = label
|
76
|
+
@label_position = label_position.to_sym
|
77
|
+
@theme = theme.to_sym
|
78
|
+
@size = size.to_sym
|
79
|
+
@rounded = rounded.to_sym
|
80
|
+
@classes = classes
|
81
|
+
@form = form
|
82
|
+
@options = options
|
83
|
+
|
84
|
+
validate_params
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def validate_params
|
90
|
+
validate_theme
|
91
|
+
validate_size
|
92
|
+
validate_rounded
|
93
|
+
validate_label_position
|
94
|
+
end
|
95
|
+
|
96
|
+
def validate_theme
|
97
|
+
return if CHECKBOX_THEME.key?(@theme)
|
98
|
+
|
99
|
+
raise ArgumentError, "Invalid theme: #{@theme}. Valid themes are: #{CHECKBOX_THEME.keys.join(', ')}"
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_size
|
103
|
+
return if CHECKBOX_SIZE.key?(@size)
|
104
|
+
|
105
|
+
raise ArgumentError, "Invalid size: #{@size}. Valid sizes are: #{CHECKBOX_SIZE.keys.join(', ')}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def validate_rounded
|
109
|
+
return if CHECKBOX_ROUNDED.key?(@rounded)
|
110
|
+
|
111
|
+
raise ArgumentError, "Invalid rounded: #{@rounded}. Valid rounded options are: #{CHECKBOX_ROUNDED.keys.join(', ')}"
|
112
|
+
end
|
113
|
+
|
114
|
+
def validate_label_position
|
115
|
+
return if [:left, :right].include?(@label_position)
|
116
|
+
|
117
|
+
raise ArgumentError, "Invalid label_position: #{@label_position}. Valid positions are: left, right"
|
118
|
+
end
|
119
|
+
|
120
|
+
def checkbox_classes
|
121
|
+
[
|
122
|
+
CHECKBOX_BASE_CLASSES,
|
123
|
+
CHECKBOX_THEME[@theme],
|
124
|
+
CHECKBOX_SIZE[@size],
|
125
|
+
CHECKBOX_ROUNDED[@rounded],
|
126
|
+
@classes
|
127
|
+
].compact.join(' ')
|
128
|
+
end
|
129
|
+
|
130
|
+
def input_attributes
|
131
|
+
attrs = {
|
132
|
+
type: 'checkbox',
|
133
|
+
name: input_name,
|
134
|
+
value: @value,
|
135
|
+
class: checkbox_classes,
|
136
|
+
checked: @checked,
|
137
|
+
required: @required,
|
138
|
+
disabled: @disabled,
|
139
|
+
id: input_id
|
140
|
+
}
|
141
|
+
|
142
|
+
# Aggiungi indeterminate via JavaScript se necessario
|
143
|
+
if @indeterminate
|
144
|
+
attrs['data-indeterminate'] = 'true'
|
145
|
+
end
|
146
|
+
|
147
|
+
# Unisci le opzioni personalizzate
|
148
|
+
attrs.merge(@options)
|
149
|
+
end
|
150
|
+
|
151
|
+
def input_name
|
152
|
+
if @form
|
153
|
+
@form.field_name(@name)
|
154
|
+
else
|
155
|
+
@name
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def input_id
|
160
|
+
@options[:id] || "checkbox_#{@name}"
|
161
|
+
end
|
162
|
+
|
163
|
+
def label_classes
|
164
|
+
[
|
165
|
+
'flex items-center cursor-pointer',
|
166
|
+
@disabled ? 'opacity-50 cursor-not-allowed' : '',
|
167
|
+
CHECKBOX_LABEL_GAP[@size]
|
168
|
+
].compact.join(' ')
|
169
|
+
end
|
170
|
+
|
171
|
+
def label_text_classes
|
172
|
+
CHECKBOX_LABEL_TEXT[@size]
|
173
|
+
end
|
174
|
+
|
175
|
+
def input_tag
|
176
|
+
if @form
|
177
|
+
form_checkbox
|
178
|
+
else
|
179
|
+
manual_input
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def form_checkbox
|
184
|
+
@form.check_box(@name, {
|
185
|
+
class: checkbox_classes,
|
186
|
+
id: input_id,
|
187
|
+
checked: @checked,
|
188
|
+
disabled: @disabled,
|
189
|
+
required: @required,
|
190
|
+
data: @indeterminate ? { indeterminate: 'true' } : {},
|
191
|
+
**@options
|
192
|
+
}, @value)
|
193
|
+
end
|
194
|
+
|
195
|
+
def manual_input
|
196
|
+
attrs = input_attributes.map do |key, value|
|
197
|
+
if value == true
|
198
|
+
key.to_s
|
199
|
+
elsif value == false || value.nil?
|
200
|
+
nil
|
201
|
+
else
|
202
|
+
"#{key}=\"#{value}\""
|
203
|
+
end
|
204
|
+
end.compact.join(' ')
|
205
|
+
|
206
|
+
"<input #{attrs} />".html_safe
|
207
|
+
end
|
208
|
+
|
209
|
+
def render_checkbox_with_label
|
210
|
+
if @label_position == :left
|
211
|
+
label_left_content
|
212
|
+
else
|
213
|
+
label_right_content
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def label_left_content
|
218
|
+
content_tag(:label, class: label_classes, for: input_id) do
|
219
|
+
safe_join([
|
220
|
+
content_tag(:span, @label, class: label_text_classes),
|
221
|
+
input_tag
|
222
|
+
])
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def label_right_content
|
227
|
+
content_tag(:label, class: label_classes, for: input_id) do
|
228
|
+
safe_join([
|
229
|
+
input_tag,
|
230
|
+
content_tag(:span, @label, class: label_text_classes)
|
231
|
+
])
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|