better_ui 0.1.0 → 0.2.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/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/dropdown/component.html.erb +14 -0
- data/app/components/better_ui/general/dropdown/component.rb +219 -0
- data/app/components/better_ui/general/dropdown/divider_component.html.erb +1 -0
- data/app/components/better_ui/general/dropdown/divider_component.rb +41 -0
- data/app/components/better_ui/general/dropdown/item_component.html.erb +6 -0
- data/app/components/better_ui/general/dropdown/item_component.rb +118 -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/modal/component.html.erb +42 -0
- data/app/components/better_ui/general/modal/component.rb +165 -0
- data/app/components/better_ui/general/pagination/component.html.erb +85 -0
- data/app/components/better_ui/general/pagination/component.rb +216 -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/tabs/component.html.erb +3 -0
- data/app/components/better_ui/general/tabs/component.rb +102 -0
- data/app/components/better_ui/general/tabs/panel_component.html.erb +3 -0
- data/app/components/better_ui/general/tabs/panel_component.rb +37 -0
- data/app/components/better_ui/general/tabs/tab_component.html.erb +13 -0
- data/app/components/better_ui/general/tabs/tab_component.rb +111 -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 +51 -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/dropdown/divider_helper.rb +32 -0
- data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +79 -0
- data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +62 -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/modal/modal_helper.rb +95 -0
- data/app/helpers/better_ui/general/components/pagination/pagination_helper.rb +82 -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/tabs/panel_helper.rb +62 -0
- data/app/helpers/better_ui/general/components/tabs/tab_helper.rb +55 -0
- data/app/helpers/better_ui/general/components/tabs/tabs_helper.rb +62 -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 +155 -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,10 @@
|
|
1
|
+
<%# Template per il divider %>
|
2
|
+
<% if show_label? %>
|
3
|
+
<%= tag.div(**divider_attributes) do %>
|
4
|
+
<div class="<%= label_line_classes %>"></div>
|
5
|
+
<span class="<%= label_classes %>"><%= @label %></span>
|
6
|
+
<div class="<%= label_line_classes %>"></div>
|
7
|
+
<% end %>
|
8
|
+
<% else %>
|
9
|
+
<%= tag.div(**divider_attributes) %>
|
10
|
+
<% end %>
|
@@ -0,0 +1,226 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module General
|
3
|
+
module Divider
|
4
|
+
class Component < ViewComponent::Base
|
5
|
+
attr_reader :theme, :orientation, :style, :size, :label, :height, :classes
|
6
|
+
|
7
|
+
# Classi base sempre presenti
|
8
|
+
DIVIDER_BASE_CLASSES = "my-4"
|
9
|
+
|
10
|
+
# Temi con classi Tailwind dirette - LOGICA CORRETTA
|
11
|
+
DIVIDER_THEME_CLASSES = {
|
12
|
+
default: "border-white", # Bordo bianco (per sfondi scuri)
|
13
|
+
white: "border-gray-300", # Bordo grigio (per sfondi chiari)
|
14
|
+
red: "border-red-500",
|
15
|
+
rose: "border-rose-500",
|
16
|
+
orange: "border-orange-500",
|
17
|
+
green: "border-green-500",
|
18
|
+
blue: "border-blue-500",
|
19
|
+
yellow: "border-yellow-500",
|
20
|
+
violet: "border-violet-500"
|
21
|
+
}
|
22
|
+
|
23
|
+
# Orientamento con classi Tailwind dirette
|
24
|
+
DIVIDER_ORIENTATION_CLASSES = {
|
25
|
+
horizontal: "w-full border-t",
|
26
|
+
vertical: "h-full border-l"
|
27
|
+
}
|
28
|
+
|
29
|
+
# Stili di linea con classi Tailwind dirette
|
30
|
+
DIVIDER_STYLE_CLASSES = {
|
31
|
+
solid: "border-solid",
|
32
|
+
dashed: "border-dashed",
|
33
|
+
dotted: "border-dotted",
|
34
|
+
double: "border-double"
|
35
|
+
}
|
36
|
+
|
37
|
+
# Dimensioni con classi Tailwind dirette
|
38
|
+
DIVIDER_SIZE_CLASSES = {
|
39
|
+
thin: {
|
40
|
+
horizontal: "border-t",
|
41
|
+
vertical: "border-l"
|
42
|
+
},
|
43
|
+
medium: {
|
44
|
+
horizontal: "border-t-2",
|
45
|
+
vertical: "border-l-2"
|
46
|
+
},
|
47
|
+
thick: {
|
48
|
+
horizontal: "border-t-4",
|
49
|
+
vertical: "border-l-4"
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
# Classi per label con classi Tailwind dirette - LOGICA CORRETTA
|
54
|
+
DIVIDER_LABEL_THEME_CLASSES = {
|
55
|
+
default: "text-white bg-transparent px-3", # Testo bianco trasparente (per sfondi scuri)
|
56
|
+
white: "text-gray-900 bg-white px-3", # Testo nero su bianco (per sfondi chiari)
|
57
|
+
red: "text-red-500 bg-white px-3",
|
58
|
+
rose: "text-rose-500 bg-white px-3",
|
59
|
+
orange: "text-orange-500 bg-white px-3",
|
60
|
+
green: "text-green-500 bg-white px-3",
|
61
|
+
blue: "text-blue-500 bg-white px-3",
|
62
|
+
yellow: "text-yellow-600 bg-white px-3",
|
63
|
+
violet: "text-violet-500 bg-white px-3"
|
64
|
+
}
|
65
|
+
|
66
|
+
# @param theme [Symbol] tema del divider (:default, :white, etc.)
|
67
|
+
# @param orientation [Symbol] orientamento del divider (:horizontal, :vertical)
|
68
|
+
# @param style [Symbol] stile della linea (:solid, :dashed, :dotted, :double)
|
69
|
+
# @param size [Symbol] dimensione della linea (:thin, :medium, :thick)
|
70
|
+
# @param label [String] testo opzionale da mostrare al centro del divider
|
71
|
+
# @param height [String] altezza per divider verticale (es. "100px", "100%")
|
72
|
+
# @param classes [String] classi CSS aggiuntive
|
73
|
+
# @param html_options [Hash] opzioni HTML per il container
|
74
|
+
def initialize(
|
75
|
+
theme: :white,
|
76
|
+
orientation: :horizontal,
|
77
|
+
style: :solid,
|
78
|
+
size: :medium,
|
79
|
+
label: nil,
|
80
|
+
height: nil,
|
81
|
+
classes: nil,
|
82
|
+
**html_options
|
83
|
+
)
|
84
|
+
@theme = theme.to_sym
|
85
|
+
@orientation = orientation.to_sym
|
86
|
+
@style = style.to_sym
|
87
|
+
@size = size.to_sym
|
88
|
+
@label = label
|
89
|
+
@height = height
|
90
|
+
@classes = classes
|
91
|
+
@html_options = html_options
|
92
|
+
|
93
|
+
validate_params
|
94
|
+
end
|
95
|
+
|
96
|
+
# Combina tutte le classi per il container
|
97
|
+
def combined_classes
|
98
|
+
base_classes = []
|
99
|
+
|
100
|
+
if @label.present? && @orientation == :horizontal
|
101
|
+
# Per divider con label orizzontale: flex layout
|
102
|
+
base_classes = [
|
103
|
+
"flex items-center text-center",
|
104
|
+
get_theme_class,
|
105
|
+
get_style_class
|
106
|
+
]
|
107
|
+
else
|
108
|
+
# Per divider normale
|
109
|
+
base_classes = [
|
110
|
+
DIVIDER_BASE_CLASSES,
|
111
|
+
get_orientation_class,
|
112
|
+
get_theme_class,
|
113
|
+
get_style_class,
|
114
|
+
get_size_class
|
115
|
+
]
|
116
|
+
end
|
117
|
+
|
118
|
+
[ *base_classes, @classes, @html_options[:class] ].compact.join(" ")
|
119
|
+
end
|
120
|
+
|
121
|
+
# Classi per il label
|
122
|
+
def label_classes
|
123
|
+
return "" unless @label.present?
|
124
|
+
|
125
|
+
[
|
126
|
+
"relative z-10 text-sm font-medium",
|
127
|
+
get_label_theme_class
|
128
|
+
].compact.join(" ")
|
129
|
+
end
|
130
|
+
|
131
|
+
# Classi per le linee before/after quando c'è un label
|
132
|
+
def label_line_classes
|
133
|
+
return "" unless @label.present? && @orientation == :horizontal
|
134
|
+
|
135
|
+
[
|
136
|
+
"flex-1 h-px",
|
137
|
+
get_theme_class,
|
138
|
+
get_style_class
|
139
|
+
].compact.join(" ")
|
140
|
+
end
|
141
|
+
|
142
|
+
# Genera gli attributi di stile inline necessari
|
143
|
+
def inline_styles
|
144
|
+
return nil unless @orientation == :vertical && @height.present?
|
145
|
+
"height: #{@height};"
|
146
|
+
end
|
147
|
+
|
148
|
+
# Restituisce gli attributi per il divider
|
149
|
+
def divider_attributes
|
150
|
+
attrs = {
|
151
|
+
class: combined_classes
|
152
|
+
}
|
153
|
+
|
154
|
+
# Aggiungi stile inline se presente
|
155
|
+
attrs[:style] = inline_styles if inline_styles.present?
|
156
|
+
|
157
|
+
# Aggiungi altri attributi HTML se presenti
|
158
|
+
@html_options.except(:class).each do |key, value|
|
159
|
+
attrs[key] = value
|
160
|
+
end
|
161
|
+
|
162
|
+
attrs
|
163
|
+
end
|
164
|
+
|
165
|
+
# Determina se mostrare il label
|
166
|
+
def show_label?
|
167
|
+
@label.present? && @orientation == :horizontal
|
168
|
+
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def get_theme_class
|
173
|
+
DIVIDER_THEME_CLASSES[@theme] || DIVIDER_THEME_CLASSES[:white]
|
174
|
+
end
|
175
|
+
|
176
|
+
def get_orientation_class
|
177
|
+
DIVIDER_ORIENTATION_CLASSES[@orientation] || DIVIDER_ORIENTATION_CLASSES[:horizontal]
|
178
|
+
end
|
179
|
+
|
180
|
+
def get_style_class
|
181
|
+
DIVIDER_STYLE_CLASSES[@style] || DIVIDER_STYLE_CLASSES[:solid]
|
182
|
+
end
|
183
|
+
|
184
|
+
def get_size_class
|
185
|
+
size_map = DIVIDER_SIZE_CLASSES[@size] || DIVIDER_SIZE_CLASSES[:medium]
|
186
|
+
size_map[@orientation] || size_map[:horizontal]
|
187
|
+
end
|
188
|
+
|
189
|
+
def get_label_theme_class
|
190
|
+
DIVIDER_LABEL_THEME_CLASSES[@theme] || DIVIDER_LABEL_THEME_CLASSES[:white]
|
191
|
+
end
|
192
|
+
|
193
|
+
def validate_params
|
194
|
+
validate_theme
|
195
|
+
validate_orientation
|
196
|
+
validate_style
|
197
|
+
validate_size
|
198
|
+
end
|
199
|
+
|
200
|
+
def validate_theme
|
201
|
+
unless DIVIDER_THEME_CLASSES.keys.include?(@theme)
|
202
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{DIVIDER_THEME_CLASSES.keys.join(', ')}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def validate_orientation
|
207
|
+
unless DIVIDER_ORIENTATION_CLASSES.keys.include?(@orientation)
|
208
|
+
raise ArgumentError, "L'orientamento deve essere uno tra: #{DIVIDER_ORIENTATION_CLASSES.keys.join(', ')}"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def validate_style
|
213
|
+
unless DIVIDER_STYLE_CLASSES.keys.include?(@style)
|
214
|
+
raise ArgumentError, "Lo stile deve essere uno tra: #{DIVIDER_STYLE_CLASSES.keys.join(', ')}"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def validate_size
|
219
|
+
unless DIVIDER_SIZE_CLASSES.keys.include?(@size)
|
220
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{DIVIDER_SIZE_CLASSES.keys.join(', ')}"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div <%= tag.attributes(container_attributes) %>>
|
2
|
+
<button <%= tag.attributes(trigger_attributes) %>>
|
3
|
+
<%= @trigger %>
|
4
|
+
<svg class="ml-2 -mr-1 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
5
|
+
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
6
|
+
</svg>
|
7
|
+
</button>
|
8
|
+
|
9
|
+
<div <%= tag.attributes(menu_attributes) %>>
|
10
|
+
<div class="py-1" role="none">
|
11
|
+
<%= content %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
</div>
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Dropdown
|
6
|
+
class Component < ViewComponent::Base
|
7
|
+
attr_reader :trigger, :position, :theme, :size, :rounded, :animation, :classes, :html_options
|
8
|
+
|
9
|
+
# Classi base per il contenitore dropdown
|
10
|
+
DROPDOWN_CONTAINER_CLASSES = "relative inline-block"
|
11
|
+
|
12
|
+
# Classi base per il pulsante trigger
|
13
|
+
DROPDOWN_TRIGGER_BASE_CLASSES = "inline-flex items-center justify-center border font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors"
|
14
|
+
|
15
|
+
# Classi base per il menu dropdown
|
16
|
+
DROPDOWN_MENU_BASE_CLASSES = "absolute z-50 mt-2 origin-top-right bg-white border border-gray-200 shadow-lg focus:outline-none"
|
17
|
+
|
18
|
+
# Temi per il trigger del dropdown con classi Tailwind dirette
|
19
|
+
DROPDOWN_TRIGGER_THEME = {
|
20
|
+
default: "bg-white border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-blue-500",
|
21
|
+
white: "bg-white border-gray-300 text-gray-900 hover:bg-gray-50 focus:ring-gray-500",
|
22
|
+
red: "bg-red-600 border-red-600 text-white hover:bg-red-700 focus:ring-red-500",
|
23
|
+
rose: "bg-rose-600 border-rose-600 text-white hover:bg-rose-700 focus:ring-rose-500",
|
24
|
+
orange: "bg-orange-600 border-orange-600 text-white hover:bg-orange-700 focus:ring-orange-500",
|
25
|
+
green: "bg-green-600 border-green-600 text-white hover:bg-green-700 focus:ring-green-500",
|
26
|
+
blue: "bg-blue-600 border-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
|
27
|
+
yellow: "bg-yellow-500 border-yellow-500 text-white hover:bg-yellow-600 focus:ring-yellow-500",
|
28
|
+
violet: "bg-violet-600 border-violet-600 text-white hover:bg-violet-700 focus:ring-violet-500"
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
# Dimensioni del trigger con classi Tailwind dirette
|
32
|
+
DROPDOWN_TRIGGER_SIZE = {
|
33
|
+
small: "px-3 py-1.5 text-sm",
|
34
|
+
medium: "px-4 py-2 text-sm",
|
35
|
+
large: "px-6 py-3 text-base"
|
36
|
+
}.freeze
|
37
|
+
|
38
|
+
# Border radius con classi Tailwind dirette
|
39
|
+
DROPDOWN_ROUNDED = {
|
40
|
+
none: "rounded-none",
|
41
|
+
small: "rounded-md",
|
42
|
+
medium: "rounded-lg",
|
43
|
+
large: "rounded-xl",
|
44
|
+
full: "rounded-full"
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
# Posizioni del menu dropdown
|
48
|
+
DROPDOWN_POSITION = {
|
49
|
+
bottom: "top-full left-0",
|
50
|
+
top: "bottom-full left-0",
|
51
|
+
left: "top-0 right-full mr-2",
|
52
|
+
right: "top-0 left-full ml-2"
|
53
|
+
}.freeze
|
54
|
+
|
55
|
+
# Animazioni del dropdown
|
56
|
+
DROPDOWN_ANIMATION = {
|
57
|
+
fade: "transition-opacity duration-150",
|
58
|
+
slide: "transition-all duration-150 transform",
|
59
|
+
none: ""
|
60
|
+
}.freeze
|
61
|
+
|
62
|
+
def initialize(
|
63
|
+
trigger:,
|
64
|
+
position: :bottom,
|
65
|
+
theme: :default,
|
66
|
+
size: :medium,
|
67
|
+
rounded: :medium,
|
68
|
+
animation: :fade,
|
69
|
+
classes: nil,
|
70
|
+
**html_options
|
71
|
+
)
|
72
|
+
@trigger = trigger
|
73
|
+
@position = position.to_sym
|
74
|
+
@theme = theme.to_sym
|
75
|
+
@size = size.to_sym
|
76
|
+
@rounded = rounded.to_sym
|
77
|
+
@animation = animation.to_sym
|
78
|
+
@classes = classes
|
79
|
+
@html_options = html_options
|
80
|
+
|
81
|
+
validate_params
|
82
|
+
end
|
83
|
+
|
84
|
+
# Combina tutte le classi per il contenitore
|
85
|
+
def container_classes
|
86
|
+
[
|
87
|
+
DROPDOWN_CONTAINER_CLASSES,
|
88
|
+
@classes
|
89
|
+
].compact.join(" ")
|
90
|
+
end
|
91
|
+
|
92
|
+
# Combina tutte le classi per il trigger
|
93
|
+
def trigger_classes
|
94
|
+
[
|
95
|
+
DROPDOWN_TRIGGER_BASE_CLASSES,
|
96
|
+
get_trigger_theme_classes,
|
97
|
+
get_trigger_size_classes,
|
98
|
+
get_trigger_rounded_classes
|
99
|
+
].compact.join(" ")
|
100
|
+
end
|
101
|
+
|
102
|
+
# Combina tutte le classi per il menu
|
103
|
+
def menu_classes
|
104
|
+
[
|
105
|
+
DROPDOWN_MENU_BASE_CLASSES,
|
106
|
+
get_position_classes,
|
107
|
+
get_animation_classes,
|
108
|
+
get_menu_rounded_classes
|
109
|
+
].compact.join(" ")
|
110
|
+
end
|
111
|
+
|
112
|
+
# Restituisce gli attributi per il contenitore
|
113
|
+
def container_attributes
|
114
|
+
attrs = {
|
115
|
+
class: container_classes,
|
116
|
+
"data-dropdown": true
|
117
|
+
}
|
118
|
+
|
119
|
+
@html_options.except(:class).each do |key, value|
|
120
|
+
attrs[key] = value
|
121
|
+
end
|
122
|
+
|
123
|
+
attrs
|
124
|
+
end
|
125
|
+
|
126
|
+
# Restituisce gli attributi per il trigger
|
127
|
+
def trigger_attributes
|
128
|
+
{
|
129
|
+
type: "button",
|
130
|
+
class: trigger_classes,
|
131
|
+
"data-dropdown-trigger": true,
|
132
|
+
"aria-expanded": "false",
|
133
|
+
"aria-haspopup": "true"
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
# Restituisce gli attributi per il menu
|
138
|
+
def menu_attributes
|
139
|
+
{
|
140
|
+
class: menu_classes,
|
141
|
+
"data-dropdown-menu": true,
|
142
|
+
role: "menu",
|
143
|
+
"aria-orientation": "vertical",
|
144
|
+
style: "display: none;"
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
# Verifica se rendere il componente
|
149
|
+
def render?
|
150
|
+
@trigger.present?
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def get_trigger_theme_classes
|
156
|
+
DROPDOWN_TRIGGER_THEME[@theme] || DROPDOWN_TRIGGER_THEME[:default]
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_trigger_size_classes
|
160
|
+
DROPDOWN_TRIGGER_SIZE[@size] || DROPDOWN_TRIGGER_SIZE[:medium]
|
161
|
+
end
|
162
|
+
|
163
|
+
def get_trigger_rounded_classes
|
164
|
+
DROPDOWN_ROUNDED[@rounded] || DROPDOWN_ROUNDED[:medium]
|
165
|
+
end
|
166
|
+
|
167
|
+
def get_menu_rounded_classes
|
168
|
+
DROPDOWN_ROUNDED[@rounded] || DROPDOWN_ROUNDED[:medium]
|
169
|
+
end
|
170
|
+
|
171
|
+
def get_position_classes
|
172
|
+
DROPDOWN_POSITION[@position] || DROPDOWN_POSITION[:bottom]
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_animation_classes
|
176
|
+
DROPDOWN_ANIMATION[@animation] || DROPDOWN_ANIMATION[:fade]
|
177
|
+
end
|
178
|
+
|
179
|
+
def validate_params
|
180
|
+
validate_theme
|
181
|
+
validate_size
|
182
|
+
validate_rounded
|
183
|
+
validate_position
|
184
|
+
validate_animation
|
185
|
+
end
|
186
|
+
|
187
|
+
def validate_theme
|
188
|
+
unless DROPDOWN_TRIGGER_THEME.keys.include?(@theme)
|
189
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{DROPDOWN_TRIGGER_THEME.keys.join(', ')}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def validate_size
|
194
|
+
unless DROPDOWN_TRIGGER_SIZE.keys.include?(@size)
|
195
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{DROPDOWN_TRIGGER_SIZE.keys.join(', ')}"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def validate_rounded
|
200
|
+
unless DROPDOWN_ROUNDED.keys.include?(@rounded)
|
201
|
+
raise ArgumentError, "Il border radius deve essere uno tra: #{DROPDOWN_ROUNDED.keys.join(', ')}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def validate_position
|
206
|
+
unless DROPDOWN_POSITION.keys.include?(@position)
|
207
|
+
raise ArgumentError, "La posizione deve essere una tra: #{DROPDOWN_POSITION.keys.join(', ')}"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def validate_animation
|
212
|
+
unless DROPDOWN_ANIMATION.keys.include?(@animation)
|
213
|
+
raise ArgumentError, "L'animazione deve essere una tra: #{DROPDOWN_ANIMATION.keys.join(', ')}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<div <%= tag.attributes(divider_attributes) %>></div>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Dropdown
|
6
|
+
class DividerComponent < ViewComponent::Base
|
7
|
+
attr_reader :classes, :html_options
|
8
|
+
|
9
|
+
# Classi base per il divisore del dropdown
|
10
|
+
DROPDOWN_DIVIDER_CLASSES = "border-t border-gray-100 my-1"
|
11
|
+
|
12
|
+
def initialize(classes: nil, **html_options)
|
13
|
+
@classes = classes
|
14
|
+
@html_options = html_options
|
15
|
+
end
|
16
|
+
|
17
|
+
# Combina tutte le classi per il divisore
|
18
|
+
def divider_classes
|
19
|
+
[
|
20
|
+
DROPDOWN_DIVIDER_CLASSES,
|
21
|
+
@classes
|
22
|
+
].compact.join(" ")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Restituisce gli attributi per il divisore
|
26
|
+
def divider_attributes
|
27
|
+
attrs = {
|
28
|
+
class: divider_classes,
|
29
|
+
role: "separator"
|
30
|
+
}
|
31
|
+
|
32
|
+
@html_options.except(:class).each do |key, value|
|
33
|
+
attrs[key] = value
|
34
|
+
end
|
35
|
+
|
36
|
+
attrs
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Dropdown
|
6
|
+
class ItemComponent < ViewComponent::Base
|
7
|
+
include BetterUi::General::Components::Icon::IconHelper
|
8
|
+
|
9
|
+
attr_reader :text, :icon, :href, :theme, :disabled, :active, :classes, :html_options
|
10
|
+
|
11
|
+
# Classi base per l'elemento del dropdown
|
12
|
+
DROPDOWN_ITEM_BASE_CLASSES = "flex items-center w-full px-4 py-2 text-sm transition-colors"
|
13
|
+
|
14
|
+
# Temi per gli elementi del dropdown con classi Tailwind dirette
|
15
|
+
DROPDOWN_ITEM_THEME = {
|
16
|
+
default: "text-gray-700 hover:bg-gray-100 hover:text-gray-900",
|
17
|
+
white: "text-gray-900 hover:bg-gray-50",
|
18
|
+
red: "text-red-700 hover:bg-red-50 hover:text-red-900",
|
19
|
+
rose: "text-rose-700 hover:bg-rose-50 hover:text-rose-900",
|
20
|
+
orange: "text-orange-700 hover:bg-orange-50 hover:text-orange-900",
|
21
|
+
green: "text-green-700 hover:bg-green-50 hover:text-green-900",
|
22
|
+
blue: "text-blue-700 hover:bg-blue-50 hover:text-blue-900",
|
23
|
+
yellow: "text-yellow-700 hover:bg-yellow-50 hover:text-yellow-900",
|
24
|
+
violet: "text-violet-700 hover:bg-violet-50 hover:text-violet-900"
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
# Stati per gli elementi del dropdown
|
28
|
+
DROPDOWN_ITEM_STATE_DISABLED = "opacity-50 cursor-not-allowed pointer-events-none"
|
29
|
+
DROPDOWN_ITEM_STATE_ACTIVE = "bg-gray-100 text-gray-900"
|
30
|
+
|
31
|
+
def initialize(
|
32
|
+
text:,
|
33
|
+
icon: nil,
|
34
|
+
href: nil,
|
35
|
+
theme: :default,
|
36
|
+
disabled: false,
|
37
|
+
active: false,
|
38
|
+
classes: nil,
|
39
|
+
**html_options
|
40
|
+
)
|
41
|
+
@text = text
|
42
|
+
@icon = icon
|
43
|
+
@href = href
|
44
|
+
@theme = theme.to_sym
|
45
|
+
@disabled = disabled
|
46
|
+
@active = active
|
47
|
+
@classes = classes
|
48
|
+
@html_options = html_options
|
49
|
+
|
50
|
+
validate_params
|
51
|
+
end
|
52
|
+
|
53
|
+
# Combina tutte le classi per l'elemento
|
54
|
+
def item_classes
|
55
|
+
classes = [
|
56
|
+
DROPDOWN_ITEM_BASE_CLASSES,
|
57
|
+
get_theme_classes,
|
58
|
+
@classes
|
59
|
+
]
|
60
|
+
|
61
|
+
classes << DROPDOWN_ITEM_STATE_DISABLED if @disabled
|
62
|
+
classes << DROPDOWN_ITEM_STATE_ACTIVE if @active
|
63
|
+
|
64
|
+
classes.compact.join(" ")
|
65
|
+
end
|
66
|
+
|
67
|
+
# Restituisce gli attributi per l'elemento
|
68
|
+
def item_attributes
|
69
|
+
attrs = {
|
70
|
+
class: item_classes,
|
71
|
+
role: "menuitem"
|
72
|
+
}
|
73
|
+
|
74
|
+
if @href.present? && !@disabled
|
75
|
+
attrs[:href] = @href
|
76
|
+
end
|
77
|
+
|
78
|
+
if @disabled
|
79
|
+
attrs["aria-disabled"] = "true"
|
80
|
+
attrs[:tabindex] = "-1"
|
81
|
+
end
|
82
|
+
|
83
|
+
@html_options.except(:class).each do |key, value|
|
84
|
+
attrs[key] = value
|
85
|
+
end
|
86
|
+
|
87
|
+
attrs
|
88
|
+
end
|
89
|
+
|
90
|
+
# Determina se usare un link o un button
|
91
|
+
def tag_name
|
92
|
+
@href.present? && !@disabled ? :a : :button
|
93
|
+
end
|
94
|
+
|
95
|
+
# Verifica se rendere il componente
|
96
|
+
def render?
|
97
|
+
@text.present?
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def get_theme_classes
|
103
|
+
DROPDOWN_ITEM_THEME[@theme] || DROPDOWN_ITEM_THEME[:default]
|
104
|
+
end
|
105
|
+
|
106
|
+
def validate_params
|
107
|
+
validate_theme
|
108
|
+
end
|
109
|
+
|
110
|
+
def validate_theme
|
111
|
+
unless DROPDOWN_ITEM_THEME.keys.include?(@theme)
|
112
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{DROPDOWN_ITEM_THEME.keys.join(', ')}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<%# Form field component template %>
|
2
|
+
<div class="<%= BASE_CLASSES %>">
|
3
|
+
<% if label.present? %>
|
4
|
+
<label for="<%= id %>" class="<%= LABEL_CLASSES %>">
|
5
|
+
<%= label %>
|
6
|
+
<% if required %>
|
7
|
+
<span class="<%= REQUIRED_CLASSES %>">*</span>
|
8
|
+
<% end %>
|
9
|
+
</label>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<div class="mt-1">
|
13
|
+
<%= content %>
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<% if error.present? %>
|
17
|
+
<div class="<%= ERROR_CLASSES %>">
|
18
|
+
<%= error %>
|
19
|
+
</div>
|
20
|
+
<% end %>
|
21
|
+
|
22
|
+
<% if help_text.present? %>
|
23
|
+
<div class="<%= HELP_TEXT_CLASSES %>">
|
24
|
+
<%= help_text %>
|
25
|
+
</div>
|
26
|
+
<% end %>
|
27
|
+
</div>
|