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,132 @@
1
+ module BetterUi
2
+ module General
3
+ class LinkComponent < ViewComponent::Base
4
+ attr_reader :label, :href, :theme, :icon, :classes, :active, :data, :method, :target
5
+
6
+ # Temi di colore disponibili
7
+ LINK_THEME = {
8
+ default: {
9
+ link: "bui-link-default-link",
10
+ active: "bui-link-default-active",
11
+ text: "bui-link-default-text"
12
+ },
13
+ white: {
14
+ link: "bui-link-white-link",
15
+ active: "bui-link-white-active",
16
+ text: "bui-link-white-text"
17
+ },
18
+ red: {
19
+ link: "bui-link-red-link",
20
+ active: "bui-link-red-active",
21
+ text: "bui-link-red-text"
22
+ },
23
+ rose: {
24
+ link: "bui-link-rose-link",
25
+ active: "bui-link-rose-active",
26
+ text: "bui-link-rose-text"
27
+ },
28
+ orange: {
29
+ link: "bui-link-orange-link",
30
+ active: "bui-link-orange-active",
31
+ text: "bui-link-orange-text"
32
+ },
33
+ green: {
34
+ link: "bui-link-green-link",
35
+ active: "bui-link-green-active",
36
+ text: "bui-link-green-text"
37
+ },
38
+ blue: {
39
+ link: "bui-link-blue-link",
40
+ active: "bui-link-blue-active",
41
+ text: "bui-link-blue-text"
42
+ },
43
+ yellow: {
44
+ link: "bui-link-yellow-link",
45
+ active: "bui-link-yellow-active",
46
+ text: "bui-link-yellow-text"
47
+ },
48
+ violet: {
49
+ link: "bui-link-violet-link",
50
+ active: "bui-link-violet-active",
51
+ text: "bui-link-violet-text"
52
+ }
53
+ }
54
+
55
+ # Inizializzazione del componente
56
+ def initialize(
57
+ label:,
58
+ href: nil,
59
+ theme: :default,
60
+ icon: nil,
61
+ classes: nil,
62
+ active: false,
63
+ data: {},
64
+ method: nil,
65
+ target: nil
66
+ )
67
+ @label = label
68
+ @href = href
69
+ @theme = theme.to_sym
70
+ @icon = icon
71
+ @classes = classes
72
+ @active = active
73
+ @data = data || {}
74
+ @method = method
75
+ @target = target
76
+ end
77
+
78
+ # Determina se è un link attivo/corrente
79
+ def active?
80
+ @active
81
+ end
82
+
83
+ # Determina se è un link o solo testo
84
+ def link?
85
+ @href.present?
86
+ end
87
+
88
+ # Genera le classi per il componente
89
+ def component_classes
90
+ theme_classes = LINK_THEME.fetch(@theme, LINK_THEME[:default])
91
+
92
+ base_classes = ["bui-link-base"]
93
+
94
+ if active?
95
+ base_classes << theme_classes[:active]
96
+ elsif link?
97
+ base_classes << theme_classes[:link]
98
+ else
99
+ base_classes << theme_classes[:text]
100
+ end
101
+
102
+ base_classes << @classes if @classes.present?
103
+
104
+ base_classes.compact.join(" ")
105
+ end
106
+
107
+ # Restituisce gli attributi per il link
108
+ def link_attributes
109
+ attrs = {
110
+ class: component_classes
111
+ }
112
+
113
+ attrs[:data] = @data.merge(turbo_method: @method) if @method.present?
114
+ attrs[:data] = @data if @data.present? && !@method.present?
115
+ attrs[:target] = @target if @target.present?
116
+
117
+ attrs
118
+ end
119
+
120
+ # Renderizza l'icona
121
+ def render_icon
122
+ return nil unless @icon.present?
123
+
124
+ if @icon.is_a?(String)
125
+ render BetterUi::General::IconComponent.new(name: @icon)
126
+ else
127
+ @icon # Assumiamo che sia già un componente renderizzato
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -1,92 +1,98 @@
1
1
  module BetterUi
2
2
  module General
3
3
  class PanelComponent < ViewComponent::Base
