better_ui 0.3.0 → 0.6.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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/better_ui/application/main/component.html.erb +1 -1
  3. data/app/components/better_ui/application/sidebar/component.html.erb +25 -3
  4. data/app/components/better_ui/application/sidebar/component.rb +62 -5
  5. data/app/components/better_ui/general/button/component.html.erb +8 -8
  6. data/app/components/better_ui/general/button/component.rb +11 -11
  7. data/app/components/better_ui/general/dropdown/component.html.erb +7 -4
  8. data/app/components/better_ui/general/dropdown/component.rb +23 -1
  9. data/app/components/better_ui/general/field/component.html.erb +3 -3
  10. data/app/components/better_ui/general/field/component.rb +3 -3
  11. data/app/components/better_ui/general/grid/cell_component.html.erb +3 -0
  12. data/app/components/better_ui/general/grid/cell_component.rb +390 -0
  13. data/app/components/better_ui/general/grid/component.html.erb +3 -0
  14. data/app/components/better_ui/general/grid/component.rb +301 -0
  15. data/app/components/better_ui/general/heading/component.html.erb +1 -1
  16. data/app/components/better_ui/general/icon/component.rb +2 -1
  17. data/app/components/better_ui/general/input/checkbox/component.rb +10 -10
  18. data/app/components/better_ui/general/input/pin/component.html.erb +1 -0
  19. data/app/components/better_ui/general/input/pin/component.rb +201 -0
  20. data/app/components/better_ui/general/input/radio/component.rb +10 -10
  21. data/app/components/better_ui/general/input/rating/component.html.erb +4 -0
  22. data/app/components/better_ui/general/input/rating/component.rb +272 -0
  23. data/app/components/better_ui/general/input/select/component.html.erb +76 -14
  24. data/app/components/better_ui/general/input/select/component.rb +166 -101
  25. data/app/components/better_ui/general/input/toggle/component.html.erb +5 -0
  26. data/app/components/better_ui/general/input/toggle/component.rb +242 -0
  27. data/app/components/better_ui/general/link/component.rb +1 -1
  28. data/app/components/better_ui/general/text/component.html.erb +1 -0
  29. data/app/components/better_ui/general/text/component.rb +194 -0
  30. data/app/helpers/better_ui/application_helper.rb +7 -0
  31. data/app/helpers/better_ui/general/components/button/button_helper.rb +6 -6
  32. data/app/helpers/better_ui/general/components/dropdown/dropdown_helper.rb +9 -0
  33. data/app/helpers/better_ui/general/components/dropdown/item_helper.rb +13 -7
  34. data/app/helpers/better_ui/general/components/field/field_helper.rb +4 -4
  35. data/app/helpers/better_ui/general/components/grid/grid_helper.rb +145 -0
  36. data/app/helpers/better_ui/general/components/input/pin/pin_helper.rb +76 -0
  37. data/app/helpers/better_ui/general/components/input/rating/rating_helper.rb +70 -0
  38. data/app/helpers/better_ui/general/components/input/select/select_helper.rb +47 -31
  39. data/app/helpers/better_ui/general/components/input/toggle/toggle_helper.rb +77 -0
  40. data/app/helpers/better_ui/general/components/text/text_helper.rb +83 -0
  41. data/lib/better_ui/version.rb +1 -1
  42. data/lib/better_ui.rb +1 -0
  43. metadata +19 -4
  44. data/app/helpers/better_ui/general/components/accordion.rb +0 -11
  45. data/app/helpers/better_ui/general/components/modal.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9f8b92fdba76f5e84f09fb6a3bf9879b209e0e7dfd8f864186784db24b808c59
4
- data.tar.gz: 4d21e971baeecb10c7a3c5952041ac7e6911342e0efe4d0015efb92f9f214652
3
+ metadata.gz: d5ce58a483a9db3c7164c77c50c5206f83304c2b52f6ebff2d92dbc2ddfe0c78
4
+ data.tar.gz: 262e9f1b61ccf46254e582c2466cf4fea4c89d6ec48cb1117cb63355996a3e2c
5
5
  SHA512:
