better_ui 0.1.0 → 0.2.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/MIT-LICENSE +1 -1
- data/README.md +225 -119
- data/app/assets/stylesheets/better_ui/application.css +0 -356
- data/app/components/better_ui/application/card/component.html.erb +20 -0
- data/app/components/better_ui/application/card/component.rb +214 -0
- data/app/components/better_ui/application/main/component.html.erb +9 -0
- data/app/components/better_ui/application/main/component.rb +123 -0
- data/app/components/better_ui/application/navbar/component.html.erb +92 -0
- data/app/components/better_ui/application/navbar/component.rb +136 -0
- data/app/components/better_ui/application/sidebar/component.html.erb +190 -0
- data/app/components/better_ui/application/sidebar/component.rb +129 -0
- data/app/components/better_ui/general/alert/component.html.erb +32 -0
- data/app/components/better_ui/general/alert/component.rb +242 -0
- data/app/components/better_ui/general/avatar/component.html.erb +20 -0
- data/app/components/better_ui/general/avatar/component.rb +301 -0
- data/app/components/better_ui/general/badge/component.html.erb +23 -0
- data/app/components/better_ui/general/badge/component.rb +248 -0
- data/app/components/better_ui/general/breadcrumb/component.html.erb +15 -0
- data/app/components/better_ui/general/breadcrumb/component.rb +187 -0
- data/app/components/better_ui/general/button/component.html.erb +34 -0
- data/app/components/better_ui/general/button/component.rb +214 -0
- data/app/components/better_ui/general/divider/component.html.erb +10 -0
- data/app/components/better_ui/general/divider/component.rb +226 -0
- data/app/components/better_ui/general/dropdown/component.html.erb +14 -0
- data/app/components/better_ui/general/dropdown/component.rb +219 -0
- data/app/components/better_ui/general/dropdown/divider_component.html.erb +1 -0
- data/app/components/better_ui/general/dropdown/divider_component.rb +41 -0
- data/app/components/better_ui/general/dropdown/item_component.html.erb +6 -0
- data/app/components/better_ui/general/dropdown/item_component.rb +118 -0
- data/app/components/better_ui/general/field/component.html.erb +27 -0
- data/app/components/better_ui/general/field/component.rb +37 -0
- data/app/components/better_ui/general/heading/component.html.erb +22 -0
- data/app/components/better_ui/general/heading/component.rb +257 -0
- data/app/components/better_ui/general/icon/component.html.erb +7 -0
- data/app/components/better_ui/general/icon/component.rb +239 -0
- data/app/components/better_ui/general/input/checkbox/component.html.erb +5 -0
- data/app/components/better_ui/general/input/checkbox/component.rb +238 -0
- data/app/components/better_ui/general/input/datetime/component.html.erb +5 -0
- data/app/components/better_ui/general/input/datetime/component.rb +223 -0
- data/app/components/better_ui/general/input/radio/component.html.erb +5 -0
- data/app/components/better_ui/general/input/radio/component.rb +230 -0
- data/app/components/better_ui/general/input/select/component.html.erb +16 -0
- data/app/components/better_ui/general/input/select/component.rb +184 -0
- data/app/components/better_ui/general/input/select/select_component.html.erb +5 -0
- data/app/components/better_ui/general/input/select/select_component.rb +37 -0
- data/app/components/better_ui/general/input/text/component.html.erb +5 -0
- data/app/components/better_ui/general/input/text/component.rb +171 -0
- data/app/components/better_ui/general/input/textarea/component.html.erb +5 -0
- data/app/components/better_ui/general/input/textarea/component.rb +166 -0
- data/app/components/better_ui/general/link/component.html.erb +18 -0
- data/app/components/better_ui/general/link/component.rb +258 -0
- data/app/components/better_ui/general/modal/component.html.erb +42 -0
- data/app/components/better_ui/general/modal/component.rb +165 -0
- data/app/components/better_ui/general/pagination/component.html.erb +85 -0
- data/app/components/better_ui/general/pagination/component.rb +216 -0
- data/app/components/better_ui/general/panel/component.html.erb +28 -0
- data/app/components/better_ui/general/panel/component.rb +249 -0
- data/app/components/better_ui/general/progress/component.html.erb +11 -0
- data/app/components/better_ui/general/progress/component.rb +160 -0
- data/app/components/better_ui/general/spinner/component.html.erb +35 -0
- data/app/components/better_ui/general/spinner/component.rb +93 -0
- data/app/components/better_ui/general/table/component.html.erb +5 -0
- data/app/components/better_ui/general/table/component.rb +217 -0
- data/app/components/better_ui/general/table/tbody_component.html.erb +3 -0
- data/app/components/better_ui/general/table/tbody_component.rb +30 -0
- data/app/components/better_ui/general/table/td_component.html.erb +3 -0
- data/app/components/better_ui/general/table/td_component.rb +44 -0
- data/app/components/better_ui/general/table/tfoot_component.html.erb +3 -0
- data/app/components/better_ui/general/table/tfoot_component.rb +28 -0
- data/app/components/better_ui/general/table/th_component.html.erb +6 -0
- data/app/components/better_ui/general/table/th_component.rb +51 -0
- data/app/components/better_ui/general/table/thead_component.html.erb +3 -0
- data/app/components/better_ui/general/table/thead_component.rb +28 -0
- data/app/components/better_ui/general/table/tr_component.html.erb +3 -0
- data/app/components/better_ui/general/table/tr_component.rb +30 -0
- data/app/components/better_ui/general/tabs/component.html.erb +3 -0
- data/app/components/better_ui/general/tabs/component.rb +102 -0
- data/app/components/better_ui/general/tabs/panel_component.html.erb +3 -0
- data/app/components/better_ui/general/tabs/panel_component.rb +37 -0
- data/app/components/better_ui/general/tabs/tab_component.html.erb +13 -0
- data/app/components/better_ui/general/tabs/tab_component.rb +111 -0
- data/app/components/better_ui/general/tag/component.html.erb +3 -0
- data/app/components/better_ui/general/tag/component.rb +104 -0
- data/app/components/better_ui/general/tooltip/component.html.erb +7 -0
- data/app/components/better_ui/general/tooltip/component.rb +239 -0
- data/app/helpers/better_ui/application/components/card/card_helper.rb +96 -0
- data/app/helpers/better_ui/application/components/card.rb +11 -0
- data/app/helpers/better_ui/application/components/main/main_helper.rb +64 -0
- data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +77 -0
- data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +51 -0
- data/app/helpers/better_ui/application_helper.rb +51 -179
- data/app/helpers/better_ui/general/components/alert/alert_helper.rb +57 -0
- data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +29 -0
- data/app/helpers/better_ui/general/components/badge/badge_helper.rb +53 -0
- data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +37 -0
- data/app/helpers/better_ui/general/components/button/button_helper.rb +65 -0
- data/app/helpers/better_ui/general/components/container/container_helper.rb +60 -0
- data/app/helpers/better_ui/general/components/divider/divider_helper.rb +63 -0
- data/app/helpers/better_ui/general/components/dropdown/divider_helper.rb +32 -0
- data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +79 -0
- data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +62 -0
- data/app/helpers/better_ui/general/components/field/field_helper.rb +26 -0
- data/app/helpers/better_ui/general/components/heading/heading_helper.rb +72 -0
- data/app/helpers/better_ui/general/components/icon/icon_helper.rb +16 -0
- data/app/helpers/better_ui/general/components/input/checkbox/checkbox_helper.rb +81 -0
- data/app/helpers/better_ui/general/components/input/datetime/datetime_helper.rb +91 -0
- data/app/helpers/better_ui/general/components/input/radio/radio_helper.rb +79 -0
- data/app/helpers/better_ui/general/components/input/radio_group/radio_group_helper.rb +124 -0
- data/app/helpers/better_ui/general/components/input/select/select_helper.rb +70 -0
- data/app/helpers/better_ui/general/components/input/text/text_helper.rb +138 -0
- data/app/helpers/better_ui/general/components/input/textarea/textarea_helper.rb +73 -0
- data/app/helpers/better_ui/general/components/link/link_helper.rb +89 -0
- data/app/helpers/better_ui/general/components/modal/modal_helper.rb +95 -0
- data/app/helpers/better_ui/general/components/pagination/pagination_helper.rb +82 -0
- data/app/helpers/better_ui/general/components/panel/panel_helper.rb +83 -0
- data/app/helpers/better_ui/general/components/progress/progress_helper.rb +53 -0
- data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +19 -0
- data/app/helpers/better_ui/general/components/table/table_helper.rb +53 -0
- data/app/helpers/better_ui/general/components/table/tbody_helper.rb +13 -0
- data/app/helpers/better_ui/general/components/table/td_helper.rb +19 -0
- data/app/helpers/better_ui/general/components/table/tfoot_helper.rb +13 -0
- data/app/helpers/better_ui/general/components/table/th_helper.rb +19 -0
- data/app/helpers/better_ui/general/components/table/thead_helper.rb +13 -0
- data/app/helpers/better_ui/general/components/table/tr_helper.rb +13 -0
- data/app/helpers/better_ui/general/components/tabs/panel_helper.rb +62 -0
- data/app/helpers/better_ui/general/components/tabs/tab_helper.rb +55 -0
- data/app/helpers/better_ui/general/components/tabs/tabs_helper.rb +62 -0
- data/app/helpers/better_ui/general/components/tag/tag_helper.rb +26 -0
- data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +60 -0
- data/app/views/layouts/better_ui/application.html.erb +6 -124
- data/config/initializers/lookbook.rb +23 -0
- data/config/routes.rb +0 -8
- data/lib/better_ui/engine.rb +5 -19
- data/lib/better_ui/railtie.rb +20 -0
- data/lib/better_ui/version.rb +1 -1
- data/lib/better_ui.rb +4 -20
- metadata +155 -28
- data/app/controllers/better_ui/docs_controller.rb +0 -41
- 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
@@ -0,0 +1,216 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Pagination
|
6
|
+
class Component < ViewComponent::Base
|
7
|
+
include BetterUi::Engine.helpers
|
8
|
+
|
9
|
+
# Costanti per temi
|
10
|
+
PAGINATION_THEME = {
|
11
|
+
default: {
|
12
|
+
container: 'border-gray-300',
|
13
|
+
page: 'border-gray-300 text-gray-500 hover:bg-gray-50 hover:text-gray-700',
|
14
|
+
current: 'border-blue-500 bg-blue-50 text-blue-600',
|
15
|
+
disabled: 'border-gray-300 text-gray-300 cursor-not-allowed'
|
16
|
+
},
|
17
|
+
blue: {
|
18
|
+
container: 'border-blue-300',
|
19
|
+
page: 'border-blue-300 text-blue-600 hover:bg-blue-50 hover:text-blue-700',
|
20
|
+
current: 'border-blue-500 bg-blue-100 text-blue-700',
|
21
|
+
disabled: 'border-blue-200 text-blue-300 cursor-not-allowed'
|
22
|
+
},
|
23
|
+
red: {
|
24
|
+
container: 'border-red-300',
|
25
|
+
page: 'border-red-300 text-red-600 hover:bg-red-50 hover:text-red-700',
|
26
|
+
current: 'border-red-500 bg-red-100 text-red-700',
|
27
|
+
disabled: 'border-red-200 text-red-300 cursor-not-allowed'
|
28
|
+
},
|
29
|
+
green: {
|
30
|
+
container: 'border-green-300',
|
31
|
+
page: 'border-green-300 text-green-600 hover:bg-green-50 hover:text-green-700',
|
32
|
+
current: 'border-green-500 bg-green-100 text-green-700',
|
33
|
+
disabled: 'border-green-200 text-green-300 cursor-not-allowed'
|
34
|
+
},
|
35
|
+
yellow: {
|
36
|
+
container: 'border-yellow-300',
|
37
|
+
page: 'border-yellow-300 text-yellow-600 hover:bg-yellow-50 hover:text-yellow-700',
|
38
|
+
current: 'border-yellow-500 bg-yellow-100 text-yellow-700',
|
39
|
+
disabled: 'border-yellow-200 text-yellow-300 cursor-not-allowed'
|
40
|
+
},
|
41
|
+
violet: {
|
42
|
+
container: 'border-violet-300',
|
43
|
+
page: 'border-violet-300 text-violet-600 hover:bg-violet-50 hover:text-violet-700',
|
44
|
+
current: 'border-violet-500 bg-violet-100 text-violet-700',
|
45
|
+
disabled: 'border-violet-200 text-violet-300 cursor-not-allowed'
|
46
|
+
},
|
47
|
+
orange: {
|
48
|
+
container: 'border-orange-300',
|
49
|
+
page: 'border-orange-300 text-orange-600 hover:bg-orange-50 hover:text-orange-700',
|
50
|
+
current: 'border-orange-500 bg-orange-100 text-orange-700',
|
51
|
+
disabled: 'border-orange-200 text-orange-300 cursor-not-allowed'
|
52
|
+
},
|
53
|
+
rose: {
|
54
|
+
container: 'border-rose-300',
|
55
|
+
page: 'border-rose-300 text-rose-600 hover:bg-rose-50 hover:text-rose-700',
|
56
|
+
current: 'border-rose-500 bg-rose-100 text-rose-700',
|
57
|
+
disabled: 'border-rose-200 text-rose-300 cursor-not-allowed'
|
58
|
+
},
|
59
|
+
white: {
|
60
|
+
container: 'border-white',
|
61
|
+
page: 'border-white text-gray-700 hover:bg-white hover:text-gray-900',
|
62
|
+
current: 'border-white bg-white text-gray-900',
|
63
|
+
disabled: 'border-white text-gray-400 cursor-not-allowed'
|
64
|
+
}
|
65
|
+
}.freeze
|
66
|
+
|
67
|
+
# Costanti per dimensioni
|
68
|
+
PAGINATION_SIZE = {
|
69
|
+
small: 'px-2 py-1 text-sm',
|
70
|
+
medium: 'px-3 py-2 text-base',
|
71
|
+
large: 'px-4 py-3 text-lg'
|
72
|
+
}.freeze
|
73
|
+
|
74
|
+
def initialize(current_page:, total_pages:, path:, theme: :default, size: :medium,
|
75
|
+
window: 2, show_info: false, per_page: nil, classes: '', **options)
|
76
|
+
@current_page = current_page.to_i
|
77
|
+
@total_pages = total_pages.to_i
|
78
|
+
@path = path
|
79
|
+
@theme = theme
|
80
|
+
@size = size
|
81
|
+
@window = window.to_i
|
82
|
+
@show_info = show_info
|
83
|
+
@per_page = per_page
|
84
|
+
@classes = classes
|
85
|
+
@options = options
|
86
|
+
|
87
|
+
validate_params
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
attr_reader :current_page, :total_pages, :path, :theme, :size, :window,
|
93
|
+
:show_info, :per_page, :classes, :options
|
94
|
+
|
95
|
+
def validate_params
|
96
|
+
validate_theme
|
97
|
+
validate_size
|
98
|
+
validate_pages
|
99
|
+
end
|
100
|
+
|
101
|
+
def validate_theme
|
102
|
+
return if PAGINATION_THEME.key?(theme)
|
103
|
+
|
104
|
+
raise ArgumentError,
|
105
|
+
"Invalid theme: #{theme}. Valid themes are: #{PAGINATION_THEME.keys.join(', ')}"
|
106
|
+
end
|
107
|
+
|
108
|
+
def validate_size
|
109
|
+
return if PAGINATION_SIZE.key?(size)
|
110
|
+
|
111
|
+
raise ArgumentError,
|
112
|
+
"Invalid size: #{size}. Valid sizes are: #{PAGINATION_SIZE.keys.join(', ')}"
|
113
|
+
end
|
114
|
+
|
115
|
+
def validate_pages
|
116
|
+
if current_page < 1 || current_page > total_pages
|
117
|
+
raise ArgumentError, "current_page must be between 1 and #{total_pages}"
|
118
|
+
end
|
119
|
+
|
120
|
+
if total_pages < 1
|
121
|
+
raise ArgumentError, "total_pages must be at least 1"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def theme_classes
|
126
|
+
PAGINATION_THEME[theme]
|
127
|
+
end
|
128
|
+
|
129
|
+
def size_classes
|
130
|
+
PAGINATION_SIZE[size]
|
131
|
+
end
|
132
|
+
|
133
|
+
def container_classes
|
134
|
+
"inline-flex -space-x-px rounded-md shadow-sm #{theme_classes[:container]} #{classes}".strip
|
135
|
+
end
|
136
|
+
|
137
|
+
def page_link_classes(page_num)
|
138
|
+
base_classes = "relative inline-flex items-center border #{size_classes} font-medium"
|
139
|
+
|
140
|
+
if page_num == current_page
|
141
|
+
"#{base_classes} #{theme_classes[:current]}"
|
142
|
+
else
|
143
|
+
"#{base_classes} #{theme_classes[:page]} focus:z-10 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def disabled_classes
|
148
|
+
"relative inline-flex items-center border #{size_classes} font-medium #{theme_classes[:disabled]}"
|
149
|
+
end
|
150
|
+
|
151
|
+
def page_range
|
152
|
+
return [1] if total_pages == 1
|
153
|
+
|
154
|
+
start_page = [current_page - window, 1].max
|
155
|
+
end_page = [current_page + window, total_pages].min
|
156
|
+
|
157
|
+
# Espandi il range se possibile
|
158
|
+
if end_page - start_page < (window * 2)
|
159
|
+
if start_page == 1
|
160
|
+
end_page = [start_page + (window * 2), total_pages].min
|
161
|
+
elsif end_page == total_pages
|
162
|
+
start_page = [end_page - (window * 2), 1].max
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
(start_page..end_page).to_a
|
167
|
+
end
|
168
|
+
|
169
|
+
def show_left_ellipsis?
|
170
|
+
page_range.first > 2
|
171
|
+
end
|
172
|
+
|
173
|
+
def show_right_ellipsis?
|
174
|
+
page_range.last < total_pages - 1
|
175
|
+
end
|
176
|
+
|
177
|
+
def build_url(page_num)
|
178
|
+
return '#' if page_num == current_page
|
179
|
+
|
180
|
+
uri = URI.parse(path)
|
181
|
+
params = URI.decode_www_form(uri.query || '')
|
182
|
+
params = params.reject { |k, _v| k == 'page' }
|
183
|
+
params << ['page', page_num] if page_num > 1
|
184
|
+
|
185
|
+
uri.query = params.empty? ? nil : URI.encode_www_form(params)
|
186
|
+
uri.to_s
|
187
|
+
end
|
188
|
+
|
189
|
+
def previous_page
|
190
|
+
current_page > 1 ? current_page - 1 : nil
|
191
|
+
end
|
192
|
+
|
193
|
+
def next_page
|
194
|
+
current_page < total_pages ? current_page + 1 : nil
|
195
|
+
end
|
196
|
+
|
197
|
+
def info_text
|
198
|
+
return '' unless show_info && per_page
|
199
|
+
|
200
|
+
start_item = ((current_page - 1) * per_page) + 1
|
201
|
+
end_item = [current_page * per_page, total_items].min
|
202
|
+
|
203
|
+
"Mostrando #{start_item}-#{end_item} di #{total_items} risultati"
|
204
|
+
end
|
205
|
+
|
206
|
+
def total_items
|
207
|
+
total_pages * per_page
|
208
|
+
end
|
209
|
+
|
210
|
+
def should_show_pagination?
|
211
|
+
total_pages > 1
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<%# Template per il panel %>
|
2
|
+
<div <%= tag.attributes(panel_attributes) %>>
|
3
|
+
<% if show_header? %>
|
4
|
+
<div class="<%= header_classes %>">
|
5
|
+
<% if @header.present? %>
|
6
|
+
<%= raw @header %>
|
7
|
+
<% elsif @title.present? %>
|
8
|
+
<div class="<%= title_classes %>"><%= @title %></div>
|
9
|
+
<% end %>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<% if show_body? %>
|
14
|
+
<div class="<%= body_classes %>">
|
15
|
+
<% if @body.present? %>
|
16
|
+
<%= raw @body %>
|
17
|
+
<% elsif content.present? %>
|
18
|
+
<%= content %>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
<% if show_footer? %>
|
24
|
+
<div class="<%= footer_classes %>">
|
25
|
+
<%= raw @footer %>
|
26
|
+
</div>
|
27
|
+
<% end %>
|
28
|
+
</div>
|
@@ -0,0 +1,249 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module General
|
3
|
+
module Panel
|
4
|
+
class Component < ViewComponent::Base
|
5
|
+
attr_reader :header, :footer, :body, :title, :padding, :theme, :style, :radius
|
6
|
+
|
7
|
+
# Classi base sempre presenti
|
8
|
+
PANEL_BASE_CLASSES = "overflow-hidden"
|
9
|
+
|
10
|
+
# Temi con classi Tailwind dirette - LOGICA CORRETTA
|
11
|
+
PANEL_THEME_CLASSES = {
|
12
|
+
default: "bg-gray-800 border-gray-700", # Scuro per sfondi scuri
|
13
|
+
white: "bg-white border-gray-200", # Chiaro per sfondi chiari
|
14
|
+
red: "bg-red-50 border-red-200",
|
15
|
+
rose: "bg-rose-50 border-rose-200",
|
16
|
+
orange: "bg-orange-50 border-orange-200",
|
17
|
+
green: "bg-green-50 border-green-200",
|
18
|
+
blue: "bg-blue-50 border-blue-200",
|
19
|
+
yellow: "bg-yellow-50 border-yellow-200",
|
20
|
+
violet: "bg-violet-50 border-violet-200"
|
21
|
+
}
|
22
|
+
|
23
|
+
# Temi per testo - LOGICA CORRETTA
|
24
|
+
PANEL_TEXT_THEME_CLASSES = {
|
25
|
+
default: "text-white", # Testo bianco per sfondi scuri
|
26
|
+
white: "text-gray-900", # Testo nero per sfondi chiari
|
27
|
+
red: "text-red-900",
|
28
|
+
rose: "text-rose-900",
|
29
|
+
orange: "text-orange-900",
|
30
|
+
green: "text-green-900",
|
31
|
+
blue: "text-blue-900",
|
32
|
+
yellow: "text-yellow-900",
|
33
|
+
violet: "text-violet-900"
|
34
|
+
}
|
35
|
+
|
36
|
+
# Stili con classi Tailwind dirette
|
37
|
+
PANEL_STYLE_CLASSES = {
|
38
|
+
default: "border shadow-sm",
|
39
|
+
flat: "border-0",
|
40
|
+
raised: "border shadow-lg",
|
41
|
+
bordered: "border-2"
|
42
|
+
}
|
43
|
+
|
44
|
+
# Padding con classi Tailwind dirette
|
45
|
+
PANEL_PADDING_CLASSES = {
|
46
|
+
none: "p-0",
|
47
|
+
small: "p-2",
|
48
|
+
medium: "p-4",
|
49
|
+
large: "p-6"
|
50
|
+
}
|
51
|
+
|
52
|
+
# Radius con classi Tailwind dirette
|
53
|
+
PANEL_RADIUS_CLASSES = {
|
54
|
+
none: "rounded-none",
|
55
|
+
small: "rounded",
|
56
|
+
medium: "rounded-md",
|
57
|
+
large: "rounded-lg",
|
58
|
+
full: "rounded-full"
|
59
|
+
}
|
60
|
+
|
61
|
+
# @param title [String] titolo del pannello (opzionale)
|
62
|
+
# @param body [String] contenuto HTML del pannello (opzionale)
|
63
|
+
# @param header [String] header personalizzato (opzionale)
|
64
|
+
# @param footer [String] footer del pannello (opzionale)
|
65
|
+
# @param theme [Symbol] tema del colore (:default, :white, etc.)
|
66
|
+
# @param style [Symbol] stile (:default, :flat, :raised, :bordered)
|
67
|
+
# @param padding [Symbol] padding interno (:none, :small, :medium, :large)
|
68
|
+
# @param radius [Symbol] raggio dei bordi (:none, :small, :medium, :large, :full)
|
69
|
+
# @param html_options [Hash] opzioni HTML aggiuntive
|
70
|
+
def initialize(
|
71
|
+
title: nil,
|
72
|
+
body: nil,
|
73
|
+
header: nil,
|
74
|
+
footer: nil,
|
75
|
+
theme: :white,
|
76
|
+
style: :default,
|
77
|
+
padding: :medium,
|
78
|
+
radius: :small,
|
79
|
+
**html_options
|
80
|
+
)
|
81
|
+
@title = title
|
82
|
+
@body = body
|
83
|
+
@header = header
|
84
|
+
@footer = footer
|
85
|
+
@theme = theme.to_sym
|
86
|
+
@style = style.to_sym
|
87
|
+
@padding = padding.to_sym
|
88
|
+
@radius = radius.to_sym
|
89
|
+
@html_options = html_options
|
90
|
+
|
91
|
+
validate_params
|
92
|
+
end
|
93
|
+
|
94
|
+
# Combina tutte le classi CSS per il panel
|
95
|
+
def combined_classes
|
96
|
+
[
|
97
|
+
PANEL_BASE_CLASSES,
|
98
|
+
get_theme_class,
|
99
|
+
get_style_class,
|
100
|
+
get_radius_class,
|
101
|
+
@html_options[:class]
|
102
|
+
].compact.join(" ")
|
103
|
+
end
|
104
|
+
|
105
|
+
# Restituisce gli attributi HTML per il panel
|
106
|
+
def panel_attributes
|
107
|
+
attrs = @html_options.except(:class)
|
108
|
+
attrs[:class] = combined_classes
|
109
|
+
attrs
|
110
|
+
end
|
111
|
+
|
112
|
+
# Classi per l'header
|
113
|
+
def header_classes
|
114
|
+
[
|
115
|
+
"border-b",
|
116
|
+
get_border_theme_class,
|
117
|
+
get_text_theme_class,
|
118
|
+
get_padding_class
|
119
|
+
].compact.join(" ")
|
120
|
+
end
|
121
|
+
|
122
|
+
# Classi per il body
|
123
|
+
def body_classes
|
124
|
+
[
|
125
|
+
get_text_theme_class,
|
126
|
+
get_padding_class
|
127
|
+
].compact.join(" ")
|
128
|
+
end
|
129
|
+
|
130
|
+
# Classi per il footer
|
131
|
+
def footer_classes
|
132
|
+
[
|
133
|
+
"border-t",
|
134
|
+
get_border_theme_class,
|
135
|
+
get_text_theme_class,
|
136
|
+
get_padding_class
|
137
|
+
].compact.join(" ")
|
138
|
+
end
|
139
|
+
|
140
|
+
# Classi per il title
|
141
|
+
def title_classes
|
142
|
+
[
|
143
|
+
"font-semibold text-lg leading-6",
|
144
|
+
get_text_theme_class
|
145
|
+
].compact.join(" ")
|
146
|
+
end
|
147
|
+
|
148
|
+
# Determina se il pannello deve essere renderizzato
|
149
|
+
def render?
|
150
|
+
@body.present? || @header.present? || @footer.present? || content.present?
|
151
|
+
end
|
152
|
+
|
153
|
+
# Determina se mostrare l'header
|
154
|
+
def show_header?
|
155
|
+
@header.present? || @title.present?
|
156
|
+
end
|
157
|
+
|
158
|
+
# Determina se mostrare il body
|
159
|
+
def show_body?
|
160
|
+
@body.present? || content.present?
|
161
|
+
end
|
162
|
+
|
163
|
+
# Determina se mostrare il footer
|
164
|
+
def show_footer?
|
165
|
+
@footer.present?
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def get_theme_class
|
171
|
+
PANEL_THEME_CLASSES[@theme] || PANEL_THEME_CLASSES[:white]
|
172
|
+
end
|
173
|
+
|
174
|
+
def get_text_theme_class
|
175
|
+
PANEL_TEXT_THEME_CLASSES[@theme] || PANEL_TEXT_THEME_CLASSES[:white]
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_border_theme_class
|
179
|
+
# Usa lo stesso colore del bordo principale ma più leggero per i separatori interni
|
180
|
+
case @theme
|
181
|
+
when :default
|
182
|
+
"border-gray-600"
|
183
|
+
when :white
|
184
|
+
"border-gray-100"
|
185
|
+
when :red
|
186
|
+
"border-red-100"
|
187
|
+
when :rose
|
188
|
+
"border-rose-100"
|
189
|
+
when :orange
|
190
|
+
"border-orange-100"
|
191
|
+
when :green
|
192
|
+
"border-green-100"
|
193
|
+
when :blue
|
194
|
+
"border-blue-100"
|
195
|
+
when :yellow
|
196
|
+
"border-yellow-100"
|
197
|
+
when :violet
|
198
|
+
"border-violet-100"
|
199
|
+
else
|
200
|
+
"border-gray-100"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def get_style_class
|
205
|
+
PANEL_STYLE_CLASSES[@style] || PANEL_STYLE_CLASSES[:default]
|
206
|
+
end
|
207
|
+
|
208
|
+
def get_radius_class
|
209
|
+
PANEL_RADIUS_CLASSES[@radius] || PANEL_RADIUS_CLASSES[:small]
|
210
|
+
end
|
211
|
+
|
212
|
+
def get_padding_class
|
213
|
+
PANEL_PADDING_CLASSES[@padding] || PANEL_PADDING_CLASSES[:medium]
|
214
|
+
end
|
215
|
+
|
216
|
+
def validate_params
|
217
|
+
validate_theme
|
218
|
+
validate_style
|
219
|
+
validate_padding
|
220
|
+
validate_radius
|
221
|
+
end
|
222
|
+
|
223
|
+
def validate_theme
|
224
|
+
unless PANEL_THEME_CLASSES.keys.include?(@theme)
|
225
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{PANEL_THEME_CLASSES.keys.join(', ')}"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def validate_style
|
230
|
+
unless PANEL_STYLE_CLASSES.keys.include?(@style)
|
231
|
+
raise ArgumentError, "Lo stile deve essere uno tra: #{PANEL_STYLE_CLASSES.keys.join(', ')}"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def validate_padding
|
236
|
+
unless PANEL_PADDING_CLASSES.keys.include?(@padding)
|
237
|
+
raise ArgumentError, "Il padding deve essere uno tra: #{PANEL_PADDING_CLASSES.keys.join(', ')}"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def validate_radius
|
242
|
+
unless PANEL_RADIUS_CLASSES.keys.include?(@radius)
|
243
|
+
raise ArgumentError, "Il raggio deve essere uno tra: #{PANEL_RADIUS_CLASSES.keys.join(', ')}"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<div class="bui-progress-wrapper">
|
2
|
+
<div <%= progress_attributes.map { |k, v| "#{k}=\"#{v}\"" }.join(' ').html_safe %>>
|
3
|
+
<div <%= bar_attributes.map { |k, v| "#{k}=\"#{v}\"" }.join(' ').html_safe %>></div>
|
4
|
+
</div>
|
5
|
+
|
6
|
+
<% if show_label? %>
|
7
|
+
<div class="bui-progress-label mt-1 text-sm text-gray-600 text-center">
|
8
|
+
<%= value %>%
|
9
|
+
</div>
|
10
|
+
<% end %>
|
11
|
+
</div>
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module General
|
3
|
+
module Progress
|
4
|
+
class Component < ViewComponent::Base
|
5
|
+
# Classi base sempre presenti
|
6
|
+
PROGRESS_BASE_CLASSES = "relative w-full bg-gray-200 rounded-full overflow-hidden"
|
7
|
+
|
8
|
+
# Classi per la barra di progresso
|
9
|
+
PROGRESS_BAR_BASE_CLASSES = "h-full transition-all duration-300 ease-in-out"
|
10
|
+
|
11
|
+
# Dimensioni della progress bar con classi Tailwind dirette
|
12
|
+
PROGRESS_SIZES = {
|
13
|
+
small: "h-2",
|
14
|
+
medium: "h-4",
|
15
|
+
large: "h-6"
|
16
|
+
}
|
17
|
+
|
18
|
+
# Temi di progress bar con classi Tailwind dirette
|
19
|
+
PROGRESS_THEMES = {
|
20
|
+
default: "bg-gray-600",
|
21
|
+
white: "bg-white border border-gray-300",
|
22
|
+
red: "bg-red-600",
|
23
|
+
rose: "bg-rose-600",
|
24
|
+
orange: "bg-orange-600",
|
25
|
+
green: "bg-green-600",
|
26
|
+
blue: "bg-blue-600",
|
27
|
+
yellow: "bg-yellow-600",
|
28
|
+
violet: "bg-violet-600"
|
29
|
+
}
|
30
|
+
|
31
|
+
# Classi per il background container
|
32
|
+
PROGRESS_CONTAINER_THEMES = {
|
33
|
+
default: "bg-gray-200",
|
34
|
+
white: "bg-gray-100",
|
35
|
+
red: "bg-red-100",
|
36
|
+
rose: "bg-rose-100",
|
37
|
+
orange: "bg-orange-100",
|
38
|
+
green: "bg-green-100",
|
39
|
+
blue: "bg-blue-100",
|
40
|
+
yellow: "bg-yellow-100",
|
41
|
+
violet: "bg-violet-100"
|
42
|
+
}
|
43
|
+
|
44
|
+
# @param value [Integer] percentuale di completamento (0-100)
|
45
|
+
# @param theme [Symbol] :default, :white, :red, :rose, :orange, :green, :blue, :yellow, :violet
|
46
|
+
# @param size [Symbol] :small, :medium, :large
|
47
|
+
# @param label [Boolean] mostra etichetta con percentuale
|
48
|
+
# @param classes [String] classi CSS aggiuntive per il container
|
49
|
+
# @param html_options [Hash] opzioni HTML per il container
|
50
|
+
def initialize(
|
51
|
+
value: 0,
|
52
|
+
theme: :white,
|
53
|
+
size: :medium,
|
54
|
+
label: false,
|
55
|
+
classes: nil,
|
56
|
+
**html_options
|
57
|
+
)
|
58
|
+
@value = [ 0, [ value.to_i, 100 ].min ].max # Clamp tra 0 e 100
|
59
|
+
@theme = theme.to_sym
|
60
|
+
@size = size.to_sym
|
61
|
+
@label = label
|
62
|
+
@classes = classes
|
63
|
+
@html_options = html_options
|
64
|
+
|
65
|
+
validate_params
|
66
|
+
end
|
67
|
+
|
68
|
+
# Combina tutte le classi per il container
|
69
|
+
def combined_classes
|
70
|
+
[
|
71
|
+
PROGRESS_BASE_CLASSES,
|
72
|
+
get_size_class,
|
73
|
+
get_container_theme_class,
|
74
|
+
@classes,
|
75
|
+
@html_options[:class]
|
76
|
+
].compact.join(" ")
|
77
|
+
end
|
78
|
+
|
79
|
+
# Combina tutte le classi per la barra di progresso
|
80
|
+
def bar_classes
|
81
|
+
[
|
82
|
+
PROGRESS_BAR_BASE_CLASSES,
|
83
|
+
get_theme_class
|
84
|
+
].compact.join(" ")
|
85
|
+
end
|
86
|
+
|
87
|
+
# Restituisce gli attributi per il container della progress bar
|
88
|
+
def progress_attributes
|
89
|
+
attrs = {
|
90
|
+
class: combined_classes,
|
91
|
+
role: "progressbar",
|
92
|
+
"aria-valuenow": @value,
|
93
|
+
"aria-valuemin": 0,
|
94
|
+
"aria-valuemax": 100,
|
95
|
+
"aria-label": "Progresso: #{@value}%"
|
96
|
+
}
|
97
|
+
|
98
|
+
# Aggiungi altri attributi HTML se presenti
|
99
|
+
@html_options.except(:class).each do |key, value|
|
100
|
+
attrs[key] = value
|
101
|
+
end
|
102
|
+
|
103
|
+
attrs
|
104
|
+
end
|
105
|
+
|
106
|
+
# Restituisce gli attributi per la barra di progresso
|
107
|
+
def bar_attributes
|
108
|
+
{
|
109
|
+
class: bar_classes,
|
110
|
+
style: "width: #{@value}%"
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
# Restituisce il valore percentuale
|
115
|
+
attr_reader :value
|
116
|
+
|
117
|
+
# Verifica se mostrare l'etichetta
|
118
|
+
def show_label?
|
119
|
+
@label
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def validate_params
|
125
|
+
validate_theme
|
126
|
+
validate_size
|
127
|
+
end
|
128
|
+
|
129
|
+
def validate_theme
|
130
|
+
valid_themes = PROGRESS_THEMES.keys
|
131
|
+
unless valid_themes.include?(@theme)
|
132
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{valid_themes.join(', ')}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def validate_size
|
137
|
+
valid_sizes = PROGRESS_SIZES.keys
|
138
|
+
unless valid_sizes.include?(@size)
|
139
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{valid_sizes.join(', ')}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Genera le classi per la dimensione
|
144
|
+
def get_size_class
|
145
|
+
PROGRESS_SIZES[@size] || PROGRESS_SIZES[:medium]
|
146
|
+
end
|
147
|
+
|
148
|
+
# Genera le classi per il tema della barra
|
149
|
+
def get_theme_class
|
150
|
+
PROGRESS_THEMES[@theme] || PROGRESS_THEMES[:white]
|
151
|
+
end
|
152
|
+
|
153
|
+
# Genera le classi per il tema del container
|
154
|
+
def get_container_theme_class
|
155
|
+
PROGRESS_CONTAINER_THEMES[@theme] || PROGRESS_CONTAINER_THEMES[:white]
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<div <%= tag.attributes(container_attributes) %>>
|
2
|
+
<div class="flex-shrink-0">
|
3
|
+
<svg class="<%= svg_classes %> animate-spin" viewBox="0 0 24 24" fill="none">
|
4
|
+
<circle
|
5
|
+
cx="12"
|
6
|
+
cy="12"
|
7
|
+
r="10"
|
8
|
+
stroke="currentColor"
|
9
|
+
stroke-width="2"
|
10
|
+
stroke-linecap="round"
|
11
|
+
stroke-dasharray="32"
|
12
|
+
stroke-dashoffset="32">
|
13
|
+
<animate
|
14
|
+
attributeName="stroke-dasharray"
|
15
|
+
dur="2s"
|
16
|
+
values="0 32;16 16;0 32;0 32"
|
17
|
+
repeatCount="indefinite" />
|
18
|
+
<animate
|
19
|
+
attributeName="stroke-dashoffset"
|
20
|
+
dur="2s"
|
21
|
+
values="0;-16;-32;-32"
|
22
|
+
repeatCount="indefinite" />
|
23
|
+
<animateTransform
|
24
|
+
attributeName="transform"
|
25
|
+
type="rotate"
|
26
|
+
dur="2s"
|
27
|
+
values="0 12 12;360 12 12"
|
28
|
+
repeatCount="indefinite" />
|
29
|
+
</circle>
|
30
|
+
</svg>
|
31
|
+
</div>
|
32
|
+
<% if show_label? %>
|
33
|
+
<span class="text-sm font-medium"><%= label %></span>
|
34
|
+
<% end %>
|
35
|
+
</div>
|