# 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 = { link: Ariadne::LinkComponent::DEFAULT_ACTIONABLE_CLASSES, none: "", default: "ariadne-text-purple-800 ariadne-bg-purple-50 hover:ariadne-bg-purple-100 ariadne-border-purple-300 focus:ariadne-ring-offset-purple-50 focus:ariadne-ring-purple-600", info: "ariadne-text-blue-800 ariadne-bg-blue-50 hover:ariadne-bg-blue-100 ariadne-border-blue-300 focus:ariadne-ring-offset-blue-50 focus:ariadne-ring-blue-600", success: "ariadne-text-green-800 ariadne-bg-green-50 hover:ariadne-bg-green-100 ariadne-border-green-300 focus:ariadne-ring-offset-green-50 focus:ariadne-ring-green-600", warning: "ariadne-text-yellow-800 ariadne-bg-yellow-50 hover:ariadne-bg-yellow-100 ariadne-border-yellow-300 focus:ariadne-ring-offset-yellow-50 focus:ariadne-ring-yellow-600", danger: "ariadne-text-red-800 ariadne-bg-red-50 hover:ariadne-bg-red-100 ariadne-border-red-300 focus:ariadne-ring-offset-red-50 focus:ariadne-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::VALID_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::VALID_PLACEMENTS) %> # @param classes [String] <%= link_to_classes_docs %> # @param attributes [Hash] Same arguments as <%= link_to_component(Ariadne::TooltipComponent) %>. renders_one :tooltip, lambda { |tag: Ariadne::TooltipComponent::DEFAULT_TAG, text:, direction: Ariadne::TooltipComponent::DEFAULT_PLACEMENT, 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? Ariadne::TooltipComponent.new(for_id: @id, 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: :sm)) { "Small" } %> # <%= render(Ariadne::ButtonComponent.new(size: :md)) { "Medium" } %> # # @example With leading visual # <%= render(Ariadne::ButtonComponent.new) do |c| %> # <% c.icon(icon: :star, variant: HeroiconsHelper::Icon::VARIANT_OUTLINE, classes: "ariadne-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 type [Symbol] <%= one_of(Ariadne::BaseButton::VALID_TYPES) %> # @param scheme [Symbol] <%= one_of(Ariadne::ButtonComponent::VALID_SCHEMES) %> # @param size [Symbol] <%= one_of(Ariadne::BaseButton::VALID_SIZES) %> # @param dropdown [Boolean] Whether or not to render a dropdown caret. # @param classes [String] <%= link_to_classes_docs %> # @param attributes [Hash] <%= link_to_attributes_docs %> def initialize( tag: Ariadne::BaseButton::DEFAULT_TAG, type: Ariadne::BaseButton::DEFAULT_TYPE, scheme: DEFAULT_SCHEME, size: BaseButton::DEFAULT_SIZE, dropdown: false, classes: "", attributes: {} ) @tag = tag @type = type @attributes = attributes @id = @attributes[:id] @size = fetch_or_raise(Ariadne::BaseButton::VALID_SIZES, size) @scheme = fetch_or_raise(VALID_SCHEMES, scheme) @scheme = LINK_SCHEME if @tag == :a @classes = class_names( SCHEME_CLASS_MAPPINGS[@scheme], classes, ) end private def dropdown? @dropdown 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