better_ui 0.1.0 → 0.4.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 +65 -1
- 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 +196 -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 +188 -0
- data/app/components/better_ui/general/breadcrumb_component.html.erb +39 -0
- data/app/components/better_ui/general/breadcrumb_component.rb +132 -0
- data/app/components/better_ui/general/button_component.html.erb +34 -0
- data/app/components/better_ui/general/button_component.rb +193 -0
- data/app/components/better_ui/general/heading_component.html.erb +25 -0
- data/app/components/better_ui/general/heading_component.rb +142 -0
- data/app/components/better_ui/general/icon_component.html.erb +2 -0
- data/app/components/better_ui/general/icon_component.rb +101 -0
- data/app/components/better_ui/general/panel_component.html.erb +27 -0
- data/app/components/better_ui/general/panel_component.rb +97 -0
- data/app/components/better_ui/general/table_component.html.erb +37 -0
- data/app/components/better_ui/general/table_component.rb +141 -0
- data/app/components/better_ui/theme_helper.rb +169 -0
- data/app/controllers/better_ui/application_controller.rb +1 -0
- data/app/controllers/better_ui/docs_controller.rb +18 -25
- data/app/helpers/better_ui_application_helper.rb +99 -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 +24 -1
- data/lib/better_ui/version.rb +1 -1
- metadata +103 -7
- 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,294 @@
|
|
1
|
+
<nav class="<%= navbar_classes %>" <%= @data&.map { |k, v| "data-#{k}=\"#{v}\"" }&.join(' ')&.html_safe %>>
|
2
|
+
<div class="<%= container_class %>">
|
3
|
+
<div class="flex items-center justify-between mx-auto py-3">
|
4
|
+
<% # Logo / Brand %>
|
5
|
+
<% if @brand.present? %>
|
6
|
+
<div class="flex items-center">
|
7
|
+
<% if @brand.is_a?(Hash) %>
|
8
|
+
<% if @brand[:logo].present? %>
|
9
|
+
<% if @brand[:url].present? %>
|
10
|
+
<a href="<%= @brand[:url] %>" class="flex items-center">
|
11
|
+
<%= @brand[:logo].html_safe %>
|
12
|
+
<% if @brand[:name].present? %>
|
13
|
+
<span class="text-base font-semibold whitespace-nowrap ml-3 text-gray-900"><%= @brand[:name] %></span>
|
14
|
+
<% end %>
|
15
|
+
</a>
|
16
|
+
<% else %>
|
17
|
+
<div class="flex items-center">
|
18
|
+
<%= @brand[:logo].html_safe %>
|
19
|
+
<% if @brand[:name].present? %>
|
20
|
+
<span class="text-base font-semibold whitespace-nowrap ml-3 text-gray-900"><%= @brand[:name] %></span>
|
21
|
+
<% end %>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
<% elsif @brand[:name].present? %>
|
25
|
+
<% if @variant == :modern %>
|
26
|
+
<% if @brand[:url].present? %>
|
27
|
+
<a href="<%= @brand[:url] %>" class="flex items-center">
|
28
|
+
<div class="bg-gray-900 rounded-md p-2 mr-2">
|
29
|
+
<svg class="w-5 h-5 text-white" aria-hidden="true" fill="currentColor" viewBox="0 0 24 24">
|
30
|
+
<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>
|
31
|
+
</svg>
|
32
|
+
</div>
|
33
|
+
<div>
|
34
|
+
<span class="text-base font-semibold text-gray-900"><%= @brand[:name] %></span>
|
35
|
+
<% if @brand[:subtitle].present? %>
|
36
|
+
<p class="text-sm text-gray-500"><%= @brand[:subtitle] %></p>
|
37
|
+
<% end %>
|
38
|
+
</div>
|
39
|
+
</a>
|
40
|
+
<% else %>
|
41
|
+
<div class="flex items-center">
|
42
|
+
<div class="bg-gray-900 rounded-md p-2 mr-2">
|
43
|
+
<svg class="w-5 h-5 text-white" aria-hidden="true" fill="currentColor" viewBox="0 0 24 24">
|
44
|
+
<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>
|
45
|
+
</svg>
|
46
|
+
</div>
|
47
|
+
<div>
|
48
|
+
<span class="text-base font-semibold text-gray-900"><%= @brand[:name] %></span>
|
49
|
+
<% if @brand[:subtitle].present? %>
|
50
|
+
<p class="text-sm text-gray-500"><%= @brand[:subtitle] %></p>
|
51
|
+
<% end %>
|
52
|
+
</div>
|
53
|
+
</div>
|
54
|
+
<% end %>
|
55
|
+
<% else %>
|
56
|
+
<% if @brand[:url].present? %>
|
57
|
+
<a href="<%= @brand[:url] %>" class="text-xl font-semibold whitespace-nowrap">
|
58
|
+
<%= @brand[:name] %>
|
59
|
+
</a>
|
60
|
+
<% else %>
|
61
|
+
<span class="text-xl font-semibold whitespace-nowrap"><%= @brand[:name] %></span>
|
62
|
+
<% end %>
|
63
|
+
<% end %>
|
64
|
+
<% end %>
|
65
|
+
<% else %>
|
66
|
+
<% if @variant == :modern %>
|
67
|
+
<div class="flex items-center">
|
68
|
+
<div class="bg-gray-900 rounded-md p-2 mr-2">
|
69
|
+
<svg class="w-5 h-5 text-white" aria-hidden="true" fill="currentColor" viewBox="0 0 24 24">
|
70
|
+
<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>
|
71
|
+
</svg>
|
72
|
+
</div>
|
73
|
+
<span class="text-base font-semibold text-gray-900"><%= @brand %></span>
|
74
|
+
</div>
|
75
|
+
<% else %>
|
76
|
+
<span class="text-xl font-semibold whitespace-nowrap"><%= @brand %></span>
|
77
|
+
<% end %>
|
78
|
+
<% end %>
|
79
|
+
</div>
|
80
|
+
<% end %>
|
81
|
+
|
82
|
+
<div class="flex items-center">
|
83
|
+
<% # Menu principale della navbar visibile su desktop %>
|
84
|
+
<div class="hidden md:block">
|
85
|
+
<% if navigation_items.any? %>
|
86
|
+
<ul class="flex items-center space-x-1">
|
87
|
+
<% navigation_items.each do |item| %>
|
88
|
+
<li class="<%= has_dropdown?(item) ? 'group relative' : '' %>">
|
89
|
+
<% if item[:url].present? %>
|
90
|
+
<a
|
91
|
+
href="<%= item[:url] %>"
|
92
|
+
class="<%= nav_link_classes(active_item?(item)) %>"
|
93
|
+
<% if item[:target].present? %>target="<%= item[:target] %>"<% end %>
|
94
|
+
>
|
95
|
+
<% if item[:icon].present? %>
|
96
|
+
<span class="flex-shrink-0 mr-2 text-gray-500 w-5 h-5 inline-flex items-center justify-center">
|
97
|
+
<% if @variant == :modern %>
|
98
|
+
<% if item[:icon] == "home" %>
|
99
|
+
<svg class="w-5 h-5" 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="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
101
|
+
</svg>
|
102
|
+
<% elsif item[:icon] == "settings" || item[:icon] == "gear" %>
|
103
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
104
|
+
<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" />
|
105
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
106
|
+
</svg>
|
107
|
+
<% elsif item[:icon] == "user" || item[:icon] == "profile" %>
|
108
|
+
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
109
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
110
|
+
</svg>
|
111
|
+
<% else %>
|
112
|
+
<%= render BetterUi::General::IconComponent.new(name: item[:icon]) %>
|
113
|
+
<% end %>
|
114
|
+
<% else %>
|
115
|
+
<%= render BetterUi::General::IconComponent.new(name: item[:icon]) %>
|
116
|
+
<% end %>
|
117
|
+
</span>
|
118
|
+
<% end %>
|
119
|
+
<span><%= item[:label] %></span>
|
120
|
+
<% if has_dropdown?(item) %>
|
121
|
+
<% if @variant == :modern %>
|
122
|
+
<svg class="w-4 h-4 ml-1 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
123
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
124
|
+
</svg>
|
125
|
+
<% else %>
|
126
|
+
<span class="ml-1">
|
127
|
+
<%= render BetterUi::General::IconComponent.new(name: "chevron-down") %>
|
128
|
+
</span>
|
129
|
+
<% end %>
|
130
|
+
<% end %>
|
131
|
+
</a>
|
132
|
+
<% else %>
|
133
|
+
<span class="<%= nav_link_classes(active_item?(item)) %>">
|
134
|
+
<% if item[:icon].present? %>
|
135
|
+
<span class="flex-shrink-0 mr-2 text-gray-500 w-5 h-5 inline-flex items-center justify-center">
|
136
|
+
<%= render BetterUi::General::IconComponent.new(name: item[:icon]) %>
|
137
|
+
</span>
|
138
|
+
<% end %>
|
139
|
+
<%= item[:label] %>
|
140
|
+
<% if has_dropdown?(item) %>
|
141
|
+
<% if @variant == :modern %>
|
142
|
+
<svg class="w-4 h-4 ml-1 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
143
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
144
|
+
</svg>
|
145
|
+
<% else %>
|
146
|
+
<span class="ml-1">
|
147
|
+
<%= render BetterUi::General::IconComponent.new(name: "chevron-down") %>
|
148
|
+
</span>
|
149
|
+
<% end %>
|
150
|
+
<% end %>
|
151
|
+
</span>
|
152
|
+
<% end %>
|
153
|
+
|
154
|
+
<% # Dropdown menu %>
|
155
|
+
<% if has_dropdown?(item) %>
|
156
|
+
<div class="<%= dropdown_classes %>">
|
157
|
+
<% item[:dropdown].each do |dropdown_item| %>
|
158
|
+
<a
|
159
|
+
href="<%= dropdown_item[:url] || '#' %>"
|
160
|
+
class="<%= dropdown_link_classes %>"
|
161
|
+
<% if dropdown_item[:target].present? %>target="<%= dropdown_item[:target] %>"<% end %>
|
162
|
+
>
|
163
|
+
<% if dropdown_item[:icon].present? %>
|
164
|
+
<span class="mr-2 inline-block text-gray-500">
|
165
|
+
<%= render BetterUi::General::IconComponent.new(name: dropdown_item[:icon]) %>
|
166
|
+
</span>
|
167
|
+
<% end %>
|
168
|
+
<%= dropdown_item[:label] %>
|
169
|
+
</a>
|
170
|
+
<% end %>
|
171
|
+
</div>
|
172
|
+
<% end %>
|
173
|
+
</li>
|
174
|
+
<% end %>
|
175
|
+
</ul>
|
176
|
+
<% end %>
|
177
|
+
</div>
|
178
|
+
|
179
|
+
<% # Azioni aggiuntive (pulsanti, ricerca, ecc.) %>
|
180
|
+
<% if action_items.any? %>
|
181
|
+
<div class="flex items-center ml-4">
|
182
|
+
<% action_items.each do |action| %>
|
183
|
+
<div class="ml-2">
|
184
|
+
<% if action.is_a?(Hash) && action[:content].present? %>
|
185
|
+
<%= action[:content].html_safe %>
|
186
|
+
<% elsif action.is_a?(String) %>
|
187
|
+
<%= action.html_safe %>
|
188
|
+
<% end %>
|
189
|
+
</div>
|
190
|
+
<% end %>
|
191
|
+
</div>
|
192
|
+
<% end %>
|
193
|
+
|
194
|
+
<% # Pulsante hamburger per menu mobile %>
|
195
|
+
<button
|
196
|
+
data-action="navbar#toggleMenu"
|
197
|
+
type="button"
|
198
|
+
class="<%= mobile_toggle_classes %>"
|
199
|
+
aria-controls="navbar-menu"
|
200
|
+
aria-expanded="false"
|
201
|
+
>
|
202
|
+
<span class="sr-only">Apri menu</span>
|
203
|
+
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
|
204
|
+
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15"/>
|
205
|
+
</svg>
|
206
|
+
</button>
|
207
|
+
</div>
|
208
|
+
</div>
|
209
|
+
|
210
|
+
<% # Menu mobile %>
|
211
|
+
<div class="hidden md:hidden mt-2 pb-2" id="navbar-menu" data-navbar-target="menu">
|
212
|
+
<% if navigation_items.any? %>
|
213
|
+
<ul class="flex flex-col border-t border-gray-100 pt-2">
|
214
|
+
<% navigation_items.each do |item| %>
|
215
|
+
<li class="py-1">
|
216
|
+
<% if item[:url].present? && !has_dropdown?(item) %>
|
217
|
+
<a
|
218
|
+
href="<%= item[:url] %>"
|
219
|
+
class="<%= nav_link_classes(active_item?(item)) %>"
|
220
|
+
<% if item[:target].present? %>target="<%= item[:target] %>"<% end %>
|
221
|
+
>
|
222
|
+
<% if item[:icon].present? %>
|
223
|
+
<span class="flex-shrink-0 mr-2 text-gray-500 w-5 h-5 inline-flex items-center justify-center">
|
224
|
+
<%= render BetterUi::General::IconComponent.new(name: item[:icon]) %>
|
225
|
+
</span>
|
226
|
+
<% end %>
|
227
|
+
<%= item[:label] %>
|
228
|
+
</a>
|
229
|
+
<% else %>
|
230
|
+
<div class="flex flex-col">
|
231
|
+
<% dropdown_id = "dropdown-#{SecureRandom.hex(4)}" %>
|
232
|
+
<button
|
233
|
+
type="button"
|
234
|
+
class="<%= nav_link_classes(active_item?(item)) %> w-full text-left flex items-center justify-between"
|
235
|
+
data-action="click->navbar#toggleDropdown"
|
236
|
+
data-navbar-target="dropdown"
|
237
|
+
aria-expanded="false"
|
238
|
+
aria-controls="<%= dropdown_id %>"
|
239
|
+
>
|
240
|
+
<div class="flex items-center">
|
241
|
+
<% if item[:icon].present? %>
|
242
|
+
<span class="flex-shrink-0 mr-2 text-gray-500 w-5 h-5 inline-flex items-center justify-center">
|
243
|
+
<%= render BetterUi::General::IconComponent.new(name: item[:icon]) %>
|
244
|
+
</span>
|
245
|
+
<% end %>
|
246
|
+
<%= item[:label] %>
|
247
|
+
</div>
|
248
|
+
<svg class="w-4 h-4 ml-1 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
249
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
250
|
+
</svg>
|
251
|
+
</button>
|
252
|
+
|
253
|
+
<% if has_dropdown?(item) %>
|
254
|
+
<div id="<%= dropdown_id %>" class="hidden pl-8 mt-1 space-y-1" data-navbar-target="submenu">
|
255
|
+
<% item[:dropdown].each do |dropdown_item| %>
|
256
|
+
<a
|
257
|
+
href="<%= dropdown_item[:url] || '#' %>"
|
258
|
+
class="block py-2 px-3 text-gray-700 hover:bg-gray-50 hover:text-gray-900 rounded-md"
|
259
|
+
<% if dropdown_item[:target].present? %>target="<%= dropdown_item[:target] %>"<% end %>
|
260
|
+
>
|
261
|
+
<% if dropdown_item[:icon].present? %>
|
262
|
+
<span class="mr-2 inline-block text-gray-500">
|
263
|
+
<%= render BetterUi::General::IconComponent.new(name: dropdown_item[:icon]) %>
|
264
|
+
</span>
|
265
|
+
<% end %>
|
266
|
+
<%= dropdown_item[:label] %>
|
267
|
+
</a>
|
268
|
+
<% end %>
|
269
|
+
</div>
|
270
|
+
<% end %>
|
271
|
+
</div>
|
272
|
+
<% end %>
|
273
|
+
</li>
|
274
|
+
<% end %>
|
275
|
+
</ul>
|
276
|
+
<% end %>
|
277
|
+
|
278
|
+
<% # Azioni aggiuntive nel menu mobile %>
|
279
|
+
<% if action_items.any? %>
|
280
|
+
<div class="mt-3 border-t border-gray-100 pt-3 px-3 space-y-2">
|
281
|
+
<% action_items.each do |action| %>
|
282
|
+
<div>
|
283
|
+
<% if action.is_a?(Hash) && action[:content].present? %>
|
284
|
+
<%= action[:content].html_safe %>
|
285
|
+
<% elsif action.is_a?(String) %>
|
286
|
+
<%= action.html_safe %>
|
287
|
+
<% end %>
|
288
|
+
</div>
|
289
|
+
<% end %>
|
290
|
+
</div>
|
291
|
+
<% end %>
|
292
|
+
</div>
|
293
|
+
</div>
|
294
|
+
</nav>
|
@@ -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
|