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,249 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module Application
|
3
|
+
class NavbarComponent < ViewComponent::Base
|
4
|
+
attr_reader :brand, :variant, :fixed, :container_class, :data, :classes, :items, :actions, :mobile_breakpoint
|
5
|
+
|
6
|
+
# Varianti di colore disponibili
|
7
|
+
VARIANTS = {
|
8
|
+
light: {
|
9
|
+
navbar: "bg-white border-gray-200 shadow-sm",
|
10
|
+
text: "text-gray-700",
|
11
|
+
hover: "hover:text-orange-500",
|
12
|
+
active: "text-orange-600 border-orange-500",
|
13
|
+
dropdown_bg: "bg-white",
|
14
|
+
dropdown_hover: "hover:bg-gray-100",
|
15
|
+
mobile_bg: "bg-white",
|
16
|
+
mobile_divider: "border-gray-100"
|
17
|
+
},
|
18
|
+
dark: {
|
19
|
+
navbar: "bg-gray-800 border-gray-700",
|
20
|
+
text: "text-gray-200",
|
21
|
+
hover: "hover:text-white",
|
22
|
+
active: "text-white border-orange-500",
|
23
|
+
dropdown_bg: "bg-gray-700",
|
24
|
+
dropdown_hover: "hover:bg-gray-600",
|
25
|
+
mobile_bg: "bg-gray-800",
|
26
|
+
mobile_divider: "border-gray-700"
|
27
|
+
},
|
28
|
+
primary: {
|
29
|
+
navbar: "bg-orange-600 border-orange-700",
|
30
|
+
text: "text-white",
|
31
|
+
hover: "hover:text-white hover:bg-orange-700",
|
32
|
+
active: "text-white bg-orange-700 border-white",
|
33
|
+
dropdown_bg: "bg-orange-700",
|
34
|
+
dropdown_hover: "hover:bg-orange-800",
|
35
|
+
mobile_bg: "bg-orange-600",
|
36
|
+
mobile_divider: "border-orange-500"
|
37
|
+
},
|
38
|
+
transparent: {
|
39
|
+
navbar: "bg-transparent",
|
40
|
+
text: "text-gray-700",
|
41
|
+
hover: "hover:text-orange-500",
|
42
|
+
active: "text-orange-600 border-orange-500",
|
43
|
+
dropdown_bg: "bg-white bg-opacity-90 backdrop-blur-sm",
|
44
|
+
dropdown_hover: "hover:bg-gray-100",
|
45
|
+
mobile_bg: "bg-white bg-opacity-90 backdrop-blur-sm",
|
46
|
+
mobile_divider: "border-gray-100"
|
47
|
+
},
|
48
|
+
modern: {
|
49
|
+
navbar: "bg-white border-gray-200 shadow-none",
|
50
|
+
text: "text-gray-700",
|
51
|
+
hover: "hover:text-gray-900 hover:bg-gray-50",
|
52
|
+
active: "text-gray-900 bg-gray-100",
|
53
|
+
dropdown_bg: "bg-white",
|
54
|
+
dropdown_hover: "hover:bg-gray-50",
|
55
|
+
mobile_bg: "bg-white",
|
56
|
+
mobile_divider: "border-gray-100"
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
# Opzioni per la posizione fissa
|
61
|
+
FIXED_POSITIONS = {
|
62
|
+
top: "fixed top-0 left-0 right-0 z-40",
|
63
|
+
bottom: "fixed bottom-0 left-0 right-0 z-40"
|
64
|
+
}
|
65
|
+
|
66
|
+
# Opzioni per il breakpoint mobile
|
67
|
+
MOBILE_BREAKPOINTS = [:sm, :md, :lg, :xl, :xxl]
|
68
|
+
|
69
|
+
# Inizializzazione del componente
|
70
|
+
def initialize(
|
71
|
+
brand: nil,
|
72
|
+
variant: :modern,
|
73
|
+
fixed: nil,
|
74
|
+
container_class: "container mx-auto px-4",
|
75
|
+
classes: nil,
|
76
|
+
data: {},
|
77
|
+
items: [],
|
78
|
+
actions: [],
|
79
|
+
mobile_breakpoint: :lg
|
80
|
+
)
|
81
|
+
@brand = brand
|
82
|
+
@variant = variant.to_sym
|
83
|
+
@fixed = fixed.to_sym if fixed
|
84
|
+
@container_class = container_class
|
85
|
+
@classes = classes
|
86
|
+
@data = data || {}
|
87
|
+
@items = items || []
|
88
|
+
@actions = actions || []
|
89
|
+
@mobile_breakpoint = mobile_breakpoint.to_sym
|
90
|
+
|
91
|
+
# Aggiungiamo il controller Stimulus per la gestione del menu mobile
|
92
|
+
@data[:controller] = "navbar" if @data[:controller].blank?
|
93
|
+
end
|
94
|
+
|
95
|
+
# Genera le classi per il container della navbar
|
96
|
+
def navbar_classes
|
97
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
98
|
+
position_class = @fixed.present? ? FIXED_POSITIONS[@fixed] : nil
|
99
|
+
|
100
|
+
cls = [
|
101
|
+
"w-full",
|
102
|
+
styles[:navbar],
|
103
|
+
position_class,
|
104
|
+
@classes
|
105
|
+
]
|
106
|
+
|
107
|
+
# Aggiungi il bordo solo se non è la variante moderna
|
108
|
+
cls << "border-b" unless @variant == :modern
|
109
|
+
|
110
|
+
cls.compact.join(" ")
|
111
|
+
end
|
112
|
+
|
113
|
+
# Genera classi per i link della navbar
|
114
|
+
def nav_link_classes(active = false)
|
115
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
116
|
+
|
117
|
+
if @variant == :modern
|
118
|
+
base_classes = "block py-2 px-3 rounded-md"
|
119
|
+
|
120
|
+
if active
|
121
|
+
[
|
122
|
+
base_classes,
|
123
|
+
styles[:active],
|
124
|
+
"font-medium"
|
125
|
+
].join(" ")
|
126
|
+
else
|
127
|
+
[
|
128
|
+
base_classes,
|
129
|
+
styles[:text],
|
130
|
+
styles[:hover]
|
131
|
+
].join(" ")
|
132
|
+
end
|
133
|
+
else
|
134
|
+
base_classes = "block py-2 px-3 rounded md:p-0 md:hover:bg-transparent"
|
135
|
+
|
136
|
+
if active
|
137
|
+
[
|
138
|
+
base_classes,
|
139
|
+
styles[:active],
|
140
|
+
"md:border-b-2 md:bg-transparent"
|
141
|
+
].join(" ")
|
142
|
+
else
|
143
|
+
[
|
144
|
+
base_classes,
|
145
|
+
styles[:text],
|
146
|
+
styles[:hover]
|
147
|
+
].join(" ")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Genera classi per il dropdown
|
153
|
+
def dropdown_classes
|
154
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
155
|
+
|
156
|
+
if @variant == :modern
|
157
|
+
[
|
158
|
+
"absolute z-10 mt-1 rounded-md shadow-lg py-1 border border-gray-200",
|
159
|
+
styles[:dropdown_bg],
|
160
|
+
"hidden group-hover:block min-w-[200px]"
|
161
|
+
].join(" ")
|
162
|
+
else
|
163
|
+
[
|
164
|
+
"absolute z-10 mt-2 rounded-lg shadow-lg py-1",
|
165
|
+
styles[:dropdown_bg],
|
166
|
+
"hidden group-hover:block"
|
167
|
+
].join(" ")
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Genera classi per i link nel dropdown
|
172
|
+
def dropdown_link_classes
|
173
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
174
|
+
|
175
|
+
if @variant == :modern
|
176
|
+
[
|
177
|
+
"block px-4 py-2 text-sm text-gray-700",
|
178
|
+
styles[:dropdown_hover]
|
179
|
+
].join(" ")
|
180
|
+
else
|
181
|
+
[
|
182
|
+
"block px-4 py-2 text-sm",
|
183
|
+
styles[:text],
|
184
|
+
styles[:dropdown_hover]
|
185
|
+
].join(" ")
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Genera la classe per il pulsante del menu mobile
|
190
|
+
def mobile_toggle_classes
|
191
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
192
|
+
|
193
|
+
if @variant == :modern
|
194
|
+
[
|
195
|
+
"inline-flex items-center p-2 rounded-md ml-3 md:hidden",
|
196
|
+
"text-gray-700 hover:bg-gray-50"
|
197
|
+
].join(" ")
|
198
|
+
else
|
199
|
+
[
|
200
|
+
"inline-flex items-center p-2 ml-3 rounded-lg md:hidden",
|
201
|
+
styles[:text],
|
202
|
+
styles[:hover]
|
203
|
+
].join(" ")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Genera classi per il menu mobile
|
208
|
+
def mobile_menu_classes
|
209
|
+
styles = VARIANTS.fetch(@variant, VARIANTS[:modern])
|
210
|
+
|
211
|
+
[
|
212
|
+
"w-full md:flex md:w-auto",
|
213
|
+
"hidden md:block", # Nascosto di default su mobile, visibile su desktop
|
214
|
+
styles[:mobile_bg]
|
215
|
+
].join(" ")
|
216
|
+
end
|
217
|
+
|
218
|
+
# Genera la classe per il breakpoint mobile
|
219
|
+
def mobile_breakpoint_class
|
220
|
+
"md"
|
221
|
+
end
|
222
|
+
|
223
|
+
# Verifica se il componente deve essere reso
|
224
|
+
def render?
|
225
|
+
true
|
226
|
+
end
|
227
|
+
|
228
|
+
# Ritorna la lista degli elementi di navigazione
|
229
|
+
def navigation_items
|
230
|
+
@items
|
231
|
+
end
|
232
|
+
|
233
|
+
# Ritorna la lista delle azioni (es. pulsanti, ricerca)
|
234
|
+
def action_items
|
235
|
+
@actions
|
236
|
+
end
|
237
|
+
|
238
|
+
# Determina se un elemento dovrebbe essere considerato attivo
|
239
|
+
def active_item?(item)
|
240
|
+
item[:active] == true
|
241
|
+
end
|
242
|
+
|
243
|
+
# Verifica se un elemento ha un dropdown
|
244
|
+
def has_dropdown?(item)
|
245
|
+
item[:dropdown].present? && item[:dropdown].is_a?(Array) && item[:dropdown].any?
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
<%# Overlay per chiudere la sidebar su mobile %>
|
2
|
+
<div data-sidebar-target="overlay" data-action="click->sidebar#close" class="<%= overlay_classes %>"></div>
|
3
|
+
|
4
|
+
<%# Container principale della sidebar %>
|
5
|
+
<div class="<%= container_classes %>" data-sidebar-target="container" <%= @data&.map { |k, v| "data-#{k}=\"#{v}\"" }&.join(' ')&.html_safe %>>
|
6
|
+
<%# Header della sidebar con titolo e pulsante di chiusura (opzionale) %>
|
7
|
+
<% if @title.present? %>
|
8
|
+
<div class="<%= header_classes %>">
|
9
|
+
<% if @title.is_a?(String) %>
|
10
|
+
<div class="flex items-center p-2">
|
11
|
+
<div class="bg-gray-900 rounded-md p-2 mr-2">
|
12
|
+
<svg class="w-5 h-5 text-white" aria-hidden="true" fill="currentColor" viewBox="0 0 24 24">
|
13
|
+
<path fill-rule="evenodd" d="M4.5 9.75a6 6 0 0111.573-2.226 3.75 3.75 0 014.133 4.303A4.5 4.5 0 0118 20.25H6.75a5.25 5.25 0 01-2.23-10.004 6.072 6.072 0 01-.02-.496z" clip-rule="evenodd"></path>
|
14
|
+
</svg>
|
15
|
+
</div>
|
16
|
+
<div>
|
17
|
+
<h2 class="text-base font-semibold text-gray-900"><%= @title %></h2>
|
18
|
+
<p class="text-sm text-gray-500">Enterprise</p>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
<% else %>
|
22
|
+
<%= @title.html_safe %>
|
23
|
+
<% end %>
|
24
|
+
|
25
|
+
<%# Pulsante per chiudere la sidebar su mobile %>
|
26
|
+
<% if @variant != :modern %>
|
27
|
+
<button
|
28
|
+
type="button"
|
29
|
+
data-action="sidebar#close"
|
30
|
+
class="md:hidden ml-auto rounded-lg p-1.5 focus:outline-none focus:ring-2 focus:ring-gray-200"
|
31
|
+
aria-label="Chiudi"
|
32
|
+
>
|
33
|
+
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
34
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
35
|
+
</svg>
|
36
|
+
</button>
|
37
|
+
<% end %>
|
38
|
+
</div>
|
39
|
+
<% end %>
|
40
|
+
|
41
|
+
<%# Menu principale della sidebar %>
|
42
|
+
<div class="flex-grow overflow-y-auto py-3">
|
43
|
+
<nav>
|
44
|
+
<%
|
45
|
+
current_section = nil
|
46
|
+
@items.each_with_index do |item, index|
|
47
|
+
%>
|
48
|
+
<% if item[:divider] %>
|
49
|
+
<hr class="<%= divider_classes %>">
|
50
|
+
<% elsif item[:heading] %>
|
51
|
+
<% current_section = item[:heading] %>
|
52
|
+
<div class="<%= section_heading_classes %> uppercase">
|
53
|
+
<%= current_section %>
|
54
|
+
</div>
|
55
|
+
<% else %>
|
56
|
+
<%
|
57
|
+
# Aggiungi automaticamente l'intestazione della sezione se è il primo elemento e non c'è un'intestazione
|
58
|
+
if index == 0 && !@items.any? { |i| i[:heading].present? }
|
59
|
+
current_section = "PLATFORM"
|
60
|
+
%>
|
61
|
+
<div class="<%= section_heading_classes %> uppercase">
|
62
|
+
<%= current_section %>
|
63
|
+
</div>
|
64
|
+
<% end %>
|
65
|
+
|
66
|
+
<div>
|
67
|
+
<% if item[:url].present? && !has_children?(item) %>
|
68
|
+
<a
|
69
|
+
href="<%= item[:url] %>"
|
70
|
+
class="<%= menu_item_classes(active_item?(item)) %>"
|
71
|
+
<% if item[:target].present? %>target="<%= item[:target] %>"<% end %>
|
72
|
+
>
|
73
|
+
<% if item[:icon].present? %>
|
74
|
+
<span class="flex-shrink-0 mr-3 text-gray-500 w-5 h-5 flex items-center justify-center">
|
75
|
+
<% if item[:icon] == "table-cells" %>
|
76
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
77
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3a2 2 0 00-2 2v10a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2H5zm0 2h3v10H5V5zm5 0h10v2H10V5zm0 4h4v6h-4V9zm6 0h4v6h-4V9z" />
|
78
|
+
</svg>
|
79
|
+
<% elsif item[:icon] == "book" %>
|
80
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
81
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
82
|
+
</svg>
|
83
|
+
<% elsif item[:icon] == "gear" %>
|
84
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
85
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
86
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
87
|
+
</svg>
|
88
|
+
<% elsif item[:icon] == "cube" %>
|
89
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
90
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
91
|
+
</svg>
|
92
|
+
<% else %>
|
93
|
+
<%= render BetterUi::General::IconComponent.new(name: item[:icon]) %>
|
94
|
+
<% end %>
|
95
|
+
</span>
|
96
|
+
<% end %>
|
97
|
+
<span class="<%= item_label_classes %>"><%= item[:label] %></span>
|
98
|
+
|
99
|
+
<svg class="w-5 h-5 ml-auto text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
100
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
101
|
+
</svg>
|
102
|
+
</a>
|
103
|
+
<% else %>
|
104
|
+
<% dropdown_id = "dropdown-#{SecureRandom.hex(4)}" %>
|
105
|
+
<div class="flex flex-col">
|
106
|
+
<button
|
107
|
+
type="button"
|
108
|
+
class="<%= menu_item_classes(active_item?(item), false, has_children?(item)) %>"
|
109
|
+
data-action="sidebar#toggleSubmenu"
|
110
|
+
data-sidebar-target="dropdown"
|
111
|
+
aria-expanded="<%= active_item?(item) ? "true" : "false" %>"
|
112
|
+
aria-controls="<%= dropdown_id %>"
|
113
|
+
>
|
114
|
+
<% if item[:icon].present? %>
|
115
|
+
<span class="flex-shrink-0 mr-3 text-gray-500 w-5 h-5 flex items-center justify-center">
|
116
|
+
<% if item[:icon] == "table-cells" %>
|
117
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
118
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3a2 2 0 00-2 2v10a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2H5zm0 2h3v10H5V5zm5 0h10v2H10V5zm0 4h4v6h-4V9zm6 0h4v6h-4V9z" />
|
119
|
+
</svg>
|
120
|
+
<% elsif item[:icon] == "book" %>
|
121
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
122
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
123
|
+
</svg>
|
124
|
+
<% elsif item[:icon] == "gear" %>
|
125
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
126
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
127
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
128
|
+
</svg>
|
129
|
+
<% elsif item[:icon] == "cube" %>
|
130
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
131
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" />
|
132
|
+
</svg>
|
133
|
+
<% else %>
|
134
|
+
<%= render BetterUi::General::IconComponent.new(name: item[:icon]) %>
|
135
|
+
<% end %>
|
136
|
+
</span>
|
137
|
+
<% end %>
|
138
|
+
|
139
|
+
<span class="<%= item_label_classes %>"><%= item[:label] %></span>
|
140
|
+
|
141
|
+
<svg class="w-5 h-5 ml-auto text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" data-sidebar-target="chevron">
|
142
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
143
|
+
</svg>
|
144
|
+
</button>
|
145
|
+
|
146
|
+
<% if has_children?(item) %>
|
147
|
+
<div
|
148
|
+
id="<%= dropdown_id %>"
|
149
|
+
class="pl-5 <%= active_item?(item) ? "" : "hidden" %>"
|
150
|
+
data-sidebar-target="submenu"
|
151
|
+
>
|
152
|
+
<% item[:children].each do |child| %>
|
153
|
+
<a
|
154
|
+
href="<%= child[:url] || '#' %>"
|
155
|
+
class="block py-2.5 pr-3 text-gray-700 hover:bg-gray-50 hover:text-gray-900"
|
156
|
+
<% if child[:target].present? %>target="<%= child[:target] %>"<% end %>
|
157
|
+
>
|
158
|
+
<%= child[:label] %>
|
159
|
+
</a>
|
160
|
+
<% end %>
|
161
|
+
</div>
|
162
|
+
<% end %>
|
163
|
+
</div>
|
164
|
+
<% end %>
|
165
|
+
</div>
|
166
|
+
<% end %>
|
167
|
+
<% end %>
|
168
|
+
</nav>
|
169
|
+
</div>
|
170
|
+
|
171
|
+
<%# Footer della sidebar (opzionale) %>
|
172
|
+
<% if @footer.present? %>
|
173
|
+
<div class="<%= footer_classes %>">
|
174
|
+
<% if @footer.is_a?(String) %>
|
175
|
+
<%= @footer.html_safe %>
|
176
|
+
<% else %>
|
177
|
+
<%= @footer.html_safe %>
|
178
|
+
<% end %>
|
179
|
+
</div>
|
180
|
+
<% end %>
|
181
|
+
|
182
|
+
<%# Pulsante per il toggle della sidebar (opzionale) %>
|
183
|
+
<% if @collapsible && @variant != :modern %>
|
184
|
+
<button
|
185
|
+
type="button"
|
186
|
+
class="<%= toggle_button_classes %>"
|
187
|
+
data-action="sidebar#toggle"
|
188
|
+
data-sidebar-target="toggleButton"
|
189
|
+
aria-label="<%= @position == :left ? 'Espandi/Contrai sidebar' : 'Espandi/Contrai sidebar' %>"
|
190
|
+
>
|
191
|
+
<svg
|
192
|
+
class="w-4 h-4 transition-transform"
|
193
|
+
data-sidebar-target="toggleIcon"
|
194
|
+
aria-hidden="true"
|
195
|
+
xmlns="http://www.w3.org/2000/svg"
|
196
|
+
fill="none"
|
197
|
+
viewBox="0 0 24 24"
|
198
|
+
>
|
199
|
+
<% if @position == :left %>
|
200
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 19l-7-7 7-7"/>
|
201
|
+
<% else %>
|
202
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 19l7-7-7-7"/>
|
203
|
+
<% end %>
|
204
|
+
</svg>
|
205
|
+
</button>
|
206
|
+
<% end %>
|
207
|
+
</div>
|