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.
Potentially problematic release.
This version of better_ui might be problematic. Click here for more details.
- 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,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>
|
@@ -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>
|