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,167 @@
1
+ module BetterUi
2
+ module General
3
+ class TableComponent < ViewComponent::Base
4
+ attr_reader :data, :headers, :caption, :striped, :hoverable, :bordered, :compact, :classes, :variant, :rounded, :footer,
5
+ :header_rows_partial, :body_row_partial, :footer_rows_partial
6
+
7
+ # Costanti per configurazione stili
8
+ TABLE_THEMES = {
9
+ default: { base: "bui-table-default", border: "bui-table-border-default", bg: "bui-table-bg-default", text: "bui-table-text-default" },
10
+ white: { base: "bui-table-white", border: "bui-table-border-white", bg: "bui-table-bg-white", text: "bui-table-text-white" },
11
+ red: { base: "bui-table-red", border: "bui-table-border-red", bg: "bui-table-bg-red", text: "bui-table-text-red" },
12
+ rose: { base: "bui-table-rose", border: "bui-table-border-rose", bg: "bui-table-bg-rose", text: "bui-table-text-rose" },
13
+ orange: { base: "bui-table-orange", border: "bui-table-border-orange", bg: "bui-table-bg-orange", text: "bui-table-text-orange" },
14
+ green: { base: "bui-table-green", border: "bui-table-border-green", bg: "bui-table-bg-green", text: "bui-table-text-green" },
15
+ blue: { base: "bui-table-blue", border: "bui-table-border-blue", bg: "bui-table-bg-blue", text: "bui-table-text-blue" },
16
+ yellow: { base: "bui-table-yellow", border: "bui-table-border-yellow", bg: "bui-table-bg-yellow", text: "bui-table-text-yellow" },
17
+ violet: { base: "bui-table-violet", border: "bui-table-border-violet", bg: "bui-table-bg-violet", text: "bui-table-text-violet" }
18
+ }.freeze
19
+
20
+ # Opzioni di bordi arrotondati standardizzati
21
+ TABLE_RADIUS = {
22
+ none: "bui-table-radius-none",
23
+ small: "bui-table-radius-small",
24
+ medium: "bui-table-radius-medium",
25
+ large: "bui-table-radius-large",
26
+ full: "bui-table-radius-full"
27
+ }.freeze
28
+
29
+ def initialize(data:, headers: nil, caption: nil, striped: false, hoverable: false, bordered: true, compact: false, classes: nil, variant: :default, rounded: :small, footer: nil, header_rows_partial: nil, body_row_partial: nil, footer_rows_partial: nil)
30
+ @data = data || []
31
+ @headers = headers
32
+ @caption = caption
33
+ @striped = !!striped
34
+ @hoverable = !!hoverable
35
+ @bordered = !!bordered
36
+ @compact = !!compact
37
+ @classes = classes
38
+ @variant = (TABLE_THEMES.key?(variant.to_sym) ? variant.to_sym : :default)
39
+ @rounded = (TABLE_RADIUS.key?(rounded.to_sym) ? rounded.to_sym : :small)
40
+ @footer = footer.is_a?(Array) ? footer : nil # Valida che sia un array
41
+ @header_rows_partial = header_rows_partial
42
+ @body_row_partial = body_row_partial
43
+ @footer_rows_partial = footer_rows_partial
44
+ end
45
+
46
+ def table_classes
47
+ [
48
+ "bui-table-base",
49
+ @bordered ? "bui-table-bordered" : nil,
50
+ @classes
51
+ ].compact.join(" ")
52
+ end
53
+
54
+ def table_container_classes
55
+ [
56
+ "bui-table-container",
57
+ get_border_radius_class
58
+ ].compact.join(" ")
59
+ end
60
+
61
+ def get_border_radius_class
62
+ TABLE_RADIUS[@rounded] || TABLE_RADIUS[:small]
63
+ end
64
+
65
+ def caption_classes
66
+ [
67
+ "bui-table-caption",
68
+ get_theme_bg_color,
69
+ get_theme_text_class,
70
+ @bordered ? "bui-table-caption-bordered #{get_theme_border_color}" : nil
71
+ ].compact.join(" ")
72
+ end
73
+
74
+ def thead_classes
75
+ "bui-table-header"
76
+ end
77
+
78
+ def tbody_classes
79
+ @striped ? "bui-table-row-striped" : nil
80
+ end
81
+
82
+ def tfoot_classes
83
+ "bui-table-footer"
84
+ end
85
+
86
+ def tr_classes(index)
87
+ [
88
+ @hoverable ? "bui-table-row-hover" : nil,
89
+ @striped ? nil : (index.odd? ? "bui-table-row-alternate" : nil)
90
+ ].compact.join(" ")
91
+ end
92
+
93
+ def th_classes
94
+ [
95
+ @compact ? "bui-table-cell-compact" : "bui-table-cell",
96
+ get_theme_bg_color,
97
+ get_theme_text_class,
98
+ @bordered ? "bui-table-cell-bordered #{get_theme_border_color}" : nil
99
+ ].compact.join(" ")
100
+ end
101
+
102
+ def td_classes
103
+ [
104
+ @compact ? "bui-table-cell-compact" : "bui-table-cell",
105
+ @bordered ? "bui-table-cell-bordered #{get_theme_border_color}" : nil
106
+ ].compact.join(" ")
107
+ end
108
+
109
+ def tf_classes
110
+ [
111
+ @compact ? "bui-table-cell-compact" : "bui-table-cell",
112
+ "bui-table-footer-cell",
113
+ get_theme_bg_color,
114
+ get_theme_text_class,
115
+ @bordered ? "bui-table-cell-bordered #{get_theme_border_color}" : nil
116
+ ].compact.join(" ")
117
+ end
118
+
119
+ def get_theme_base_class
120
+ TABLE_THEMES[@variant][:base] || TABLE_THEMES[:default][:base]
121
+ end
122
+
123
+ def get_theme_border_color
124
+ TABLE_THEMES[@variant][:border] || TABLE_THEMES[:default][:border]
125
+ end
126
+
127
+ def get_theme_bg_color
128
+ TABLE_THEMES[@variant][:bg] || TABLE_THEMES[:default][:bg]
129
+ end
130
+
131
+ def get_theme_text_class
132
+ TABLE_THEMES[@variant][:text] || TABLE_THEMES[:default][:text]
133
+ end
134
+
135
+ def headers_for_display
136
+ return @headers if @headers.present?
137
+ return [] if @data.empty?
138
+
139
+ case first_item = @data.first
140
+ when Hash
141
+ first_item.keys
142
+ when -> (item) { item.respond_to?(:attributes) }
143
+ first_item.attributes.keys - %w[id created_at updated_at]
144
+ else
145
+ []
146
+ end
147
+ end
148
+
149
+ # Ottiene il valore di una cella in modo consistente
150
+ def get_cell_value(row, header)
151
+ if row.is_a?(Hash)
152
+ row[header.to_s] || row[header.to_sym] || "—"
153
+ elsif row.respond_to?(header.to_sym)
154
+ row.send(header.to_sym)
155
+ elsif row.is_a?(Array) && headers_for_display.index(header)
156
+ row[headers_for_display.index(header)] || "—"
157
+ else
158
+ "—"
159
+ end
160
+ end
161
+
162
+ def render?
163
+ @data.present?
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module ThemeHelper
5
+ # Colori principali del tema
6
+ THEME_COLORS = {
7
+ # Varianti cromatiche
8
+ default: {
9
+ bg: 'bg-black',
10
+ text: 'text-white',
11
+ hover: 'hover:bg-gray-900'
12
+ },
13
+ white: {
14
+ bg: 'bg-white',
15
+ text: 'text-black',
16
+ hover: 'hover:bg-gray-100'
17
+ },
18
+ red: {
19
+ bg: 'bg-red-500',
20
+ text: 'text-white',
21
+ hover: 'hover:bg-red-600'
22
+ },
23
+ rose: {
24
+ bg: 'bg-rose-500',
25
+ text: 'text-white',
26
+ hover: 'hover:bg-rose-600'
27
+ },
28
+ orange: {
29
+ bg: 'bg-orange-500',
30
+ text: 'text-white',
31
+ hover: 'hover:bg-orange-600'
32
+ },
33
+ green: {
34
+ bg: 'bg-green-500',
35
+ text: 'text-white',
36
+ hover: 'hover:bg-green-600'
37
+ },
38
+ blue: {
39
+ bg: 'bg-blue-500',
40
+ text: 'text-white',
41
+ hover: 'hover:bg-blue-600'
42
+ },
43
+ yellow: {
44
+ bg: 'bg-yellow-500',
45
+ text: 'text-black',
46
+ hover: 'hover:bg-yellow-600'
47
+ },
48
+ violet: {
49
+ bg: 'bg-violet-500',
50
+ text: 'text-white',
51
+ hover: 'hover:bg-violet-600'
52
+ }
53
+ }
54
+
55
+ # Bordi del tema
56
+ THEME_BORDERS = {
57
+ default: 'border-gray-900',
58
+ white: 'border-gray-200',
59
+ red: 'border-red-600',
60
+ rose: 'border-rose-600',
61
+ orange: 'border-orange-600',
62
+ green: 'border-green-600',
63
+ blue: 'border-blue-600',
64
+ yellow: 'border-yellow-600',
65
+ violet: 'border-violet-600'
66
+ }
67
+
68
+ # NOTA: I valori di border-radius sono ora definiti nelle costanti specifiche di ogni componente
69
+ # per garantire una nomenclatura standardizzata:
70
+ # - none: nessun arrotondamento (rounded-none)
71
+ # - small: arrotondamento piccolo (rounded-md)
72
+ # - medium: arrotondamento medio (rounded-lg)
73
+ # - large: arrotondamento grande (rounded-xl)
74
+ # - full: arrotondamento completo (rounded-full)
75
+
76
+ # Effetti hover
77
+ THEME_HOVER = THEME_COLORS.transform_values { |v| v[:hover] }
78
+
79
+ # Stili base comuni
80
+ COMMON_STYLES = {
81
+ shadow: 'shadow-sm',
82
+ transitions: 'transition-all duration-200'
83
+ }
84
+
85
+ # Stili per elementi di layout
86
+ LAYOUT_STYLES = {
87
+ panel: {
88
+ base: 'border shadow-sm',
89
+ header: 'px-4 py-3 border-b',
90
+ body: 'p-4',
91
+ footer: 'px-4 py-3 border-t'
92
+ },
93
+ table: {
94
+ container: 'overflow-hidden border shadow-sm',
95
+ header: 'bg-gray-50 text-left text-xs uppercase tracking-wider text-gray-500 font-medium',
96
+ row: {
97
+ base: '',
98
+ hover: 'hover:bg-gray-50',
99
+ striped: 'even:bg-white odd:bg-gray-50'
100
+ },
101
+ cell: 'px-4 py-3 text-sm text-gray-700'
102
+ }
103
+ }
104
+
105
+ # Genera classi per un componente basato su variante
106
+ def self.generate_component_classes(component_type, variant, options = {})
107
+ base_classes = []
108
+
109
+ case component_type
110
+ when :button
111
+ base_classes << 'inline-flex items-center justify-center'
112
+ base_classes << 'font-medium'
113
+ base_classes << COMMON_STYLES[:transitions]
114
+
115
+ variant_colors = THEME_COLORS[variant.to_sym] || THEME_COLORS[:default]
116
+
117
+ # Versione piena
118
+ base_classes << variant_colors[:bg]
119
+ base_classes << variant_colors[:text]
120
+ base_classes << THEME_HOVER[variant.to_sym] || THEME_HOVER[:default]
121
+
122
+ # Dimensione
123
+ case options[:size]
124
+ when :sm
125
+ base_classes << 'px-2.5 py-1.5 text-xs'
126
+ when :lg
127
+ base_classes << 'px-6 py-3 text-base'
128
+ else # :md (default)
129
+ base_classes << 'px-4 py-2 text-sm'
130
+ end
131
+
132
+ # Disabilitato
133
+ if options[:disabled]
134
+ base_classes << 'opacity-50 cursor-not-allowed'
135
+ end
136
+
137
+ # Larghezza piena
138
+ if options[:full_width]
139
+ base_classes << 'w-full'
140
+ end
141
+
142
+ when :panel
143
+ base_classes << LAYOUT_STYLES[:panel][:base]
144
+
145
+ variant_colors = THEME_COLORS[variant.to_sym] || THEME_COLORS[:default]
146
+
147
+ base_classes << variant_colors[:bg]
148
+ base_classes << variant_colors[:text]
149
+ base_classes << THEME_BORDERS[variant.to_sym] || THEME_BORDERS[:default]
150
+
151
+ when :table
152
+ base_classes << 'min-w-full'
153
+
154
+ variant_colors = THEME_COLORS[variant.to_sym] || THEME_COLORS[:default]
155
+ base_classes << variant_colors[:bg]
156
+ base_classes << variant_colors[:text]
157
+
158
+ # Bordi
159
+ if options[:bordered]
160
+ base_classes << 'border-collapse'
161
+ base_classes << (THEME_BORDERS[variant.to_sym] || THEME_BORDERS[:default])
162
+ end
163
+
164
+ # Classi personalizzate
165
+ base_classes << options[:classes] if options[:classes].present?
166
+ end
167
+
168
+ base_classes.compact.join(' ')
169
+ end
170
+ end
171
+ end
@@ -1,4 +1,5 @@
1
1
  module BetterUi
