better_ui 0.4.0 → 0.5.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +165 -105
  3. data/app/components/better_ui/application/alert_component.html.erb +1 -1
  4. data/app/components/better_ui/application/alert_component.rb +95 -89
  5. data/app/components/better_ui/application/card_component.html.erb +24 -0
  6. data/app/components/better_ui/application/card_component.rb +53 -0
  7. data/app/components/better_ui/application/card_container_component.html.erb +8 -0
  8. data/app/components/better_ui/application/card_container_component.rb +14 -0
  9. data/app/components/better_ui/application/toast_component.rb +92 -57
  10. data/app/components/better_ui/general/avatar_component.html.erb +19 -0
  11. data/app/components/better_ui/general/avatar_component.rb +171 -0
  12. data/app/components/better_ui/general/badge_component.html.erb +19 -0
  13. data/app/components/better_ui/general/badge_component.rb +123 -0
  14. data/app/components/better_ui/general/breadcrumb_component.html.erb +7 -31
  15. data/app/components/better_ui/general/breadcrumb_component.rb +64 -66
  16. data/app/components/better_ui/general/button_component.html.erb +4 -4
  17. data/app/components/better_ui/general/button_component.rb +64 -95
  18. data/app/components/better_ui/general/heading_component.html.erb +3 -3
  19. data/app/components/better_ui/general/heading_component.rb +76 -70
  20. data/app/components/better_ui/general/icon_component.html.erb +1 -1
  21. data/app/components/better_ui/general/icon_component.rb +22 -23
  22. data/app/components/better_ui/general/link_component.html.erb +17 -0
  23. data/app/components/better_ui/general/link_component.rb +132 -0
  24. data/app/components/better_ui/general/panel_component.rb +62 -56
  25. data/app/components/better_ui/general/spinner_component.html.erb +15 -0
  26. data/app/components/better_ui/general/spinner_component.rb +79 -0
  27. data/app/components/better_ui/general/table_component.html.erb +56 -20
  28. data/app/components/better_ui/general/table_component.rb +106 -80
  29. data/app/components/better_ui/theme_helper.rb +77 -75
  30. data/app/views/components/better_ui/general/table/_custom_body_row.html.erb +17 -0
  31. data/app/views/components/better_ui/general/table/_custom_footer_rows.html.erb +17 -0
  32. data/app/views/components/better_ui/general/table/_custom_header_rows.html.erb +12 -0
  33. data/lib/better_ui/engine.rb +4 -10
  34. data/lib/better_ui/version.rb +1 -1
  35. data/lib/better_ui.rb +4 -0
  36. data/lib/generators/better_ui/stylesheet_generator.rb +96 -0
  37. data/lib/generators/better_ui/templates/README +56 -0
  38. data/lib/generators/better_ui/templates/components/_avatar.scss +151 -0
  39. data/lib/generators/better_ui/templates/components/_badge.scss +142 -0
  40. data/lib/generators/better_ui/templates/components/_breadcrumb.scss +107 -0
  41. data/lib/generators/better_ui/templates/components/_button.scss +106 -0
  42. data/lib/generators/better_ui/templates/components/_card.scss +69 -0
  43. data/lib/generators/better_ui/templates/components/_heading.scss +180 -0
  44. data/lib/generators/better_ui/templates/components/_icon.scss +90 -0
  45. data/lib/generators/better_ui/templates/components/_link.scss +130 -0
  46. data/lib/generators/better_ui/templates/components/_panel.scss +144 -0
  47. data/lib/generators/better_ui/templates/components/_spinner.scss +132 -0
  48. data/lib/generators/better_ui/templates/components/_table.scss +105 -0
  49. data/lib/generators/better_ui/templates/components/_variables.scss +33 -0
  50. data/lib/generators/better_ui/templates/components_stylesheet.scss +66 -0
  51. metadata +51 -22
  52. data/app/helpers/better_ui_application_helper.rb +0 -99