6
- metadata.gz: 778ee8d067130b37947a227ccf1322e8cf5b82d19c9b3f7a0371cc16b62071daaeb5eb6a191898ef2b06419486fe656bc22bea4314fbf98884fdf16259ac8409
7
- data.tar.gz: 1df545b428bdb281fb6f78b7be2e58e455bd6bd67f0202684f51b9e756c9ccc3e928a283f97d98a924185e1bb859b63f8cf2a91857ef49478cf6bfc3f254629d
6
+ metadata.gz: 9d024e430e9eb3d69c30adc09b8cff4a8a79a3585ea0769abe0e279c7dcb54b36faf7239ad393436f5558dc0db67c29a66e7eac8f32a11232d12cffbffc2cb96
7
+ data.tar.gz: 4c18bd0f1df604a289bab875eee931283616cd22f7b1a24e239446643392228a09b92f0e9b54888b9c3d8bdc2074a089ff0d35373aca0e60bccda86ef7b843fe
@@ -1,4 +1,4 @@
1
- <main class="w-full fixed <%= get_layout_class %> <%= get_padding_class(padding) %><%= classes ? ' ' + classes : '' %>">
1
+ <main class="w-full <%= get_layout_class %> <%= get_padding_class(padding) %><%= classes ? ' ' + classes : '' %>">
2
2
  <div class="h-[calc(100vh-81px)] p-4">
3
3
  <div class="h-full w-full bg-white p-4 <%= get_padding_class(inner_padding) %> <%= get_rounded_class %> <%= get_shadow_class %> overflow-y-auto">
4
4
  <div class="h-full overflow-y-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]">
@@ -2,7 +2,8 @@
2
2
  data-bui-sidebar-width-value="<%= @width == :md ? 256 : 320 %>"
3
3
  data-bui-sidebar-min-width-value="200"
4
4
  data-bui-sidebar-max-width-value="400"
5
- data-bui-sidebar-pinned-value="true">
5
+ data-bui-sidebar-pinned-value="true"
6
+ class="<%= wrapper_classes %>">
6
7
 
7
8
  <!-- Mobile Overlay -->
8
9
  <div data-bui-sidebar-target="overlay" class="fixed inset-0 bg-black bg-opacity-50 z-40 hidden md:hidden"></div>
@@ -174,7 +175,28 @@
174
175
  <!-- Footer Section -->
175
176
  <% if has_footer? %>
176
177
  <div class="px-6 py-4 border-t border-gray-200">
177
- <% if footer[:user_info].present? %>
178
+ <% if has_user_dropdown? %>
179
+ <!-- User Dropdown -->
180
+ <% user_dropdown = footer[:user_dropdown] %>
181
+ <%= bui_dropdown(
182
+ trigger: user_dropdown_trigger,
183
+ position: :top,
184
+ theme: :white,
185
+ fullwidth: true,
186
+ show_chevron: false
187
+ ) do %>
188
+ <% (user_dropdown[:menu_items] || []).each do |item| %>
189
+ <%= bui_dropdown_item(
190
+ text: item[:text],
191
+ icon: item[:icon],
192
+ href: item[:href],
193
+ theme: item[:theme] || :default,
194
+ active: item[:active] || false,
195
+ disabled: item[:disabled] || false
196
+ ) %>
197
+ <% end %>
198
+ <% end %>
199
+ <% elsif footer[:user_info].present? %>
178
200
  <div class="flex items-center">
179
201
  <% if footer[:user_info][:avatar].present? %>
180
202
  <div class="flex-shrink-0">
@@ -206,7 +228,7 @@
206
228
  <% end %>
207
229
 
208
230
  <% if footer[:content].present? %>
209
- <div class="<%= footer[:user_info].present? ? 'mt-4' : '' %>">
231
+ <div class="<%= (footer[:user_info].present? || has_user_dropdown?) ? 'mt-4' : '' %>">
210
232
  <%= footer[:content] %>