2
2
  class ApplicationController < ActionController::Base
3
+ layout 'better_ui/application'
3
4
  end
4
5
  end
@@ -1,41 +1,34 @@
1
1
  module BetterUi
2
2
  class DocsController < ApplicationController
3
3
  def index
4
- @components = [
5
- { name: 'Button', path: better_ui.docs_component_path('button') },
6
- { name: 'Alert', path: better_ui.docs_component_path('alert') },
7
- { name: 'Card', path: better_ui.docs_component_path('card') },
8
- { name: 'Modal', path: better_ui.docs_component_path('modal') },
9
- { name: 'Tabs', path: better_ui.docs_component_path('tabs') },
10
- { name: 'Form Elements', path: better_ui.docs_component_path('form_elements') }
11
- ]
4
+ # Pagina principale della documentazione
12
5
  end
13
6
 
14
7
  def show
15
- doc_file = Rails.root.join('docs', "#{params[:page]}.md")
8
+ # Verifica se esiste una vista con il nome richiesto
9
+ page = params[:page]
10
+ valid_pages = %w[introduction installation customization components/buttons components/icons components/panel components/table]
16
11
 
17
- if File.exist?(doc_file)
18
- @content = File.read(doc_file)
12
+ if valid_pages.include?(page)
13
+ if page.start_with?('components/')
14
+ # Per i componenti, rendiamo esplicitamente il template nella directory corretta
15
+ component_name = page.split('/').last
16
+ render template: "better_ui/docs/components/#{component_name}"
17
+ else
18
+ render page
19
+ end
19
20
  else