@@ -0,0 +1,171 @@
1
+ module BetterUi
2
+ module General
3
+ class AvatarComponent < ViewComponent::Base
4
+ attr_reader :name, :src, :size, :shape, :status, :status_position, :type, :classes, :id
5
+
6
+ # Temi di colore disponibili
7
+ AVATAR_THEME = {
8
+ default: "bui-avatar-default",
9
+ white: "bui-avatar-white",
10
+ red: "bui-avatar-red",
11
+ rose: "bui-avatar-rose",
12
+ orange: "bui-avatar-orange",
13
+ green: "bui-avatar-green",
14
+ blue: "bui-avatar-blue",
15
+ yellow: "bui-avatar-yellow",
16
+ violet: "bui-avatar-violet",
17
+ gray: "bui-avatar-gray"
18
+ }
19
+
20
+ # Dimensioni disponibili
21
+ AVATAR_SIZES = {
22
+ xxsmall: "bui-avatar-size-xxsmall",
23
+ xsmall: "bui-avatar-size-xsmall",
24
+ small: "bui-avatar-size-small",
25
+ medium: "bui-avatar-size-medium",
26
+ large: "bui-avatar-size-large",
27
+ xlarge: "bui-avatar-size-xlarge",
28
+ xxlarge: "bui-avatar-size-xxlarge"
29
+ }
30
+
31
+ # Forme disponibili
32
+ AVATAR_SHAPES = {
33
+ circle: "bui-avatar-shape-circle",
34
+ square: "bui-avatar-shape-square",
35
+ rounded: "bui-avatar-shape-rounded"
36
+ }
37
+
38
+ # Stati online disponibili
39
+ AVATAR_STATUS = {
40
+ online: "bui-avatar-status-online",
41
+ offline: "bui-avatar-status-offline",
42
+ busy: "bui-avatar-status-busy",
43
+ away: "bui-avatar-status-away"
44
+ }
45
+
46
+ # Posizioni dell'indicatore di stato
47
+ AVATAR_STATUS_POSITION = {
48
+ bottom_right: "bui-avatar-status-position-bottom-right",
49
+ bottom_left: "bui-avatar-status-position-bottom-left",
50
+ top_right: "bui-avatar-status-position-top-right",
51
+ top_left: "bui-avatar-status-position-top-left"
52
+ }
53
+
54
+ def initialize(
55
+ name: nil,
56
+ src: nil,
57
+ size: :medium,
58
+ shape: :circle,
59
+ status: nil,
60
+ status_position: :bottom_right,
61
+ type: :default,
62
+ classes: nil,
63
+ id: nil
64
+ )
65
+ @name = name
66
+ @src = src
67
+ @size = size.to_sym
68
+ @shape = shape.to_sym
69
+ @status = status&.to_sym
70
+ @status_position = status_position.to_sym
71
+ @type = type.to_sym
72
+ @classes = classes
73
+ @id = id
74
+ end
75
+
76
+ # Combina tutte le classi
77
+ def combined_classes
78
+ [
79
+ "bui-avatar", # Classe base per tutti gli avatar
80
+ get_avatar_theme_class,
81
+ get_avatar_size_class,
82
+ get_avatar_shape_class,
83
+ @classes
84
+ ].compact.join(" ")
85
+ end
86
+
87
+ def get_avatar_theme_class
88
+ AVATAR_THEME[@type] || AVATAR_THEME[:default]
89
+ end
90
+
91
+ def get_avatar_size_class
92
+ AVATAR_SIZES[@size] || AVATAR_SIZES[:medium]
93
+ end
94
+
95
+ def get_avatar_shape_class
96
+ AVATAR_SHAPES[@shape] || AVATAR_SHAPES[:circle]
97
+ end
98
+
99
+ def get_avatar_status_class
100
+ AVATAR_STATUS[@status] || ""
101
+ end
102
+
103
+ def get_avatar_status_position_class
104
+ AVATAR_STATUS_POSITION[@status_position] || AVATAR_STATUS_POSITION[:bottom_right]
105
+ end
106
+
107
+ # Restituisce gli attributi per l'avatar
108
+ def avatar_attributes
109
+ attrs = {
110
+ class: combined_classes,
111
+ id: @id
112
+ }
113
+
114
+ attrs
115
+ end
116
+
117
+ # Restituisce le classi per l'indicatore di stato
118
+ def status_indicator_classes
119
+ [
120
+ "bui-avatar-status-indicator",
121
+ get_avatar_status_class,
122
+ get_avatar_status_position_class
123
+ ].compact.join(" ")
124
+ end
125
+
126
+ # Determina se mostrare l'indicatore di stato
127
+ def show_status?
128
+ @status.present? && AVATAR_STATUS.key?(@status)
129
+ end
130
+
131
+ # Ottiene le iniziali dal nome
132
+ def initials
133
+ return "" unless @name.present?
134
+
135
+ words = @name.strip.split(/\s+/)
136
+ if words.size >= 2
137
+ "#{words[0][0]}#{words[1][0]}".upcase
138
+ else
139
+ @name[0..1].upcase
140
+ end
141
+ end
142
+
143
+ # Determina se mostrare l'immagine
144
+ def show_image?
145
+ @src.present?
146
+ end
147
+
148
+ # Ottiene le dimensioni dell'avatar in pixel
149
+ def pixel_size
150
+ case @size
151
+ when :xxsmall
152
+ 20
153
+ when :xsmall
154
+ 24
155
+ when :small
156
+ 32
157
+ when :medium
158
+ 40
159
+ when :large
160
+ 48
161
+ when :xlarge
162
+ 64
163
+ when :xxlarge
164
+ 96
165
+ else
166
+ 40
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,19 @@
1
+ <span <%= badge_attributes.to_s.html_safe %>>
2
+ <% if @icon && @icon_position == :left %>
3
+ <span class="bui-badge-icon-left">
4
+ <%= render_icon(@icon) %>
5
+ </span>
6
+ <% end %>
7
+
8
+ <% if @label.present? %>
9
+ <span class="bui-badge-label"><%= @label %></span>
10
+ <% end %>
11
+
12
+ <% if @icon && @icon_position == :right %>
13
+ <span class="bui-badge-icon-right">
14
+ <%= render_icon(@icon) %>
15
+ </span>
16
+ <% end %>
17
+
18
+ <%= content %>
19
+ </span>
@@ -0,0 +1,123 @@
1
+ module BetterUi
2
+ module General
3
+ class BadgeComponent < ViewComponent::Base
4
+ attr_reader :label, :type, :size, :icon, :icon_position, :rounded, :notification, :outline, :classes, :id
5
+
6
+ # Temi di colore disponibili
7
+ BADGE_THEME = {
8
+ default: "bui-badge-default",
9
+ white: "bui-badge-white",
10
+ red: "bui-badge-red",
11
+ rose: "bui-badge-rose",
12
+ orange: "bui-badge-orange",
13
+ green: "bui-badge-green",
14
+ blue: "bui-badge-blue",
15
+ yellow: "bui-badge-yellow",
16
+ violet: "bui-badge-violet",
17
+ gray: "bui-badge-gray"
18
+ }
19
+
20
+ # Dimensioni disponibili
21
+ BADGE_SIZES = {
22
+ small: "bui-badge-size-small",
23
+ medium: "bui-badge-size-medium",
24
+ large: "bui-badge-size-large"
25
+ }
26
+
27
+ # Border radius disponibili
28
+ BADGE_RADIUS = {
29
+ none: "bui-badge-radius-none",
30
+ small: "bui-badge-radius-small",
31
+ medium: "bui-badge-radius-medium",
32
+ large: "bui-badge-radius-large",
33
+ full: "bui-badge-radius-full"
34
+ }
35
+
36
+ # Stati e varianti
37
+ BADGE_VARIANTS = {
38
+ outline: "bui-badge-outline",
39
+ notification: "bui-badge-notification"
40
+ }
41
+
42
+ # Inizializzazione del componente
43
+ def initialize(
44
+ label: nil,
45
+ type: :default,
46
+ size: :medium,
47
+ icon: nil,
48
+ icon_position: :left,
49
+ rounded: :medium,
50
+ notification: false,
51
+ outline: false,
52
+ classes: nil,
53
+ id: nil
54
+ )
55
+ @label = label
56
+ @type = type.to_sym
57
+ @size = size.to_sym
58
+ @icon = icon
59
+ @icon_position = icon_position.to_sym
60
+ @rounded = rounded.to_sym
61
+ @notification = notification
62
+ @outline = outline
63
+ @classes = classes
64
+ @id = id
65
+ end
66
+
67
+ # Combina tutte le classi
68
+ def combined_classes
69
+ [
70
+ "bui-badge", # Classe base per tutti i badge
71
+ get_badge_type_class,
72
+ get_badge_size_class,
73
+ get_border_radius_class,
74
+ @outline ? BADGE_VARIANTS[:outline] : "",
75
+ @notification ? BADGE_VARIANTS[:notification] : "",
76
+ @classes
77
+ ].compact.join(" ")
78
+ end
79
+
80
+ def get_badge_type_class
81
+ BADGE_THEME[@type] || BADGE_THEME[:default]
82
+ end
83
+
84
+ def get_badge_size_class
85
+ BADGE_SIZES[@size] || BADGE_SIZES[:medium]
86
+ end
87
+
88
+ def get_border_radius_class
89
+ BADGE_RADIUS[@rounded] || BADGE_RADIUS[:medium]
90
+ end
91
+
92
+ # Restituisce gli attributi per il badge
93
+ def badge_attributes
94
+ attrs = {
95
+ class: combined_classes,
96
+ id: @id
97
+ }
98
+
99
+ attrs
100
+ end
101
+
102
+ # Helper per renderizzare le icone
103
+ def render_icon(icon_name)
104
+ # Mappa le dimensioni del badge alle dimensioni dell'icona
105
+ icon_size = case @size
106
+ when :large
107
+ :small
108
+ when :small
109
+ :tiny
110
+ else
111
+ :tiny
112
+ end
113
+
114
+ # Utilizziamo il componente Icon
115
+ render BetterUi::General::IconComponent.new(
116
+ name: icon_name,
117
+ size: icon_size,
118
+ fixed_width: true
119
+ )
120
+ end
121
+ end
122
+ end
123
+ end
@@ -1,37 +1,13 @@
1
1
  <nav aria-label="Breadcrumb" class="<%= container_classes %>">
