better_ui 0.1.0 → 0.5.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/README.md +199 -75
- data/app/assets/javascripts/better_ui/controllers/navbar_controller.js +138 -0
- data/app/assets/javascripts/better_ui/controllers/sidebar_controller.js +211 -0
- data/app/assets/javascripts/better_ui/controllers/toast_controller.js +161 -0
- data/app/assets/javascripts/better_ui/index.js +159 -0
- data/app/assets/javascripts/better_ui/toast_manager.js +77 -0
- data/app/assets/stylesheets/better_ui/application.css +25 -351
- data/app/components/better_ui/application/alert_component.html.erb +27 -0
- data/app/components/better_ui/application/alert_component.rb +202 -0
- data/app/components/better_ui/application/card_component.html.erb +24 -0
- data/app/components/better_ui/application/card_component.rb +53 -0
- data/app/components/better_ui/application/card_container_component.html.erb +8 -0
- data/app/components/better_ui/application/card_container_component.rb +14 -0
- data/app/components/better_ui/application/header_component.html.erb +88 -0
- data/app/components/better_ui/application/header_component.rb +188 -0
- data/app/components/better_ui/application/navbar_component.html.erb +294 -0
- data/app/components/better_ui/application/navbar_component.rb +249 -0
- data/app/components/better_ui/application/sidebar_component.html.erb +207 -0
- data/app/components/better_ui/application/sidebar_component.rb +318 -0
- data/app/components/better_ui/application/toast_component.html.erb +35 -0
- data/app/components/better_ui/application/toast_component.rb +223 -0
- data/app/components/better_ui/general/avatar_component.html.erb +19 -0
- data/app/components/better_ui/general/avatar_component.rb +171 -0
- data/app/components/better_ui/general/badge_component.html.erb +19 -0
- data/app/components/better_ui/general/badge_component.rb +123 -0
- data/app/components/better_ui/general/breadcrumb_component.html.erb +15 -0
- data/app/components/better_ui/general/breadcrumb_component.rb +130 -0
- data/app/components/better_ui/general/button_component.html.erb +34 -0
- data/app/components/better_ui/general/button_component.rb +162 -0
- data/app/components/better_ui/general/heading_component.html.erb +25 -0
- data/app/components/better_ui/general/heading_component.rb +148 -0
- data/app/components/better_ui/general/icon_component.html.erb +2 -0
- data/app/components/better_ui/general/icon_component.rb +100 -0
- data/app/components/better_ui/general/link_component.html.erb +17 -0
- data/app/components/better_ui/general/link_component.rb +132 -0
- data/app/components/better_ui/general/panel_component.html.erb +27 -0
- data/app/components/better_ui/general/panel_component.rb +103 -0
- data/app/components/better_ui/general/spinner_component.html.erb +15 -0
- data/app/components/better_ui/general/spinner_component.rb +79 -0
- data/app/components/better_ui/general/table_component.html.erb +73 -0
- data/app/components/better_ui/general/table_component.rb +167 -0
- data/app/components/better_ui/theme_helper.rb +171 -0
- data/app/controllers/better_ui/application_controller.rb +1 -0
- data/app/controllers/better_ui/docs_controller.rb +18 -25
- 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 +6 -1
- data/lib/better_ui/engine.rb +18 -1
- data/lib/better_ui/version.rb +1 -1
- data/lib/better_ui.rb +4 -0
- data/lib/generators/better_ui/stylesheet_generator.rb +96 -0
- data/lib/generators/better_ui/templates/README +56 -0
- data/lib/generators/better_ui/templates/components/_avatar.scss +151 -0
- data/lib/generators/better_ui/templates/components/_badge.scss +142 -0
- data/lib/generators/better_ui/templates/components/_breadcrumb.scss +107 -0
- data/lib/generators/better_ui/templates/components/_button.scss +106 -0
- data/lib/generators/better_ui/templates/components/_card.scss +69 -0
- data/lib/generators/better_ui/templates/components/_heading.scss +180 -0
- data/lib/generators/better_ui/templates/components/_icon.scss +90 -0
- data/lib/generators/better_ui/templates/components/_link.scss +130 -0
- data/lib/generators/better_ui/templates/components/_panel.scss +144 -0
- data/lib/generators/better_ui/templates/components/_spinner.scss +132 -0
- data/lib/generators/better_ui/templates/components/_table.scss +105 -0
- data/lib/generators/better_ui/templates/components/_variables.scss +33 -0
- data/lib/generators/better_ui/templates/components_stylesheet.scss +66 -0
- metadata +135 -10
- data/app/helpers/better_ui/application_helper.rb +0 -183
- 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
- data/app/views/layouts/better_ui/application.html.erb +0 -135
@@ -0,0 +1,318 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module Application
|
3
|
+
class SidebarComponent < ViewComponent::Base
|
4
|
+
attr_reader :title, :variant, :items, :footer, :classes, :data, :collapsible, :collapsed_default, :position, :width, :overlay_on_mobile
|
5
|
+
|
6
|
+
# Varianti di colore disponibili
|
7
|
+
VARIANTS = {
|
8
|
+
light: {
|
9
|
+
bg: "bg-white",
|
10
|
+
border: "border-gray-200",
|
11
|
+
text: "text-gray-700",
|
12
|
+
active_bg: "bg-gray-50",
|
13
|
+
active_text: "text-gray-900",
|
14
|
+
hover: "hover:bg-gray-50 hover:text-gray-900",
|
15
|
+
divider: "border-gray-200",
|
16
|
+
shadow: "shadow-sm",
|
17
|
+
icon: "text-gray-500",
|
18
|
+
chevron: "text-gray-400",
|
19
|
+
heading: "text-gray-500"
|
20
|
+
},
|
21
|
+
dark: {
|
22
|
+
bg: "bg-gray-800",
|
23
|
+
border: "border-gray-700",
|
24
|
+
text: "text-gray-200",
|
25
|
+
active_bg: "bg-gray-700",
|
26
|
+
active_text: "text-white",
|
27
|
+
hover: "hover:bg-gray-700 hover:text-white",
|
28
|
+
divider: "border-gray-700",
|
29
|
+
shadow: "shadow-lg",
|
30
|
+
icon: "text-gray-400",
|
31
|
+
chevron: "text-gray-500",
|
32
|
+
heading: "text-gray-400"
|
33
|
+
},
|
34
|
+
primary: {
|
35
|
+
bg: "bg-orange-600",
|
36
|
+
border: "border-orange-700",
|
37
|
+
text: "text-white",
|
38
|
+
active_bg: "bg-orange-700",
|
39
|
+
active_text: "text-white",
|
40
|
+
hover: "hover:bg-orange-700",
|
41
|
+
divider: "border-orange-500",
|
42
|
+
shadow: "shadow-lg",
|
43
|
+
icon: "text-orange-300",
|
44
|
+
chevron: "text-orange-400",
|
45
|
+
heading: "text-orange-200"
|
46
|
+
},
|
47
|
+
blue: {
|
48
|
+
bg: "bg-blue-800",
|
49
|
+
border: "border-blue-700",
|
50
|
+
text: "text-gray-100",
|
51
|
+
active_bg: "bg-blue-700",
|
52
|
+
active_text: "text-white",
|
53
|
+
hover: "hover:bg-blue-700 hover:text-white",
|
54
|
+
divider: "border-blue-700",
|
55
|
+
shadow: "shadow-lg",
|
56
|
+
icon: "text-blue-300",
|
57
|
+
chevron: "text-blue-400",
|
58
|
+
heading: "text-blue-200"
|
59
|
+
},
|
60
|
+
modern: {
|
61
|
+
bg: "bg-white",
|
62
|
+
border: "border-gray-200",
|
63
|
+
text: "text-gray-700",
|
64
|
+
active_bg: "bg-gray-100",
|
65
|
+
active_text: "text-gray-900",
|
66
|
+
hover: "hover:bg-gray-50",
|
67
|
+
divider: "border-gray-100",
|
68
|
+
shadow: "shadow-none",
|
69
|
+
icon: "text-gray-500",
|
70
|
+
chevron: "text-gray-400",
|
71
|
+
heading: "text-gray-500 uppercase text-xs font-medium"
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
# Dimensioni disponibili per la sidebar
|
76
|
+
WIDTHS = {
|
77
|
+
narrow: "w-60",
|
78
|
+
medium: "w-64",
|
79
|
+
wide: "w-72",
|
80
|
+
custom: "" # La larghezza custom viene specificata nelle classi aggiuntive
|
81
|
+
}
|
82
|
+
|
83
|
+
# Posizioni disponibili
|
84
|
+
POSITIONS = {
|
85
|
+
left: "left-0",
|
86
|
+
right: "right-0"
|
87
|
+
}
|
88
|
+
|
89
|
+
# Inizializzazione del componente
|
90
|
+
def initialize(
|
91
|
+
title: nil,
|
92
|
+
variant: :modern,
|
93
|
+
items: [],
|
94
|
+
footer: nil,
|
95
|
+
classes: nil,
|
96
|
+
data: {},
|
97
|
+
collapsible: true,
|
98
|
+
collapsed_default: false,
|
99
|
+
position: :left,
|
100
|
+
width: :narrow,
|
101
|
+
overlay_on_mobile: true
|
102
|
+
)
|
103
|
+
@title = title
|
104
|
+
@variant = variant.to_sym
|
105
|
+
@items = items || []
|
106
|
+
@footer = footer
|
107
|
+
@classes = classes
|
108
|
+
@data = data || {}
|
109
|
+
@collapsible = collapsible
|
110
|
+
@collapsed_default = collapsed_default
|
111
|
+
@position = position.to_sym
|
112
|
+
@width = width.to_sym
|
113
|
+
@overlay_on_mobile = overlay_on_mobile
|
114
|
+
|
115
|
+
# Aggiungiamo controller Stimulus per la gestione della sidebar
|
116
|
+
@data[:controller] = "sidebar" if @data[:controller].blank?
|
117
|
+
@data[:sidebar_position_value] = @position.to_s
|
118
|
+
@data[:sidebar_collapsed_value] = @collapsed_default.to_s
|
119
|
+
@data[:sidebar_overlay_on_mobile_value] = @overlay_on_mobile.to_s
|
120
|
+
end
|
121
|
+
|
122
|
+
# Genera le classi per il container della sidebar
|
123
|
+
def container_classes
|
124
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
125
|
+
width_class = WIDTHS.fetch(@width, WIDTHS[:narrow])
|
126
|
+
position_class = POSITIONS.fetch(@position, POSITIONS[:left])
|
127
|
+
|
128
|
+
[
|
129
|
+
"h-screen relative z-40",
|
130
|
+
styles[:bg],
|
131
|
+
styles[:border],
|
132
|
+
@position == :left ? "border-r" : "border-l",
|
133
|
+
styles[:shadow],
|
134
|
+
width_class,
|
135
|
+
position_class,
|
136
|
+
@classes,
|
137
|
+
"transition-transform duration-300 ease-in-out transform",
|
138
|
+
"flex flex-col"
|
139
|
+
].compact.join(" ")
|
140
|
+
end
|
141
|
+
|
142
|
+
# Genera le classi per l'overlay
|
143
|
+
def overlay_classes
|
144
|
+
[
|
145
|
+
"fixed inset-0 bg-black bg-opacity-50 z-30",
|
146
|
+
"transition-opacity duration-300 ease-in-out",
|
147
|
+
"hidden"
|
148
|
+
].join(" ")
|
149
|
+
end
|
150
|
+
|
151
|
+
# Genera le classi per il pulsante del toggle
|
152
|
+
def toggle_button_classes
|
153
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
154
|
+
position_class = @position == :left ? "right-0 -mr-3" : "left-0 -ml-3"
|
155
|
+
|
156
|
+
[
|
157
|
+
"absolute top-16",
|
158
|
+
position_class,
|
159
|
+
"z-50 flex items-center justify-center",
|
160
|
+
"w-6 h-12 rounded-lg",
|
161
|
+
styles[:bg],
|
162
|
+
styles[:border],
|
163
|
+
"cursor-pointer shadow-lg",
|
164
|
+
"transform transition-transform duration-300 ease-in-out"
|
165
|
+
].compact.join(" ")
|
166
|
+
end
|
167
|
+
|
168
|
+
# Genera le classi per l'intestazione
|
169
|
+
def header_classes
|
170
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
171
|
+
|
172
|
+
[
|
173
|
+
"px-4 py-4 flex items-center",
|
174
|
+
@variant == :modern ? "" : "border-b #{styles[:border]}"
|
175
|
+
].compact.join(" ")
|
176
|
+
end
|
177
|
+
|
178
|
+
# Genera le classi per i link nel menu
|
179
|
+
def menu_item_classes(active = false, nested = false, has_children = false)
|
180
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
181
|
+
|
182
|
+
padding_x = @variant == :modern ? "pl-5 pr-3" : "px-3"
|
183
|
+
padding_y = @variant == :modern ? "py-2.5" : "py-2"
|
184
|
+
rounded = @variant == :modern ? "" : "rounded-md"
|
185
|
+
|
186
|
+
base_classes = [
|
187
|
+
"flex items-center #{padding_y} #{padding_x} #{rounded} my-0.5 w-full",
|
188
|
+
]
|
189
|
+
|
190
|
+
if active
|
191
|
+
active_classes = @variant == :modern ? "bg-gray-100 font-medium" : styles[:active_bg]
|
192
|
+
[
|
193
|
+
*base_classes,
|
194
|
+
active_classes,
|
195
|
+
styles[:active_text],
|
196
|
+
].join(" ")
|
197
|
+
else
|
198
|
+
[
|
199
|
+
*base_classes,
|
200
|
+
styles[:text],
|
201
|
+
styles[:hover],
|
202
|
+
has_children ? "cursor-pointer" : ""
|
203
|
+
].join(" ")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Genera le classi per i separatori
|
208
|
+
def divider_classes
|
209
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
210
|
+
|
211
|
+
[
|
212
|
+
"my-2 border-t",
|
213
|
+
styles[:divider]
|
214
|
+
].join(" ")
|
215
|
+
end
|
216
|
+
|
217
|
+
# Genera le classi per il footer
|
218
|
+
def footer_classes
|
219
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
220
|
+
|
221
|
+
[
|
222
|
+
"mt-auto px-4 py-3",
|
223
|
+
@variant == :modern ? "" : "border-t #{styles[:border]}"
|
224
|
+
].join(" ")
|
225
|
+
end
|
226
|
+
|
227
|
+
# Genera le classi per le icone
|
228
|
+
def icon_classes
|
229
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
230
|
+
|
231
|
+
[
|
232
|
+
"mr-3 flex-shrink-0",
|
233
|
+
styles[:icon]
|
234
|
+
].join(" ")
|
235
|
+
end
|
236
|
+
|
237
|
+
# Genera le classi per la label della voce di menu
|
238
|
+
def item_label_classes
|
239
|
+
[
|
240
|
+
"flex-1 truncate"
|
241
|
+
].join(" ")
|
242
|
+
end
|
243
|
+
|
244
|
+
# Genera le classi per il badge nella voce di menu
|
245
|
+
def badge_classes(type = :default)
|
246
|
+
badge_types = {
|
247
|
+
default: "bg-gray-200 text-gray-800",
|
248
|
+
primary: "bg-orange-100 text-orange-800",
|
249
|
+
success: "bg-green-100 text-green-800",
|
250
|
+
warning: "bg-yellow-100 text-yellow-800",
|
251
|
+
danger: "bg-red-100 text-red-800",
|
252
|
+
info: "bg-blue-100 text-blue-800"
|
253
|
+
}
|
254
|
+
|
255
|
+
[
|
256
|
+
"px-2 py-0.5 text-xs rounded-full",
|
257
|
+
badge_types.fetch(type.to_sym, badge_types[:default])
|
258
|
+
].join(" ")
|
259
|
+
end
|
260
|
+
|
261
|
+
# Genera le classi per il titolo di sezione
|
262
|
+
def section_heading_classes
|
263
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
264
|
+
|
265
|
+
if @variant == :modern
|
266
|
+
"px-5 py-2 text-xs font-medium text-gray-500"
|
267
|
+
else
|
268
|
+
[
|
269
|
+
"px-4 py-2 text-xs tracking-wider",
|
270
|
+
styles[:heading]
|
271
|
+
].join(" ")
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# Genera le classi per icona chevron
|
276
|
+
def chevron_classes
|
277
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
278
|
+
|
279
|
+
[
|
280
|
+
"ml-auto",
|
281
|
+
styles[:chevron]
|
282
|
+
].join(" ")
|
283
|
+
end
|
284
|
+
|
285
|
+
# Verifica se la sidebar deve essere resa
|
286
|
+
def render?
|
287
|
+
true
|
288
|
+
end
|
289
|
+
|
290
|
+
# Determina se un elemento dovrebbe essere considerato attivo
|
291
|
+
def active_item?(item)
|
292
|
+
item[:active] == true
|
293
|
+
end
|
294
|
+
|
295
|
+
# Verifica se un elemento ha figli
|
296
|
+
def has_children?(item)
|
297
|
+
item[:children].present? && item[:children].is_a?(Array) && item[:children].any?
|
298
|
+
end
|
299
|
+
|
300
|
+
# Verifica se un elemento ha un badge
|
301
|
+
def has_badge?(item)
|
302
|
+
item[:badge].present?
|
303
|
+
end
|
304
|
+
|
305
|
+
# Ottiene il tipo di badge per un elemento
|
306
|
+
def badge_type(item)
|
307
|
+
return :default unless item[:badge].is_a?(Hash)
|
308
|
+
(item[:badge][:type] || :default).to_sym
|
309
|
+
end
|
310
|
+
|
311
|
+
# Ottiene il testo del badge per un elemento
|
312
|
+
def badge_text(item)
|
313
|
+
return item[:badge].to_s unless item[:badge].is_a?(Hash)
|
314
|
+
item[:badge][:text].to_s
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<div role="status" aria-live="polite" class="<%= container_classes %>"
|
2
|
+
<%= @data&.map { |k, v| "data-#{k}=\"#{v}\"" }&.join(' ')&.html_safe %>>
|
3
|
+
|
4
|
+
<% if effective_icon.present? %>
|
5
|
+
<div class="<%= icon_classes %>">
|
6
|
+
<%= render BetterUi::General::IconComponent.new(name: effective_icon) %>
|
7
|
+
</div>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<div class="<%= content_classes %>">
|
11
|
+
<% if @title.present? %>
|
12
|
+
<div class="<%= title_classes %>"><%= @title %></div>
|
13
|
+
<% end %>
|
14
|
+
|
15
|
+
<% if @message.present? %>
|
16
|
+
<div class="<%= message_classes %>"><%= @message %></div>
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
<% if content.present? %>
|
20
|
+
<div class="<%= message_classes %>"><%= content %></div>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<% if @auto_hide %>
|
24
|
+
<div class="w-full bg-gray-200 h-1 mt-2 rounded overflow-hidden">
|
25
|
+
<div class="bg-current h-1 transition-all" data-toast-target="progressBar"></div>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<% if @dismissible %>
|
31
|
+
<button type="button" class="<%= close_button_classes %>" data-action="toast#hide" aria-label="Chiudi">
|
32
|
+
<%= render BetterUi::General::IconComponent.new(name: "xmark") %>
|
33
|
+
</button>
|
34
|
+
<% end %>
|
35
|
+
</div>
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module Application
|
3
|
+
class ToastComponent < ViewComponent::Base
|
4
|
+
attr_reader :title, :message, :variant, :icon, :dismissible, :classes, :data, :position, :duration, :auto_hide, :rounded
|
5
|
+
|
6
|
+
# Varianti di colore disponibili
|
7
|
+
VARIANTS = {
|
8
|
+
default: {
|
9
|
+
bg: "bg-black",
|
10
|
+
border: "border-gray-900",
|
11
|
+
title: "text-white",
|
12
|
+
text: "text-white",
|
13
|
+
icon: "text-white",
|
14
|
+
close: "text-white hover:bg-gray-800"
|
15
|
+
},
|
16
|
+
white: {
|
17
|
+
bg: "bg-white",
|
18
|
+
border: "border-gray-200",
|
19
|
+
title: "text-black",
|
20
|
+
text: "text-black",
|
21
|
+
icon: "text-black",
|
22
|
+
close: "text-black hover:bg-gray-100"
|
23
|
+
},
|
24
|
+
red: {
|
25
|
+
bg: "bg-red-500",
|
26
|
+
border: "border-red-600",
|
27
|
+
title: "text-white",
|
28
|
+
text: "text-white",
|
29
|
+
icon: "text-white",
|
30
|
+
close: "text-white hover:bg-red-600"
|
31
|
+
},
|
32
|
+
rose: {
|
33
|
+
bg: "bg-rose-500",
|
34
|
+
border: "border-rose-600",
|
35
|
+
title: "text-white",
|
36
|
+
text: "text-white",
|
37
|
+
icon: "text-white",
|
38
|
+
close: "text-white hover:bg-rose-600"
|
39
|
+
},
|
40
|
+
orange: {
|
41
|
+
bg: "bg-orange-500",
|
42
|
+
border: "border-orange-600",
|
43
|
+
title: "text-white",
|
44
|
+
text: "text-white",
|
45
|
+
icon: "text-white",
|
46
|
+
close: "text-white hover:bg-orange-600"
|
47
|
+
},
|
48
|
+
green: {
|
49
|
+
bg: "bg-green-500",
|
50
|
+
border: "border-green-600",
|
51
|
+
title: "text-white",
|
52
|
+
text: "text-white",
|
53
|
+
icon: "text-white",
|
54
|
+
close: "text-white hover:bg-green-600"
|
55
|
+
},
|
56
|
+
blue: {
|
57
|
+
bg: "bg-blue-500",
|
58
|
+
border: "border-blue-600",
|
59
|
+
title: "text-white",
|
60
|
+
text: "text-white",
|
61
|
+
icon: "text-white",
|
62
|
+
close: "text-white hover:bg-blue-600"
|
63
|
+
},
|
64
|
+
yellow: {
|
65
|
+
bg: "bg-yellow-500",
|
66
|
+
border: "border-yellow-600",
|
67
|
+
title: "text-black",
|
68
|
+
text: "text-black",
|
69
|
+
icon: "text-black",
|
70
|
+
close: "text-black hover:bg-yellow-600"
|
71
|
+
},
|
72
|
+
violet: {
|
73
|
+
bg: "bg-violet-500",
|
74
|
+
border: "border-violet-600",
|
75
|
+
title: "text-white",
|
76
|
+
text: "text-white",
|
77
|
+
icon: "text-white",
|
78
|
+
close: "text-white hover:bg-violet-600"
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
# Icone predefinite per ciascuna variante
|
83
|
+
DEFAULT_ICONS = {
|
84
|
+
default: "bell",
|
85
|
+
white: "info-circle",
|
86
|
+
red: "exclamation-circle",
|
87
|
+
rose: "exclamation-circle",
|
88
|
+
orange: "bell",
|
89
|
+
green: "check-circle",
|
90
|
+
blue: "info-circle",
|
91
|
+
yellow: "exclamation-triangle",
|
92
|
+
violet: "shield-exclamation"
|
93
|
+
}
|
94
|
+
|
95
|
+
# Posizioni possibili per i toast
|
96
|
+
POSITIONS = {
|
97
|
+
"top-right": "top-right",
|
98
|
+
"top-left": "top-left",
|
99
|
+
"bottom-right": "bottom-right",
|
100
|
+
"bottom-left": "bottom-left",
|
101
|
+
"top-center": "top-center",
|
102
|
+
"bottom-center": "bottom-center"
|
103
|
+
}
|
104
|
+
|
105
|
+
# Inizializzazione del componente
|
106
|
+
def initialize(
|
107
|
+
title: nil,
|
108
|
+
message: nil,
|
109
|
+
variant: :default,
|
110
|
+
icon: nil,
|
111
|
+
dismissible: true,
|
112
|
+
classes: nil,
|
113
|
+
data: {},
|
114
|
+
position: :"top-right",
|
115
|
+
duration: 5000,
|
116
|
+
auto_hide: true,
|
117
|
+
rounded: :sm
|
118
|
+
)
|
119
|
+
@title = title
|
120
|
+
@message = message
|
121
|
+
@variant = variant.to_sym
|
122
|
+
@icon = icon
|
123
|
+
@dismissible = dismissible
|
124
|
+
@classes = classes
|
125
|
+
@data = data || {}
|
126
|
+
@position = position.to_sym
|
127
|
+
@duration = duration
|
128
|
+
@auto_hide = auto_hide
|
129
|
+
@rounded = rounded.to_sym
|
130
|
+
|
131
|
+
# Aggiungiamo dati per il comportamento del toast
|
132
|
+
@data[:controller] = "toast" if @data[:controller].blank?
|
133
|
+
@data[:toast_duration_value] = @duration if @auto_hide
|
134
|
+
@data[:toast_auto_hide_value] = @auto_hide.to_s
|
135
|
+
@data[:toast_position_value] = @position.to_s
|
136
|
+
end
|
137
|
+
|
138
|
+
# Genera l'icona in base alla variante se non specificata
|
139
|
+
def effective_icon
|
140
|
+
return @icon if @icon.present?
|
141
|
+
DEFAULT_ICONS[@variant] || DEFAULT_ICONS[:default]
|
142
|
+
end
|
143
|
+
|
144
|
+
# Genera le classi per il container
|
145
|
+
def container_classes
|
146
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:default])
|
147
|
+
position_class = POSITIONS.fetch(@position, POSITIONS[:"top-right"])
|
148
|
+
|
149
|
+
[
|
150
|
+
"fixed z-50 p-4 border shadow-lg",
|
151
|
+
get_border_radius_class,
|
152
|
+
"transform transition-transform duration-300",
|
153
|
+
"data-toast-enter-from-class='translate-y-2 opacity-0'",
|
154
|
+
"data-toast-enter-to-class='translate-y-0 opacity-100'",
|
155
|
+
"data-toast-leave-from-class='translate-y-0 opacity-100'",
|
156
|
+
"data-toast-leave-to-class='translate-y-2 opacity-0'",
|
157
|
+
styles[:bg],
|
158
|
+
styles[:border],
|
159
|
+
position_class,
|
160
|
+
"min-w-[20rem] max-w-sm",
|
161
|
+
"flex items-start",
|
162
|
+
@classes
|
163
|
+
].compact.join(" ")
|
164
|
+
end
|
165
|
+
|
166
|
+
# Genera il border-radius
|
167
|
+
def get_border_radius_class
|
168
|
+
ThemeHelper::BORDER_RADIUS[@rounded] || ThemeHelper::BORDER_RADIUS[:sm]
|
169
|
+
end
|
170
|
+
|
171
|
+
# Genera le classi per il titolo
|
172
|
+
def title_classes
|
173
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:default])
|
174
|
+
|
175
|
+
[
|
176
|
+
"font-medium",
|
177
|
+
styles[:title]
|
178
|
+
].compact.join(" ")
|
179
|
+
end
|
180
|
+
|
181
|
+
# Genera le classi per il messaggio
|
182
|
+
def message_classes
|
183
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:default])
|
184
|
+
|
185
|
+
[
|
186
|
+
"mt-1",
|
187
|
+
styles[:text]
|
188
|
+
].compact.join(" ")
|
189
|
+
end
|
190
|
+
|
191
|
+
# Genera le classi per l'icona
|
192
|
+
def icon_classes
|
193
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:default])
|
194
|
+
|
195
|
+
[
|
196
|
+
"flex-shrink-0",
|
197
|
+
"mr-3 mt-0.5",
|
198
|
+
styles[:icon]
|
199
|
+
].compact.join(" ")
|
200
|
+
end
|
201
|
+
|
202
|
+
# Genera le classi per il pulsante di chiusura
|
203
|
+
def close_button_classes
|
204
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:default])
|
205
|
+
|
206
|
+
[
|
207
|
+
"ml-auto -mr-1.5 -mt-1.5 inline-flex h-8 w-8 rounded-lg p-1.5 focus:ring-2 focus:ring-gray-400",
|
208
|
+
styles[:close]
|
209
|
+
].compact.join(" ")
|
210
|
+
end
|
211
|
+
|
212
|
+
# Genera le classi per il contenuto
|
213
|
+
def content_classes
|
214
|
+
"flex-1"
|
215
|
+
end
|
216
|
+
|
217
|
+
# Verifica se il componente deve essere reso
|
218
|
+
def render?
|
219
|
+
@title.present? || @message.present? || content.present?
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<div <%= avatar_attributes.to_s.html_safe %>>
|
2
|
+
<% if show_image? %>
|
3
|
+
<img
|
4
|
+
src="<%= @src %>"
|
5
|
+
alt="<%= @name || 'Avatar' %>"
|
6
|
+
class="bui-avatar-image"
|
7
|
+
width="<%= pixel_size %>"
|
8
|
+
height="<%= pixel_size %>"
|
9
|
+
>
|
10
|
+
<% else %>
|
11
|
+
<div class="bui-avatar-initials">
|
12
|
+
<%= initials %>
|
13
|
+
</div>
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<% if show_status? %>
|
17
|
+
<span class="<%= status_indicator_classes %>"></span>
|
18
|
+
<% end %>
|
19
|
+
</div>
|