20
- @content = "# Documentazione non trovata\nLa pagina richiesta non è stata trovata."
21
+ # Pagina non trovata
22
+ render plain: "Pagina non trovata", status: :not_found
21
23
  end
22
24
  end
23
25
 
24
26
  def component
27
+ # Gestisce le rotte tipo /better_ui/docs/components/button
25
28
  component_name = params[:component]
26
-
27
- # Percorso relativo ai file di documentazione dei componenti
28
- doc_file = BetterUi::Engine.root.join('docs', 'components', "#{component_name}.md")
29
-
30
- if File.exist?(doc_file)
31
- @content = File.read(doc_file)
32
- else
33
- @content = "# Componente non trovato\nLa documentazione per il componente '#{component_name}' non è stata trovata."
34
- end
35
-
36
- # Decidere quali esempi mostrare in base al componente
37
- @component_name = component_name
38
- render :component
29
+ render template: "better_ui/docs/components/#{component_name}"
30
+ rescue ActionView::MissingTemplate
31
+ render plain: "Componente non trovato", status: :not_found
39
32
  end
40
33
  end
41
34
  end
@@ -0,0 +1,17 @@
1
+ <tr class="<%= component.tr_classes(index) %>">
2
+ <% headers.each_with_index do |header, cell_index| %>
3
+ <td class="<%= component.td_classes %>">
4
+ <% cell_value = component.get_cell_value(row, header) %>
5
+ <% if cell_index == 0 %>
6
+ <div class="flex items-center">
7
+ <div class="w-8 h-8 rounded-full bg-blue-100 mr-2 flex items-center justify-center text-blue-700">
8
+ <%= cell_value.to_s.first&.upcase || 'N/A' %>
9
+ </div>
10
+ <%= cell_value %>
11
+ </div>
12
+ <% else %>
13
+ <%= cell_value %>
14
+ <% end %>
15
+ </td>
16
+ <% end %>
17
+ </tr>
@@ -0,0 +1,17 @@
1
+ <tr>
2
+ <% footer.each_with_index do |value, index| %>
3
+ <td class="<%= component.tf_classes %>">
4
+ <% if index == 0 %>
5
+ <div class="flex items-center">
6
+ <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
7
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2
8
+ 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path>
9
+ </svg>
10
+ <strong><%= value %></strong>
11
+ </div>
12
+ <% else %>
13
+ <%= value %>
14
+ <% end %>
15
+ </td>
16
+ <% end %>
17
+ </tr>
@@ -0,0 +1,12 @@
1
+ <tr>
2
+ <% headers.each do |header| %>
3
+ <th class="<%= component.th_classes %>">
4
+ <div class="flex items-center justify-between">
5
+ <%= header.to_s.humanize %>
6
+ <svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
7
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
8
+ </svg>
9
+ </div>
10
+ </th>
11
+ <% end %>
12
+ </tr>
@@ -0,0 +1,32 @@
1
+ <!DOCTYPE html>
2
+ <html style="background-color: <%= params.dig(:lookbook, :display, :bg_color) || "white" %>">
3
+ <head>
4
+ <title><%= content_for(:title) || "BetterUI" %></title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <meta name="apple-mobile-web-app-capable" content="yes">
7
+ <meta name="mobile-web-app-capable" content="yes">
8
+ <%= csrf_meta_tags %>
9
+ <%= csp_meta_tag %>
10
+
11
+ <%= yield :head %>
12
+
13
+ <link rel="icon" href="/icon.png" type="image/png">
14
+ <link rel="icon" href="/icon.svg" type="image/svg+xml">
15
+ <link rel="apple-touch-icon" href="/icon.png">
16
+
17
+ <%# Includes all stylesheet files in app/assets/stylesheets %>
18
+ <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
19
+ <%= stylesheet_link_tag "better_ui/application" %>
20
+ </head>
21
+
22
+ <body class="bg-white text-gray-800">
23
+ <main class="min-h-screen p-8 bg-yellow-500">
24
+ <div style="
25
+ margin-left: auto;
26
+ margin-right: auto;
27
+ max-width: <%= params.dig(:lookbook, :display, :max_width) || "100%" %>
28
+ ">
29
+ <%= yield %> <!-- rendered preview will be injected here -->
30
+ </main>
31
+ </body>
32
+ </html>
@@ -0,0 +1,23 @@
1
+ if defined?(Lookbook)
2
+ Lookbook.configure do |config|
3
+ # Configura la directory delle preview dei componenti
4
+ config.preview_paths << BetterUi::Engine.root.join('test/components/previews')
5
+
6
+ # Configura il titolo della pagina Lookbook
7
+ config.page_title = 'BetterUI - Catalogo Componenti'
8
+
9
+ # Configura i tag principali
10
+ config.preview_display_names = {
11
+ 'BetterUi::General' => 'Componenti Generali',
12
+ 'BetterUi::Application' => 'Componenti Applicazione',
13
+ 'BetterUi::Website' => 'Componenti Sito Web'
14
+ }
15
+
16
+ # Per supportare i componenti ViewComponent
17
+ config.listen_for_changes = {
18
+ component: 'app/components/**/*.rb',
19
+ preview: 'test/components/previews/**/*_preview.rb',
20
+ template: 'app/components/**/*.erb'
21
+ }
22
+ end
23
+ end
data/config/routes.rb CHANGED
@@ -5,6 +5,11 @@ BetterUi::Engine.routes.draw do
5
5
  # Route per le pagine di documentazione generiche