2
- <ol class="flex flex-wrap items-center">
2
+ <ol class="bui-breadcrumb-list">
3
3
  <% @items.each_with_index do |item, index| %>
4
- <li class="flex items-center">
5
- <% if last_item?(index) %>
6
- <span class="<%= item_classes(true) %> flex items-center" aria-current="page">
7
- <% if item_icon(item) %>
8
- <span class="mr-1.5"><%= item_icon(item) %></span>
9
- <% end %>
10
- <%= item_text(item) %>
4
+ <li class="bui-breadcrumb-item">
5
+ <%= render link_for_item(item, active: last_item?(index)) %>
6
+
7
+ <% unless last_item?(index) %>
8
+ <span class="<%= separator_classes %>" aria-hidden="true">
9
+ <%= separator_text %>
11
10
  </span>
12
- <% else %>
13
- <% url = item_url(item) %>
14
- <% if url.present? %>
15
- <%= link_to url, class: "#{item_classes} flex items-center" do %>
16
- <% if item_icon(item) %>
17
- <span class="mr-1.5"><%= item_icon(item) %></span>
18
- <% end %>
19
- <%= item_text(item) %>
20
- <% end %>
21
- <% else %>
22
- <span class="<%= item_classes %> flex items-center">
23
- <% if item_icon(item) %>
24
- <span class="mr-1.5"><%= item_icon(item) %></span>
25
- <% end %>
26
- <%= item_text(item) %>
27
- </span>
28
- <% end %>
29
-
30
- <% unless last_item?(index) %>
31
- <span class="<%= separator_classes %>" aria-hidden="true">
32
- <%= separator_text %>
33
- </span>
34
- <% end %>
35
11
  <% end %>
