# frozen_string_literal: true module Ariadne # Use `Button` for actions (e.g. in forms). Use links for destinations, or moving from one page to another. class ButtonComponent < Ariadne::Component include IconHelper DEFAULT_SCHEME = :default LINK_SCHEME = :link SCHEME_CLASS_MAPPINGS = { default: "text-blue-800 bg-blue-50 hover:bg-blue-100 border-blue-300 focus:ring-offset-blue-50 focus:ring-blue-600", info: "text-blue-800 bg-blue-50 hover:bg-blue-100 border-blue-300 focus:ring-offset-blue-50 focus:ring-blue-600", success: "text-green-800 bg-green-50 hover:bg-green-100 border-green-300 focus:ring-offset-green-50 focus:ring-green-600", warning: "text-yellow-800 bg-yellow-50 hover:bg-yellow-100 border-yellow-300 focus:ring-offset-yellow-50 focus:ring-yellow-600", danger: "text-red-800 bg-red-50 hover:bg-red-100 border-red-300 focus:ring-offset-red-50 focus:ring-red-600", }.freeze VALID_SCHEMES = SCHEME_CLASS_MAPPINGS.keys.freeze # Leading visuals appear to the left of the button text. # # Use: # # - `icon` for a <%= link_to_component(Ariadne::HeroiconComponent) %>. # # @param tag [Symbol, String] The rendered tag name # @param classes [String] <%= link_to_classes_docs %> # @param icon [String] Name of <%= link_to_heroicons %> to use. # @param variant [String] <%= one_of(HeroiconsHelper::Icon::VARIANTS, sort: false) %> # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::HeroiconComponent) %>. renders_one :icon, lambda { |tag: :svg, icon:, variant:, classes: "", attributes: {}| @icon = icon @variant = variant tag = check_incoming_tag(:svg, tag) Ariadne::HeroiconComponent.new(tag: tag, icon: icon, variant: variant, classes: classes, attributes: attributes) } # Trailing visuals appear to the right of the button text. # # Use: # # - `counter` for a <%= link_to_component(Ariadne::CounterComponent) %>. # # @param tag [Symbol, String] The rendered tag name # @param classes [String] <%= link_to_classes_docs %> # @param counter [Number] The starting counter value # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::CounterComponent) %>. renders_one :counter, lambda { |tag: :span, count: 0, classes: "", attributes: {}| tag = check_incoming_tag(:span, tag) attributes[:ml] = check_incoming_value(2, ml: attributes[:ml]) Ariadne::CounterComponent.new(tag: tag, count: count, classes: classes, attributes: attributes) } # `Tooltip` that appears on mouse hover or keyboard focus over the button. Use tooltips sparingly and as a last resort. # **Important:** This tooltip defaults to `type: :description`. In a few scenarios, `type: :label` may be more appropriate. # Consult the <%= link_to_component(Ariadne::TooltipComponent) %> documentation for more information. # # @param tag [Symbol, String] The rendered tag name # @param text [String] The text content of the tooltip. This should be brief and no longer than a sentence. # @param direction [Symbol] <%= one_of(Ariadne::TooltipComponent::DIRECTION_OPTIONS) %> # @param classes [String] <%= link_to_classes_docs %> # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::TooltipComponent) %>. renders_one :tooltip, lambda { |tag: :"tool-tip", text:, direction: Ariadne::TooltipComponent::DIRECTION_DEFAULT, type: Ariadne::TooltipComponent::TYPE_DEFAULT, classes: "", attributes: {}| raise ArgumentError, "Buttons with a tooltip must have a unique `id` set on the `Button`." if @id.blank? # TODO: test this tag = check_incoming_tag(:"tool-tip", tag) attributes[:for] = @id attributes[:type] = check_incoming_attribute(:description, attributes[:type]) Ariadne::TooltipComponent.new(tag: tag, text: text, direction: direction, type: type, classes: classes, attributes: attributes) } # @example Schemes # <%= render(Ariadne::ButtonComponent.new) { "Default" } %> # <%= render(Ariadne::ButtonComponent.new(scheme: :default)) { "Default" } %> # <%= render(Ariadne::ButtonComponent.new(scheme: :info)) { "Info" } %> # <%= render(Ariadne::ButtonComponent.new(scheme: :success)) { "Success" } %> # <%= render(Ariadne::ButtonComponent.new(scheme: :warning)) { "Warning" } %> # <%= render(Ariadne::ButtonComponent.new(scheme: :danger)) { "Danger" } %> # # @example Sizes # <%= render(Ariadne::ButtonComponent.new(size: :s)) { "Small" } %> # <%= render(Ariadne::ButtonComponent.new(size: :m)) { "Medium" } %> # # @example With leading visual # <%= render(Ariadne::ButtonComponent.new) do |c| %> # <% c.icon(icon: :star, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE, classes: "text-yellow-600") %> # Button # <% end %> # # @example With trailing visual # <%= render(Ariadne::ButtonComponent.new) do |c| %> # <% c.counter(count: 15) %> # Button # <% end %> # # @example With leading and trailing visuals # <%= render(Ariadne::ButtonComponent.new) do |c| %> # <% c.icon(icon: :star, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE) %> # <% c.counter(count: 15) %> # Button # <% end %> # # @example With tooltip # @description # Use tooltips sparingly and as a last resort. Consult the <%= link_to_component(Ariadne::TooltipComponent) %> documentation for more information. # @code # <%= render(Ariadne::ButtonComponent.new(attributes: { id: "button-with-tooltip" })) do |c| %> # <% c.tooltip(text: "Tooltip text") %> # Button # <% end %> # # @param tag [Symbol] <%= one_of(Ariadne::BaseButton::TAG_OPTIONS) %> # @param scheme [Symbol] <%= one_of(Ariadne::ButtonComponent::VALID_SCHEMES) %> # @param size [Symbol] <%= one_of(Ariadne::BaseButton::VALID_SIZES) %> # @param type [Symbol] <%= one_of(Ariadne::BaseButton::TYPE_OPTIONS) %> # @param classes [String] <%= link_to_classes_docs %> # @param attributes [Hash] <%= link_to_attributes_docs %> def initialize( tag: Ariadne::BaseButton::DEFAULT_TAG, scheme: DEFAULT_SCHEME, size: BaseButton::DEFAULT_SIZE, classes: "", attributes: {} ) @tag = tag @scheme = scheme @attributes = attributes @id = @attributes[:id] @size = fetch_or_raise(Ariadne::BaseButton::VALID_SIZES, size) @scheme = fetch_or_raise(VALID_SCHEMES, scheme) @classes = class_names( SCHEME_CLASS_MAPPINGS[@scheme], classes ) end 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 private def link? @scheme == LINK_SCHEME end end end