211
233
  </div>
212
234
  <% end %>
@@ -4,10 +4,12 @@ module BetterUi
4
4
  module Application
5
5
  module Sidebar
6
6
  class Component < ViewComponent::Base
7
- # Include degli helper per utilizzare bui_icon, bui_avatar e bui_button
7
+ # Include degli helper per utilizzare bui_icon, bui_avatar, bui_button e bui_dropdown
8
8
  include BetterUi::General::Components::Icon::IconHelper
9
9
  include BetterUi::General::Components::Avatar::AvatarHelper
10
10
  include BetterUi::General::Components::Button::ButtonHelper
11
+ include BetterUi::General::Components::Dropdown::DropdownHelper
12
+ include BetterUi::General::Components::Dropdown::ItemHelper
11
13
  attr_reader :width, :position, :theme, :shadow, :border, :header, :footer, :navigation_sections, :collapsible, :classes
12
14
 
13
15
  # Larghezze sidebar con classi Tailwind dirette
@@ -46,7 +48,7 @@ module BetterUi
46
48
  # @param shadow [Symbol] Tipo di ombra (:none, :sm, :md, :lg), default :lg
47
49
  # @param border [Boolean] Se mostrare il bordo destro/sinistro, default true
48
50
  # @param header [Hash] Configurazione header (logo, title, subtitle)
49
- # @param footer [Hash] Configurazione footer (content, user_info)
51
+ # @param footer [Hash] Configurazione footer (content, user_info, user_dropdown)
50
52
  # @param navigation_sections [Array] Array di sezioni di navigazione
51
53
  # @param collapsible [Boolean] Se abilitare sezioni collassabili, default true
52
54
  # @param classes [String] Classi CSS aggiuntive
@@ -74,8 +76,8 @@ module BetterUi
74
76
  @classes = classes
75
77
  end
76
78
 
77
- def container_classes
78
- base_classes = %w[fixed inset-y-0 h-screen z-50 flex flex-col]
79
+ def wrapper_classes
80
+ base_classes = %w[fixed top-0 inset-y-0 h-screen z-[9999]]
79
81
 
80
82
  # Posizione
81
83
  base_classes << (position == :right ? "right-0" : "left-0")
@@ -83,6 +85,12 @@ module BetterUi
83
85
  # Larghezza
84
86
  base_classes << width_class
85
87
 
88
+ base_classes.compact.join(" ")
89
+ end
90
+
91
+ def container_classes
92
+ base_classes = %w[flex flex-col h-full]
93
+
86
94
  # Tema
87
95
  base_classes.concat(theme_classes)
88
96
 
@@ -104,7 +112,56 @@ module BetterUi
104
112
  end
105
113
 
106
114
  def has_footer?
107
- footer.present? && (footer[:content].present? || footer[:user_info].present?)
115
+ footer.present? && (footer[:content].present? || footer[:user_info].present? || footer[:user_dropdown].present?)
116
+ end
117
+
118
+ def has_user_dropdown?
119
+ footer.present? && footer[:user_dropdown].present?
120
+ end
121
+
122
+ def user_dropdown_trigger
123
+ return '' unless has_user_dropdown?
124
+
125
+ user_dropdown = footer[:user_dropdown]
126
+ avatar_html = if user_dropdown[:avatar].present?
127
+ if user_dropdown[:avatar].is_a?(Hash)
128
+ bui_avatar(**user_dropdown[:avatar])
129
+ else
130
+ user_dropdown[:avatar].html_safe
131
+ end
132
+ else
133
+ ''
134
+ end
135
+
136
+ content_tag(:div, class: "flex items-center w-full text-left") do
137
+ avatar_section = if user_dropdown[:avatar].present?
138
+ content_tag(:div, avatar_html, class: "flex-shrink-0")
139
+ else
140
+ ''
141
+ end
142
+
143
+ text_section = content_tag(:div, class: user_dropdown[:avatar].present? ? 'ml-3 min-w-0 flex-1' : 'min-w-0 flex-1') do
144
+ name_part = if user_dropdown[:name].present?
145
+ content_tag(:p, user_dropdown[:name], class: "text-sm font-medium text-gray-700 truncate")
146
+ else
147
+ ''
148
+ end
149
+
150
+ subtitle_part = if user_dropdown[:subtitle].present?
151
+ content_tag(:p, user_dropdown[:subtitle], class: "text-xs text-gray-500 truncate")
152
+ else
153
+ ''
154
+ end
155
+
156
+ (name_part + subtitle_part).html_safe
157
+ end
158
+
159
+ chevron_section = content_tag(:div, class: "ml-auto flex-shrink-0") do
160
+ bui_icon("chevron-down", size: :small, classes: "text-gray-400")
161
+ end
162
+
163
+ (avatar_section + text_section + chevron_section).html_safe
164
+ end
108
165
  end