36
12
  </li>
37
13
  <% end %>
@@ -1,39 +1,57 @@
1
1
  module BetterUi
2
2
  module General
3
3
  class BreadcrumbComponent < ViewComponent::Base
4
- attr_reader :items, :separator, :size, :variant, :classes, :inverse
4
+ attr_reader :items, :separator, :size, :theme, :classes
5
5
 
6
- # Varianti di colore disponibili
7
- VARIANTS = {
6
+ # Temi di colore disponibili
7
+ BREADCRUMB_THEME = {
8
8
  default: {
9
- container: "text-gray-600",
10
- item: "text-gray-500 hover:text-gray-700",
11
- active: "text-gray-900 font-medium",
12
- separator: "text-gray-400"
9
+ container: "bui-breadcrumb-default-container",
10
+ separator: "bui-breadcrumb-default-separator"
13
11
  },
14
- primary: {
15
- container: "text-orange-600",
16
- item: "text-orange-500 hover:text-orange-700",
17
- active: "text-orange-800 font-medium",
18
- separator: "text-orange-300"
12
+ white: {
13
+ container: "bui-breadcrumb-white-container",
14
+ separator: "bui-breadcrumb-white-separator"
19
15
  },
20
- light: {
21
- container: "text-gray-100",
22
- item: "text-gray-200 hover:text-white",
23
- active: "text-white font-medium",
24
- separator: "text-gray-300"
16
+ red: {
17
+ container: "bui-breadcrumb-red-container",
18
+ separator: "bui-breadcrumb-red-separator"
19
+ },
20
+ rose: {
21
+ container: "bui-breadcrumb-rose-container",
22
+ separator: "bui-breadcrumb-rose-separator"
23
+ },
24
+ orange: {
25
+ container: "bui-breadcrumb-orange-container",
26
+ separator: "bui-breadcrumb-orange-separator"
27
+ },
28
+ green: {
29
+ container: "bui-breadcrumb-green-container",
30
+ separator: "bui-breadcrumb-green-separator"
31
+ },
32
+ blue: {
33
+ container: "bui-breadcrumb-blue-container",
34
+ separator: "bui-breadcrumb-blue-separator"
35
+ },
36
+ yellow: {
37
+ container: "bui-breadcrumb-yellow-container",
38
+ separator: "bui-breadcrumb-yellow-separator"
39
+ },
40
+ violet: {
41
+ container: "bui-breadcrumb-violet-container",
42
+ separator: "bui-breadcrumb-violet-separator"
25
43
  }
26
44
  }
27
45
 
28
46
  # Dimensioni disponibili
29
- SIZES = {
30
- sm: "text-xs",
31
- md: "text-sm",
32
- lg: "text-base"
47
+ BREADCRUMB_SIZES = {
48
+ small: "bui-breadcrumb-small",
49
+ medium: "bui-breadcrumb-medium",
50
+ large: "bui-breadcrumb-large"
33
51
  }
34
52
 
35
53
  # Separatori predefiniti
36
- SEPARATORS = {
54
+ BREADCRUMB_SEPARATORS = {
37
55
  slash: "/",
38
56
  chevron: "›",
39
57
  arrow: "→",
@@ -45,23 +63,21 @@ module BetterUi
45
63
  def initialize(
46
64
  items: [],
47
65
  separator: :chevron,
48
- size: :md,
49
- variant: :default,
50
- classes: nil,
51
- inverse: false
66
+ size: :medium,
67
+ theme: :default,
68
+ classes: nil
52
69
  )
53
70
  @items = items || []
54
71
  @separator = separator.to_sym
55
72
  @size = size.to_sym
56
- @variant = variant.to_sym
73
+ @theme = theme.to_sym
57
74
  @classes = classes
58
- @inverse = inverse
59
75
  end
60
76
 
61
77
  # Restituisce il separatore come stringa
62
78
  def separator_text
63
- if SEPARATORS.key?(@separator)
64
- SEPARATORS[@separator]
79
+ if BREADCRUMB_SEPARATORS.key?(@separator)
80
+ BREADCRUMB_SEPARATORS[@separator]
65
81
  else
66
82
  @separator.to_s
67
83
  end
@@ -70,30 +86,18 @@ module BetterUi
70
86
  # Genera le classi per il container
71
87
  def container_classes
72
88
  [
73
- "flex items-center flex-wrap",
74
- SIZES.fetch(@size, SIZES[:md]),
75
- VARIANTS.fetch(@variant, VARIANTS[:default])[:container],
76
- @inverse ? "bg-gray-800 p-2 rounded" : "",
89
+ "bui-breadcrumb-container",
90
+ BREADCRUMB_SIZES.fetch(@size, BREADCRUMB_SIZES[:medium]),
91
+ BREADCRUMB_THEME.fetch(@theme, BREADCRUMB_THEME[:default])[:container],
77
92
  @classes
78
93
  ].compact.join(" ")
79
94
  end
80
95
 
81
- # Genera le classi per un elemento
82
- def item_classes(active = false)
83
- variant_classes = VARIANTS.fetch(@variant, VARIANTS[:default])
84
-
85
- if active
86
- variant_classes[:active]
87
- else
88
- variant_classes[:item]
89
- end
90
- end
91
-
92
96
  # Genera le classi per il separatore
93
97
  def separator_classes
94
98
  [
95
- "mx-2",
96
- VARIANTS.fetch(@variant, VARIANTS[:default])[:separator]
99
+ "bui-breadcrumb-separator",
100
+ BREADCRUMB_THEME.fetch(@theme, BREADCRUMB_THEME[:default])[:separator]
97
101
  ].compact.join(" ")
98
102
  end
99
103
 
@@ -102,27 +106,21 @@ module BetterUi
102
106
  index == @items.length - 1
103
107
  end
104
108
 
105
- # Formatta il testo dell'item
106
- def item_text(item)
107
- item.is_a?(Hash) ? item[:label] : item.to_s
108
- end
109
-
110
- # Restituisce l'URL dell'item
111
- def item_url(item)
112
- item.is_a?(Hash) ? item[:url] : nil
113
- end
114
-
115
- # Restituisce l'icona dell'item se presente
116
- def item_icon(item)
117
- return nil unless item.is_a?(Hash) && item[:icon].present?
109
+ # Crea un componente link per l'item
110
+ def link_for_item(item, active: false)
111
+ label = item.is_a?(Hash) ? item[:label] : item.to_s
112
+ href = item.is_a?(Hash) ? item[:url] : nil
113
+ icon = item.is_a?(Hash) ? item[:icon] : nil
118
114
 
119
- if item[:icon].is_a?(String)
120
- render BetterUi::General::IconComponent.new(name: item[:icon])
121
- else
122
- item[:icon] # Assumiamo che sia già un componente renderizzato
123
- end
115
+ BetterUi::General::LinkComponent.new(
116
+ label: label,
117
+ href: href,
118
+ theme: @theme,
119
+ icon: icon,
120
+ active: active
121
+ )
124
122
  end
125
-
123
+
126
124
  # Verifica se rendere il componente
127
125
  def render?
128
126
  @items.present? && @items.length > 0
@@ -2,7 +2,7 @@
2
2
  <% if link? %>
3
3
  <%= link_to @href, **link_attributes do %>
4
4
  <% if @icon && @icon_position == :left %>
5
- <span class="mr-2"><%= render_icon(@icon) %></span>
5
+ <span class="bui-btn-icon-left"><%= render_icon(@icon) %></span>
6
6
  <% end %>
7
7
 
8
8
  <% if @label %>
@@ -10,7 +10,7 @@
10
10
  <% end %>
11
11
 
12
12
  <% if @icon && @icon_position == :right %>
13
- <span class="ml-2"><%= render_icon(@icon) %></span>
13
+ <span class="bui-btn-icon-right"><%= render_icon(@icon) %></span>
14
14
  <% end %>
15
15
 
16
16
  <%= content %>
@@ -18,7 +18,7 @@
18
18
  <% else %>
19
19
  <%= tag.button(**button_attributes) do %>
20
20
  <% if @icon && @icon_position == :left %>
21
- <span class="mr-2"><%= render_icon(@icon) %></span>
21
+ <span class="bui-btn-icon-left"><%= render_icon(@icon) %></span>
22
22
  <% end %>
23
23
 
24
24
  <% if @label %>
@@ -26,7 +26,7 @@
26
26
  <% end %>
27
27
 
28
28
  <% if @icon && @icon_position == :right %>
29
- <span class="ml-2"><%= render_icon(@icon) %></span>
29
+ <span class="bui-btn-icon-right"><%= render_icon(@icon) %></span>
30
30
  <% end %>
31
31
 
32
32
  <%= content %>