better_ui 0.1.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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +199 -75
  3. data/app/assets/javascripts/better_ui/controllers/navbar_controller.js +138 -0
  4. data/app/assets/javascripts/better_ui/controllers/sidebar_controller.js +211 -0
  5. data/app/assets/javascripts/better_ui/controllers/toast_controller.js +161 -0
  6. data/app/assets/javascripts/better_ui/index.js +159 -0
  7. data/app/assets/javascripts/better_ui/toast_manager.js +77 -0
  8. data/app/assets/stylesheets/better_ui/application.css +25 -351
  9. data/app/components/better_ui/application/alert_component.html.erb +27 -0
  10. data/app/components/better_ui/application/alert_component.rb +202 -0
  11. data/app/components/better_ui/application/card_component.html.erb +24 -0
  12. data/app/components/better_ui/application/card_component.rb +53 -0
  13. data/app/components/better_ui/application/card_container_component.html.erb +8 -0
  14. data/app/components/better_ui/application/card_container_component.rb +14 -0
  15. data/app/components/better_ui/application/header_component.html.erb +88 -0
  16. data/app/components/better_ui/application/header_component.rb +188 -0
  17. data/app/components/better_ui/application/navbar_component.html.erb +294 -0
  18. data/app/components/better_ui/application/navbar_component.rb +249 -0
  19. data/app/components/better_ui/application/sidebar_component.html.erb +207 -0
  20. data/app/components/better_ui/application/sidebar_component.rb +318 -0
  21. data/app/components/better_ui/application/toast_component.html.erb +35 -0
  22. data/app/components/better_ui/application/toast_component.rb +223 -0
  23. data/app/components/better_ui/general/avatar_component.html.erb +19 -0
  24. data/app/components/better_ui/general/avatar_component.rb +171 -0
  25. data/app/components/better_ui/general/badge_component.html.erb +19 -0
  26. data/app/components/better_ui/general/badge_component.rb +123 -0
  27. data/app/components/better_ui/general/breadcrumb_component.html.erb +15 -0
  28. data/app/components/better_ui/general/breadcrumb_component.rb +130 -0
  29. data/app/components/better_ui/general/button_component.html.erb +34 -0
  30. data/app/components/better_ui/general/button_component.rb +162 -0
  31. data/app/components/better_ui/general/heading_component.html.erb +25 -0
  32. data/app/components/better_ui/general/heading_component.rb +148 -0
  33. data/app/components/better_ui/general/icon_component.html.erb +2 -0
  34. data/app/components/better_ui/general/icon_component.rb +100 -0
  35. data/app/components/better_ui/general/link_component.html.erb +17 -0
  36. data/app/components/better_ui/general/link_component.rb +132 -0
  37. data/app/components/better_ui/general/panel_component.html.erb +27 -0
  38. data/app/components/better_ui/general/panel_component.rb +103 -0
  39. data/app/components/better_ui/general/spinner_component.html.erb +15 -0
  40. data/app/components/better_ui/general/spinner_component.rb +79 -0
  41. data/app/components/better_ui/general/table_component.html.erb +73 -0
  42. data/app/components/better_ui/general/table_component.rb +167 -0
  43. data/app/components/better_ui/theme_helper.rb +171 -0
  44. data/app/controllers/better_ui/application_controller.rb +1 -0
  45. data/app/controllers/better_ui/docs_controller.rb +18 -25
  46. data/app/views/components/better_ui/general/table/_custom_body_row.html.erb +17 -0
  47. data/app/views/components/better_ui/general/table/_custom_footer_rows.html.erb +17 -0
  48. data/app/views/components/better_ui/general/table/_custom_header_rows.html.erb +12 -0
  49. data/app/views/layouts/component_preview.html.erb +32 -0
  50. data/config/initializers/lookbook.rb +23 -0
  51. data/config/routes.rb +6 -1
  52. data/lib/better_ui/engine.rb +18 -1
  53. data/lib/better_ui/version.rb +1 -1
  54. data/lib/better_ui.rb +4 -0
  55. data/lib/generators/better_ui/stylesheet_generator.rb +96 -0
  56. data/lib/generators/better_ui/templates/README +56 -0
  57. data/lib/generators/better_ui/templates/components/_avatar.scss +151 -0
  58. data/lib/generators/better_ui/templates/components/_badge.scss +142 -0
  59. data/lib/generators/better_ui/templates/components/_breadcrumb.scss +107 -0
  60. data/lib/generators/better_ui/templates/components/_button.scss +106 -0
  61. data/lib/generators/better_ui/templates/components/_card.scss +69 -0
  62. data/lib/generators/better_ui/templates/components/_heading.scss +180 -0
  63. data/lib/generators/better_ui/templates/components/_icon.scss +90 -0
  64. data/lib/generators/better_ui/templates/components/_link.scss +130 -0
  65. data/lib/generators/better_ui/templates/components/_panel.scss +144 -0
  66. data/lib/generators/better_ui/templates/components/_spinner.scss +132 -0
  67. data/lib/generators/better_ui/templates/components/_table.scss +105 -0
  68. data/lib/generators/better_ui/templates/components/_variables.scss +33 -0
  69. data/lib/generators/better_ui/templates/components_stylesheet.scss +66 -0
  70. metadata +135 -10
  71. data/app/helpers/better_ui/application_helper.rb +0 -183
  72. data/app/views/better_ui/docs/component.html.erb +0 -365
  73. data/app/views/better_ui/docs/index.html.erb +0 -100
  74. data/app/views/better_ui/docs/show.html.erb +0 -60
  75. data/app/views/layouts/better_ui/application.html.erb +0 -135
