Bouton
Un composant bouton polyvalent avec plusieurs variantes (primary, secondary, ghost) et tailles (sm, md, lg). Prend en charge les icônes, les états de chargement, et peut être rendu comme lien ou bouton de formulaire.
Aperçu et code
_button.html.erb
<%#
Button Component (Classic)
Usage:
<%= render "components/button", label: "Click me" %>
<%= render "components/button", label: "Submit", variant: :primary, size: :lg %>
<%= render "components/button", label: "Cancel", variant: :ghost, href: "/" %>
%>
<%
variant ||= :primary
size ||= :md
href ||= nil
method ||= nil
disabled ||= false
icon ||= nil
icon_position ||= :leading
type ||= "button"
additional_class ||= defined?(binding.local_variable_get(:class)) ? binding.local_variable_get(:class) : ""
data ||= {}
base_classes = "inline-flex items-center justify-center gap-2 font-medium cursor-pointer transition-all duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 dark:ring-offset-slate-900 disabled:opacity-50 disabled:cursor-not-allowed"
variant_classes = case variant.to_sym
when :primary
"bg-slate-900 text-white hover:bg-slate-800 active:bg-slate-950 focus:ring-slate-900 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white dark:active:bg-slate-200 dark:focus:ring-slate-300"
when :secondary
"bg-white text-slate-900 border border-slate-200 hover:bg-slate-50 hover:border-slate-300 active:bg-slate-100 focus:ring-slate-400 dark:bg-slate-800 dark:text-white dark:border-slate-700 dark:hover:bg-slate-700 dark:hover:border-slate-600 dark:active:bg-slate-900 dark:focus:ring-slate-500"
when :ghost
"text-slate-600 hover:text-slate-900 hover:bg-slate-100 active:bg-slate-200 focus:ring-slate-400 dark:text-slate-400 dark:hover:text-white dark:hover:bg-slate-800 dark:active:bg-slate-700 dark:focus:ring-slate-500"
else
"bg-slate-900 text-white hover:bg-slate-800 active:bg-slate-950 focus:ring-slate-900 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white dark:active:bg-slate-200 dark:focus:ring-slate-300"
end
size_classes = case size.to_sym
when :sm
"text-sm px-3 py-1.5 rounded-lg"
when :md
"text-sm px-4 py-2.5 rounded-lg"
when :lg
"text-base px-5 py-3 rounded-xl"
else
"text-sm px-4 py-2.5 rounded-lg"
end
all_classes = [base_classes, variant_classes, size_classes, additional_class].join(" ")
%>
<% if href.present? %>
<%= link_to href, class: all_classes, method: method, data: data do %>
<% if icon.present? && icon_position == :leading %>
<%= icon.html_safe %>
<% end %>
<%= label %>
<% if icon.present? && icon_position == :trailing %>
<%= icon.html_safe %>
<% end %>
<% end %>
<% else %>
<button type="<%= type %>" class="<%= all_classes %>" <%= "disabled" if disabled %> <% data.each do |key, value| %>data-<%= key %>="<%= value %>"<% end %>>
<% if icon.present? && icon_position == :leading %>
<%= icon.html_safe %>
<% end %>
<%= label %>
<% if icon.present? && icon_position == :trailing %>
<%= icon.html_safe %>
<% end %>
</button>
<% end %>
button_component.html.erb
<% if link? %>
<%= link_to @href, class: button_classes, method: @method, data: @data do %>
<% if @icon.present? && @icon_position == :leading %>
<%= @icon.html_safe %>
<% end %>
<%= @label %>
<% if @icon.present? && @icon_position == :trailing %>
<%= @icon.html_safe %>
<% end %>
<% end %>
<% else %>
<button type="<%= @type %>" class="<%= button_classes %>" <%= "disabled" if @disabled %> <% @data.each do |key, value| %>data-<%= key %>="<%= value %>"<% end %>>
<% if @icon.present? && @icon_position == :leading %>
<%= @icon.html_safe %>
<% end %>
<%= @label %>
<% if @icon.present? && @icon_position == :trailing %>
<%= @icon.html_safe %>
<% end %>
</button>
<% end %>
button_component.rb
# frozen_string_literal: true
class ButtonComponent < ViewComponent::Base
VARIANTS = %i[primary secondary ghost].freeze
SIZES = %i[sm md lg].freeze
attr_reader :label, :variant, :size, :href, :method, :disabled, :icon, :icon_position, :type, :data
def initialize(
label:,
variant: :primary,
size: :md,
href: nil,
method: nil,
disabled: false,
icon: nil,
icon_position: :leading,
type: "button",
data: {},
**extra_classes
)
@label = label
@variant = variant.to_sym
@size = size.to_sym
@href = href
@method = method
@disabled = disabled
@icon = icon
@icon_position = icon_position.to_sym
@type = type
@data = data
@extra_classes = extra_classes[:class] || ""
end
def button_classes
[ base_classes, variant_classes, size_classes, @extra_classes ].compact.join(" ")
end
def link?
@href.present?
end
private
def base_classes
"inline-flex items-center justify-center gap-2 font-medium cursor-pointer transition-all duration-150 ease-in-out focus:outline-none focus:ring-2 focus:ring-offset-2 dark:ring-offset-slate-900 disabled:opacity-50 disabled:cursor-not-allowed"
end
def variant_classes
case @variant
when :primary
"bg-slate-900 text-white hover:bg-slate-800 active:bg-slate-950 focus:ring-slate-900 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white dark:active:bg-slate-200 dark:focus:ring-slate-300"
when :secondary
"bg-white text-slate-900 border border-slate-200 hover:bg-slate-50 hover:border-slate-300 active:bg-slate-100 focus:ring-slate-400 dark:bg-slate-800 dark:text-white dark:border-slate-700 dark:hover:bg-slate-700 dark:hover:border-slate-600 dark:active:bg-slate-900 dark:focus:ring-slate-500"
when :ghost
"text-slate-600 hover:text-slate-900 hover:bg-slate-100 active:bg-slate-200 focus:ring-slate-400 dark:text-slate-400 dark:hover:text-white dark:hover:bg-slate-800 dark:active:bg-slate-700 dark:focus:ring-slate-500"
else
"bg-slate-900 text-white hover:bg-slate-800 active:bg-slate-950 focus:ring-slate-900 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white dark:active:bg-slate-200 dark:focus:ring-slate-300"
end
end
def size_classes
case @size
when :sm
"text-sm px-3 py-1.5 rounded-lg"
when :md
"text-sm px-4 py-2.5 rounded-lg"
when :lg
"text-base px-5 py-3 rounded-xl"
else
"text-sm px-4 py-2.5 rounded-lg"
end
end
end
Journal des modifications
- Initial release with primary, secondary, and ghost variants
- Three size options: sm, md (default), lg
- Icon support with leading/trailing positions
- Link and button rendering modes
Vous aimez ce composant gratuit ? Obtenez les 78 composants avec un paiement unique.
Obtenir Tous les Composants