109
166
 
110
167
  private
@@ -2,15 +2,15 @@
2
2
  <% if link? %>
3
3
  <%= link_to @href, **link_attributes do %>
4
4
  <% if @icon && @icon_position == :left %>
5
- <span class="flex-shrink-0<%= @label.present? ? ' mr-2' : '' %>"><%= render_icon(@icon) %></span>
5
+ <span class="flex-shrink-0<%= @text.present? ? ' mr-2' : '' %>"><%= render_icon(@icon) %></span>
6
6
  <% end %>
7
7
 
8
- <% if @label %>
9
- <span class="flex-grow"><%= @label %></span>
8
+ <% if @text %>
9
+ <span class="flex-grow"><%= @text %></span>
10
10
  <% end %>
11
11
 
12
12
  <% if @icon && @icon_position == :right %>
13
- <span class="flex-shrink-0<%= @label.present? ? ' ml-2' : '' %>"><%= render_icon(@icon) %></span>
13
+ <span class="flex-shrink-0<%= @text.present? ? ' ml-2' : '' %>"><%= render_icon(@icon) %></span>
14
14
  <% end %>
15
15
 
16
16
  <%= content %>
@@ -18,15 +18,15 @@
18
18
  <% else %>
19
19
  <%= tag.button(**button_attributes) do %>
20
20
  <% if @icon && @icon_position == :left %>
21
- <span class="flex-shrink-0<%= @label.present? ? ' mr-2' : '' %>"><%= render_icon(@icon) %></span>
21
+ <span class="flex-shrink-0<%= @text.present? ? ' mr-2' : '' %>"><%= render_icon(@icon) %></span>
22
22
  <% end %>
23
23
 
24
- <% if @label %>
25
- <span class="flex-grow"><%= @label %></span>
24
+ <% if @text %>
25
+ <span class="flex-grow"><%= @text %></span>
26
26
  <% end %>
27
27
 
28
28
  <% if @icon && @icon_position == :right %>
29
- <span class="flex-shrink-0<%= @label.present? ? ' ml-2' : '' %>"><%= render_icon(@icon) %></span>
29
+ <span class="flex-shrink-0<%= @text.present? ? ' ml-2' : '' %>"><%= render_icon(@icon) %></span>
30
30
  <% end %>
31
31
 
32
32
  <%= content %>
@@ -2,7 +2,7 @@ module BetterUi
2
2
  module General
3
3
  module Button
4
4
  class Component < ViewComponent::Base
5
- attr_reader :label, :type, :size, :full_width, :disabled,
5
+ attr_reader :text, :theme, :size, :full_width, :disabled,
6
6
  :icon, :icon_position, :href, :method, :data, :classes, :id, :rounded, :button_type, :html_options
7
7
 
8
8
  # Classi base sempre presenti
@@ -40,8 +40,8 @@ module BetterUi
40
40
 
41
41
  # Inizializzazione del componente
