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,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Input
|
6
|
+
module Select
|
7
|
+
class SelectComponent < ViewComponent::Base
|
8
|
+
attr_reader :name, :options, :selected, :required, :disabled, :multiple, :options_html, :classes, :html_options
|
9
|
+
|
10
|
+
BASE_SELECT_CLASSES = "h-10 px-3 py-2 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm disabled:bg-gray-100 disabled:cursor-not-allowed"
|
11
|
+
# @param name [String] Nome del campo
|
12
|
+
# @param options [Array] Opzioni del select ([{value => label}, {value => label}, ...])
|
13
|
+
# @param selected [String, Array] Valore selezionato
|
14
|
+
# @param required [Boolean] Se il campo è obbligatorio
|
15
|
+
# @param disabled [Boolean] Se il campo è disabilitato
|
16
|
+
# @param multiple [Boolean] Se il select è multiplo
|
17
|
+
# @param options_html [Hash] Opzioni HTML aggiuntive per le option
|
18
|
+
# @param classes [String] Classi del campo
|
19
|
+
# @param html_options [Hash] Opzioni HTML del campo
|
20
|
+
def initialize(name:, options:, selected: nil, required: false, disabled: false, multiple: false, options_html: {}, classes: '', **html_options)
|
21
|
+
@name = name
|
22
|
+
@type = :select
|
23
|
+
@required = required
|
24
|
+
@disabled = disabled
|
25
|
+
@options = options.map { |option| { value: option[:value], label: option[:label] } }
|
26
|
+
@selected = selected
|
27
|
+
@multiple = multiple
|
28
|
+
@options_html = options_html
|
29
|
+
@classes = BASE_SELECT_CLASSES + classes
|
30
|
+
|
31
|
+
puts "Optionssssssssssssssssssssssss: #{selected}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Input
|
6
|
+
module Text
|
7
|
+
class Component < ViewComponent::Base
|
8
|
+
attr_reader :name, :value, :placeholder, :required, :disabled, :classes, :options,
|
9
|
+
:theme, :size, :rounded, :form, :type
|
10
|
+
|
11
|
+
# Temi supportati per il Text Input
|
12
|
+
TEXT_INPUT_THEME = {
|
13
|
+
default: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500',
|
14
|
+
white: 'border-white focus:border-gray-300 focus:ring-gray-300 bg-white',
|
15
|
+
red: 'border-red-300 focus:border-red-500 focus:ring-red-500',
|
16
|
+
rose: 'border-rose-300 focus:border-rose-500 focus:ring-rose-500',
|
17
|
+
orange: 'border-orange-300 focus:border-orange-500 focus:ring-orange-500',
|
18
|
+
green: 'border-green-300 focus:border-green-500 focus:ring-green-500',
|
19
|
+
blue: 'border-blue-300 focus:border-blue-500 focus:ring-blue-500',
|
20
|
+
yellow: 'border-yellow-300 focus:border-yellow-500 focus:ring-yellow-500',
|
21
|
+
violet: 'border-violet-300 focus:border-violet-500 focus:ring-violet-500'
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
# Dimensioni supportate per il Text Input
|
25
|
+
TEXT_INPUT_SIZES = {
|
26
|
+
small: 'h-8 px-2 py-1 text-xs',
|
27
|
+
medium: 'h-10 px-3 py-2 text-sm',
|
28
|
+
large: 'h-12 px-4 py-3 text-base'
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
# Border radius supportati per il Text Input
|
32
|
+
TEXT_INPUT_RADIUS = {
|
33
|
+
none: 'rounded-none',
|
34
|
+
small: 'rounded-sm',
|
35
|
+
medium: 'rounded-md',
|
36
|
+
large: 'rounded-lg',
|
37
|
+
full: 'rounded-full'
|
38
|
+
}.freeze
|
39
|
+
|
40
|
+
# Tipi supportati per il Text Input
|
41
|
+
TEXT_INPUT_TYPES = [
|
42
|
+
:text, :password, :email, :tel, :url, :number, :search,
|
43
|
+
:date, :time, :datetime_local, :month, :week, :color
|
44
|
+
].freeze
|
45
|
+
|
46
|
+
# Classi base per il Text Input
|
47
|
+
TEXT_INPUT_BASE_CLASSES = 'block w-full border shadow-sm disabled:bg-gray-100 disabled:cursor-not-allowed focus:outline-none focus:ring-1'
|
48
|
+
|
49
|
+
# @param name [String] Nome del campo input
|
50
|
+
# @param value [String] Valore del campo
|
51
|
+
# @param placeholder [String] Placeholder del campo
|
52
|
+
# @param required [Boolean] Se il campo è obbligatorio
|
53
|
+
# @param disabled [Boolean] Se il campo è disabilitato
|
54
|
+
# @param type [Symbol] Tipo del campo input (:text, :password, :email, :tel, :url, :number, :search, :date, :time, :datetime_local, :month, :week, :color)
|
55
|
+
# @param theme [Symbol] Tema del componente (:default, :white, :red, :rose, :orange, :green, :blue, :yellow, :violet)
|
56
|
+
# @param size [Symbol] Dimensione del componente (:small, :medium, :large)
|
57
|
+
# @param rounded [Symbol] Border radius (:none, :small, :medium, :large, :full)
|
58
|
+
# @param classes [String] Classi CSS aggiuntive
|
59
|
+
# @param form [ActionView::Helpers::FormBuilder] Form builder Rails opzionale
|
60
|
+
# @param options [Hash] Opzioni aggiuntive per l'input
|
61
|
+
def initialize(name:, value: nil, placeholder: nil, required: false, disabled: false,
|
62
|
+
type: :text, theme: :default, size: :medium, rounded: :medium, classes: '', form: nil, **options)
|
63
|
+
@name = name
|
64
|
+
@value = value
|
65
|
+
@placeholder = placeholder
|
66
|
+
@required = required
|
67
|
+
@disabled = disabled
|
68
|
+
@type = type
|
69
|
+
@theme = theme
|
70
|
+
@size = size
|
71
|
+
@rounded = rounded
|
72
|
+
@classes = classes
|
73
|
+
@form = form
|
74
|
+
@options = options
|
75
|
+
|
76
|
+
validate_params
|
77
|
+
super()
|
78
|
+
end
|
79
|
+
|
80
|
+
# Attributi per l'elemento input standalone
|
81
|
+
def input_attributes
|
82
|
+
{
|
83
|
+
type: @type,
|
84
|
+
name: @name,
|
85
|
+
id: @name,
|
86
|
+
value: @value,
|
87
|
+
placeholder: @placeholder,
|
88
|
+
required: @required,
|
89
|
+
disabled: @disabled,
|
90
|
+
class: build_classes
|
91
|
+
}.merge(@options)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Attributi per l'elemento input con form builder
|
95
|
+
def form_input_attributes
|
96
|
+
{
|
97
|
+
class: build_classes,
|
98
|
+
placeholder: @placeholder,
|
99
|
+
required: @required,
|
100
|
+
disabled: @disabled
|
101
|
+
}.merge(@options)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
# Costruisce le classi CSS complete
|
107
|
+
def build_classes
|
108
|
+
[
|
109
|
+
TEXT_INPUT_BASE_CLASSES,
|
110
|
+
get_theme_classes,
|
111
|
+
get_size_classes,
|
112
|
+
get_rounded_classes,
|
113
|
+
@classes
|
114
|
+
].compact.join(' ')
|
115
|
+
end
|
116
|
+
|
117
|
+
# Restituisce le classi del tema
|
118
|
+
def get_theme_classes
|
119
|
+
TEXT_INPUT_THEME[@theme]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Restituisce le classi della dimensione
|
123
|
+
def get_size_classes
|
124
|
+
TEXT_INPUT_SIZES[@size]
|
125
|
+
end
|
126
|
+
|
127
|
+
# Restituisce le classi del border radius
|
128
|
+
def get_rounded_classes
|
129
|
+
TEXT_INPUT_RADIUS[@rounded]
|
130
|
+
end
|
131
|
+
|
132
|
+
# Valida i parametri del componente
|
133
|
+
def validate_params
|
134
|
+
validate_type
|
135
|
+
validate_theme
|
136
|
+
validate_size
|
137
|
+
validate_rounded
|
138
|
+
end
|
139
|
+
|
140
|
+
# Valida il tema
|
141
|
+
def validate_theme
|
142
|
+
return if TEXT_INPUT_THEME.key?(@theme)
|
143
|
+
|
144
|
+
raise ArgumentError, "Tema non valido: #{@theme}. Temi supportati: #{TEXT_INPUT_THEME.keys.join(', ')}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Valida la dimensione
|
148
|
+
def validate_size
|
149
|
+
return if TEXT_INPUT_SIZES.key?(@size)
|
150
|
+
|
151
|
+
raise ArgumentError, "Dimensione non valida: #{@size}. Dimensioni supportate: #{TEXT_INPUT_SIZES.keys.join(', ')}"
|
152
|
+
end
|
153
|
+
|
154
|
+
# Valida il border radius
|
155
|
+
def validate_rounded
|
156
|
+
return if TEXT_INPUT_RADIUS.key?(@rounded)
|
157
|
+
|
158
|
+
raise ArgumentError, "Border radius non valido: #{@rounded}. Valori supportati: #{TEXT_INPUT_RADIUS.keys.join(', ')}"
|
159
|
+
end
|
160
|
+
|
161
|
+
# Valida il tipo
|
162
|
+
def validate_type
|
163
|
+
return if TEXT_INPUT_TYPES.include?(@type)
|
164
|
+
|
165
|
+
raise ArgumentError, "Tipo non valido: #{@type}. Tipi supportati: #{TEXT_INPUT_TYPES.join(', ')}"
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Input
|
6
|
+
module Textarea
|
7
|
+
class Component < ViewComponent::Base
|
8
|
+
attr_reader :name, :value, :placeholder, :required, :disabled, :classes, :options,
|
9
|
+
:theme, :size, :rounded, :form, :rows
|
10
|
+
|
11
|
+
# Temi supportati per il Textarea
|
12
|
+
TEXTAREA_THEME = {
|
13
|
+
default: 'border-gray-300 focus:border-blue-500 focus:ring-blue-500',
|
14
|
+
white: 'border-white focus:border-gray-300 focus:ring-gray-300 bg-white',
|
15
|
+
red: 'border-red-300 focus:border-red-500 focus:ring-red-500',
|
16
|
+
rose: 'border-rose-300 focus:border-rose-500 focus:ring-rose-500',
|
17
|
+
orange: 'border-orange-300 focus:border-orange-500 focus:ring-orange-500',
|
18
|
+
green: 'border-green-300 focus:border-green-500 focus:ring-green-500',
|
19
|
+
blue: 'border-blue-300 focus:border-blue-500 focus:ring-blue-500',
|
20
|
+
yellow: 'border-yellow-300 focus:border-yellow-500 focus:ring-yellow-500',
|
21
|
+
violet: 'border-violet-300 focus:border-violet-500 focus:ring-violet-500'
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
# Dimensioni supportate per il Textarea
|
25
|
+
TEXTAREA_SIZES = {
|
26
|
+
small: 'px-2 py-1 text-xs',
|
27
|
+
medium: 'px-3 py-2 text-sm',
|
28
|
+
large: 'px-4 py-3 text-base'
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
# Border radius supportati per il Textarea
|
32
|
+
TEXTAREA_RADIUS = {
|
33
|
+
none: 'rounded-none',
|
34
|
+
small: 'rounded-sm',
|
35
|
+
medium: 'rounded-md',
|
36
|
+
large: 'rounded-lg',
|
37
|
+
full: 'rounded-2xl'
|
38
|
+
}.freeze
|
39
|
+
|
40
|
+
# Classi base per il Textarea
|
41
|
+
TEXTAREA_BASE_CLASSES = 'block w-full border shadow-sm disabled:bg-gray-100 disabled:cursor-not-allowed focus:outline-none focus:ring-1 resize-y'
|
42
|
+
|
43
|
+
# @param name [String] Nome del campo textarea
|
44
|
+
# @param value [String] Valore del campo
|
45
|
+
# @param placeholder [String] Placeholder del campo
|
46
|
+
# @param required [Boolean] Se il campo è obbligatorio
|
47
|
+
# @param disabled [Boolean] Se il campo è disabilitato
|
48
|
+
# @param rows [Integer] Numero di righe per la textarea
|
49
|
+
# @param theme [Symbol] Tema del componente (:default, :white, :red, :rose, :orange, :green, :blue, :yellow, :violet)
|
50
|
+
# @param size [Symbol] Dimensione del componente (:small, :medium, :large)
|
51
|
+
# @param rounded [Symbol] Border radius (:none, :small, :medium, :large, :full)
|
52
|
+
# @param classes [String] Classi CSS aggiuntive
|
53
|
+
# @param form [ActionView::Helpers::FormBuilder] Form builder Rails opzionale
|
54
|
+
# @param options [Hash] Opzioni aggiuntive per la textarea
|
55
|
+
def initialize(name:, value: nil, placeholder: nil, required: false, disabled: false,
|
56
|
+
rows: 3, theme: :default, size: :medium, rounded: :medium, classes: '', form: nil, **options)
|
57
|
+
@name = name
|
58
|
+
@value = value
|
59
|
+
@placeholder = placeholder
|
60
|
+
@required = required
|
61
|
+
@disabled = disabled
|
62
|
+
@rows = rows
|
63
|
+
@theme = theme
|
64
|
+
@size = size
|
65
|
+
@rounded = rounded
|
66
|
+
@classes = classes
|
67
|
+
@form = form
|
68
|
+
@options = options
|
69
|
+
|
70
|
+
validate_params
|
71
|
+
super()
|
72
|
+
end
|
73
|
+
|
74
|
+
# Attributi per l'elemento textarea standalone
|
75
|
+
def textarea_attributes
|
76
|
+
{
|
77
|
+
name: @name,
|
78
|
+
id: @name,
|
79
|
+
value: @value,
|
80
|
+
placeholder: @placeholder,
|
81
|
+
required: @required,
|
82
|
+
disabled: @disabled,
|
83
|
+
rows: @rows,
|
84
|
+
class: build_classes
|
85
|
+
}.merge(@options)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Attributi per l'elemento textarea con form builder
|
89
|
+
def form_textarea_attributes
|
90
|
+
{
|
91
|
+
class: build_classes,
|
92
|
+
placeholder: @placeholder,
|
93
|
+
required: @required,
|
94
|
+
disabled: @disabled,
|
95
|
+
rows: @rows
|
96
|
+
}.merge(@options)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# Costruisce le classi CSS complete
|
102
|
+
def build_classes
|
103
|
+
[
|
104
|
+
TEXTAREA_BASE_CLASSES,
|
105
|
+
get_theme_classes,
|
106
|
+
get_size_classes,
|
107
|
+
get_rounded_classes,
|
108
|
+
@classes
|
109
|
+
].compact.join(' ')
|
110
|
+
end
|
111
|
+
|
112
|
+
# Restituisce le classi del tema
|
113
|
+
def get_theme_classes
|
114
|
+
TEXTAREA_THEME[@theme]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Restituisce le classi della dimensione
|
118
|
+
def get_size_classes
|
119
|
+
TEXTAREA_SIZES[@size]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Restituisce le classi del border radius
|
123
|
+
def get_rounded_classes
|
124
|
+
TEXTAREA_RADIUS[@rounded]
|
125
|
+
end
|
126
|
+
|
127
|
+
# Valida i parametri del componente
|
128
|
+
def validate_params
|
129
|
+
validate_theme
|
130
|
+
validate_size
|
131
|
+
validate_rounded
|
132
|
+
validate_rows
|
133
|
+
end
|
134
|
+
|
135
|
+
# Valida il tema
|
136
|
+
def validate_theme
|
137
|
+
return if TEXTAREA_THEME.key?(@theme)
|
138
|
+
|
139
|
+
raise ArgumentError, "Tema non valido: #{@theme}. Temi supportati: #{TEXTAREA_THEME.keys.join(', ')}"
|
140
|
+
end
|
141
|
+
|
142
|
+
# Valida la dimensione
|
143
|
+
def validate_size
|
144
|
+
return if TEXTAREA_SIZES.key?(@size)
|
145
|
+
|
146
|
+
raise ArgumentError, "Dimensione non valida: #{@size}. Dimensioni supportate: #{TEXTAREA_SIZES.keys.join(', ')}"
|
147
|
+
end
|
148
|
+
|
149
|
+
# Valida il border radius
|
150
|
+
def validate_rounded
|
151
|
+
return if TEXTAREA_RADIUS.key?(@rounded)
|
152
|
+
|
153
|
+
raise ArgumentError, "Border radius non valido: #{@rounded}. Valori supportati: #{TEXTAREA_RADIUS.keys.join(', ')}"
|
154
|
+
end
|
155
|
+
|
156
|
+
# Valida il numero di righe
|
157
|
+
def validate_rows
|
158
|
+
return if @rows.is_a?(Integer) && @rows.positive?
|
159
|
+
|
160
|
+
raise ArgumentError, "Il numero di righe deve essere un intero positivo: #{@rows}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%# Template per il link %>
|
2
|
+
<% if link? %>
|
3
|
+
<%= link_to @href, **element_attributes do %>
|
4
|
+
<% if show_icon? %>
|
5
|
+
<span class="<%= icon_classes %>"><%= render_icon %></span>
|
6
|
+
<% end %>
|
7
|
+
<span class="<%= text_classes %>"><%= @label %></span>
|
8
|
+
<%= content %>
|
9
|
+
<% end %>
|
10
|
+
<% else %>
|
11
|
+
<%= tag.span(**element_attributes) do %>
|
12
|
+
<% if show_icon? %>
|
13
|
+
<span class="<%= icon_classes %>"><%= render_icon %></span>
|
14
|
+
<% end %>
|
15
|
+
<span class="<%= text_classes %>"><%= @label %></span>
|
16
|
+
<%= content %>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
@@ -0,0 +1,258 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module General
|
3
|
+
module Link
|
4
|
+
class Component < ViewComponent::Base
|
5
|
+
attr_reader :label, :href, :theme, :orientation, :style, :size, :icon, :active, :disabled, :data, :method, :target
|
6
|
+
|
7
|
+
# Classi base sempre presenti
|
8
|
+
LINK_BASE_CLASSES = "transition-colors duration-200 no-underline"
|
9
|
+
|
10
|
+
# Temi con classi Tailwind dirette - LOGICA CORRETTA
|
11
|
+
LINK_THEME_CLASSES = {
|
12
|
+
default: "text-white hover:text-gray-300", # Bianco per sfondi scuri
|
13
|
+
white: "text-gray-900 hover:text-gray-700", # Nero per sfondi chiari
|
14
|
+
red: "text-red-500 hover:text-red-600",
|
15
|
+
rose: "text-rose-500 hover:text-rose-600",
|
16
|
+
orange: "text-orange-500 hover:text-orange-600",
|
17
|
+
green: "text-green-500 hover:text-green-600",
|
18
|
+
blue: "text-blue-500 hover:text-blue-600",
|
19
|
+
yellow: "text-yellow-600 hover:text-yellow-700",
|
20
|
+
violet: "text-violet-500 hover:text-violet-600"
|
21
|
+
}
|
22
|
+
|
23
|
+
# Orientamenti con classi Tailwind dirette
|
24
|
+
LINK_ORIENTATION_CLASSES = {
|
25
|
+
horizontal: "inline-flex items-center",
|
26
|
+
vertical: "flex flex-col items-center"
|
27
|
+
}
|
28
|
+
|
29
|
+
# Stili con classi Tailwind dirette
|
30
|
+
LINK_STYLE_CLASSES = {
|
31
|
+
default: "",
|
32
|
+
underline: "underline",
|
33
|
+
bold: "font-bold",
|
34
|
+
text: "no-underline"
|
35
|
+
}
|
36
|
+
|
37
|
+
# Dimensioni con classi Tailwind dirette
|
38
|
+
LINK_SIZE_CLASSES = {
|
39
|
+
extra_small: "text-[0.65rem]",
|
40
|
+
small: "text-sm",
|
41
|
+
medium: "text-base",
|
42
|
+
large: "text-lg"
|
43
|
+
}
|
44
|
+
|
45
|
+
# Stati con classi Tailwind dirette
|
46
|
+
LINK_STATE_CLASSES = {
|
47
|
+
normal: "",
|
48
|
+
active: "font-semibold",
|
49
|
+
disabled: "opacity-50 cursor-not-allowed pointer-events-none"
|
50
|
+
}
|
51
|
+
|
52
|
+
# @param label [String] testo del link
|
53
|
+
# @param href [String] URL di destinazione (nil per semplice testo)
|
54
|
+
# @param theme [Symbol] tema del colore (:default, :white, etc.)
|
55
|
+
# @param orientation [Symbol] orientamento (:horizontal, :vertical)
|
56
|
+
# @param style [Symbol] stile (:default, :underline, :bold, :text)
|
57
|
+
# @param size [Symbol] dimensione (:extra_small, :small, :medium, :large)
|
58
|
+
# @param icon [String] icona opzionale
|
59
|
+
# @param active [Boolean] stato attivo del link
|
60
|
+
# @param disabled [Boolean] stato disabilitato del link
|
61
|
+
# @param data [Hash] attributi data
|
62
|
+
# @param method [Symbol] metodo HTTP (per Turbo)
|
63
|
+
# @param target [String] target del link
|
64
|
+
# @param html_options [Hash] opzioni HTML aggiuntive
|
65
|
+
def initialize(
|
66
|
+
label:,
|
67
|
+
href: nil,
|
68
|
+
theme: :white,
|
69
|
+
orientation: :horizontal,
|
70
|
+
style: :default,
|
71
|
+
size: :medium,
|
72
|
+
icon: nil,
|
73
|
+
active: false,
|
74
|
+
disabled: false,
|
75
|
+
data: {},
|
76
|
+
method: nil,
|
77
|
+
target: nil,
|
78
|
+
**html_options
|
79
|
+
)
|
80
|
+
@label = label
|
81
|
+
@href = href
|
82
|
+
@theme = theme.to_sym
|
83
|
+
@orientation = orientation.to_sym
|
84
|
+
@style = style.to_sym
|
85
|
+
@size = size.to_sym
|
86
|
+
@icon = icon
|
87
|
+
@active = active
|
88
|
+
@disabled = disabled
|
89
|
+
@data = data || {}
|
90
|
+
@method = method
|
91
|
+
@target = target
|
92
|
+
@html_options = html_options
|
93
|
+
|
94
|
+
validate_params
|
95
|
+
end
|
96
|
+
|
97
|
+
# Determina se è un link attivo/corrente
|
98
|
+
def active?
|
99
|
+
@active
|
100
|
+
end
|
101
|
+
|
102
|
+
# Determina se è disabilitato
|
103
|
+
def disabled?
|
104
|
+
@disabled
|
105
|
+
end
|
106
|
+
|
107
|
+
# Determina se è un link o solo testo
|
108
|
+
def link?
|
109
|
+
@href.present? && !@disabled
|
110
|
+
end
|
111
|
+
|
112
|
+
# Combina tutte le classi CSS
|
113
|
+
def combined_classes
|
114
|
+
[
|
115
|
+
LINK_BASE_CLASSES,
|
116
|
+
get_theme_class,
|
117
|
+
get_orientation_class,
|
118
|
+
get_style_class,
|
119
|
+
get_size_class,
|
120
|
+
get_state_class,
|
121
|
+
@html_options[:class]
|
122
|
+
].compact.join(" ")
|
123
|
+
end
|
124
|
+
|
125
|
+
# Classi per l'icona con dimensionamento proporzionale
|
126
|
+
def icon_classes
|
127
|
+
return "" unless @icon.present?
|
128
|
+
|
129
|
+
# Definisce spacing e dimensioni icona basate su size
|
130
|
+
base_spacing = case @orientation
|
131
|
+
when :horizontal
|
132
|
+
"mr-2"
|
133
|
+
when :vertical
|
134
|
+
"mb-1"
|
135
|
+
else
|
136
|
+
"mr-2"
|
137
|
+
end
|
138
|
+
|
139
|
+
icon_size = case @size
|
140
|
+
when :extra_small
|
141
|
+
"w-3 h-3"
|
142
|
+
when :small
|
143
|
+
"w-4 h-4"
|
144
|
+
when :medium
|
145
|
+
"w-5 h-5"
|
146
|
+
when :large
|
147
|
+
"w-6 h-6"
|
148
|
+
else
|
149
|
+
"w-5 h-5"
|
150
|
+
end
|
151
|
+
|
152
|
+
"#{base_spacing} #{icon_size} inline-block"
|
153
|
+
end
|
154
|
+
|
155
|
+
# Classi per il testo
|
156
|
+
def text_classes
|
157
|
+
"inline-block"
|
158
|
+
end
|
159
|
+
|
160
|
+
# Restituisce gli attributi per il link/span
|
161
|
+
def element_attributes
|
162
|
+
attrs = @html_options.except(:class)
|
163
|
+
attrs[:class] = combined_classes
|
164
|
+
|
165
|
+
if link?
|
166
|
+
# Attributi specifici per i link
|
167
|
+
if @method.present?
|
168
|
+
attrs[:data] = @data.merge(turbo_method: @method)
|
169
|
+
elsif @data.present?
|
170
|
+
attrs[:data] = @data
|
171
|
+
end
|
172
|
+
|
173
|
+
attrs[:target] = @target if @target.present?
|
174
|
+
attrs[:aria] ||= {}
|
175
|
+
attrs[:aria][:current] = "page" if active?
|
176
|
+
else
|
177
|
+
# Attributi per span (testo semplice o disabilitato)
|
178
|
+
attrs[:aria] ||= {}
|
179
|
+
attrs[:aria][:disabled] = true if disabled?
|
180
|
+
end
|
181
|
+
|
182
|
+
attrs
|
183
|
+
end
|
184
|
+
|
185
|
+
# Determina se mostrare l'icona
|
186
|
+
def show_icon?
|
187
|
+
@icon.present?
|
188
|
+
end
|
189
|
+
|
190
|
+
# Renderizza l'icona
|
191
|
+
def render_icon
|
192
|
+
return nil unless show_icon?
|
193
|
+
|
194
|
+
if @icon.is_a?(String)
|
195
|
+
render BetterUi::General::IconComponent.new(name: @icon)
|
196
|
+
else
|
197
|
+
@icon # Assumiamo che sia già un componente renderizzato
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def get_theme_class
|
204
|
+
LINK_THEME_CLASSES[@theme] || LINK_THEME_CLASSES[:white]
|
205
|
+
end
|
206
|
+
|
207
|
+
def get_orientation_class
|
208
|
+
LINK_ORIENTATION_CLASSES[@orientation] || LINK_ORIENTATION_CLASSES[:horizontal]
|
209
|
+
end
|
210
|
+
|
211
|
+
def get_style_class
|
212
|
+
LINK_STYLE_CLASSES[@style] || LINK_STYLE_CLASSES[:default]
|
213
|
+
end
|
214
|
+
|
215
|
+
def get_size_class
|
216
|
+
LINK_SIZE_CLASSES[@size] || LINK_SIZE_CLASSES[:medium]
|
217
|
+
end
|
218
|
+
|
219
|
+
def get_state_class
|
220
|
+
return LINK_STATE_CLASSES[:disabled] if disabled?
|
221
|
+
return LINK_STATE_CLASSES[:active] if active?
|
222
|
+
LINK_STATE_CLASSES[:normal]
|
223
|
+
end
|
224
|
+
|
225
|
+
def validate_params
|
226
|
+
validate_theme
|
227
|
+
validate_orientation
|
228
|
+
validate_style
|
229
|
+
validate_size
|
230
|
+
end
|
231
|
+
|
232
|
+
def validate_theme
|
233
|
+
unless LINK_THEME_CLASSES.keys.include?(@theme)
|
234
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{LINK_THEME_CLASSES.keys.join(', ')}"
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def validate_orientation
|
239
|
+
unless LINK_ORIENTATION_CLASSES.keys.include?(@orientation)
|
240
|
+
raise ArgumentError, "L'orientamento deve essere uno tra: #{LINK_ORIENTATION_CLASSES.keys.join(', ')}"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def validate_style
|
245
|
+
unless LINK_STYLE_CLASSES.keys.include?(@style)
|
246
|
+
raise ArgumentError, "Lo stile deve essere uno tra: #{LINK_STYLE_CLASSES.keys.join(', ')}"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def validate_size
|
251
|
+
unless LINK_SIZE_CLASSES.keys.include?(@size)
|
252
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{LINK_SIZE_CLASSES.keys.join(', ')}"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<%# Template per il panel %>
|
2
|
+
<div <%= tag.attributes(panel_attributes) %>>
|
3
|
+
<% if show_header? %>
|
4
|
+
<div class="<%= header_classes %>">
|
5
|
+
<% if @header.present? %>
|
6
|
+
<%= raw @header %>
|
7
|
+
<% elsif @title.present? %>
|
8
|
+
<div class="<%= title_classes %>"><%= @title %></div>
|
9
|
+
<% end %>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<% if show_body? %>
|
14
|
+
<div class="<%= body_classes %>">
|
15
|
+
<% if @body.present? %>
|
16
|
+
<%= raw @body %>
|
17
|
+
<% elsif content.present? %>
|
18
|
+
<%= content %>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<% if show_footer? %>
|
24
|
+
<div class="<%= footer_classes %>">
|
25
|
+
<%= raw @footer %>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
|
+
</div>
|