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.
- checksums.yaml +4 -4
- data/README.md +199 -75
- data/app/assets/javascripts/better_ui/controllers/navbar_controller.js +138 -0
- data/app/assets/javascripts/better_ui/controllers/sidebar_controller.js +211 -0
- data/app/assets/javascripts/better_ui/controllers/toast_controller.js +161 -0
- data/app/assets/javascripts/better_ui/index.js +159 -0
- data/app/assets/javascripts/better_ui/toast_manager.js +77 -0
- data/app/assets/stylesheets/better_ui/application.css +25 -351
- data/app/components/better_ui/application/alert_component.html.erb +27 -0
- data/app/components/better_ui/application/alert_component.rb +202 -0
- data/app/components/better_ui/application/card_component.html.erb +24 -0
- data/app/components/better_ui/application/card_component.rb +53 -0
- data/app/components/better_ui/application/card_container_component.html.erb +8 -0
- data/app/components/better_ui/application/card_container_component.rb +14 -0
- data/app/components/better_ui/application/header_component.html.erb +88 -0
- data/app/components/better_ui/application/header_component.rb +188 -0
- data/app/components/better_ui/application/navbar_component.html.erb +294 -0
- data/app/components/better_ui/application/navbar_component.rb +249 -0
- data/app/components/better_ui/application/sidebar_component.html.erb +207 -0
- data/app/components/better_ui/application/sidebar_component.rb +318 -0
- data/app/components/better_ui/application/toast_component.html.erb +35 -0
- data/app/components/better_ui/application/toast_component.rb +223 -0
- data/app/components/better_ui/general/avatar_component.html.erb +19 -0
- data/app/components/better_ui/general/avatar_component.rb +171 -0
- data/app/components/better_ui/general/badge_component.html.erb +19 -0
- data/app/components/better_ui/general/badge_component.rb +123 -0
- data/app/components/better_ui/general/breadcrumb_component.html.erb +15 -0
- data/app/components/better_ui/general/breadcrumb_component.rb +130 -0
- data/app/components/better_ui/general/button_component.html.erb +34 -0
- data/app/components/better_ui/general/button_component.rb +162 -0
- data/app/components/better_ui/general/heading_component.html.erb +25 -0
- data/app/components/better_ui/general/heading_component.rb +148 -0
- data/app/components/better_ui/general/icon_component.html.erb +2 -0
- data/app/components/better_ui/general/icon_component.rb +100 -0
- data/app/components/better_ui/general/link_component.html.erb +17 -0
- data/app/components/better_ui/general/link_component.rb +132 -0
- data/app/components/better_ui/general/panel_component.html.erb +27 -0
- data/app/components/better_ui/general/panel_component.rb +103 -0
- data/app/components/better_ui/general/spinner_component.html.erb +15 -0
- data/app/components/better_ui/general/spinner_component.rb +79 -0
- data/app/components/better_ui/general/table_component.html.erb +73 -0
- data/app/components/better_ui/general/table_component.rb +167 -0
- data/app/components/better_ui/theme_helper.rb +171 -0
- data/app/controllers/better_ui/application_controller.rb +1 -0
- data/app/controllers/better_ui/docs_controller.rb +18 -25
- data/app/views/components/better_ui/general/table/_custom_body_row.html.erb +17 -0
- data/app/views/components/better_ui/general/table/_custom_footer_rows.html.erb +17 -0
- data/app/views/components/better_ui/general/table/_custom_header_rows.html.erb +12 -0
- data/app/views/layouts/component_preview.html.erb +32 -0
- data/config/initializers/lookbook.rb +23 -0
- data/config/routes.rb +6 -1
- data/lib/better_ui/engine.rb +18 -1
- data/lib/better_ui/version.rb +1 -1
- data/lib/better_ui.rb +4 -0
- data/lib/generators/better_ui/stylesheet_generator.rb +96 -0
- data/lib/generators/better_ui/templates/README +56 -0
- data/lib/generators/better_ui/templates/components/_avatar.scss +151 -0
- data/lib/generators/better_ui/templates/components/_badge.scss +142 -0
- data/lib/generators/better_ui/templates/components/_breadcrumb.scss +107 -0
- data/lib/generators/better_ui/templates/components/_button.scss +106 -0
- data/lib/generators/better_ui/templates/components/_card.scss +69 -0
- data/lib/generators/better_ui/templates/components/_heading.scss +180 -0
- data/lib/generators/better_ui/templates/components/_icon.scss +90 -0
- data/lib/generators/better_ui/templates/components/_link.scss +130 -0
- data/lib/generators/better_ui/templates/components/_panel.scss +144 -0
- data/lib/generators/better_ui/templates/components/_spinner.scss +132 -0
- data/lib/generators/better_ui/templates/components/_table.scss +105 -0
- data/lib/generators/better_ui/templates/components/_variables.scss +33 -0
- data/lib/generators/better_ui/templates/components_stylesheet.scss +66 -0
- metadata +135 -10
- data/app/helpers/better_ui/application_helper.rb +0 -183
- data/app/views/better_ui/docs/component.html.erb +0 -365
- data/app/views/better_ui/docs/index.html.erb +0 -100
- data/app/views/better_ui/docs/show.html.erb +0 -60
- 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,41 +1,34 @@
|
|
1
1
|
module BetterUi
|
2
2
|
class DocsController < ApplicationController
|
3
3
|
def index
|
4
|
-
|
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
|
-
|
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
|
18
|
-
|
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
|
-
|
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
|
-
|
28
|
-
|
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
|
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
|
data/lib/better_ui/engine.rb
CHANGED
@@ -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('
|
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
|
data/lib/better_ui/version.rb
CHANGED
data/lib/better_ui.rb
CHANGED
@@ -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
|