42
42
  def initialize(
43
- label: nil,
44
- type: :white,
43
+ text: nil,
44
+ theme: :white,
45
45
  size: :medium,
46
46
  full_width: false,
47
47
  disabled: false,
@@ -56,8 +56,8 @@ module BetterUi
56
56
  button_type: :button,
57
57
  **html_options
58
58
  )
59
- @label = label
60
- @type = type.to_sym
59
+ @text = text
60
+ @theme = theme.to_sym
61
61
  @size = size.to_sym
62
62
  @full_width = full_width
63
63
  @disabled = disabled
@@ -95,7 +95,7 @@ module BetterUi
95
95
  end
96
96
 
97
97
  def get_button_type_classes
98
- BUTTON_THEME[@type] || BUTTON_THEME[:white]
98
+ BUTTON_THEME[@theme] || BUTTON_THEME[:white]
99
99
  end
100
100
 
101
101
  def get_border_radius_class
@@ -173,21 +173,21 @@ module BetterUi
173
173
 
174
174
  # Verifica se rendere il componente
175
175
  def render?
176
- @label.present? || @icon.present? || content.present?
176
+ @text.present? || @icon.present? || content.present?
177
177
  end
178
178
 
179
179
  private
180
180
 
181
181
  def validate_params
182
- validate_type
182
+ validate_theme
183
183
  validate_size
184
184
  validate_icon_position
185
185
  validate_rounded
186
186
  end
187
187
 
188
- def validate_type
189
- unless BUTTON_THEME.keys.include?(@type)
190
- raise ArgumentError, "Il tipo deve essere uno tra: #{BUTTON_THEME.keys.join(', ')}"
188
+ def validate_theme
189
+ unless BUTTON_THEME.keys.include?(@theme)
190
+ raise ArgumentError, "Il tema deve essere uno tra: #{BUTTON_THEME.keys.join(', ')}"
191
191
  end
192
192
  end
193
193
 
@@ -1,19 +1,22 @@
1
- <div class="relative inline-block <%= @classes %>"
1
+ <div class="relative <%= @classes %> <%= fullwidth_classes %>"
2
2
  data-controller="bui-dropdown"
3
3
  data-bui-dropdown-open-value="false"
4
+ data-bui-dropdown-selectable-value="<%= @selectable.to_s %>"
4
5
  <%= tag.attributes(@html_options.except(:class)) %>>
5
6
 
6
7
  <button type="button"
7
- class="inline-flex items-center justify-center border font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors <%= dynamic_trigger_classes %>"
8
+ class="items-center border font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors <%= dynamic_trigger_classes %> <%= fullwidth_classes %>"
8
9
  data-bui-dropdown-target="trigger"
9
10
  data-action="click->bui-dropdown#toggle keydown->bui-dropdown#keydown"
10
11
  aria-expanded="false"
11
12
  aria-haspopup="true">
12
13
  <%= @trigger %>
13
- <%= bui_icon("chevron-down", size: :small, classes: "ml-2 -mr-1") %>
14
+ <% if @show_chevron %>
15
+ <%= bui_icon("chevron-down", size: :small, classes: "ml-2 -mr-1") %>
16
+ <% end %>
14
17
  </button>
15
18
 
16
- <div class="absolute z-50 mt-2 origin-top-right bg-white border border-gray-200 shadow-lg focus:outline-none <%= dynamic_menu_classes %>"
19
+ <div class="absolute z-[1000] my-2 origin-top-right bg-white border border-gray-200 shadow-lg focus:outline-none <%= dynamic_menu_classes %> <%= fullwidth_classes %>"
17
20
  data-bui-dropdown-target="menu"
18
21
  role="menu"
19
22
  aria-orientation="vertical"
@@ -6,7 +6,7 @@ module BetterUi
6
6
  class Component < ViewComponent::Base
7
7
  include BetterUi::General::Components::Icon::IconHelper
8
8
 
9
- attr_reader :trigger, :position, :theme, :size, :rounded, :animation, :classes, :html_options
9
+ attr_reader :trigger, :position, :theme, :size, :rounded, :animation, :fullwidth, :show_chevron, :selectable, :classes, :html_options
10
10
 