6
6
  get 'docs/:page', to: 'docs#show', as: :docs_page
7
7
 
8
- # Route per la documentazione dei componenti specifici
8
+ # Route specifica per i componenti
9
9
  get 'docs/components/:component', to: 'docs#component', as: :docs_component
10
+
11
+ # Montaggio di Lookbook per lo sviluppo e il test
12
+ if Rails.env.development? || Rails.env.test?
13
+ mount Lookbook::Engine, at: "/lookbook"
14
+ end
10
15
  end
@@ -1,6 +1,11 @@
1
1
  require 'view_component'
2
2
  require 'redcarpet'
3
3
  require 'tailwindcss-rails'
4
+ require 'coderay'
5
+ require 'font-awesome-sass'
6
+ require 'lookbook'
7
+ require 'listen'
8
+ require 'action_cable'
4
9
 
5
10
  module BetterUi
6
11
  class Engine < ::Rails::Engine
@@ -12,8 +17,13 @@ module BetterUi
12
17
  app.config.assets.paths << root.join('app', 'assets', 'images')
13
18
  end
14
19
 
20
+ # Assicuriamo che gli helper siano correttamente caricati
21
+ config.autoload_paths << root.join('app', 'helpers')
22
+
15
23
  # Configurazione per rendere disponibili i componenti all'applicazione host