@@ -0,0 +1,148 @@
1
+ module BetterUi
2
+ module General
3
+ class HeadingComponent < ViewComponent::Base
4
+ attr_reader :text, :level, :variant, :size, :align, :classes, :icon, :subtitle, :with_divider
5
+
6
+ # Varianti di colore disponibili
7
+ HEADER_THEME = {
8
+ default: {
9
+ heading: "bui-header-default-heading",
10
+ subtitle: "bui-header-default-subtitle",
11
+ divider: "bui-header-default-divider"
12
+ },
13
+ white: {
14
+ heading: "bui-header-white-heading",
15
+ subtitle: "bui-header-white-subtitle",
16
+ divider: "bui-header-white-divider"
17
+ },
18
+ red: {
19
+ heading: "bui-header-red-heading",
20
+ subtitle: "bui-header-red-subtitle",
21
+ divider: "bui-header-red-divider"
22
+ },
23
+ rose: {
24
+ heading: "bui-header-rose-heading",
25
+ subtitle: "bui-header-rose-subtitle",
26
+ divider: "bui-header-rose-divider"
27
+ },
28
+ orange: {
29
+ heading: "bui-header-orange-heading",
30
+ subtitle: "bui-header-orange-subtitle",
31
+ divider: "bui-header-orange-divider"
32
+ },
33
+ green: {
34
+ heading: "bui-header-green-heading",
35
+ subtitle: "bui-header-green-subtitle",
36
+ divider: "bui-header-green-divider"
37
+ },
38
+ blue: {
39
+ heading: "bui-header-blue-heading",
40
+ subtitle: "bui-header-blue-subtitle",
41
+ divider: "bui-header-blue-divider"
42
+ },
43
+ yellow: {
44
+ heading: "bui-header-yellow-heading",
45
+ subtitle: "bui-header-yellow-subtitle",
46
+ divider: "bui-header-yellow-divider"
47
+ },
48
+ violet: {
49
+ heading: "bui-header-violet-heading",
50
+ subtitle: "bui-header-violet-subtitle",
51
+ divider: "bui-header-violet-divider"
52
+ }
53
+ }
54
+
55
+ # Dimensioni disponibili
56
+ HEADER_SIZES = {
57
+ small: {
58
+ heading: "bui-header-small-heading",
59
+ subtitle: "bui-header-small-subtitle"
60
+ },
61
+ medium: {
62
+ heading: "bui-header-medium-heading",
63
+ subtitle: "bui-header-medium-subtitle"
64
+ },
65
+ large: {
66
+ heading: "bui-header-large-heading",
67
+ subtitle: "bui-header-large-subtitle"
68
+ }
69
+ }
70
+
71
+ # Allineamenti disponibili
72
+ HEADER_ALIGNMENTS = {
73
+ left: "bui-header-left",
74
+ center: "bui-header-center",
75
+ right: "bui-header-right"
76
+ }
77
+
78
+ # Inizializzazione del componente
79
+ def initialize(
80
+ text: nil,
81
+ level: 2,
82
+ variant: :default,
83
+ size: :medium,
84
+ align: :left,
85
+ classes: nil,
86
+ icon: nil,
87
+ subtitle: nil,
88
+ with_divider: false
89
+ )
90
+ @text = text
91
+ @level = level.to_i.clamp(1, 6)
92
+ @variant = variant.to_sym
93
+ @size = size.to_sym
94
+ @align = align.to_sym
95
+ @classes = classes
96
+ @icon = icon
97
+ @subtitle = subtitle
98
+ @with_divider = with_divider
99
+ end
100
+
101
+ # Genera le classi per l'heading
102
+ def heading_classes
103
+ [
104
+ HEADER_THEME.fetch(@variant, HEADER_THEME[:default])[:heading],
105
+ HEADER_SIZES.fetch(@size, HEADER_SIZES[:medium])[:heading],
106
+ HEADER_ALIGNMENTS.fetch(@align, HEADER_ALIGNMENTS[:left]),
107
+ "bui-header-heading-base",
108
+ @classes
109
+ ].compact.join(" ")
110
+ end
111
+
112
+ # Genera le classi per il sottotitolo
113
+ def subtitle_classes
114
+ [
115
+ HEADER_THEME.fetch(@variant, HEADER_THEME[:default])[:subtitle],
116
+ HEADER_SIZES.fetch(@size, HEADER_SIZES[:medium])[:subtitle],
117
+ HEADER_ALIGNMENTS.fetch(@align, HEADER_ALIGNMENTS[:left]),
118
+ "bui-header-subtitle-base"
119
+ ].compact.join(" ")
120
+ end
121
+
122
+ # Genera le classi per il divisore
123
+ def divider_classes
124
+ [
125
+ "bui-header-divider-base",
126
+ HEADER_THEME.fetch(@variant, HEADER_THEME[:default])[:divider]
127
+ ].compact.join(" ")
128
+ end
129
+
130
+ # Genera le classi per il container
131
+ def container_classes
132
+ @with_divider ? "bui-header-container-with-divider" : "bui-header-container-base"
133
+ end
134
+
135
+ # Helper per determinare la dimensione dell'icona in base alla dimensione dell'heading
136
+ def get_icon_size
137
+ case @size
138
+ when :large
139
+ :medium
140
+ when :small
141
+ :xsmall
142
+ else
143
+ :small
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,2 @@
1
+ <%# Template per l'icona %>
2
+ <i class="bui-icon-base <%= combined_classes %>" aria-hidden="true"></i>
@@ -0,0 +1,100 @@
1
+ module BetterUi
2
+ module General
3
+ class IconComponent < ViewComponent::Base
4
+ # Dimensioni disponibili
5
+ ICON_SIZES = {
6
+ xsmall: "bui-icon-xsmall",
7
+ small: "bui-icon-small",
8
+ medium: "bui-icon-medium",
9
+ large: "bui-icon-large",
10
+ xlarge: "bui-icon-xlarge"
11
+ }
12
+
13
+ # Stili disponibili
14
+ ICON_STYLES = {
15
+ solid: "bui-icon-solid",
16
+ regular: "bui-icon-regular",
17
+ light: "bui-icon-light",
18
+ brands: "bui-icon-brands",
19
+ duotone: "bui-icon-duotone"
20
+ }
21
+
22
+ # Inizializzazione del componente
23
+ def initialize(
24
+ name:,
25
+ size: :medium,
26
+ style: :solid,
27
+ fixed_width: false,
28
+ spin: false,
29
+ pulse: false,
30
+ border: false,
31
+ flip: nil,
32
+ rotation: nil,
33
+ classes: nil
34
+ )
35
+ @name = name.to_s.gsub('_', '-') # Convertiamo da snake_case a kebab-case per Font Awesome
36
+ @size = size.to_sym
37
+ @style = style.to_sym
38
+ @fixed_width = fixed_width
39
+ @spin = spin
40
+ @pulse = pulse
41
+ @border = border
42
+ @flip = flip
43
+ @rotation = rotation
44
+ @classes = classes
45
+ end
46
+
47
+ # Classe CSS per lo stile dell'icona
48
+ def style_class
49
+ ICON_STYLES[@style] || ICON_STYLES[:solid]
50
+ end
51
+
52
+ # Classe CSS per la dimensione
53
+ def size_class
54
+ ICON_SIZES[@size] || ICON_SIZES[:medium]
55
+ end
56
+
57
+ # Classe per rotazione
58
+ def rotation_class
59
+ return "" unless @rotation
60
+ "bui-icon-rotate-#{@rotation}"
61
+ end
62
+
63
+ # Classe per rovesciamento
64
+ def flip_class
65
+ return "" unless @flip
66
+ "bui-icon-flip-#{@flip}"
67
+ end
68
+
69
+ # Classi per animazioni
70
+ def animation_classes
71
+ classes = []
72
+ classes << "bui-icon-spin" if @spin
73
+ classes << "bui-icon-pulse" if @pulse
74
+ classes.join(" ")
75
+ end
76
+
77
+ # Classi per caratteristiche aggiuntive
78
+ def feature_classes
79
+ classes = []
80
+ classes << "bui-icon-fw" if @fixed_width
81
+ classes << "bui-icon-border" if @border
82
+ classes.join(" ")
83
+ end
84
+
85
+ # Combinazione di tutte le classi
86
+ def combined_classes
87
+ [
88
+ style_class,
89
+ "fa-#{@name}", # Questa classe deve rimanere specifica di Font Awesome
90
+ size_class,
91
+ rotation_class,
92
+ flip_class,
93
+ animation_classes,
94
+ feature_classes,
95
+ @classes
96
+ ].reject(&:blank?).join(" ")
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,17 @@
1
+ <% if link? %>
2
+ <%= link_to href, **link_attributes do %>
3
+ <% if render_icon %>
4
+ <span class="bui-link-icon-wrapper"><%= render_icon %></span>
5
+ <% end %>
6
+ <span><%= label %></span>
7
+ <%= content if content.present? %>
8
+ <% end %>
9
+ <% else %>
10
+ <span class="<%= component_classes %>" <%= "aria-current='page'" if active? %>>
11
+ <% if render_icon %>
12
+ <span class="bui-link-icon-wrapper"><%= render_icon %></span>
13
+ <% end %>
14
+ <span><%= label %></span>
15
+ <%= content if content.present? %>
16
+ </span>
17
+ <% end %>
@@ -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
@@ -0,0 +1,27 @@
1
+ <div class="<%= panel_classes %>">
2
+ <% if @header.present? %>
3
+ <div class="<%= header_classes %>">
4
+ <%= raw @header %>
5
+ </div>
6
+ <% elsif @title.present? %>
7
+ <div class="<%= header_classes %>">
8
+ <div class="<%= title_classes %>"><%= @title %></div>
9
+ </div>
10
+ <% end %>
11
+
12
+ <% if @body.present? %>
13
+ <div class="<%= body_classes %>">
14
+ <%= raw @body %>
15
+ </div>
16
+ <% elsif content.present? %>
17
+ <div class="<%= body_classes %>">
18
+ <%= content %>
19
+ </div>
20
+ <% end %>
21
+
22
+ <% if @footer.present? %>
23
+ <div class="<%= footer_classes %>">
24
+ <%= raw @footer %>
25
+ </div>
26
+ <% end %>
27
+ </div>
@@ -0,0 +1,103 @@
1
+ module BetterUi
2
+ module General
3
+ class PanelComponent < ViewComponent::Base
4
+ attr_reader :header, :footer, :body, :title, :padding, :variant, :rounded
5
+
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'
12
+ }.freeze
13
+
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)
50
+ @title = title
51
+ @body = body
52
+ @header = header
53
+ @footer = footer
54
+ @padding = padding.to_sym
55
+ @variant = variant.to_sym
56
+ @rounded = rounded.to_sym
57
+ end
58
+
59
+ def panel_classes
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]
68
+ end
69
+
70
+ def header_classes
71
+ [
72
+ 'bui-panel-header',
73
+ PANEL_HEADER_THEME[@variant] || PANEL_HEADER_THEME[:default],
74
+ PANEL_PADDING.fetch(@padding, PANEL_PADDING[:medium])
75
+ ].compact.join(' ')
76
+ end
77
+
78
+ def body_classes
79
+ [
80
+ 'bui-panel-body',
81
+ 'bui-panel-body-content',
82
+ PANEL_PADDING.fetch(@padding, PANEL_PADDING[:medium])
83
+ ].compact.join(' ')
84
+ end
85
+
86
+ def footer_classes
87
+ [
88
+ 'bui-panel-footer',
89
+ PANEL_FOOTER_THEME[@variant] || PANEL_FOOTER_THEME[:default],
90
+ PANEL_PADDING.fetch(@padding, PANEL_PADDING[:medium])
91
+ ].compact.join(' ')
92
+ end
93
+
94
+ def title_classes
95
+ 'bui-panel-title'
96
+ end
97
+
98
+ def render?
99
+ @body.present? || @header.present? || @footer.present? || content.present?
100
+ end
101
+ end
102
+ end
103
+ end
@@ -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
@@ -0,0 +1,73 @@
1
+ <div class="<%= table_container_classes %>">
2
+ <table class="<%= table_classes %>">
3
+ <% if caption.present? %>
4
+ <caption class="<%= caption_classes %>">
5
+ <%= caption %>
6
+ </caption>
7
+ <% end %>
8
+
9
+ <thead class="<%= thead_classes %>">
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 %>
24
+ </thead>
25
+
26
+ <tbody class="<%= tbody_classes %>">
27
+ <% data.each_with_index do |row, index| %>
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 %>
50
+ <% end %>
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 %>
72
+ </table>
73
+ </div>