better_ui 0.1.1 → 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/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/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/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/helpers/better_ui/application_helper.rb +9 -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 +95 -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 +62 -0
- data/lib/better_ui/version.rb +1 -1
- metadata +25 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b6dbaeb8dede2be21e4bcff93ba5a7793a08f9e84521c2b46bd8e879be6d072
|
4
|
+
data.tar.gz: b9f58c90c95a748e694018c85aff6ceeb23ecee0f155b8cb45d9b64dc78da2e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7bba346a1d310e3869a232212c8708bbff5ef18f048d9361acc895efa78709b6de1e78bfee44dc3b30db77daf3cf380093331d1443b90d0bb6c18a7ad5d9e2e3
|
7
|
+
data.tar.gz: be0e614d6d37d86946d7908e8f2ae0ff83a452dcfc7fc1dd29c42560f77a368bab654e556e767e2d06dcf26236281b9f39987221f0bbee451ff7a16d1125cf39
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div <%= tag.attributes(container_attributes) %>>
|
2
|
+
<button <%= tag.attributes(trigger_attributes) %>>
|
3
|
+
<%= @trigger %>
|
4
|
+
<svg class="ml-2 -mr-1 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
5
|
+
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
6
|
+
</svg>
|
7
|
+
</button>
|
8
|
+
|
9
|
+
<div <%= tag.attributes(menu_attributes) %>>
|
10
|
+
<div class="py-1" role="none">
|
11
|
+
<%= content %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
</div>
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Dropdown
|
6
|
+
class Component < ViewComponent::Base
|
7
|
+
attr_reader :trigger, :position, :theme, :size, :rounded, :animation, :classes, :html_options
|
8
|
+
|
9
|
+
# Classi base per il contenitore dropdown
|
10
|
+
DROPDOWN_CONTAINER_CLASSES = "relative inline-block"
|
11
|
+
|
12
|
+
# Classi base per il pulsante trigger
|
13
|
+
DROPDOWN_TRIGGER_BASE_CLASSES = "inline-flex items-center justify-center border font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors"
|
14
|
+
|
15
|
+
# Classi base per il menu dropdown
|
16
|
+
DROPDOWN_MENU_BASE_CLASSES = "absolute z-50 mt-2 origin-top-right bg-white border border-gray-200 shadow-lg focus:outline-none"
|
17
|
+
|
18
|
+
# Temi per il trigger del dropdown con classi Tailwind dirette
|
19
|
+
DROPDOWN_TRIGGER_THEME = {
|
20
|
+
default: "bg-white border-gray-300 text-gray-700 hover:bg-gray-50 focus:ring-blue-500",
|
21
|
+
white: "bg-white border-gray-300 text-gray-900 hover:bg-gray-50 focus:ring-gray-500",
|
22
|
+
red: "bg-red-600 border-red-600 text-white hover:bg-red-700 focus:ring-red-500",
|
23
|
+
rose: "bg-rose-600 border-rose-600 text-white hover:bg-rose-700 focus:ring-rose-500",
|
24
|
+
orange: "bg-orange-600 border-orange-600 text-white hover:bg-orange-700 focus:ring-orange-500",
|
25
|
+
green: "bg-green-600 border-green-600 text-white hover:bg-green-700 focus:ring-green-500",
|
26
|
+
blue: "bg-blue-600 border-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
|
27
|
+
yellow: "bg-yellow-500 border-yellow-500 text-white hover:bg-yellow-600 focus:ring-yellow-500",
|
28
|
+
violet: "bg-violet-600 border-violet-600 text-white hover:bg-violet-700 focus:ring-violet-500"
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
# Dimensioni del trigger con classi Tailwind dirette
|
32
|
+
DROPDOWN_TRIGGER_SIZE = {
|
33
|
+
small: "px-3 py-1.5 text-sm",
|
34
|
+
medium: "px-4 py-2 text-sm",
|
35
|
+
large: "px-6 py-3 text-base"
|
36
|
+
}.freeze
|
37
|
+
|
38
|
+
# Border radius con classi Tailwind dirette
|
39
|
+
DROPDOWN_ROUNDED = {
|
40
|
+
none: "rounded-none",
|
41
|
+
small: "rounded-md",
|
42
|
+
medium: "rounded-lg",
|
43
|
+
large: "rounded-xl",
|
44
|
+
full: "rounded-full"
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
# Posizioni del menu dropdown
|
48
|
+
DROPDOWN_POSITION = {
|
49
|
+
bottom: "top-full left-0",
|
50
|
+
top: "bottom-full left-0",
|
51
|
+
left: "top-0 right-full mr-2",
|
52
|
+
right: "top-0 left-full ml-2"
|
53
|
+
}.freeze
|
54
|
+
|
55
|
+
# Animazioni del dropdown
|
56
|
+
DROPDOWN_ANIMATION = {
|
57
|
+
fade: "transition-opacity duration-150",
|
58
|
+
slide: "transition-all duration-150 transform",
|
59
|
+
none: ""
|
60
|
+
}.freeze
|
61
|
+
|
62
|
+
def initialize(
|
63
|
+
trigger:,
|
64
|
+
position: :bottom,
|
65
|
+
theme: :default,
|
66
|
+
size: :medium,
|
67
|
+
rounded: :medium,
|
68
|
+
animation: :fade,
|
69
|
+
classes: nil,
|
70
|
+
**html_options
|
71
|
+
)
|
72
|
+
@trigger = trigger
|
73
|
+
@position = position.to_sym
|
74
|
+
@theme = theme.to_sym
|
75
|
+
@size = size.to_sym
|
76
|
+
@rounded = rounded.to_sym
|
77
|
+
@animation = animation.to_sym
|
78
|
+
@classes = classes
|
79
|
+
@html_options = html_options
|
80
|
+
|
81
|
+
validate_params
|
82
|
+
end
|
83
|
+
|
84
|
+
# Combina tutte le classi per il contenitore
|
85
|
+
def container_classes
|
86
|
+
[
|
87
|
+
DROPDOWN_CONTAINER_CLASSES,
|
88
|
+
@classes
|
89
|
+
].compact.join(" ")
|
90
|
+
end
|
91
|
+
|
92
|
+
# Combina tutte le classi per il trigger
|
93
|
+
def trigger_classes
|
94
|
+
[
|
95
|
+
DROPDOWN_TRIGGER_BASE_CLASSES,
|
96
|
+
get_trigger_theme_classes,
|
97
|
+
get_trigger_size_classes,
|
98
|
+
get_trigger_rounded_classes
|
99
|
+
].compact.join(" ")
|
100
|
+
end
|
101
|
+
|
102
|
+
# Combina tutte le classi per il menu
|
103
|
+
def menu_classes
|
104
|
+
[
|
105
|
+
DROPDOWN_MENU_BASE_CLASSES,
|
106
|
+
get_position_classes,
|
107
|
+
get_animation_classes,
|
108
|
+
get_menu_rounded_classes
|
109
|
+
].compact.join(" ")
|
110
|
+
end
|
111
|
+
|
112
|
+
# Restituisce gli attributi per il contenitore
|
113
|
+
def container_attributes
|
114
|
+
attrs = {
|
115
|
+
class: container_classes,
|
116
|
+
"data-dropdown": true
|
117
|
+
}
|
118
|
+
|
119
|
+
@html_options.except(:class).each do |key, value|
|
120
|
+
attrs[key] = value
|
121
|
+
end
|
122
|
+
|
123
|
+
attrs
|
124
|
+
end
|
125
|
+
|
126
|
+
# Restituisce gli attributi per il trigger
|
127
|
+
def trigger_attributes
|
128
|
+
{
|
129
|
+
type: "button",
|
130
|
+
class: trigger_classes,
|
131
|
+
"data-dropdown-trigger": true,
|
132
|
+
"aria-expanded": "false",
|
133
|
+
"aria-haspopup": "true"
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
# Restituisce gli attributi per il menu
|
138
|
+
def menu_attributes
|
139
|
+
{
|
140
|
+
class: menu_classes,
|
141
|
+
"data-dropdown-menu": true,
|
142
|
+
role: "menu",
|
143
|
+
"aria-orientation": "vertical",
|
144
|
+
style: "display: none;"
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
# Verifica se rendere il componente
|
149
|
+
def render?
|
150
|
+
@trigger.present?
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def get_trigger_theme_classes
|
156
|
+
DROPDOWN_TRIGGER_THEME[@theme] || DROPDOWN_TRIGGER_THEME[:default]
|
157
|
+
end
|
158
|
+
|
159
|
+
def get_trigger_size_classes
|
160
|
+
DROPDOWN_TRIGGER_SIZE[@size] || DROPDOWN_TRIGGER_SIZE[:medium]
|
161
|
+
end
|
162
|
+
|
163
|
+
def get_trigger_rounded_classes
|
164
|
+
DROPDOWN_ROUNDED[@rounded] || DROPDOWN_ROUNDED[:medium]
|
165
|
+
end
|
166
|
+
|
167
|
+
def get_menu_rounded_classes
|
168
|
+
DROPDOWN_ROUNDED[@rounded] || DROPDOWN_ROUNDED[:medium]
|
169
|
+
end
|
170
|
+
|
171
|
+
def get_position_classes
|
172
|
+
DROPDOWN_POSITION[@position] || DROPDOWN_POSITION[:bottom]
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_animation_classes
|
176
|
+
DROPDOWN_ANIMATION[@animation] || DROPDOWN_ANIMATION[:fade]
|
177
|
+
end
|
178
|
+
|
179
|
+
def validate_params
|
180
|
+
validate_theme
|
181
|
+
validate_size
|
182
|
+
validate_rounded
|
183
|
+
validate_position
|
184
|
+
validate_animation
|
185
|
+
end
|
186
|
+
|
187
|
+
def validate_theme
|
188
|
+
unless DROPDOWN_TRIGGER_THEME.keys.include?(@theme)
|
189
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{DROPDOWN_TRIGGER_THEME.keys.join(', ')}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def validate_size
|
194
|
+
unless DROPDOWN_TRIGGER_SIZE.keys.include?(@size)
|
195
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{DROPDOWN_TRIGGER_SIZE.keys.join(', ')}"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def validate_rounded
|
200
|
+
unless DROPDOWN_ROUNDED.keys.include?(@rounded)
|
201
|
+
raise ArgumentError, "Il border radius deve essere uno tra: #{DROPDOWN_ROUNDED.keys.join(', ')}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def validate_position
|
206
|
+
unless DROPDOWN_POSITION.keys.include?(@position)
|
207
|
+
raise ArgumentError, "La posizione deve essere una tra: #{DROPDOWN_POSITION.keys.join(', ')}"
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def validate_animation
|
212
|
+
unless DROPDOWN_ANIMATION.keys.include?(@animation)
|
213
|
+
raise ArgumentError, "L'animazione deve essere una tra: #{DROPDOWN_ANIMATION.keys.join(', ')}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<div <%= tag.attributes(divider_attributes) %>></div>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Dropdown
|
6
|
+
class DividerComponent < ViewComponent::Base
|
7
|
+
attr_reader :classes, :html_options
|
8
|
+
|
9
|
+
# Classi base per il divisore del dropdown
|
10
|
+
DROPDOWN_DIVIDER_CLASSES = "border-t border-gray-100 my-1"
|
11
|
+
|
12
|
+
def initialize(classes: nil, **html_options)
|
13
|
+
@classes = classes
|
14
|
+
@html_options = html_options
|
15
|
+
end
|
16
|
+
|
17
|
+
# Combina tutte le classi per il divisore
|
18
|
+
def divider_classes
|
19
|
+
[
|
20
|
+
DROPDOWN_DIVIDER_CLASSES,
|
21
|
+
@classes
|
22
|
+
].compact.join(" ")
|
23
|
+
end
|
24
|
+
|
25
|
+
# Restituisce gli attributi per il divisore
|
26
|
+
def divider_attributes
|
27
|
+
attrs = {
|
28
|
+
class: divider_classes,
|
29
|
+
role: "separator"
|
30
|
+
}
|
31
|
+
|
32
|
+
@html_options.except(:class).each do |key, value|
|
33
|
+
attrs[key] = value
|
34
|
+
end
|
35
|
+
|
36
|
+
attrs
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterUi
|
4
|
+
module General
|
5
|
+
module Dropdown
|
6
|
+
class ItemComponent < ViewComponent::Base
|
7
|
+
include BetterUi::General::Components::Icon::IconHelper
|
8
|
+
|
9
|
+
attr_reader :text, :icon, :href, :theme, :disabled, :active, :classes, :html_options
|
10
|
+
|
11
|
+
# Classi base per l'elemento del dropdown
|
12
|
+
DROPDOWN_ITEM_BASE_CLASSES = "flex items-center w-full px-4 py-2 text-sm transition-colors"
|
13
|
+
|
14
|
+
# Temi per gli elementi del dropdown con classi Tailwind dirette
|
15
|
+
DROPDOWN_ITEM_THEME = {
|
16
|
+
default: "text-gray-700 hover:bg-gray-100 hover:text-gray-900",
|
17
|
+
white: "text-gray-900 hover:bg-gray-50",
|
18
|
+
red: "text-red-700 hover:bg-red-50 hover:text-red-900",
|
19
|
+
rose: "text-rose-700 hover:bg-rose-50 hover:text-rose-900",
|
20
|
+
orange: "text-orange-700 hover:bg-orange-50 hover:text-orange-900",
|
21
|
+
green: "text-green-700 hover:bg-green-50 hover:text-green-900",
|
22
|
+
blue: "text-blue-700 hover:bg-blue-50 hover:text-blue-900",
|
23
|
+
yellow: "text-yellow-700 hover:bg-yellow-50 hover:text-yellow-900",
|
24
|
+
violet: "text-violet-700 hover:bg-violet-50 hover:text-violet-900"
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
# Stati per gli elementi del dropdown
|
28
|
+
DROPDOWN_ITEM_STATE_DISABLED = "opacity-50 cursor-not-allowed pointer-events-none"
|
29
|
+
DROPDOWN_ITEM_STATE_ACTIVE = "bg-gray-100 text-gray-900"
|
30
|
+
|
31
|
+
def initialize(
|
32
|
+
text:,
|
33
|
+
icon: nil,
|
34
|
+
href: nil,
|
35
|
+
theme: :default,
|
36
|
+
disabled: false,
|
37
|
+
active: false,
|
38
|
+
classes: nil,
|
39
|
+
**html_options
|
40
|
+
)
|
41
|
+
@text = text
|
42
|
+
@icon = icon
|
43
|
+
@href = href
|
44
|
+
@theme = theme.to_sym
|
45
|
+
@disabled = disabled
|
46
|
+
@active = active
|
47
|
+
@classes = classes
|
48
|
+
@html_options = html_options
|
49
|
+
|
50
|
+
validate_params
|
51
|
+
end
|
52
|
+
|
53
|
+
# Combina tutte le classi per l'elemento
|
54
|
+
def item_classes
|
55
|
+
classes = [
|
56
|
+
DROPDOWN_ITEM_BASE_CLASSES,
|
57
|
+
get_theme_classes,
|
58
|
+
@classes
|
59
|
+
]
|
60
|
+
|
61
|
+
classes << DROPDOWN_ITEM_STATE_DISABLED if @disabled
|
62
|
+
classes << DROPDOWN_ITEM_STATE_ACTIVE if @active
|
63
|
+
|
64
|
+
classes.compact.join(" ")
|
65
|
+
end
|
66
|
+
|
67
|
+
# Restituisce gli attributi per l'elemento
|
68
|
+
def item_attributes
|
69
|
+
attrs = {
|
70
|
+
class: item_classes,
|
71
|
+
role: "menuitem"
|
72
|
+
}
|
73
|
+
|
74
|
+
if @href.present? && !@disabled
|
75
|
+
attrs[:href] = @href
|
76
|
+
end
|
77
|
+
|
78
|
+
if @disabled
|
79
|
+
attrs["aria-disabled"] = "true"
|
80
|
+
attrs[:tabindex] = "-1"
|
81
|
+
end
|
82
|
+
|
83
|
+
@html_options.except(:class).each do |key, value|
|
84
|
+
attrs[key] = value
|
85
|
+
end
|
86
|
+
|
87
|
+
attrs
|
88
|
+
end
|
89
|
+
|
90
|
+
# Determina se usare un link o un button
|
91
|
+
def tag_name
|
92
|
+
@href.present? && !@disabled ? :a : :button
|
93
|
+
end
|
94
|
+
|
95
|
+
# Verifica se rendere il componente
|
96
|
+
def render?
|
97
|
+
@text.present?
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def get_theme_classes
|
103
|
+
DROPDOWN_ITEM_THEME[@theme] || DROPDOWN_ITEM_THEME[:default]
|
104
|
+
end
|
105
|
+
|
106
|
+
def validate_params
|
107
|
+
validate_theme
|
108
|
+
end
|
109
|
+
|
110
|
+
def validate_theme
|
111
|
+
unless DROPDOWN_ITEM_THEME.keys.include?(@theme)
|
112
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{DROPDOWN_ITEM_THEME.keys.join(', ')}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
<%# Template per il modal %>
|
2
|
+
<% if @backdrop %>
|
3
|
+
<%= tag.div **backdrop_attributes do %>
|
4
|
+
<%= tag.div **container_attributes do %>
|
5
|
+
<%# Header del modal %>
|
6
|
+
<%= tag.div **header_attributes do %>
|
7
|
+
<h3 class="text-lg font-semibold" id="modal-title"><%= @title %></h3>
|
8
|
+
<% if @closable %>
|
9
|
+
<button type="button" class="text-gray-400 hover:text-gray-600 focus:outline-none focus:text-gray-600 transition-colors duration-200" aria-label="Chiudi modal">
|
10
|
+
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
11
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
12
|
+
</svg>
|
13
|
+
</button>
|
14
|
+
<% end %>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<%# Body del modal %>
|
18
|
+
<div class="p-6">
|
19
|
+
<%= content %>
|
20
|
+
</div>
|
21
|
+
<% end %>
|
22
|
+
<% end %>
|
23
|
+
<% else %>
|
24
|
+
<%= tag.div **container_attributes do %>
|
25
|
+
<%# Header del modal %>
|
26
|
+
<%= tag.div **header_attributes do %>
|
27
|
+
<h3 class="text-lg font-semibold" id="modal-title"><%= @title %></h3>
|
28
|
+
<% if @closable %>
|
29
|
+
<button type="button" class="text-gray-400 hover:text-gray-600 focus:outline-none focus:text-gray-600 transition-colors duration-200" aria-label="Chiudi modal">
|
30
|
+
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
31
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
32
|
+
</svg>
|
33
|
+
</button>
|
34
|
+
<% end %>
|
35
|
+
<% end %>
|
36
|
+
|
37
|
+
<%# Body del modal %>
|
38
|
+
<div class="p-6">
|
39
|
+
<%= content %>
|
40
|
+
</div>
|
41
|
+
<% end %>
|
42
|
+
<% end %>
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module BetterUi
|
2
|
+
module General
|
3
|
+
module Modal
|
4
|
+
class Component < ViewComponent::Base
|
5
|
+
attr_reader :title, :theme, :size, :backdrop, :closable, :classes, :html_options
|
6
|
+
|
7
|
+
# Classi base sempre presenti per il backdrop
|
8
|
+
MODAL_BACKDROP_CLASSES = "fixed inset-0 z-50 flex items-center justify-center p-4 bg-black bg-opacity-50"
|
9
|
+
|
10
|
+
# Classi base per il contenitore del modal
|
11
|
+
MODAL_CONTAINER_CLASSES = "relative bg-white shadow-xl w-full"
|
12
|
+
|
13
|
+
# Temi dell'header del modal con classi Tailwind dirette
|
14
|
+
MODAL_THEME = {
|
15
|
+
default: "bg-gray-50 border-b border-gray-200 text-gray-900",
|
16
|
+
white: "bg-white border-b border-gray-200 text-gray-900",
|
17
|
+
red: "bg-red-50 border-b border-red-200 text-red-900",
|
18
|
+
rose: "bg-rose-50 border-b border-rose-200 text-rose-900",
|
19
|
+
orange: "bg-orange-50 border-b border-orange-200 text-orange-900",
|
20
|
+
green: "bg-green-50 border-b border-green-200 text-green-900",
|
21
|
+
blue: "bg-blue-50 border-b border-blue-200 text-blue-900",
|
22
|
+
yellow: "bg-yellow-50 border-b border-yellow-200 text-yellow-900",
|
23
|
+
violet: "bg-violet-50 border-b border-violet-200 text-violet-900"
|
24
|
+
}
|
25
|
+
|
26
|
+
# Dimensioni con classi Tailwind dirette
|
27
|
+
MODAL_SIZES = {
|
28
|
+
small: "max-w-sm",
|
29
|
+
medium: "max-w-md",
|
30
|
+
large: "max-w-2xl"
|
31
|
+
}
|
32
|
+
|
33
|
+
# Border radius con classi Tailwind dirette
|
34
|
+
MODAL_ROUNDED = {
|
35
|
+
none: "rounded-none",
|
36
|
+
small: "rounded-md",
|
37
|
+
medium: "rounded-lg",
|
38
|
+
large: "rounded-xl",
|
39
|
+
full: "rounded-full"
|
40
|
+
}
|
41
|
+
|
42
|
+
# Inizializzazione del componente
|
43
|
+
def initialize(
|
44
|
+
title:,
|
45
|
+
theme: :default,
|
46
|
+
size: :medium,
|
47
|
+
rounded: :medium,
|
48
|
+
backdrop: true,
|
49
|
+
closable: true,
|
50
|
+
classes: nil,
|
51
|
+
**html_options
|
52
|
+
)
|
53
|
+
@title = title
|
54
|
+
@theme = theme.to_sym
|
55
|
+
@size = size.to_sym
|
56
|
+
@rounded = rounded.to_sym
|
57
|
+
@backdrop = backdrop
|
58
|
+
@closable = closable
|
59
|
+
@classes = classes
|
60
|
+
@html_options = html_options
|
61
|
+
|
62
|
+
validate_params
|
63
|
+
end
|
64
|
+
|
65
|
+
# Combina tutte le classi per il backdrop
|
66
|
+
def backdrop_classes
|
67
|
+
MODAL_BACKDROP_CLASSES
|
68
|
+
end
|
69
|
+
|
70
|
+
# Combina tutte le classi per il contenitore
|
71
|
+
def container_classes
|
72
|
+
[
|
73
|
+
MODAL_CONTAINER_CLASSES,
|
74
|
+
get_modal_size_classes,
|
75
|
+
get_modal_rounded_classes,
|
76
|
+
@classes,
|
77
|
+
@html_options[:class]
|
78
|
+
].compact.join(" ")
|
79
|
+
end
|
80
|
+
|
81
|
+
# Combina tutte le classi per l'header
|
82
|
+
def header_classes
|
83
|
+
[
|
84
|
+
"flex items-center justify-between p-6",
|
85
|
+
get_modal_theme_classes
|
86
|
+
].compact.join(" ")
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_modal_theme_classes
|
90
|
+
MODAL_THEME[@theme] || MODAL_THEME[:default]
|
91
|
+
end
|
92
|
+
|
93
|
+
def get_modal_size_classes
|
94
|
+
MODAL_SIZES[@size] || MODAL_SIZES[:medium]
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_modal_rounded_classes
|
98
|
+
MODAL_ROUNDED[@rounded] || MODAL_ROUNDED[:medium]
|
99
|
+
end
|
100
|
+
|
101
|
+
# Restituisce gli attributi per il backdrop
|
102
|
+
def backdrop_attributes
|
103
|
+
attrs = {
|
104
|
+
class: backdrop_classes
|
105
|
+
}
|
106
|
+
|
107
|
+
# Aggiungi altri attributi HTML se presenti
|
108
|
+
@html_options.except(:class).each do |key, value|
|
109
|
+
attrs[key] = value
|
110
|
+
end
|
111
|
+
|
112
|
+
attrs
|
113
|
+
end
|
114
|
+
|
115
|
+
# Restituisce gli attributi per il contenitore
|
116
|
+
def container_attributes
|
117
|
+
{
|
118
|
+
class: container_classes,
|
119
|
+
role: "dialog",
|
120
|
+
"aria-modal": "true",
|
121
|
+
"aria-labelledby": "modal-title"
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
# Restituisce gli attributi per l'header
|
126
|
+
def header_attributes
|
127
|
+
{
|
128
|
+
class: header_classes
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
# Verifica se rendere il componente
|
133
|
+
def render?
|
134
|
+
@title.present?
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def validate_params
|
140
|
+
validate_theme
|
141
|
+
validate_size
|
142
|
+
validate_rounded
|
143
|
+
end
|
144
|
+
|
145
|
+
def validate_theme
|
146
|
+
unless MODAL_THEME.keys.include?(@theme)
|
147
|
+
raise ArgumentError, "Il tema deve essere uno tra: #{MODAL_THEME.keys.join(', ')}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def validate_size
|
152
|
+
unless MODAL_SIZES.keys.include?(@size)
|
153
|
+
raise ArgumentError, "La dimensione deve essere una tra: #{MODAL_SIZES.keys.join(', ')}"
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def validate_rounded
|
158
|
+
unless MODAL_ROUNDED.keys.include?(@rounded)
|
159
|
+
raise ArgumentError, "Il border radius deve essere uno tra: #{MODAL_ROUNDED.keys.join(', ')}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|