4
- attr_reader :header, :footer, :body, :title, :padding, :variant
4
+ attr_reader :header, :footer, :body, :title, :padding, :variant, :rounded
5
5
 
6
- PADDING_OPTIONS = {
7
- none: '',
8
- small: 'p-2',
9
- medium: 'p-4',
10
- large: 'p-6'
6
+ # Opzioni di padding disponibili
7
+ PANEL_PADDING = {
8
+ none: 'bui-panel-padding-none',
9
+ small: 'bui-panel-padding-small',
10
+ medium: 'bui-panel-padding-medium',
11
+ large: 'bui-panel-padding-large'
11
12
  }.freeze
12
13
 
13
- def initialize(title: nil, body: nil, header: nil, footer: nil, padding: :medium, variant: :default)
14
+ # Temi di colore per l'header
15
+ PANEL_HEADER_THEME = {
16
+ default: 'bui-panel-default-header',
17
+ white: 'bui-panel-white-header',
18
+ red: 'bui-panel-red-header',
19
+ rose: 'bui-panel-rose-header',
20
+ orange: 'bui-panel-orange-header',
21
+ green: 'bui-panel-green-header',
22
+ blue: 'bui-panel-blue-header',
23
+ yellow: 'bui-panel-yellow-header',
24
+ violet: 'bui-panel-violet-header'
25
+ }.freeze
26
+
27
+ # Temi di colore per il footer
28
+ PANEL_FOOTER_THEME = {
29
+ default: 'bui-panel-default-footer',
30
+ white: 'bui-panel-white-footer',
31
+ red: 'bui-panel-red-footer',
32
+ rose: 'bui-panel-rose-footer',
33
+ orange: 'bui-panel-orange-footer',
34
+ green: 'bui-panel-green-footer',
35
+ blue: 'bui-panel-blue-footer',
36
+ yellow: 'bui-panel-yellow-footer',
37
+ violet: 'bui-panel-violet-footer'
38
+ }.freeze
39
+
40
+ # Opzioni di bordi arrotondati standardizzati
41
+ PANEL_RADIUS = {
42
+ none: 'bui-panel-radius-none',
43
+ small: 'bui-panel-radius-small',
44
+ medium: 'bui-panel-radius-medium',
45
+ large: 'bui-panel-radius-large',
46
+ full: 'bui-panel-radius-full'
47
+ }.freeze
48
+
49
+ def initialize(title: nil, body: nil, header: nil, footer: nil, padding: :medium, variant: :default, rounded: :small)
14
50
  @title = title
15
51
  @body = body
16
52
  @header = header
17
53
  @footer = footer
18
54
  @padding = padding.to_sym
19
55
  @variant = variant.to_sym
56
+ @rounded = rounded.to_sym
20
57
  end
21
58
 
22
59
  def panel_classes
23
- ThemeHelper.generate_component_classes(:panel, @variant)
60
+ [
61
+ 'bui-panel-base',
62
+ get_border_radius_class
63
+ ].compact.join(' ')
64
+ end
65
+
66
+ def get_border_radius_class
67
+ PANEL_RADIUS[@rounded] || PANEL_RADIUS[:small]
24
68
  end
25
69
 
26
70
  def header_classes
27
71
  [
28
- 'panel-header',
29
- ThemeHelper::LAYOUT_STYLES[:panel][:header],
30
- header_color_classes,
31
- PADDING_OPTIONS.fetch(@padding, PADDING_OPTIONS[:medium])
72
+ 'bui-panel-header',
73
+ PANEL_HEADER_THEME[@variant] || PANEL_HEADER_THEME[:default],
74
+ PANEL_PADDING.fetch(@padding, PANEL_PADDING[:medium])
32
75
  ].compact.join(' ')
33
76
  end
34
77
 
35
78
  def body_classes
36
79
  [
37
- 'panel-body',
38
- ThemeHelper::LAYOUT_STYLES[:panel][:body],
39
- 'overflow-x-auto break-words',
40
- PADDING_OPTIONS.fetch(@padding, PADDING_OPTIONS[:medium])
80
+ 'bui-panel-body',
81
+ 'bui-panel-body-content',
82
+ PANEL_PADDING.fetch(@padding, PANEL_PADDING[:medium])
41
83
  ].compact.join(' ')
42
84
  end
43
85
 
44
86
  def footer_classes
45
87
  [
46
- 'panel-footer',
47
- ThemeHelper::LAYOUT_STYLES[:panel][:footer],
48
- footer_color_classes,
49
- 'overflow-x-auto break-words',
50
- PADDING_OPTIONS.fetch(@padding, PADDING_OPTIONS[:medium])
88
+ 'bui-panel-footer',
89
+ PANEL_FOOTER_THEME[@variant] || PANEL_FOOTER_THEME[:default],
90
+ PANEL_PADDING.fetch(@padding, PANEL_PADDING[:medium])
51
91
  ].compact.join(' ')
52
92
  end
53
93
 
54
94
  def title_classes
55
- 'text-lg font-medium'
56
- end
57
-
58
- def header_color_classes
59
- case @variant
60
- when :primary
61
- 'bg-orange-50 text-orange-700'
62
- when :success
63
- 'bg-green-50 text-green-700'
64
- when :warning
65
- 'bg-amber-50 text-amber-700'
66
- when :danger
67
- 'bg-red-50 text-red-700'
68
- when :info
69
- 'bg-blue-50 text-blue-700'
70
- else
71
- 'bg-gray-50 text-gray-700'
72
- end
73
- end
74
-
75
- def footer_color_classes
76
- case @variant
77
- when :primary
78
- 'bg-orange-50 text-orange-600'
79
- when :success
80
- 'bg-green-50 text-green-600'
81
- when :warning
82
- 'bg-amber-50 text-amber-600'
83
- when :danger
84
- 'bg-red-50 text-red-600'
85
- when :info
86
- 'bg-blue-50 text-blue-600'
87
- else
88
- 'bg-gray-50 text-gray-600'
89
- end
95
+ 'bui-panel-title'
90
96
  end
91
97
 
92
98
  def render?
@@ -0,0 +1,15 @@
1
+ <div <%= spinner_attributes.to_s.html_safe %>>
2
+ <div class="bui-spinner-animation"></div>
3
+
4
+ <% if @label.present? %>
5
+ <div class="bui-spinner-label">
6
+ <%= @label %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <% if content.present? %>
11
+ <div class="bui-spinner-content">
12
+ <%= content %>
13
+ </div>
14
+ <% end %>
15
+ </div>
@@ -0,0 +1,79 @@
1
+ module BetterUi
2
+ module General
3
+ class SpinnerComponent < ViewComponent::Base
4
+ attr_reader :size, :theme, :fullscreen, :label, :classes, :id
5
+
6
+ # Temi di colore disponibili
7
+ SPINNER_THEME = {
8
+ default: "bui-spinner-default",
9
+ white: "bui-spinner-white",
10
+ red: "bui-spinner-red",
11
+ rose: "bui-spinner-rose",
12
+ orange: "bui-spinner-orange",
13
+ green: "bui-spinner-green",
14
+ blue: "bui-spinner-blue",
15
+ yellow: "bui-spinner-yellow",
16
+ violet: "bui-spinner-violet"
17
+ }
18
+
19
+ # Dimensioni disponibili
20
+ SPINNER_SIZES = {
21
+ small: "bui-spinner-small",
22
+ medium: "bui-spinner-medium",
23
+ large: "bui-spinner-large"
24
+ }
25
+
26
+ # Stati e comportamenti dello spinner
27
+ SPINNER_STATES = {
28
+ fullscreen: "bui-spinner-fullscreen"
29
+ }
30
+
31
+ # Inizializzazione del componente
32
+ def initialize(
33
+ size: :medium,
34
+ theme: :default,
35
+ fullscreen: false,
36
+ label: nil,
37
+ classes: nil,
38
+ id: nil
39
+ )
40
+ @size = size.to_sym
41
+ @theme = theme.to_sym
42
+ @fullscreen = fullscreen
43
+ @label = label
44
+ @classes = classes
45
+ @id = id
46
+ end
47
+
48
+ # Combina tutte le classi
49
+ def combined_classes
50
+ [
51
+ "bui-spinner", # Classe base per tutti gli spinner
52
+ get_spinner_theme_class,
53
+ get_spinner_size_class,
54
+ @fullscreen ? SPINNER_STATES[:fullscreen] : "",
55
+ @classes
56
+ ].compact.join(" ")
57
+ end
58
+
59
+ def get_spinner_theme_class
60
+ SPINNER_THEME[@theme] || SPINNER_THEME[:default]
61
+ end
62
+
63
+ def get_spinner_size_class
64
+ SPINNER_SIZES[@size] || SPINNER_SIZES[:medium]
65
+ end
66
+
67
+ # Restituisce gli attributi per lo spinner
68
+ def spinner_attributes
69
+ attrs = {
70
+ class: combined_classes,
71
+ id: @id,
72
+ role: "status"
73
+ }
74
+
75
+ attrs
76
+ end
77
+ end
78
+ end
79
+ end
@@ -7,31 +7,67 @@
7
7
  <% end %>
8
8
 
9
9
  <thead class="<%= thead_classes %>">
10
- <tr>
11
- <% headers_for_display.each do |header| %>
12
- <th scope="col" class="<%= th_classes %>">
13
- <%= header.to_s.humanize %>
14
- </th>
15
- <% end %>
16
- </tr>
10
+ <% if header_rows_partial.present? %>
11
+ <%= render partial: header_rows_partial, locals: {
12
+ component: self,
13
+ headers: headers_for_display
14
+ } %>
15
+ <% else %>
16
+ <tr>
17
+ <% headers_for_display.each do |header| %>
18
+ <th scope="col" class="<%= th_classes %>">
19
+ <%= header.to_s.humanize %>
20
+ </th>
21
+ <% end %>
22
+ </tr>
23
+ <% end %>
17
24
  </thead>
18
25
 
19
26
  <tbody class="<%= tbody_classes %>">
20
27
  <% data.each_with_index do |row, index| %>
21
- <tr class="<%= tr_classes(index) %>">
22
- <% headers_for_display.each do |header| %>
23
- <td class="<%= td_classes %>">
24
- <% if row.is_a?(Hash) %>
25
- <%= row[header.to_s] || row[header.to_sym] %>
26
- <% elsif row.respond_to?(header.to_sym) %>
27
- <%= row.send(header.to_sym) %>
28
- <% else %>
29
-
30
- <% end %>
31
- </td>
32
- <% end %>
33
- </tr>
28
+ <% if body_row_partial.present? %>
29
+ <%= render partial: body_row_partial, locals: {
30
+ component: self,
31
+ row: row,
32
+ index: index,
33
+ headers: headers_for_display
34
+ } %>
35
+ <% else %>
36
+ <tr class="<%= tr_classes(index) %>">
37
+ <% headers_for_display.each do |header| %>
38
+ <td class="<%= td_classes %>">
39
+ <% if row.is_a?(Hash) %>
40
+ <%= row[header.to_s] || row[header.to_sym] %>
41
+ <% elsif row.respond_to?(header.to_sym) %>
42
+ <%= row.send(header.to_sym) %>
43
+ <% else %>
44
+
45
+ <% end %>
46
+ </td>
47
+ <% end %>
48
+ </tr>
49
+ <% end %>
34
50
  <% end %>
35
51
  </tbody>
52
+
53
+ <% if footer.present? %>
54
+ <tfoot class="<%= tfoot_classes %>">
55
+ <% if footer_rows_partial.present? %>
56
+ <%= render partial: footer_rows_partial, locals: {
57
+ component: self,
58
+ footer: footer,
59
+ headers: headers_for_display
60
+ } %>
61
+ <% else %>
62
+ <tr>
63
+ <% footer.each_with_index do |value, index| %>
64
+ <td class="<%= tf_classes %>">
65
+ <%= value || "—" %>
66
+ </td>
67
+ <% end %>
68
+ </tr>
69
+ <% end %>
70
+ </tfoot>
71
+ <% end %>
36
72
  </table>
37
73
  </div>