better_ui_tmp 0.5.1

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 (143) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +211 -0
  4. data/Rakefile +8 -0
  5. data/app/assets/builds/application.js +1 -0
  6. data/app/assets/builds/better_ui.css +1 -0
  7. data/app/assets/stylesheets/better_ui.scss +3 -0
  8. data/app/components/better_ui/application/main/component.html.erb +5 -0
  9. data/app/components/better_ui/application/main/component.rb +99 -0
  10. data/app/components/better_ui/application/navbar/component.html.erb +219 -0
  11. data/app/components/better_ui/application/navbar/component.rb +148 -0
  12. data/app/components/better_ui/application/sidebar/component.html.erb +184 -0
  13. data/app/components/better_ui/application/sidebar/component.rb +129 -0
  14. data/app/components/better_ui/general/alert/component.html.erb +32 -0
  15. data/app/components/better_ui/general/alert/component.rb +242 -0
  16. data/app/components/better_ui/general/avatar/component.html.erb +20 -0
  17. data/app/components/better_ui/general/avatar/component.rb +301 -0
  18. data/app/components/better_ui/general/badge/component.html.erb +23 -0
  19. data/app/components/better_ui/general/badge/component.rb +248 -0
  20. data/app/components/better_ui/general/breadcrumb/component.html.erb +15 -0
  21. data/app/components/better_ui/general/breadcrumb/component.rb +186 -0
  22. data/app/components/better_ui/general/button/component.html.erb +34 -0
  23. data/app/components/better_ui/general/button/component.rb +214 -0
  24. data/app/components/better_ui/general/card/component.html.erb +21 -0
  25. data/app/components/better_ui/general/card/component.rb +37 -0
  26. data/app/components/better_ui/general/container/component.html.erb +8 -0
  27. data/app/components/better_ui/general/container/component.rb +158 -0
  28. data/app/components/better_ui/general/divider/component.html.erb +10 -0
  29. data/app/components/better_ui/general/divider/component.rb +226 -0
  30. data/app/components/better_ui/general/heading/component.html.erb +22 -0
  31. data/app/components/better_ui/general/heading/component.rb +257 -0
  32. data/app/components/better_ui/general/icon/component.html.erb +1 -0
  33. data/app/components/better_ui/general/icon/component.rb +222 -0
  34. data/app/components/better_ui/general/link/component.html.erb +18 -0
  35. data/app/components/better_ui/general/link/component.rb +255 -0
  36. data/app/components/better_ui/general/panel/component.html.erb +28 -0
  37. data/app/components/better_ui/general/panel/component.rb +249 -0
  38. data/app/components/better_ui/general/progress/component.html.erb +11 -0
  39. data/app/components/better_ui/general/progress/component.rb +160 -0
  40. data/app/components/better_ui/general/spinner/component.html.erb +35 -0
  41. data/app/components/better_ui/general/spinner/component.rb +93 -0
  42. data/app/components/better_ui/general/table/component.html.erb +5 -0
  43. data/app/components/better_ui/general/table/component.rb +217 -0
  44. data/app/components/better_ui/general/table/tbody_component.html.erb +3 -0
  45. data/app/components/better_ui/general/table/tbody_component.rb +30 -0
  46. data/app/components/better_ui/general/table/td_component.html.erb +3 -0
  47. data/app/components/better_ui/general/table/td_component.rb +44 -0
  48. data/app/components/better_ui/general/table/tfoot_component.html.erb +3 -0
  49. data/app/components/better_ui/general/table/tfoot_component.rb +28 -0
  50. data/app/components/better_ui/general/table/th_component.html.erb +6 -0
  51. data/app/components/better_ui/general/table/th_component.rb +51 -0
  52. data/app/components/better_ui/general/table/thead_component.html.erb +3 -0
  53. data/app/components/better_ui/general/table/thead_component.rb +28 -0
  54. data/app/components/better_ui/general/table/tr_component.html.erb +3 -0
  55. data/app/components/better_ui/general/table/tr_component.rb +30 -0
  56. data/app/components/better_ui/general/tag/component.html.erb +3 -0
  57. data/app/components/better_ui/general/tag/component.rb +104 -0
  58. data/app/components/better_ui/general/tooltip/component.html.erb +7 -0
  59. data/app/components/better_ui/general/tooltip/component.rb +239 -0
  60. data/app/controllers/better_ui/application_controller.rb +5 -0
  61. data/app/helpers/better_ui/application/components/main/main_helper.rb +42 -0
  62. data/app/helpers/better_ui/application/components/main.rb +13 -0
  63. data/app/helpers/better_ui/application/components/navbar/navbar_helper.rb +51 -0
  64. data/app/helpers/better_ui/application/components/navbar.rb +13 -0
  65. data/app/helpers/better_ui/application/components/sidebar/sidebar_helper.rb +51 -0
  66. data/app/helpers/better_ui/application/components/sidebar.rb +13 -0
  67. data/app/helpers/better_ui/application_helper.rb +10 -0
  68. data/app/helpers/better_ui/form_helper.rb +5 -0
  69. data/app/helpers/better_ui/general/components/alert/alert_helper.rb +29 -0
  70. data/app/helpers/better_ui/general/components/alert.rb +13 -0
  71. data/app/helpers/better_ui/general/components/avatar/avatar_helper.rb +29 -0
  72. data/app/helpers/better_ui/general/components/avatar.rb +13 -0
  73. data/app/helpers/better_ui/general/components/badge/badge_helper.rb +53 -0
  74. data/app/helpers/better_ui/general/components/badge.rb +13 -0
  75. data/app/helpers/better_ui/general/components/breadcrumb/breadcrumb_helper.rb +37 -0
  76. data/app/helpers/better_ui/general/components/breadcrumb.rb +13 -0
  77. data/app/helpers/better_ui/general/components/button/button_helper.rb +65 -0
  78. data/app/helpers/better_ui/general/components/button.rb +13 -0
  79. data/app/helpers/better_ui/general/components/card/card_helper.rb +37 -0
  80. data/app/helpers/better_ui/general/components/card.rb +13 -0
  81. data/app/helpers/better_ui/general/components/container/container_helper.rb +60 -0
  82. data/app/helpers/better_ui/general/components/container.rb +13 -0
  83. data/app/helpers/better_ui/general/components/divider/divider_helper.rb +63 -0
  84. data/app/helpers/better_ui/general/components/divider.rb +13 -0
  85. data/app/helpers/better_ui/general/components/heading/heading_helper.rb +72 -0
  86. data/app/helpers/better_ui/general/components/heading.rb +13 -0
  87. data/app/helpers/better_ui/general/components/icon/icon_helper.rb +16 -0
  88. data/app/helpers/better_ui/general/components/icon.rb +13 -0
  89. data/app/helpers/better_ui/general/components/link/link_helper.rb +89 -0
  90. data/app/helpers/better_ui/general/components/link.rb +13 -0
  91. data/app/helpers/better_ui/general/components/panel/panel_helper.rb +83 -0
  92. data/app/helpers/better_ui/general/components/panel.rb +13 -0
  93. data/app/helpers/better_ui/general/components/progress/progress_helper.rb +53 -0
  94. data/app/helpers/better_ui/general/components/progress.rb +11 -0
  95. data/app/helpers/better_ui/general/components/spinner/spinner_helper.rb +17 -0
  96. data/app/helpers/better_ui/general/components/spinner.rb +10 -0
  97. data/app/helpers/better_ui/general/components/table/table_helper.rb +13 -0
  98. data/app/helpers/better_ui/general/components/table/tbody_helper.rb +13 -0
  99. data/app/helpers/better_ui/general/components/table/td_helper.rb +19 -0
  100. data/app/helpers/better_ui/general/components/table/tfoot_helper.rb +13 -0
  101. data/app/helpers/better_ui/general/components/table/th_helper.rb +19 -0
  102. data/app/helpers/better_ui/general/components/table/thead_helper.rb +13 -0
  103. data/app/helpers/better_ui/general/components/table/tr_helper.rb +13 -0
  104. data/app/helpers/better_ui/general/components/table.rb +25 -0
  105. data/app/helpers/better_ui/general/components/tag/tag_helper.rb +26 -0
  106. data/app/helpers/better_ui/general/components/tag.rb +15 -0
  107. data/app/helpers/better_ui/general/components/tooltip/tooltip_helper.rb +60 -0
  108. data/app/helpers/better_ui/general/components/tooltip.rb +13 -0
  109. data/app/helpers/better_ui/general_helper.rb +24 -0
  110. data/app/helpers/better_ui_helper.rb +16 -0
  111. data/app/javascript/application.js +1 -0
  112. data/app/jobs/better_ui/application_job.rb +4 -0
  113. data/app/mailers/better_ui/application_mailer.rb +6 -0
  114. data/app/models/better_ui/application_record.rb +5 -0
  115. data/app/views/components/better_ui/general/table/_custom_body_row.html.erb +17 -0
  116. data/app/views/components/better_ui/general/table/_custom_footer_rows.html.erb +17 -0
  117. data/app/views/components/better_ui/general/table/_custom_header_rows.html.erb +12 -0
  118. data/app/views/layouts/component_preview.html.erb +32 -0
  119. data/config/initializers/lookbook.rb +23 -0
  120. data/config/routes.rb +3 -0
  121. data/lib/better_ui/engine.rb +109 -0
  122. data/lib/better_ui/version.rb +3 -0
  123. data/lib/better_ui.rb +37 -0
  124. data/lib/generators/better_ui/install_generator.rb +103 -0
  125. data/lib/generators/better_ui/stylesheet_generator.rb +159 -0
  126. data/lib/generators/better_ui/templates/components/_avatar.scss +200 -0
  127. data/lib/generators/better_ui/templates/components/_badge.scss +154 -0
  128. data/lib/generators/better_ui/templates/components/_breadcrumb.scss +106 -0
  129. data/lib/generators/better_ui/templates/components/_button.scss +109 -0
  130. data/lib/generators/better_ui/templates/components/_card.scss +60 -0
  131. data/lib/generators/better_ui/templates/components/_heading.scss +81 -0
  132. data/lib/generators/better_ui/templates/components/_icon.scss +134 -0
  133. data/lib/generators/better_ui/templates/components/_index.scss +17 -0
  134. data/lib/generators/better_ui/templates/components/_link.scss +100 -0
  135. data/lib/generators/better_ui/templates/components/_panel.scss +104 -0
  136. data/lib/generators/better_ui/templates/components/_spinner.scss +129 -0
  137. data/lib/generators/better_ui/templates/components/_table.scss +156 -0
  138. data/lib/generators/better_ui/templates/components/_variables.scss +0 -0
  139. data/lib/generators/better_ui/templates/components_stylesheet.scss +35 -0
  140. data/lib/generators/better_ui/templates/index.scss +18 -0
  141. data/lib/generators/better_ui/templates/initializer.rb +41 -0
  142. data/lib/tasks/better_ui_tasks.rake +4 -0
  143. metadata +260 -0