16
24
  initializer 'better_ui.view_helpers' do
25
+ require_relative '../../app/helpers/better_ui_application_helper'
26
+
17
27
  ActiveSupport.on_load :action_controller do
18
28
  helper BetterUi::ApplicationHelper
19
29
  end
@@ -24,6 +34,13 @@ module BetterUi
24
34
  end
25
35
 
26
36
  # Configurazione per ViewComponent
27
- config.view_component.preview_paths << root.join('spec', 'components', 'previews')
37
+ config.view_component.preview_paths << root.join('test', 'components', 'previews')
38
+
39
+ # Configurazione per Lookbook in sviluppo e test
40
+ initializer 'better_ui.lookbook' do
41
+ if Rails.env.development? || Rails.env.test?
42
+ config.lookbook = Lookbook
43
+ end
44
+ end
28
45
  end
29
46
  end
@@ -1,3 +1,3 @@
1
1
  module BetterUi
2
- VERSION = "0.1.0"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/better_ui.rb CHANGED
@@ -23,3 +23,7 @@ module BetterUi
23
23
  end
24
24
  end
25
25
  end
26
+
27
+ # Caricamento del generator
28
+ require 'rails/generators'
29
+ require 'generators/better_ui/stylesheet_generator' if defined?(Rails::Generators)
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BetterUi
4
+ module Generators
5
+ class StylesheetGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('templates', __dir__)
7
+
8
+ desc "Genera un foglio di stile SCSS di base per personalizzare i componenti di BetterUi"
9
+
10
+ class_option :prefix, type: :string, default: "custom",
11
+ desc: "Prefisso da utilizzare per il foglio di stile (default: 'custom')"
12
+
13
+ def create_stylesheet
14
+ # Crea il file principale
15
+ template "components_stylesheet.scss",
16
+ File.join("app/assets/stylesheets", "#{options[:prefix]}_better_ui_components.scss")
17
+
18
+ # Crea la directory dei componenti se non esiste
19
+ directory = File.join("app/assets/stylesheets", "components")
20
+ FileUtils.mkdir_p(directory) unless File.directory?(directory)
21
+
22
+ # Lista dei file dei componenti
23
+ component_files = ['_variables.scss', '_button.scss', '_heading.scss', '_breadcrumb.scss',
24
+ '_link.scss', '_panel.scss', '_icon.scss', '_table.scss', '_badge.scss',
25
+ '_spinner.scss', '_avatar.scss']
26
+
27
+ # Copia i file originali
28
+ component_files.each do |filename|
29
+ copy_file "components/#{filename}", File.join(directory, filename)
30
+ end
31
+ end
32
+
33
+ def create_overrides
34
+ # Crea la directory dei componenti se non esiste
35
+ directory = File.join("app/assets/stylesheets", "components")
36
+ FileUtils.mkdir_p(directory) unless File.directory?(directory)
37
+
38
+ # Lista dei file dei componenti
39
+ component_files = ['_variables.scss', '_button.scss', '_heading.scss', '_breadcrumb.scss',
40
+ '_link.scss', '_panel.scss', '_icon.scss', '_table.scss', '_badge.scss',
41
+ '_spinner.scss', '_avatar.scss']
42
+
43
+ # Crea i file di override
44
+ component_files.each do |filename|
45
+ # Nome del file di override
46
+ override_filename = filename.sub('.scss', '_overrides.scss')
47
+ override_path = File.join(directory, override_filename)
48
+
49
+ # Crea il file di override solo se non esiste già
50
+ unless File.exist?(override_path)
51
+ # Leggi il contenuto del file originale dalla directory templates
52
+ template_path = File.join(self.class.source_root, "components", filename)
53
+
54
+ # Se il file template esiste, crea il file di override
55
+ if File.exist?(template_path)
56
+ original_content = File.read(template_path)
57
+ override_content = generate_empty_override(original_content, filename)
58
+ create_file override_path, override_content
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def show_readme
65
+ readme "README" if behavior == :invoke
66
+ end
67
+
68
+ private
69
+
70
+ # Genera un file di override vuoto con le stesse classi del file originale
71
+ def generate_empty_override(content, filename)
72
+ component_name = filename.sub('_', '').sub('.scss', '')
73
+ result = "/* ==============================\n"
74
+ result += " * #{component_name.capitalize} Component Overrides\n"
75
+ result += " * ==============================\n"
76
+ result += " * Questo file contiene le classi vuote per sovrascrivere gli stili di default.\n"
77
+ result += " * Aggiungi qui le tue personalizzazioni.\n"
78
+ result += " */\n\n"
79
+
80
+ # Estrae le classi tramite regex
81
+ class_matches = content.scan(/\.(bui-[a-zA-Z0-9_-]+)\s*\{/)
82
+ class_matches.flatten.uniq.each do |css_class|
83
+ result += ".#{css_class} {\n // Sovrascrivi qui gli stili per .#{css_class}\n}\n\n"
84
+ end
85
+
86
+ # Estrae anche classi annidate usando la sintassi SCSS
87
+ nested_matches = content.scan(/\.(bui-[a-zA-Z0-9_-]+)\s*{[^{]*\.([\w-]+)\s*{/)
88
+ nested_matches.each do |parent_class, child_class|
89
+ result += ".#{parent_class} {\n .#{child_class} {\n // Sovrascrivi qui gli stili annidati\n }\n}\n\n"
90
+ end
91
+
92
+ result
93
+ end
94
+ end
95
+ end
96
+ end