# typed: false # frozen_string_literal: true module Ariadne module UI module Button class Component < Ariadne::BaseComponent attr_reader :icon_only include Ariadne::Behaviors::Captionable include Ariadne::Behaviors::Tooltipable option :as, default: proc { :button } option :href, default: proc { nil } option :type, default: proc { "button" } option :state, default: proc { "" } option :theme, default: proc { :primary } option :size, default: proc { :md } option :width, default: proc { :narrow } # Leading visuals appear to the left of the button text. # # Use: # # - `leading_visual_heroicon` for a <%= link_to_component(Ariadne::UI::Heroicon::Component) %>. renders_one :leading_visual, types: { heroicon: lambda { |**options| options[:size] = @size Ariadne::UI::Heroicon::Component.new(**options) }, } # Trailing visuals appear to the right of the button text. # # Use: # # - `trailing_visual_heroicon` for a <%= link_to_component(Ariadne::UI::Heroicon::Component) %>. renders_one :trailing_visual, types: { heroicon: lambda { |**options| options[:size] = @size Ariadne::UI::Heroicon::Component.new(**options) }, } accepts_html_attributes do |html_attrs| html_attrs[:class] = Ariadne::ViewComponents.tailwind_merger.merge([style(theme:, size:, icon_only:, width:), html_attrs[:class]].join(" ")) if link? raise ArgumentError, "Button needs an `href` defined when using `as: :link`" if href.blank? html_attrs[:href] = href html_attrs.delete(:disabled) else html_attrs[:type] = type html_attrs[:disabled] ||= false end end def initialize(**options) super end def as_icon(**options) validate_aria_label!(html_attrs) @icon_only = true @aria_label = aria(html_attrs, "label") @aria_description = aria(html_attrs, "description") options[:size] = @size if options[:svg] @svg = options[:svg] else @icon = Ariadne::UI::Heroicon::Component.new(**options) end self end def icon_or_svg return @svg if @svg render(@icon) end style do base do [ "ariadne-inline-flex", "ariadne-items-center", "ariadne-justify-center", "[&>svg:not(.heroicon)]:ariadne-fill-current", "ariadne-gap-x-1.5", "ariadne-rounded", "ariadne-px-2", # "ariadne-py-1", "ariadne-font-semibold", "ariadne-shadow-sm", "ariadne-text-black", "focus-visible:ariadne-outline", "focus-visible:ariadne-outline-2", "focus-visible:ariadne-outline-offset-2", "disabled:ariadne-border-transparent", "disabled:ariadne-cursor-not-allowed", ] end variants do theme do primary do [ "ariadne-bg-primary", "ariadne-text-primary-foreground", "hover:ariadne-bg-primary-hover", "active:ariadne-bg-primary-active", "disabled:ariadne-bg-primary-disabled-dark", "disabled:ariadne-text-primary-disabled-foreground-dark", ] end secondary do [ "ariadne-text-black", "ariadne-bg-zinc-100", "hover:ariadne-bg-zinc-300/20", "active:ariadne-bg-zinc-200/80", "dark:ariadne-text-slate-100", "dark:ariadne-bg-zinc-900", "dark:ariadne-hover:bg-zinc-800/80", "dark:active:ariadne-bg-zinc-700/20", ] end outline do [ "ariadne-border", "ariadne-border-input", "ariadne-bg-background", "hover:ariadne-bg-accent", "hover:ariadne-text-accent-foreground", ] end nude do [ "ariadne-text-black", "dark:ariadne-text-white", "ariadne-shadow-none", "hover:ariadne-bg-zinc-200/20", "active:ariadne-bg-zinc-500/10", "active:ariadne-border-transparent", "dark:hover:ariadne-bg-zinc-600/20", "dark:active:ariadne-bg-zinc-500/10", ] end danger do [ "ariadne-border", "ariadne-border-solid", "ariadne-border-red-600", "ariadne-bg-transparent", "ariadne-text-red-600", "hover:ariadne-bg-red-500", "hover:ariadne-text-white", "active:ariadne-text-white", "active:ariadne-bg-red-600", "dark:active:ariadne-bg-red-400", ] end none do [ "ariadne-shadow-none", ] end end size do xs do [ "ariadne-text-xs", "[&>svg:not(.heroicon)]:ariadne-size-4", "ariadne-p-1", ] end sm do [ "ariadne-text-sm", "ariadne-leading-5", "[&>svg:not(.heroicon)]:ariadne-size-5", "ariadne-py-1", ] end md do [ "ariadne-rounded-md", "ariadne-px-2.5", "ariadne-py-1.5", "ariadne-text-base", "ariadne-leading-7", "[&>svg:not(.heroicon)]:ariadne-size-6", ] end lg do [ "ariadne-rounded-md", "ariadne-px-3", "ariadne-py-2", "ariadne-text-lg", "ariadne-leading-8", "[&>svg:not(.heroicon)]:ariadne-size-10", ] end xl do [ "ariadne-rounded-md", "ariadne-px-3.5", "ariadne-py-2.5", "ariadne-text-xl", "ariadne-leading-10", "[&>svg:not(.heroicon)]:ariadne-size-12ß", ] end end width do narrow {} full { "ariadne-w-full" } end end end private def button_tag if link? "a" else "button" end end private def link? = as == :link private def button? = as == :button private def trimmed_content return if content.blank? trimmed_content = content.strip return trimmed_content unless content.html_safe? # strip unsets `html_safe`, so we have to set it back again to guarantee that HTML blocks won't break trimmed_content.html_safe # rubocop:disable Rails/OutputSafety end end end end end