better_ui 0.1.1 → 0.3.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/app/components/better_ui/application/sidebar/component.html.erb +53 -16
- data/app/components/better_ui/application/sidebar/component.rb +3 -2
- data/app/components/better_ui/general/accordion/component.html.erb +5 -0
- data/app/components/better_ui/general/accordion/component.rb +92 -0
- data/app/components/better_ui/general/accordion/item_component.html.erb +12 -0
- data/app/components/better_ui/general/accordion/item_component.rb +176 -0
- data/app/components/better_ui/general/button/component.html.erb +4 -4
- data/app/components/better_ui/general/dropdown/component.html.erb +25 -0
- data/app/components/better_ui/general/dropdown/component.rb +170 -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 +119 -0
- data/app/components/better_ui/general/modal/component.html.erb +5 -0
- data/app/components/better_ui/general/modal/component.rb +47 -0
- data/app/components/better_ui/general/modal/modal_component.html.erb +52 -0
- data/app/components/better_ui/general/modal/modal_component.rb +160 -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/tabs/component.html.erb +11 -0
- data/app/components/better_ui/general/tabs/component.rb +120 -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/helpers/better_ui/application_helper.rb +12 -3
- data/app/helpers/better_ui/general/components/accordion/accordion_helper.rb +73 -0
- data/app/helpers/better_ui/general/components/accordion.rb +11 -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/modal/modal_helper.rb +85 -0
- data/app/helpers/better_ui/general/components/modal.rb +11 -0
- data/app/helpers/better_ui/general/components/pagination/pagination_helper.rb +82 -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 +95 -0
- data/lib/better_ui/version.rb +1 -1
- metadata +35 -2
@@ -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,11 @@
|
|
1
|
+
<div <%= tag.attributes(wrapper_attributes) %>>
|
2
|
+
<!-- Navigation Tabs -->
|
3
|
+
<div <%= tag.attributes(navigation_attributes) %>>
|
4
|
+
<%= navigation %>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<!-- Tab Panels -->
|
8
|
+
<div <%= tag.attributes(panels_attributes) %>>
|
9
|
+
<%= panels %>
|
10
|
+
</div>
|
11
|
+
</div>
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Tabs
|
6
|
+
class Component < ViewComponent::Base
|
7
|
+
renders_one :navigation
|
8
|
+
renders_one :panels
|
9
|
+
TABS_VARIANT = {
|
10
|
+
pills: 'bg-gray-100 rounded-lg p-1',
|
11
|
+
underline: 'border-b border-gray-200',
|
12
|
+
bordered: 'border border-gray-200 rounded-lg',
|
13
|
+
minimal: ''
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
TABS_THEME_DEFAULT = {
|
17
|
+
default: 'text-gray-600',
|
18
|
+
blue: 'text-blue-600',
|
19
|
+
red: 'text-red-600',
|
20
|
+
green: 'text-green-600',
|
21
|
+
yellow: 'text-yellow-600',
|
22
|
+
violet: 'text-violet-600',
|
23
|
+
orange: 'text-orange-600',
|
24
|
+
rose: 'text-rose-600',
|
25
|
+
white: 'text-white'
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
TABS_SIZE = {
|
29
|
+
small: 'text-sm',
|
30
|
+
medium: 'text-base',
|
31
|
+
large: 'text-lg'
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
TABS_ORIENTATION = {
|
35
|
+
horizontal: 'flex-row',
|
36
|
+
vertical: 'flex-col'
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
def initialize(variant: :pills, theme: :default, size: :medium, orientation: :horizontal,
|
40
|
+
navigation_classes: '', panels_classes: 'mt-4', **options)
|
41
|
+
@variant = variant
|
42
|
+
@theme = theme
|
43
|
+
@size = size
|
44
|
+
@orientation = orientation
|
45
|
+
@navigation_classes = navigation_classes
|
46
|
+
@panels_classes = panels_classes
|
47
|
+
@options = options
|
48
|
+
|
49
|
+
validate_params
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
attr_reader :variant, :theme, :size, :orientation, :navigation_classes, :panels_classes, :options
|
55
|
+
|
56
|
+
def validate_params
|
57
|
+
validate_variant
|
58
|
+
validate_theme
|
59
|
+
validate_size
|
60
|
+
validate_orientation
|
61
|
+
end
|
62
|
+
|
63
|
+
def validate_variant
|
64
|
+
return if TABS_VARIANT.key?(variant)
|
65
|
+
|
66
|
+
raise ArgumentError, "Invalid variant: #{variant}. Must be one of #{TABS_VARIANT.keys}"
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_theme
|
70
|
+
return if TABS_THEME_DEFAULT.key?(theme)
|
71
|
+
|
72
|
+
raise ArgumentError, "Invalid theme: #{theme}. Must be one of #{TABS_THEME_DEFAULT.keys}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate_size
|
76
|
+
return if TABS_SIZE.key?(size)
|
77
|
+
|
78
|
+
raise ArgumentError, "Invalid size: #{size}. Must be one of #{TABS_SIZE.keys}"
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate_orientation
|
82
|
+
return if TABS_ORIENTATION.key?(orientation)
|
83
|
+
|
84
|
+
raise ArgumentError, "Invalid orientation: #{orientation}. Must be one of #{TABS_ORIENTATION.keys}"
|
85
|
+
end
|
86
|
+
|
87
|
+
# Attributi per il wrapper principale (con data-controller)
|
88
|
+
def wrapper_attributes
|
89
|
+
{
|
90
|
+
'data-controller': 'bui-tabs'
|
91
|
+
}.merge(options)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Attributi per il container della navigazione tabs
|
95
|
+
def navigation_attributes
|
96
|
+
base_classes = [
|
97
|
+
'flex',
|
98
|
+
TABS_ORIENTATION[orientation],
|
99
|
+
TABS_VARIANT[variant],
|
100
|
+
TABS_THEME_DEFAULT[theme],
|
101
|
+
TABS_SIZE[size],
|
102
|
+
navigation_classes
|
103
|
+
].compact.join(' ')
|
104
|
+
|
105
|
+
{
|
106
|
+
class: base_classes,
|
107
|
+
role: 'tablist'
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
# Attributi per il container dei panel
|
112
|
+
def panels_attributes
|
113
|
+
{
|
114
|
+
class: panels_classes
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Tabs
|
6
|
+
class PanelComponent < ViewComponent::Base
|
7
|
+
def initialize(id:, active: false, classes: '', **options)
|
8
|
+
@id = id
|
9
|
+
@active = active
|
10
|
+
@classes = classes
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :id, :active, :classes, :options
|
17
|
+
|
18
|
+
def panel_attributes
|
19
|
+
base_classes = [
|
20
|
+
'focus:outline-none',
|
21
|
+
'hidden', # Sempre nascosto inizialmente, JavaScript gestisce la visibilità
|
22
|
+
classes
|
23
|
+
].compact.join(' ')
|
24
|
+
|
25
|
+
{
|
26
|
+
class: base_classes,
|
27
|
+
role: 'tabpanel',
|
28
|
+
id: id,
|
29
|
+
'aria-labelledby': "tab-#{id}",
|
30
|
+
'data-bui-tabs-target': 'panel',
|
31
|
+
tabindex: '0'
|
32
|
+
}.merge(options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<button <%= tag.attributes(tab_attributes) %>>
|
2
|
+
<% if has_icon? %>
|
3
|
+
<%= bui_icon(name: icon, classes: 'w-5 h-5') %>
|
4
|
+
<% end %>
|
5
|
+
|
6
|
+
<span><%= text %></span>
|
7
|
+
|
8
|
+
<% if has_badge? %>
|
9
|
+
<span class="ml-2 bg-red-100 text-red-800 text-xs font-medium px-2 py-0.5 rounded-full">
|
10
|
+
<%= badge %>
|
11
|
+
</span>
|
12
|
+
<% end %>
|
13
|
+
</button>
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Tabs
|
6
|
+
class TabComponent < ViewComponent::Base
|
7
|
+
include BetterUi::General::Components::Icon::IconHelper
|
8
|
+
|
9
|
+
TAB_THEME_ACTIVE = {
|
10
|
+
default: 'bg-white text-gray-900 shadow-sm',
|
11
|
+
blue: 'bg-blue-600 text-white',
|
12
|
+
red: 'bg-red-600 text-white',
|
13
|
+
green: 'bg-green-600 text-white',
|
14
|
+
yellow: 'bg-yellow-600 text-white',
|
15
|
+
violet: 'bg-violet-600 text-white',
|
16
|
+
orange: 'bg-orange-600 text-white',
|
17
|
+
rose: 'bg-rose-600 text-white',
|
18
|
+
white: 'bg-white text-gray-900'
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
TAB_THEME_INACTIVE = {
|
22
|
+
default: 'text-gray-500 hover:text-gray-700',
|
23
|
+
blue: 'text-blue-600 hover:text-blue-700',
|
24
|
+
red: 'text-red-600 hover:text-red-700',
|
25
|
+
green: 'text-green-600 hover:text-green-700',
|
26
|
+
yellow: 'text-yellow-600 hover:text-yellow-700',
|
27
|
+
violet: 'text-violet-600 hover:text-violet-700',
|
28
|
+
orange: 'text-orange-600 hover:text-orange-700',
|
29
|
+
rose: 'text-rose-600 hover:text-rose-700',
|
30
|
+
white: 'text-gray-600 hover:text-gray-700'
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
TAB_SIZE = {
|
34
|
+
small: 'px-3 py-1.5 text-sm',
|
35
|
+
medium: 'px-4 py-2 text-base',
|
36
|
+
large: 'px-6 py-3 text-lg'
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
def initialize(text:, target:, active: false, icon: nil, disabled: false, badge: nil,
|
40
|
+
theme: :default, size: :medium, classes: '', **options)
|
41
|
+
@text = text
|
42
|
+
@target = target
|
43
|
+
@active = active
|
44
|
+
@icon = icon
|
45
|
+
@disabled = disabled
|
46
|
+
@badge = badge
|
47
|
+
@theme = theme
|
48
|
+
@size = size
|
49
|
+
@classes = classes
|
50
|
+
@options = options
|
51
|
+
|
52
|
+
validate_params
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader :text, :target, :active, :icon, :disabled, :badge, :theme, :size, :classes, :options
|
58
|
+
|
59
|
+
def validate_params
|
60
|
+
validate_theme
|
61
|
+
validate_size
|
62
|
+
end
|
63
|
+
|
64
|
+
def validate_theme
|
65
|
+
return if TAB_THEME_ACTIVE.key?(theme)
|
66
|
+
|
67
|
+
raise ArgumentError, "Invalid theme: #{theme}. Must be one of #{TAB_THEME_ACTIVE.keys}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def validate_size
|
71
|
+
return if TAB_SIZE.key?(size)
|
72
|
+
|
73
|
+
raise ArgumentError, "Invalid size: #{size}. Must be one of #{TAB_SIZE.keys}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def tab_attributes
|
77
|
+
theme_classes = active ? TAB_THEME_ACTIVE[theme] : TAB_THEME_INACTIVE[theme]
|
78
|
+
|
79
|
+
base_classes = [
|
80
|
+
'inline-flex items-center justify-center gap-2 font-medium rounded-md transition-colors',
|
81
|
+
'focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500',
|
82
|
+
TAB_SIZE[size],
|
83
|
+
theme_classes,
|
84
|
+
disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer',
|
85
|
+
classes
|
86
|
+
].compact.join(' ')
|
87
|
+
|
88
|
+
{
|
89
|
+
class: base_classes,
|
90
|
+
role: 'tab',
|
91
|
+
'aria-selected': active.to_s,
|
92
|
+
'aria-controls': target,
|
93
|
+
'data-bui-tabs-target': 'tab',
|
94
|
+
'data-target': target,
|
95
|
+
'data-action': disabled ? '' : 'click->bui-tabs#switchTab keydown->bui-tabs#keydown',
|
96
|
+
tabindex: active ? '0' : '-1',
|
97
|
+
id: "tab-#{target}"
|
98
|
+
}.merge(options)
|
99
|
+
end
|
100
|
+
|
101
|
+
def has_icon?
|
102
|
+
icon.present?
|
103
|
+
end
|
104
|
+
|
105
|
+
def has_badge?
|
106
|
+
badge.present?
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -4,18 +4,26 @@ module BetterUi
|
|
4
4
|
# Questo assicura che tutti gli helper specifici dei componenti siano disponibili.
|
5
5
|
|
6
6
|
# General Components
|
7
|
+
include General::Components::Accordion::AccordionHelper
|
7
8
|
include General::Components::Alert::AlertHelper
|
8
9
|
include General::Components::Avatar::AvatarHelper
|
9
10
|
include General::Components::Badge::BadgeHelper
|
10
11
|
include General::Components::Breadcrumb::BreadcrumbHelper
|
11
12
|
include General::Components::Button::ButtonHelper
|
12
13
|
include General::Components::Divider::DividerHelper
|
14
|
+
include General::Components::Dropdown::DropdownHelper
|
15
|
+
include General::Components::Dropdown::ItemHelper
|
16
|
+
include General::Components::Dropdown::DividerHelper
|
13
17
|
include General::Components::Heading::HeadingHelper
|
14
18
|
include General::Components::Icon::IconHelper
|
15
19
|
include General::Components::Link::LinkHelper
|
20
|
+
include General::Components::Modal::ModalHelper
|
21
|
+
include General::Components::Pagination::PaginationHelper
|
16
22
|
include General::Components::Panel::PanelHelper
|
17
23
|
include General::Components::Progress::ProgressHelper
|
18
24
|
include General::Components::Spinner::SpinnerHelper
|
25
|
+
include General::Components::Tag::TagHelper
|
26
|
+
include General::Components::Tooltip::TooltipHelper
|
19
27
|
|
20
28
|
include General::Components::Table::TableHelper
|
21
29
|
include General::Components::Table::TbodyHelper
|
@@ -24,9 +32,10 @@ module BetterUi
|
|
24
32
|
include General::Components::Table::ThHelper
|
25
33
|
include General::Components::Table::TheadHelper
|
26
34
|
include General::Components::Table::TrHelper
|
27
|
-
|
28
|
-
include General::Components::
|
29
|
-
include General::Components::
|
35
|
+
|
36
|
+
include General::Components::Tabs::TabsHelper
|
37
|
+
include General::Components::Tabs::TabHelper
|
38
|
+
include General::Components::Tabs::PanelHelper
|
30
39
|
|
31
40
|
# General Form Components
|
32
41
|
include General::Components::Input::Checkbox::CheckboxHelper
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Components
|
6
|
+
module Accordion
|
7
|
+
module AccordionHelper
|
8
|
+
# Rendering di un accordion con item collassabili/espandibili
|
9
|
+
#
|
10
|
+
# @param multiple [Boolean] se permettere multipli item aperti contemporaneamente
|
11
|
+
# @param theme [Symbol] tema di colore per l'accordion
|
12
|
+
# @param variant [Symbol] variante visuale dell'accordion
|
13
|
+
# @param size [Symbol] dimensione del testo e padding
|
14
|
+
# @param classes [String] classi CSS aggiuntive per il wrapper
|
15
|
+
# @param options [Hash] attributi HTML aggiuntivi da passare al componente
|
16
|
+
# @return [String] HTML renderizzato dell'accordion
|
17
|
+
#
|
18
|
+
# @example Uso base standalone
|
19
|
+
# <%= bui_accordion do |accordion| %>
|
20
|
+
# <%= accordion.with_item(title: "Domanda 1") do %>
|
21
|
+
# <p>Risposta alla prima domanda</p>
|
22
|
+
# <% end %>
|
23
|
+
# <% end %>
|
24
|
+
#
|
25
|
+
# @example Con tema e configurazioni
|
26
|
+
# <%= bui_accordion(multiple: true, theme: :blue, size: :large) do |accordion| %>
|
27
|
+
# <%= accordion.with_item(title: "FAQ 1", expanded: true) do %>
|
28
|
+
# <p>Contenuto della prima FAQ</p>
|
29
|
+
# <% end %>
|
30
|
+
# <%= accordion.with_item(title: "FAQ 2") do %>
|
31
|
+
# <p>Contenuto della seconda FAQ</p>
|
32
|
+
# <% end %>
|
33
|
+
# <% end %>
|
34
|
+
#
|
35
|
+
# @example Con varianti stilistiche
|
36
|
+
# <%= bui_accordion(variant: :separated, theme: :green) do |accordion| %>
|
37
|
+
# <%= accordion.with_item(title: "Sezione 1") do %>
|
38
|
+
# <div>Contenuto complesso con HTML</div>
|
39
|
+
# <% end %>
|
40
|
+
# <% end %>
|
41
|
+
#
|
42
|
+
# @example Con attributi HTML aggiuntivi
|
43
|
+
# <%= bui_accordion(id: "faq-accordion", "data-testid": "main-faq") do |accordion| %>
|
44
|
+
# <%= accordion.with_item(title: "Come funziona?") do %>
|
45
|
+
# <p>Spiegazione dettagliata...</p>
|
46
|
+
# <% end %>
|
47
|
+
# <% end %>
|
48
|
+
#
|
49
|
+
# @example Con item disabilitati e icone personalizzate
|
50
|
+
# <%= bui_accordion(theme: :violet, size: :small) do |accordion| %>
|
51
|
+
# <%= accordion.with_item(title: "Disponibile", icon: "check-circle") do %>
|
52
|
+
# <p>Questa sezione è disponibile</p>
|
53
|
+
# <% end %>
|
54
|
+
# <%= accordion.with_item(title: "Non disponibile", disabled: true) do %>
|
55
|
+
# <p>Questa sezione è disabilitata</p>
|
56
|
+
# <% end %>
|
57
|
+
# <% end %>
|
58
|
+
def bui_accordion(multiple: false, theme: :default, variant: :bordered, size: :medium,
|
59
|
+
classes: '', **options, &block)
|
60
|
+
render BetterUi::General::Accordion::Component.new(
|
61
|
+
multiple: multiple,
|
62
|
+
theme: theme,
|
63
|
+
variant: variant,
|
64
|
+
size: size,
|
65
|
+
classes: classes,
|
66
|
+
**options
|
67
|
+
), &block
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Components
|
6
|
+
module Dropdown
|
7
|
+
module DividerHelper
|
8
|
+
##
|
9
|
+
# Crea un divisore per separare gruppi di elementi nel menu dropdown.
|
10
|
+
#
|
11
|
+
# @param classes [String] Classi CSS aggiuntive
|
12
|
+
# @param options [Hash] Attributi HTML aggiuntivi
|
13
|
+
#
|
14
|
+
# @return [String] Il markup HTML del divisore dropdown
|
15
|
+
#
|
16
|
+
# @example Uso base
|
17
|
+
# <%= bui_dropdown_divider %>
|
18
|
+
#
|
19
|
+
# @example Con classi personalizzate
|
20
|
+
# <%= bui_dropdown_divider(classes: "my-4") %>
|
21
|
+
#
|
22
|
+
def bui_dropdown_divider(classes: '', **options)
|
23
|
+
render BetterUi::General::Dropdown::DividerComponent.new(
|
24
|
+
classes: classes,
|
25
|
+
**options
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|