better_ui_tmp 0.5.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +211 -0
- data/Rakefile +8 -0
- data/app/assets/builds/application.js +1 -0
- data/app/assets/builds/better_ui.css +1 -0
- data/app/assets/stylesheets/better_ui.scss +3 -0
- data/app/components/better_ui/application/main/component.html.erb +5 -0
- data/app/components/better_ui/application/main/component.rb +99 -0
- data/app/components/better_ui/application/navbar/component.html.erb +219 -0
- data/app/components/better_ui/application/navbar/component.rb +148 -0
- data/app/components/better_ui/application/sidebar/component.html.erb +184 -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 +186 -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/card/component.html.erb +21 -0
- data/app/components/better_ui/general/card/component.rb +37 -0
- data/app/components/better_ui/general/container/component.html.erb +8 -0
- data/app/components/better_ui/general/container/component.rb +158 -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/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 +1 -0
- data/app/components/better_ui/general/icon/component.rb +222 -0
- data/app/components/better_ui/general/link/component.html.erb +18 -0
- data/app/components/better_ui/general/link/component.rb +255 -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/controllers/better_ui/application_controller.rb +5 -0
- data/app/helpers/better_ui/application/components/main/main_helper.rb +42 -0
- data/app/helpers/better_ui/application/components/main.rb +13 -0
- data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +51 -0
- data/app/helpers/better_ui/application/components/navbar.rb +13 -0
- data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +51 -0
- data/app/helpers/better_ui/application/components/sidebar.rb +13 -0
- data/app/helpers/better_ui/application_helper.rb +10 -0
- data/app/helpers/better_ui/form_helper.rb +5 -0
- data/app/helpers/better_ui/general/components/alert/alert_helper.rb +29 -0
- data/app/helpers/better_ui/general/components/alert.rb +13 -0
- data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +29 -0
- data/app/helpers/better_ui/general/components/avatar.rb +13 -0
- data/app/helpers/better_ui/general/components/badge/badge_helper.rb +53 -0
- data/app/helpers/better_ui/general/components/badge.rb +13 -0
- data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +37 -0
- data/app/helpers/better_ui/general/components/breadcrumb.rb +13 -0
- data/app/helpers/better_ui/general/components/button/button_helper.rb +65 -0
- data/app/helpers/better_ui/general/components/button.rb +13 -0
- data/app/helpers/better_ui/general/components/card/card_helper.rb +37 -0
- data/app/helpers/better_ui/general/components/card.rb +13 -0
- data/app/helpers/better_ui/general/components/container/container_helper.rb +60 -0
- data/app/helpers/better_ui/general/components/container.rb +13 -0
- data/app/helpers/better_ui/general/components/divider/divider_helper.rb +63 -0
- data/app/helpers/better_ui/general/components/divider.rb +13 -0
- data/app/helpers/better_ui/general/components/heading/heading_helper.rb +72 -0
- data/app/helpers/better_ui/general/components/heading.rb +13 -0
- data/app/helpers/better_ui/general/components/icon/icon_helper.rb +16 -0
- data/app/helpers/better_ui/general/components/icon.rb +13 -0
- data/app/helpers/better_ui/general/components/link/link_helper.rb +89 -0
- data/app/helpers/better_ui/general/components/link.rb +13 -0
- data/app/helpers/better_ui/general/components/panel/panel_helper.rb +83 -0
- data/app/helpers/better_ui/general/components/panel.rb +13 -0
- data/app/helpers/better_ui/general/components/progress/progress_helper.rb +53 -0
- data/app/helpers/better_ui/general/components/progress.rb +11 -0
- data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +17 -0
- data/app/helpers/better_ui/general/components/spinner.rb +10 -0
- data/app/helpers/better_ui/general/components/table/table_helper.rb +13 -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/table.rb +25 -0
- data/app/helpers/better_ui/general/components/tag/tag_helper.rb +26 -0
- data/app/helpers/better_ui/general/components/tag.rb +15 -0
- data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +60 -0
- data/app/helpers/better_ui/general/components/tooltip.rb +13 -0
- data/app/helpers/better_ui/general_helper.rb +24 -0
- data/app/helpers/better_ui_helper.rb +16 -0
- data/app/javascript/application.js +1 -0
- data/app/jobs/better_ui/application_job.rb +4 -0
- data/app/mailers/better_ui/application_mailer.rb +6 -0
- data/app/models/better_ui/application_record.rb +5 -0
- data/app/views/components/better_ui/general/table/_custom_body_row.html.erb +17 -0
- data/app/views/components/better_ui/general/table/_custom_footer_rows.html.erb +17 -0
- data/app/views/components/better_ui/general/table/_custom_header_rows.html.erb +12 -0
- data/app/views/layouts/component_preview.html.erb +32 -0
- data/config/initializers/lookbook.rb +23 -0
- data/config/routes.rb +3 -0
- data/lib/better_ui/engine.rb +109 -0
- data/lib/better_ui/version.rb +3 -0
- data/lib/better_ui.rb +37 -0
- data/lib/generators/better_ui/install_generator.rb +103 -0
- data/lib/generators/better_ui/stylesheet_generator.rb +159 -0
- data/lib/generators/better_ui/templates/components/_avatar.scss +200 -0
- data/lib/generators/better_ui/templates/components/_badge.scss +154 -0
- data/lib/generators/better_ui/templates/components/_breadcrumb.scss +106 -0
- data/lib/generators/better_ui/templates/components/_button.scss +109 -0
- data/lib/generators/better_ui/templates/components/_card.scss +60 -0
- data/lib/generators/better_ui/templates/components/_heading.scss +81 -0
- data/lib/generators/better_ui/templates/components/_icon.scss +134 -0
- data/lib/generators/better_ui/templates/components/_index.scss +17 -0
- data/lib/generators/better_ui/templates/components/_link.scss +100 -0
- data/lib/generators/better_ui/templates/components/_panel.scss +104 -0
- data/lib/generators/better_ui/templates/components/_spinner.scss +129 -0
- data/lib/generators/better_ui/templates/components/_table.scss +156 -0
- data/lib/generators/better_ui/templates/components/_variables.scss +0 -0
- data/lib/generators/better_ui/templates/components_stylesheet.scss +35 -0
- data/lib/generators/better_ui/templates/index.scss +18 -0
- data/lib/generators/better_ui/templates/initializer.rb +41 -0
- data/lib/tasks/better_ui_tasks.rake +4 -0
- metadata +260 -0
@@ -0,0 +1,248 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module General
|
3
|
+
module Badge
|
4
|
+
class Component < ViewComponent::Base
|
5
|
+
attr_reader :label, :theme, :size, :shape, :style, :variant, :icon, :icon_position, :classes, :id
|
6
|
+
|
7
|
+
# Classi base sempre presenti
|
8
|
+
BADGE_BASE_CLASSES = "inline-flex items-center justify-center font-medium"
|
9
|
+
|
10
|
+
# Classi per elementi interni
|
11
|
+
BADGE_ICON_LEFT_CLASSES = "flex-shrink-0 -ml-0.5 mr-1.5"
|
12
|
+
BADGE_ICON_RIGHT_CLASSES = "flex-shrink-0 -mr-0.5 ml-1.5"
|
13
|
+
BADGE_TEXT_CLASSES = "whitespace-nowrap"
|
14
|
+
BADGE_DOT_CLASSES = "h-2 w-2 rounded-full mr-1.5"
|
15
|
+
|
16
|
+
# Temi di badge con stile FILLED - classi Tailwind dirette
|
17
|
+
BADGE_THEME_FILLED_CLASSES = {
|
18
|
+
default: "bg-black text-white",
|
19
|
+
white: "bg-white text-black",
|
20
|
+
red: "bg-red-500 text-white",
|
21
|
+
rose: "bg-rose-500 text-white",
|
22
|
+
orange: "bg-orange-500 text-white",
|
23
|
+
green: "bg-green-500 text-white",
|
24
|
+
blue: "bg-blue-500 text-white",
|
25
|
+
yellow: "bg-yellow-500 text-black",
|
26
|
+
violet: "bg-violet-500 text-white",
|
27
|
+
gray: "bg-gray-500 text-white"
|
28
|
+
}
|
29
|
+
|
30
|
+
# Temi di badge con stile OUTLINE - classi Tailwind dirette
|
31
|
+
BADGE_THEME_OUTLINE_CLASSES = {
|
32
|
+
default: "border border-black text-black bg-transparent",
|
33
|
+
white: "border border-gray-300 text-gray-700 bg-transparent",
|
34
|
+
red: "border border-red-500 text-red-500 bg-transparent",
|
35
|
+
rose: "border border-rose-500 text-rose-500 bg-transparent",
|
36
|
+
orange: "border border-orange-500 text-orange-500 bg-transparent",
|
37
|
+
green: "border border-green-500 text-green-500 bg-transparent",
|
38
|
+
blue: "border border-blue-500 text-blue-500 bg-transparent",
|
39
|
+
yellow: "border border-yellow-500 text-yellow-500 bg-transparent",
|
40
|
+
violet: "border border-violet-500 text-violet-500 bg-transparent",
|
41
|
+
gray: "border border-gray-500 text-gray-500 bg-transparent"
|
42
|
+
}
|
43
|
+
|
44
|
+
# Dimensioni con classi Tailwind dirette
|
45
|
+
BADGE_SIZE_CLASSES = {
|
46
|
+
small: "text-xs px-2 py-0.5",
|
47
|
+
medium: "text-sm px-2.5 py-0.5",
|
48
|
+
large: "text-sm px-3 py-1"
|
49
|
+
}
|
50
|
+
|
51
|
+
# Forme con classi Tailwind dirette
|
52
|
+
BADGE_SHAPE_CLASSES = {
|
53
|
+
square: "rounded-md",
|
54
|
+
rounded: "rounded-full"
|
55
|
+
}
|
56
|
+
|
57
|
+
# Colori dot per ogni tema
|
58
|
+
BADGE_DOT_COLOR_CLASSES = {
|
59
|
+
default: "bg-gray-500",
|
60
|
+
white: "bg-gray-400",
|
61
|
+
red: "bg-red-700",
|
62
|
+
rose: "bg-rose-700",
|
63
|
+
orange: "bg-orange-700",
|
64
|
+
green: "bg-green-700",
|
65
|
+
blue: "bg-blue-700",
|
66
|
+
yellow: "bg-yellow-700",
|
67
|
+
violet: "bg-violet-700",
|
68
|
+
gray: "bg-gray-700"
|
69
|
+
}
|
70
|
+
|
71
|
+
# @param label [String] Testo del badge
|
72
|
+
# @param theme [Symbol] default, white, red, rose, orange, green, blue, yellow, violet, gray
|
73
|
+
# @param size [Symbol] small, medium, large
|
74
|
+
# @param shape [Symbol] square, rounded
|
75
|
+
# @param style [Symbol] filled, outline
|
76
|
+
# @param variant [Symbol] nil, notification, counter, dot
|
77
|
+
# @param icon [String] Nome icona (opzionale)
|
78
|
+
# @param icon_position [Symbol] left, right
|
79
|
+
# @param html_options [Hash] Opzioni HTML aggiuntive
|
80
|
+
def initialize(
|
81
|
+
label: nil,
|
82
|
+
theme: :white,
|
83
|
+
size: :medium,
|
84
|
+
shape: :rounded,
|
85
|
+
style: :filled,
|
86
|
+
variant: nil,
|
87
|
+
icon: nil,
|
88
|
+
icon_position: :left,
|
89
|
+
classes: nil,
|
90
|
+
id: nil,
|
91
|
+
**html_options
|
92
|
+
)
|
93
|
+
@label = label
|
94
|
+
@theme = theme.to_sym
|
95
|
+
@size = size.to_sym
|
96
|
+
@shape = shape.to_sym
|
97
|
+
@style = style.to_sym
|
98
|
+
@variant = variant.present? ? variant.to_sym : nil
|
99
|
+
@icon = icon
|
100
|
+
@icon_position = icon_position.to_sym
|
101
|
+
@classes = classes
|
102
|
+
@id = id
|
103
|
+
@html_options = html_options
|
104
|
+
|
105
|
+
validate_params
|
106
|
+
end
|
107
|
+
|
108
|
+
# Combina tutte le classi
|
109
|
+
def combined_classes
|
110
|
+
[
|
111
|
+
BADGE_BASE_CLASSES,
|
112
|
+
get_theme_class,
|
113
|
+
get_size_class,
|
114
|
+
get_shape_class,
|
115
|
+
@classes,
|
116
|
+
@html_options[:class]
|
117
|
+
].compact.join(" ")
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_theme_class
|
121
|
+
if @style == :outline
|
122
|
+
BADGE_THEME_OUTLINE_CLASSES[@theme] || BADGE_THEME_OUTLINE_CLASSES[:white]
|
123
|
+
else
|
124
|
+
BADGE_THEME_FILLED_CLASSES[@theme] || BADGE_THEME_FILLED_CLASSES[:white]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_size_class
|
129
|
+
BADGE_SIZE_CLASSES[@size] || BADGE_SIZE_CLASSES[:medium]
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_shape_class
|
133
|
+
BADGE_SHAPE_CLASSES[@shape] || BADGE_SHAPE_CLASSES[:rounded]
|
134
|
+
end
|
135
|
+
|
136
|
+
# Restituisce gli attributi per il badge
|
137
|
+
def badge_attributes
|
138
|
+
attrs = {
|
139
|
+
class: combined_classes,
|
140
|
+
id: @id
|
141
|
+
}
|
142
|
+
|
143
|
+
# Aggiungi altri attributi HTML se presenti
|
144
|
+
@html_options.except(:class).each do |key, value|
|
145
|
+
attrs[key] = value
|
146
|
+
end
|
147
|
+
|
148
|
+
attrs
|
149
|
+
end
|
150
|
+
|
151
|
+
def icon_classes
|
152
|
+
if @icon_position == :left
|
153
|
+
BADGE_ICON_LEFT_CLASSES
|
154
|
+
else
|
155
|
+
BADGE_ICON_RIGHT_CLASSES
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def text_classes
|
160
|
+
BADGE_TEXT_CLASSES
|
161
|
+
end
|
162
|
+
|
163
|
+
def dot_classes
|
164
|
+
[
|
165
|
+
BADGE_DOT_CLASSES,
|
166
|
+
get_dot_color_class
|
167
|
+
].compact.join(" ")
|
168
|
+
end
|
169
|
+
|
170
|
+
def get_dot_color_class
|
171
|
+
BADGE_DOT_COLOR_CLASSES[@theme] || BADGE_DOT_COLOR_CLASSES[:white]
|
172
|
+
end
|
173
|
+
|
174
|
+
# Helper per renderizzare le icone
|
175
|
+
def render_icon(icon_name)
|
176
|
+
# Mappa le dimensioni del badge alle dimensioni dell'icona
|
177
|
+
icon_size = case @size
|
178
|
+
when :large
|
179
|
+
:small
|
180
|
+
when :small
|
181
|
+
:tiny
|
182
|
+
else
|
183
|
+
:tiny
|
184
|
+
end
|
185
|
+
|
186
|
+
# Utilizziamo il componente Icon
|
187
|
+
render BetterUi::General::IconComponent.new(
|
188
|
+
name: icon_name,
|
189
|
+
size: icon_size,
|
190
|
+
fixed_width: true
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
def validate_params
|
197
|
+
validate_theme
|
198
|
+
validate_size
|
199
|
+
validate_shape
|
200
|
+
validate_style
|
201
|
+
validate_variant
|
202
|
+
validate_icon_position
|
203
|
+
end
|
204
|
+
|
205
|
+
def validate_theme
|
206
|
+
unless BADGE_THEME_FILLED_CLASSES.keys.include?(@theme)
|
207
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{BADGE_THEME_FILLED_CLASSES.keys.join(', ')}"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def validate_size
|
212
|
+
unless BADGE_SIZE_CLASSES.keys.include?(@size)
|
213
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{BADGE_SIZE_CLASSES.keys.join(', ')}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def validate_shape
|
218
|
+
unless BADGE_SHAPE_CLASSES.keys.include?(@shape)
|
219
|
+
raise ArgumentError, "La forma deve essere una tra: #{BADGE_SHAPE_CLASSES.keys.join(', ')}"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def validate_style
|
224
|
+
valid_styles = [:filled, :outline]
|
225
|
+
unless valid_styles.include?(@style)
|
226
|
+
raise ArgumentError, "Lo stile deve essere uno tra: #{valid_styles.join(', ')}"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def validate_variant
|
231
|
+
return if @variant.nil?
|
232
|
+
|
233
|
+
valid_variants = [:notification, :counter, :dot]
|
234
|
+
unless valid_variants.include?(@variant)
|
235
|
+
raise ArgumentError, "La variante deve essere una tra: #{valid_variants.join(', ')}"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def validate_icon_position
|
240
|
+
valid_positions = [:left, :right]
|
241
|
+
unless valid_positions.include?(@icon_position)
|
242
|
+
raise ArgumentError, "La posizione dell'icona deve essere una tra: #{valid_positions.join(', ')}"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<nav <%= tag.attributes(breadcrumb_attributes) %>>
|
2
|
+
<ol class="<%= BREADCRUMB_LIST_CLASSES %>">
|
3
|
+
<% @items.each_with_index do |item, index| %>
|
4
|
+
<li class="<%= BREADCRUMB_ITEM_CLASSES %>">
|
5
|
+
<%= render link_for_item(item, active: last_item?(index)) %>
|
6
|
+
|
7
|
+
<% unless last_item?(index) %>
|
8
|
+
<span class="<%= separator_classes %>" aria-hidden="true">
|
9
|
+
<%= separator_text %>
|
10
|
+
</span>
|
11
|
+
<% end %>
|
12
|
+
</li>
|
13
|
+
<% end %>
|
14
|
+
</ol>
|
15
|
+
</nav>
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module General
|
3
|
+
module Breadcrumb
|
4
|
+
class Component < ViewComponent::Base
|
5
|
+
attr_reader :items, :separator, :size, :theme, :classes, :html_options
|
6
|
+
|
7
|
+
# Classi base sempre presenti
|
8
|
+
BREADCRUMB_BASE_CLASSES = "flex items-center flex-wrap"
|
9
|
+
|
10
|
+
# Classi per lista e items
|
11
|
+
BREADCRUMB_LIST_CLASSES = "flex flex-wrap items-center"
|
12
|
+
BREADCRUMB_ITEM_CLASSES = "flex items-center"
|
13
|
+
|
14
|
+
# Temi di breadcrumb con classi Tailwind dirette
|
15
|
+
BREADCRUMB_THEME_CLASSES = {
|
16
|
+
default: "text-white",
|
17
|
+
white: "text-black",
|
18
|
+
red: "text-white",
|
19
|
+
rose: "text-white",
|
20
|
+
orange: "text-white",
|
21
|
+
green: "text-white",
|
22
|
+
blue: "text-white",
|
23
|
+
yellow: "text-black",
|
24
|
+
violet: "text-white",
|
25
|
+
gray: "text-gray-900"
|
26
|
+
}
|
27
|
+
|
28
|
+
# Classi per separatori con temi
|
29
|
+
BREADCRUMB_SEPARATOR_THEME_CLASSES = {
|
30
|
+
default: "text-gray-500",
|
31
|
+
white: "text-gray-400",
|
32
|
+
red: "text-red-300",
|
33
|
+
rose: "text-rose-300",
|
34
|
+
orange: "text-orange-300",
|
35
|
+
green: "text-green-300",
|
36
|
+
blue: "text-blue-300",
|
37
|
+
yellow: "text-yellow-600",
|
38
|
+
violet: "text-violet-300",
|
39
|
+
gray: "text-gray-500"
|
40
|
+
}
|
41
|
+
|
42
|
+
# Dimensioni con classi Tailwind dirette
|
43
|
+
BREADCRUMB_SIZE_CLASSES = {
|
44
|
+
small: "text-xs",
|
45
|
+
medium: "text-sm",
|
46
|
+
large: "text-base"
|
47
|
+
}
|
48
|
+
|
49
|
+
# Separatori predefiniti
|
50
|
+
BREADCRUMB_SEPARATOR_TYPES = {
|
51
|
+
slash: "/",
|
52
|
+
chevron: "›",
|
53
|
+
arrow: "→",
|
54
|
+
dot: "•",
|
55
|
+
pipe: "|"
|
56
|
+
}
|
57
|
+
|
58
|
+
# Inizializzazione del componente
|
59
|
+
def initialize(
|
60
|
+
items: [],
|
61
|
+
separator: :chevron,
|
62
|
+
theme: :white,
|
63
|
+
size: :medium,
|
64
|
+
classes: nil,
|
65
|
+
**html_options
|
66
|
+
)
|
67
|
+
@items = items || []
|
68
|
+
@separator = separator.to_sym
|
69
|
+
@theme = theme.to_sym
|
70
|
+
@size = size.to_sym
|
71
|
+
@classes = classes
|
72
|
+
@html_options = html_options
|
73
|
+
|
74
|
+
validate_params
|
75
|
+
end
|
76
|
+
|
77
|
+
# Restituisce il separatore come stringa
|
78
|
+
def separator_text
|
79
|
+
if BREADCRUMB_SEPARATOR_TYPES.key?(@separator)
|
80
|
+
BREADCRUMB_SEPARATOR_TYPES[@separator]
|
81
|
+
else
|
82
|
+
@separator.to_s
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Genera le classi per il container
|
87
|
+
def container_classes
|
88
|
+
[
|
89
|
+
BREADCRUMB_BASE_CLASSES,
|
90
|
+
get_theme_class,
|
91
|
+
get_size_class,
|
92
|
+
@classes,
|
93
|
+
@html_options[:class]
|
94
|
+
].compact.join(" ")
|
95
|
+
end
|
96
|
+
|
97
|
+
# Verifica se un item è l'ultimo (attivo)
|
98
|
+
def last_item?(index)
|
99
|
+
index == @items.length - 1
|
100
|
+
end
|
101
|
+
|
102
|
+
# Crea un componente link per l'item
|
103
|
+
def link_for_item(item, active: false)
|
104
|
+
label = item.is_a?(Hash) ? item[:label] : item.to_s
|
105
|
+
href = item.is_a?(Hash) ? item[:url] : nil
|
106
|
+
icon = item.is_a?(Hash) ? item[:icon] : nil
|
107
|
+
|
108
|
+
BetterUi::General::Link::Component.new(
|
109
|
+
label: label,
|
110
|
+
href: href,
|
111
|
+
theme: @theme,
|
112
|
+
size: @size,
|
113
|
+
icon: icon,
|
114
|
+
active: active
|
115
|
+
)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Restituisce gli attributi per il breadcrumb
|
119
|
+
def breadcrumb_attributes
|
120
|
+
attrs = {
|
121
|
+
"aria-label": "Breadcrumb",
|
122
|
+
class: container_classes
|
123
|
+
}
|
124
|
+
|
125
|
+
# Aggiungi altri attributi HTML se presenti
|
126
|
+
@html_options.except(:class).each do |key, value|
|
127
|
+
attrs[key] = value
|
128
|
+
end
|
129
|
+
|
130
|
+
attrs
|
131
|
+
end
|
132
|
+
|
133
|
+
# Restituisce le classi CSS per il separatore
|
134
|
+
def separator_classes
|
135
|
+
[
|
136
|
+
"mx-2",
|
137
|
+
get_separator_theme_class
|
138
|
+
].compact.join(" ")
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_separator_theme_class
|
142
|
+
BREADCRUMB_SEPARATOR_THEME_CLASSES[@theme] || BREADCRUMB_SEPARATOR_THEME_CLASSES[:white]
|
143
|
+
end
|
144
|
+
|
145
|
+
# Verifica se rendere il componente
|
146
|
+
def render?
|
147
|
+
@items.present? && @items.length > 0
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def get_theme_class
|
153
|
+
BREADCRUMB_THEME_CLASSES[@theme] || BREADCRUMB_THEME_CLASSES[:white]
|
154
|
+
end
|
155
|
+
|
156
|
+
def get_size_class
|
157
|
+
BREADCRUMB_SIZE_CLASSES[@size] || BREADCRUMB_SIZE_CLASSES[:medium]
|
158
|
+
end
|
159
|
+
|
160
|
+
def validate_params
|
161
|
+
validate_theme
|
162
|
+
validate_size
|
163
|
+
validate_separator
|
164
|
+
end
|
165
|
+
|
166
|
+
def validate_theme
|
167
|
+
unless BREADCRUMB_THEME_CLASSES.keys.include?(@theme)
|
168
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{BREADCRUMB_THEME_CLASSES.keys.join(', ')}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def validate_size
|
173
|
+
unless BREADCRUMB_SIZE_CLASSES.keys.include?(@size)
|
174
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{BREADCRUMB_SIZE_CLASSES.keys.join(', ')}"
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def validate_separator
|
179
|
+
return if !@separator.is_a?(Symbol) || BREADCRUMB_SEPARATOR_TYPES.keys.include?(@separator)
|
180
|
+
|
181
|
+
raise ArgumentError, "Il separatore predefinito deve essere uno tra: #{BREADCRUMB_SEPARATOR_TYPES.keys.join(', ')}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<%# Template per il bottone %>
|
2
|
+
<% if link? %>
|
3
|
+
<%= link_to @href, **link_attributes do %>
|
4
|
+
<% if @icon && @icon_position == :left %>
|
5
|
+
<span class="flex-shrink-0 mr-2"><%= render_icon(@icon) %></span>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<% if @label %>
|
9
|
+
<span class="flex-grow"><%= @label %></span>
|
10
|
+
<% end %>
|
11
|
+
|
12
|
+
<% if @icon && @icon_position == :right %>
|
13
|
+
<span class="flex-shrink-0 ml-2"><%= render_icon(@icon) %></span>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<%= content %>
|
17
|
+
<% end %>
|
18
|
+
<% else %>
|
19
|
+
<%= tag.button(**button_attributes) do %>
|
20
|
+
<% if @icon && @icon_position == :left %>
|
21
|
+
<span class="flex-shrink-0 mr-2"><%= render_icon(@icon) %></span>
|
22
|
+
<% end %>
|
23
|
+
|
24
|
+
<% if @label %>
|
25
|
+
<span class="flex-grow"><%= @label %></span>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
<% if @icon && @icon_position == :right %>
|
29
|
+
<span class="flex-shrink-0 ml-2"><%= render_icon(@icon) %></span>
|
30
|
+
<% end %>
|
31
|
+
|
32
|
+
<%= content %>
|
33
|
+
<% end %>
|
34
|
+
<% end %>
|
@@ -0,0 +1,214 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module General
|
3
|
+
module Button
|
4
|
+
class Component < ViewComponent::Base
|
5
|
+
attr_reader :label, :type, :size, :full_width, :disabled,
|
6
|
+
:icon, :icon_position, :href, :method, :data, :classes, :id, :rounded, :button_type, :html_options
|
7
|
+
|
8
|
+
# Classi base sempre presenti
|
9
|
+
BUTTON_BASE_CLASSES = "inline-flex items-center justify-center font-medium transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2"
|
10
|
+
|
11
|
+
# Temi di bottoni con classi Tailwind dirette
|
12
|
+
BUTTON_THEME = {
|
13
|
+
default: "bg-black text-white hover:bg-gray-900 focus:ring-gray-900",
|
14
|
+
white: "bg-white text-black border border-gray-300 hover:bg-gray-50 focus:ring-gray-400",
|
15
|
+
red: "bg-red-500 text-white hover:bg-red-600 focus:ring-red-500",
|
16
|
+
rose: "bg-rose-500 text-white hover:bg-rose-600 focus:ring-rose-500",
|
17
|
+
orange: "bg-orange-500 text-white hover:bg-orange-600 focus:ring-orange-500",
|
18
|
+
green: "bg-green-500 text-white hover:bg-green-600 focus:ring-green-500",
|
19
|
+
blue: "bg-blue-500 text-white hover:bg-blue-600 focus:ring-blue-500",
|
20
|
+
yellow: "bg-yellow-500 text-black hover:bg-yellow-600 focus:ring-yellow-500",
|
21
|
+
violet: "bg-violet-500 text-white hover:bg-violet-600 focus:ring-violet-500",
|
22
|
+
purple: "bg-purple-500 text-white hover:bg-purple-600 focus:ring-purple-500",
|
23
|
+
}
|
24
|
+
|
25
|
+
# Dimensioni con classi Tailwind dirette
|
26
|
+
BUTTON_SIZES = {
|
27
|
+
small: "px-2.5 py-1.5 text-xs",
|
28
|
+
medium: "px-4 py-2 text-sm",
|
29
|
+
large: "px-6 py-3 text-base"
|
30
|
+
}
|
31
|
+
|
32
|
+
# Border radius con classi Tailwind dirette
|
33
|
+
BUTTON_RADIUS = {
|
34
|
+
none: "rounded-none",
|
35
|
+
small: "rounded-md",
|
36
|
+
medium: "rounded-lg",
|
37
|
+
large: "rounded-xl",
|
38
|
+
full: "rounded-full"
|
39
|
+
}
|
40
|
+
|
41
|
+
# Inizializzazione del componente
|
42
|
+
def initialize(
|
43
|
+
label: nil,
|
44
|
+
type: :white,
|
45
|
+
size: :medium,
|
46
|
+
full_width: false,
|
47
|
+
disabled: false,
|
48
|
+
icon: nil,
|
49
|
+
icon_position: :left,
|
50
|
+
href: nil,
|
51
|
+
method: nil,
|
52
|
+
data: {},
|
53
|
+
classes: nil,
|
54
|
+
id: nil,
|
55
|
+
rounded: :medium,
|
56
|
+
button_type: :button,
|
57
|
+
**html_options
|
58
|
+
)
|
59
|
+
@label = label
|
60
|
+
@type = type.to_sym
|
61
|
+
@size = size.to_sym
|
62
|
+
@full_width = full_width
|
63
|
+
@disabled = disabled
|
64
|
+
@icon = icon
|
65
|
+
@icon_position = icon_position.to_sym
|
66
|
+
@href = href
|
67
|
+
@method = method
|
68
|
+
@data = data
|
69
|
+
@classes = classes
|
70
|
+
@id = id
|
71
|
+
@rounded = rounded.to_sym
|
72
|
+
@button_type = button_type.to_sym
|
73
|
+
@html_options = html_options
|
74
|
+
|
75
|
+
validate_params
|
76
|
+
end
|
77
|
+
|
78
|
+
# Determina se il bottone è un link o un button
|
79
|
+
def link?
|
80
|
+
@href.present?
|
81
|
+
end
|
82
|
+
|
83
|
+
# Combina tutte le classi
|
84
|
+
def combined_classes
|
85
|
+
[
|
86
|
+
BUTTON_BASE_CLASSES,
|
87
|
+
get_button_type_classes,
|
88
|
+
get_button_size_classes,
|
89
|
+
get_border_radius_class,
|
90
|
+
@full_width ? "w-full" : nil,
|
91
|
+
@disabled ? "opacity-50 cursor-not-allowed" : nil,
|
92
|
+
@classes,
|
93
|
+
@html_options[:class]
|
94
|
+
].compact.join(" ")
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_button_type_classes
|
98
|
+
BUTTON_THEME[@type] || BUTTON_THEME[:white]
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_border_radius_class
|
102
|
+
BUTTON_RADIUS[@rounded] || BUTTON_RADIUS[:medium]
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_button_size_classes
|
106
|
+
BUTTON_SIZES[@size] || BUTTON_SIZES[:medium]
|
107
|
+
end
|
108
|
+
|
109
|
+
# Restituisce gli attributi per il bottone
|
110
|
+
def button_attributes
|
111
|
+
attrs = {
|
112
|
+
class: combined_classes,
|
113
|
+
type: button_type,
|
114
|
+
id: @id
|
115
|
+
}
|
116
|
+
|
117
|
+
attrs[:disabled] = true if @disabled
|
118
|
+
attrs[:data] = @data if @data.present?
|
119
|
+
|
120
|
+
# Aggiungi altri attributi HTML se presenti
|
121
|
+
@html_options.except(:class).each do |key, value|
|
122
|
+
attrs[key] = value
|
123
|
+
end
|
124
|
+
|
125
|
+
attrs
|
126
|
+
end
|
127
|
+
|
128
|
+
# Restituisce gli attributi per il link
|
129
|
+
def link_attributes
|
130
|
+
attrs = {
|
131
|
+
class: combined_classes,
|
132
|
+
id: @id
|
133
|
+
}
|
134
|
+
|
135
|
+
attrs[:data] = @data.merge(turbo_method: @method) if @method.present?
|
136
|
+
attrs[:data] = @data if @data.present? && !@method.present?
|
137
|
+
attrs[:href] = @disabled ? nil : @href
|
138
|
+
attrs[:role] = "button"
|
139
|
+
attrs[:tabindex] = @disabled ? "-1" : "0"
|
140
|
+
attrs[:aria] = { disabled: @disabled } if @disabled
|
141
|
+
|
142
|
+
# Aggiungi altri attributi HTML se presenti
|
143
|
+
@html_options.except(:class).each do |key, value|
|
144
|
+
attrs[key] = value
|
145
|
+
end
|
146
|
+
|
147
|
+
attrs
|
148
|
+
end
|
149
|
+
|
150
|
+
def button_type
|
151
|
+
@button_type || 'button'
|
152
|
+
end
|
153
|
+
|
154
|
+
# Helper per renderizzare le icone
|
155
|
+
def render_icon(icon_name)
|
156
|
+
# Mappa le dimensioni del bottone alle dimensioni dell'icona
|
157
|
+
icon_size = case @size
|
158
|
+
when :large
|
159
|
+
:large
|
160
|
+
when :small
|
161
|
+
:small
|
162
|
+
else
|
163
|
+
:medium
|
164
|
+
end
|
165
|
+
|
166
|
+
# Utilizziamo il componente Icon
|
167
|
+
render BetterUi::General::Icon::Component.new(
|
168
|
+
name: icon_name,
|
169
|
+
size: icon_size,
|
170
|
+
fixed_width: true
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Verifica se rendere il componente
|
175
|
+
def render?
|
176
|
+
@label.present? || @icon.present? || content.present?
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def validate_params
|
182
|
+
validate_type
|
183
|
+
validate_size
|
184
|
+
validate_icon_position
|
185
|
+
validate_rounded
|
186
|
+
end
|
187
|
+
|
188
|
+
def validate_type
|
189
|
+
unless BUTTON_THEME.keys.include?(@type)
|
190
|
+
raise ArgumentError, "Il tipo deve essere uno tra: #{BUTTON_THEME.keys.join(', ')}"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def validate_size
|
195
|
+
unless BUTTON_SIZES.keys.include?(@size)
|
196
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{BUTTON_SIZES.keys.join(', ')}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def validate_icon_position
|
201
|
+
unless [:left, :right].include?(@icon_position)
|
202
|
+
raise ArgumentError, "La posizione dell'icona deve essere :left o :right"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def validate_rounded
|
207
|
+
unless BUTTON_RADIUS.keys.include?(@rounded)
|
208
|
+
raise ArgumentError, "Il bordo deve essere uno tra: #{BUTTON_RADIUS.keys.join(', ')}"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|