@@ -0,0 +1,35 @@
1
+ <div <%= tag.attributes(container_attributes) %>>
2
+ <div class="flex-shrink-0">
3
+ <svg class="<%= svg_classes %> animate-spin" viewBox="0 0 24 24" fill="none">
4
+ <circle
5
+ cx="12"
6
+ cy="12"
7
+ r="10"
8
+ stroke="currentColor"
9
+ stroke-width="2"
10
+ stroke-linecap="round"
11
+ stroke-dasharray="32"
12
+ stroke-dashoffset="32">
13
+ <animate
14
+ attributeName="stroke-dasharray"
15
+ dur="2s"
16
+ values="0 32;16 16;0 32;0 32"
17
+ repeatCount="indefinite" />
18
+ <animate
19
+ attributeName="stroke-dashoffset"
20
+ dur="2s"
21
+ values="0;-16;-32;-32"
22
+ repeatCount="indefinite" />
23
+ <animateTransform
24
+ attributeName="transform"
25
+ type="rotate"
26
+ dur="2s"
27
+ values="0 12 12;360 12 12"
28
+ repeatCount="indefinite" />
29
+ </circle>
30
+ </svg>
31
+ </div>
32
+ <% if show_label? %>
33
+ <span class="text-sm font-medium"><%= label %></span>
34
+ <% end %>
35
+ </div>
@@ -0,0 +1,93 @@
1
+ module BetterUi
2
+ module General
3
+ module Spinner
4
+ class Component < ViewComponent::Base
5
+ THEMES = %i[default white red rose orange green blue yellow violet].freeze
6
+ SIZES = %i[small medium large].freeze
7
+ STYLES = %i[default outline].freeze
8
+
9
+ # Classi base sempre presenti
10
+ SPINNER_BASE_CLASSES = "inline-flex items-center gap-2"
11
+
12
+ # Dimensioni SVG con classi Tailwind dirette
13
+ SPINNER_SIZES = {
14
+ small: "w-4 h-4", # 16px
15
+ medium: "w-6 h-6", # 24px
16
+ large: "w-8 h-8" # 32px
17
+ }
18
+
19
+ # Temi colore con classi Tailwind dirette
20
+ SPINNER_THEMES = {
21
+ default: "text-gray-900",
22
+ white: "text-white",
23
+ red: "text-red-500",
24
+ rose: "text-rose-500",
25
+ orange: "text-orange-500",
26
+ green: "text-green-500",
27
+ blue: "text-blue-500",
28
+ yellow: "text-yellow-500",
29
+ violet: "text-violet-500"
30
+ }
31
+
32
+ # Stili con classi Tailwind dirette
33
+ SPINNER_STYLES = {
34
+ default: "",
35
+ outline: "opacity-75"
36
+ }
37
+
38
+ def initialize(theme: :default, size: :medium, style: :default, label: nil, **html_options)
39
+ @theme = theme.to_sym
40
+ @size = size.to_sym
41
+ @style = style.to_sym
42
+ @label = label
43
+ @html_options = html_options
44
+
45
+ validate_options!
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :theme, :size, :style, :label, :html_options
51
+
52
+ def validate_options!
53
+ raise ArgumentError, "Theme deve essere uno di: #{THEMES.join(', ')}" unless THEMES.include?(theme)
54
+ raise ArgumentError, "Size deve essere uno di: #{SIZES.join(', ')}" unless SIZES.include?(size)
55
+ raise ArgumentError, "Style deve essere uno di: #{STYLES.join(', ')}" unless STYLES.include?(style)
56
+ end
57
+
58
+ def combined_classes
59
+ [
60
+ SPINNER_BASE_CLASSES,
61
+ get_spinner_theme_classes,
62
+ get_spinner_style_classes,
63
+ html_options[:class]
64
+ ].compact.join(' ')
65
+ end
66
+
67
+ def get_spinner_theme_classes
68
+ SPINNER_THEMES[theme] || SPINNER_THEMES[:default]
69
+ end
70
+
71
+ def get_spinner_style_classes
72
+ SPINNER_STYLES[style] || SPINNER_STYLES[:default]
73
+ end
74
+
75
+ def get_spinner_size_classes
76
+ SPINNER_SIZES[size] || SPINNER_SIZES[:medium]
77
+ end
78
+
79
+ def container_attributes
80
+ html_options.except(:class).merge(class: combined_classes)
81
+ end
82
+
83
+ def svg_classes
84
+ get_spinner_size_classes
85
+ end
86
+
87
+ def show_label?
88
+ label.present?
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,5 @@
1
+ <div <%= tag.attributes(container_attributes) %>>
2
+ <table <%= tag.attributes(table_attributes) %>>
3
+ <%= content %>
4
+ </table>
5
+ </div>
@@ -0,0 +1,217 @@
1
+ module BetterUi
2
+ module General
3
+ module Table
4
+ class Component < ViewComponent::Base
5
+ attr_reader :data, :headers, :caption, :striped, :hoverable, :bordered, :compact, :minimal, :footer,
6
+ :header_rows_partial, :body_row_partial, :footer_rows_partial, :thead_partial, :tfoot_partial
7
+
8
+ # Classi base sempre presenti
9
+ TABLE_BASE_CLASSES = "w-full table-auto border-collapse"
10
+
11
+ # Temi di colore con classi Tailwind dirette
12
+ TABLE_THEME = {
13
+ default: "bg-gray-50 text-gray-900",
14
+ white: "bg-white text-gray-900",
15
+ red: "bg-red-50 text-red-900",
16
+ rose: "bg-rose-50 text-rose-900",
17
+ orange: "bg-orange-50 text-orange-900",
18
+ green: "bg-green-50 text-green-900",
19
+ blue: "bg-blue-50 text-blue-900",
20
+ yellow: "bg-yellow-50 text-yellow-900",
21
+ violet: "bg-violet-50 text-violet-900"
22
+ }.freeze
23
+
24
+ # Opzioni di bordi arrotondati con classi Tailwind dirette
25
+ TABLE_RADIUS = {
26
+ none: "rounded-none",
27
+ small: "rounded-md",
28
+ medium: "rounded-lg",
29
+ large: "rounded-xl",
30
+ full: "rounded-full"
31
+ }.freeze
32
+
33
+
34
+ # Classi per container
35
+ CONTAINER_BASE_CLASSES = "overflow-x-auto"
36
+
37
+ # Classi per elementi della tabella
38
+ THEAD_CLASSES = "bg-gray-100 border-b border-gray-200"
39
+ TBODY_CLASSES = ""
40
+ TFOOT_CLASSES = "bg-gray-50 border-t border-gray-200"
41
+ TR_CLASSES = "border-b border-gray-100 hover:bg-gray-50"
42
+ TH_CLASSES = "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
43
+ TD_CLASSES = "px-4 py-3 text-sm text-gray-900"
44
+ TF_CLASSES = "px-4 py-3 text-sm font-medium text-gray-900"
45
+ CAPTION_CLASSES = "mb-2 text-sm text-gray-600 text-left"
46
+
47
+ def initialize(
48
+ data: nil,
49
+ headers: nil,
50
+ caption: nil,
51
+ theme: :default,
52
+ radius: :small,
53
+ striped: false,
54
+ hoverable: false,
55
+ bordered: false,
56
+ compact: false,
57
+ minimal: false,
58
+ footer: nil,
59
+ header_rows_partial: nil,
60
+ body_row_partial: nil,
61
+ footer_rows_partial: nil,
62
+ thead_partial: nil,
63
+ tfoot_partial: nil,
64
+ **html_options
65
+ )
66
+ @data = data || []
67
+ @headers = headers
68
+ @caption = caption
69
+ @theme = theme.to_sym
70
+ @radius = radius.to_sym
71
+ # Flag boolean combinabili
72
+ @striped = !!striped
73
+ @hoverable = !!hoverable
74
+ @bordered = !!bordered
75
+ @compact = !!compact
76
+ @minimal = !!minimal
77
+ @footer = footer.is_a?(Array) ? footer : nil
78
+ @header_rows_partial = header_rows_partial
79
+ @body_row_partial = body_row_partial
80
+ @footer_rows_partial = footer_rows_partial
81
+ @thead_partial = thead_partial
82
+ @tfoot_partial = tfoot_partial
83
+ @html_options = html_options
84
+
85
+ validate_params
86
+ end
87
+
88
+ # Combina tutte le classi per la tabella
89
+ def combined_classes
90
+ [
91
+ TABLE_BASE_CLASSES,
92
+ get_theme_class,
93
+ @bordered ? "border border-gray-200" : nil,
94
+ @striped ? "[&_tbody_tr:nth-child(odd)]:bg-gray-50" : nil,
95
+ @hoverable ? "[&_tbody_tr]:hover:bg-gray-50" : nil,
96
+ @compact ? "[&_td]:py-1 [&_th]:py-1" : nil,
97
+ @minimal ? "border-0" : nil,
98
+ @html_options[:class]
99
+ ].compact.join(" ")
100
+ end
101
+
102
+ # Restituisce gli attributi HTML per la tabella
103
+ def table_attributes
104
+ attrs = @html_options.except(:class)
105
+ attrs[:class] = combined_classes
106
+ attrs
107
+ end
108
+
109
+ # Combina le classi per il container
110
+ def table_container_classes
111
+ [
112
+ CONTAINER_BASE_CLASSES,
113
+ get_radius_class
114
+ ].compact.join(" ")
115
+ end
116
+
117
+ # Restituisce gli attributi HTML per il container
118
+ def container_attributes
119
+ {
120
+ class: table_container_classes
121
+ }
122
+ end
123
+
124
+ def get_radius_class
125
+ TABLE_RADIUS[@radius] || TABLE_RADIUS[:small]
126
+ end
127
+
128
+ def get_theme_class
129
+ TABLE_THEME[@theme] || TABLE_THEME[:default]
130
+ end
131
+
132
+
133
+ def caption_classes
134
+ CAPTION_CLASSES
135
+ end
136
+
137
+ def thead_classes
138
+ THEAD_CLASSES
139
+ end
140
+
141
+ def tbody_classes
142
+ TBODY_CLASSES
143
+ end
144
+
145
+ def tfoot_classes
146
+ TFOOT_CLASSES
147
+ end
148
+
149
+ def tr_classes(index)
150
+ TR_CLASSES
151
+ end
152
+
153
+ def th_classes
154
+ TH_CLASSES
155
+ end
156
+
157
+ def td_classes
158
+ TD_CLASSES
159
+ end
160
+
161
+ def tf_classes
162
+ TF_CLASSES
163
+ end
164
+
165
+ def headers_for_display
166
+ return @headers if @headers.present?
167
+ return [] if @data.empty?
168
+
169
+ case first_item = @data.first
170
+ when Hash
171
+ first_item.keys
172
+ when -> (item) { item.respond_to?(:attributes) }
173
+ first_item.attributes.keys - %w[id created_at updated_at]
174
+ else
175
+ []
176
+ end
177
+ end
178
+
179
+ # Ottiene il valore di una cella in modo consistente
180
+ def get_cell_value(row, header)
181
+ if row.is_a?(Hash)
182
+ row[header.to_s] || row[header.to_sym] || "—"
183
+ elsif row.respond_to?(header.to_sym)
184
+ row.send(header.to_sym)
185
+ elsif row.is_a?(Array) && headers_for_display.index(header)
186
+ row[headers_for_display.index(header)] || "—"
187
+ else
188
+ "—"
189
+ end
190
+ end
191
+
192
+ def render?
193
+ true
194
+ end
195
+
196
+ private
197
+
198
+ def validate_params
199
+ validate_theme
200
+ validate_radius
201
+ end
202
+
203
+ def validate_theme
204
+ unless TABLE_THEME.keys.include?(@theme)
205
+ raise ArgumentError, "Il tema deve essere uno tra: #{TABLE_THEME.keys.join(', ')}"
206
+ end
207
+ end
208
+
209
+ def validate_radius
210
+ unless TABLE_RADIUS.keys.include?(@radius)
211
+ raise ArgumentError, "Il radius deve essere uno tra: #{TABLE_RADIUS.keys.join(', ')}"
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,3 @@
1
+ <tbody <%= tag.attributes(tbody_attributes) %>>
2
+ <%= content %>
3
+ </tbody>
@@ -0,0 +1,30 @@
1
+ module BetterUi
2
+ module General
3
+ module Table
4
+ class TbodyComponent < ViewComponent::Base
5
+ attr_reader :theme, :striped, :hoverable
6
+
7
+ def initialize(theme: :default, striped: false, hoverable: false, **html_options)
8
+ @theme = theme.to_sym
9
+ @striped = !!striped
10
+ @hoverable = !!hoverable
11
+ @html_options = html_options
12
+ end
13
+
14
+ def tbody_classes
15
+ [
16
+ @striped ? "[&_tr:nth-child(odd)]:bg-gray-50" : nil,
17
+ @hoverable ? "[&_tr]:hover:bg-gray-50" : nil,
18
+ @html_options[:class]
19
+ ].compact.join(" ")
20
+ end
21
+
22
+ def tbody_attributes
23
+ attrs = @html_options.except(:class)
24
+ attrs[:class] = tbody_classes
25
+ attrs
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ <td <%= tag.attributes(td_attributes) %>>
2
+ <%= content %>
3
+ </td>
@@ -0,0 +1,44 @@
1
+ module BetterUi
2
+ module General
3
+ module Table
4
+ class TdComponent < ViewComponent::Base
5
+ attr_reader :theme, :align, :compact
6
+
7
+ def initialize(theme: :default, align: :left, compact: false, **html_options)
8
+ @theme = theme.to_sym
9
+ @align = align.to_sym
10
+ @compact = !!compact
11
+ @html_options = html_options
12
+ end
13
+
14
+ def td_classes
15
+ [
16
+ "px-4 py-3 text-sm text-gray-900",
17
+ alignment_class,
18
+ @compact ? "py-1" : nil,
19
+ @html_options[:class]
20
+ ].compact.join(" ")
21
+ end
22
+
23
+ def td_attributes
24
+ attrs = @html_options.except(:class)
25
+ attrs[:class] = td_classes
26
+ attrs
27
+ end
28
+
29
+ private
30
+
31
+ def alignment_class
32
+ case @align
33
+ when :center
34
+ "text-center"
35
+ when :right
36
+ "text-right"
37
+ else
38
+ "text-left"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ <tfoot <%= tag.attributes(tfoot_attributes) %>>
2
+ <%= content %>
3
+ </tfoot>
@@ -0,0 +1,28 @@
1
+ module BetterUi
2
+ module General
3
+ module Table
4
+ class TfootComponent < ViewComponent::Base
5
+ attr_reader :theme, :bordered
6
+
7
+ def initialize(theme: :default, bordered: false, **html_options)
8
+ @theme = theme.to_sym
9
+ @bordered = !!bordered
10
+ @html_options = html_options
11
+ end
12
+
13
+ def tfoot_classes
14
+ [
15
+ "bg-gray-50 border-t border-gray-200",
16
+ @html_options[:class]
17
+ ].compact.join(" ")
18
+ end
19
+
20
+ def tfoot_attributes
21
+ attrs = @html_options.except(:class)
22
+ attrs[:class] = tfoot_classes
23
+ attrs
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ <th <%= tag.attributes(th_attributes) %>>
2
+ <%= content %>
3
+ <% if @sortable %>
4
+ <span class="ml-1 text-gray-400"><%= sort_icon %></span>
5
+ <% end %>
6
+ </th>
@@ -0,0 +1,51 @@
1
+ module BetterUi
2
+ module General
3
+ module Table
4
+ class ThComponent < ViewComponent::Base
5
+ attr_reader :theme, :sortable, :sorted, :sort_direction, :scope
6
+
7
+ def initialize(
8
+ theme: :default,
9
+ sortable: false,
10
+ sorted: false,
11
+ sort_direction: :asc,
12
+ scope: "col",
13
+ **html_options
14
+ )
15
+ @theme = theme.to_sym
16
+ @sortable = !!sortable
17
+ @sorted = !!sorted
18
+ @sort_direction = sort_direction.to_sym
19
+ @scope = scope
20
+ @html_options = html_options
21
+ end
22
+
23
+ def th_classes
24
+ [
25
+ "px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider",
26
+ @sortable ? "cursor-pointer hover:bg-gray-200" : nil,
27
+ @sorted ? "bg-gray-200" : nil,
28
+ @html_options[:class]
29
+ ].compact.join(" ")
30
+ end
31
+
32
+ def th_attributes
33
+ attrs = @html_options.except(:class)
34
+ attrs[:class] = th_classes
35
+ attrs[:scope] = @scope
36
+ attrs
37
+ end
38
+
39
+ def sort_icon
40
+ return unless @sortable
41
+
42
+ if @sorted
43
+ @sort_direction == :asc ? "↑" : "↓"
44
+ else
45
+ "↕"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ <thead <%= tag.attributes(thead_attributes) %>>
2
+ <%= content %>
3
+ </thead>
@@ -0,0 +1,28 @@
1
+ module BetterUi
2
+ module General
3
+ module Table
4
+ class TheadComponent < ViewComponent::Base
5
+ attr_reader :theme, :bordered
6
+
7
+ def initialize(theme: :default, bordered: false, **html_options)
8
+ @theme = theme.to_sym
9
+ @bordered = !!bordered
10
+ @html_options = html_options
11
+ end
12
+
13
+ def thead_classes
14
+ [
15
+ "bg-gray-100 border-b border-gray-200",
16
+ @html_options[:class]
17
+ ].compact.join(" ")
18
+ end
19
+
20
+ def thead_attributes
21
+ attrs = @html_options.except(:class)
22
+ attrs[:class] = thead_classes
23
+ attrs
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ <tr <%= tag.attributes(tr_attributes) %>>
2
+ <%= content %>
3
+ </tr>
@@ -0,0 +1,30 @@
1
+ module BetterUi
2
+ module General
3
+ module Table
4
+ class TrComponent < ViewComponent::Base
5
+ attr_reader :theme, :highlighted, :striped_index
6
+
7
+ def initialize(theme: :default, highlighted: false, striped_index: nil, **html_options)
8
+ @theme = theme.to_sym
9
+ @highlighted = !!highlighted
10
+ @striped_index = striped_index
11
+ @html_options = html_options
12
+ end
13
+
14
+ def tr_classes
15
+ [
16
+ "border-b border-gray-100",
17
+ @highlighted ? "bg-blue-50" : nil,
18
+ @html_options[:class]
19
+ ].compact.join(" ")
20
+ end
21
+
22
+ def tr_attributes
23
+ attrs = @html_options.except(:class)
24
+ attrs[:class] = tr_classes
25
+ attrs
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ <span class="<%= tag_classes %>">
2
+ <%= text %>
3
+ </span>