11
11
  # Classi base spostate nel template HTML per migliore leggibilità
12
12
 
@@ -54,6 +54,12 @@ module BetterUi
54
54
  none: ""
55
55
  }.freeze
56
56
 
57
+ # Stili per full-width del trigger
58
+ DROPDOWN_FULLWIDTH = {
59
+ true => "w-full text-left justify-start",
60
+ false => "inline-flex justify-center"
61
+ }.freeze
62
+
57
63
  def initialize(
58
64
  trigger:,
59
65
  position: :bottom,
@@ -61,6 +67,9 @@ module BetterUi
61
67
  size: :medium,
62
68
  rounded: :medium,
63
69
  animation: :fade,
70
+ fullwidth: false,
71
+ show_chevron: true,
72
+ selectable: false,
64
73
  classes: nil,
65
74
  **html_options
66
75
  )
@@ -70,12 +79,21 @@ module BetterUi
70
79
  @size = size.to_sym
71
80
  @rounded = rounded.to_sym
72
81
  @animation = animation.to_sym
82
+ @fullwidth = fullwidth
83
+ @show_chevron = show_chevron
84
+ @selectable = selectable
73
85
  @classes = classes
74
86
  @html_options = html_options
75
87
 
76
88
  validate_params
77
89
  end
78
90
 
91
+ def fullwidth_classes
92
+ [
93
+ get_fullwidth_classes
94
+ ].compact.join(" ")
95
+ end
96
+
79
97
  # Restituisce solo le classi dinamiche per il trigger
80
98
  def dynamic_trigger_classes
81
99
  [
@@ -127,6 +145,10 @@ module BetterUi
127
145
  DROPDOWN_ANIMATION[@animation] || DROPDOWN_ANIMATION[:fade]
128
146
  end
129
147
 
148
+ def get_fullwidth_classes
149
+ DROPDOWN_FULLWIDTH[@fullwidth] || DROPDOWN_FULLWIDTH[false]
150
+ end
151
+
130
152
  def validate_params
131
153
  validate_theme
132
154
  validate_size
@@ -1,8 +1,8 @@
1
1
  <%# Form field component template %>
2
2
  <div class="<%= BASE_CLASSES %>">
3
- <% if label.present? %>
3
+ <% if text.present? %>
4
4
  <label for="<%= id %>" class="<%= LABEL_CLASSES %>">
5
- <%= label %>
5
+ <%= text %>
6
6
  <% if required %>
7
7
  <span class="<%= REQUIRED_CLASSES %>">*</span>
8
8
  <% end %>
@@ -24,4 +24,4 @@
24
24
  <%= help_text %>
25
25
  </div>
26
26
  <% end %>
27
- </div>
27
+ </div>
@@ -4,7 +4,7 @@ module BetterUi
4
4
  module General
5
5
  module Field
6
6
  class Component < ViewComponent::Base
7
- attr_reader :label, :name, :required, :error, :help_text, :id
7
+ attr_reader :text, :name, :required, :error, :help_text, :id
8
8
 
9
9
  renders_one :input
10
10
 
@@ -14,8 +14,8 @@ module BetterUi
14
14
  ERROR_CLASSES = "text-sm text-red-600 mt-1"
15
15
  HELP_TEXT_CLASSES = "text-sm text-gray-500 mt-1"
16
16
 
17
- def initialize(label:, name:, required: false, error: nil, help_text: nil, id: nil)
18
- @label = label
17
+ def initialize(text:, name:, required: false, error: nil, help_text: nil, id: nil)
18
+ @text = text
19
19
  @name = name
20
20
  @required = required
21
21
  @error = error
@@ -0,0 +1,3 @@
1
+ <div class="<%= combined_classes %>"<% if @id %> id="<%= @id %>"<% end %><% @html_options.except(:class).each do |attr, value| %> <%= attr %>="<%= value %>"<% end %>>
2
+ <%= content %>
3
+ </div>