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,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,5 @@
1
+ <% if @label.present? %>
2
+ <%= render_checkbox_with_label %>
3
+ <% else %>
4
+ <%= input_tag %>
5
+ <% 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
@@ -0,0 +1,5 @@
1
+ <% if @form %>
2
+ <%= @form.public_send(form_field_method, @name, **form_input_attributes) %>
3
+ <% else %>
4
+ <%= tag.input(**input_attributes) %>
5
+ <% end %>