app/components/avo/actions_component.rb

Summary

Maintainability
A
25 mins
Test Coverage
# frozen_string_literal: true

class Avo::ActionsComponent < Avo::BaseComponent
  include Avo::ApplicationHelper

  prop :as_row_control, default: false
  prop :icon
  prop :size, default: :md
  prop :title
  prop :color do |value|
    value || :primary
  end
  prop :include, default: [].freeze do |include|
    Array(include).to_set
  end
  prop :custom_list, default: false
  prop :label do |label|
    if @custom_list
      label
    else
      label || I18n.t("avo.actions")
    end
  end
  prop :style, default: :outline
  prop :actions, default: [].freeze
  prop :exclude, default: [].freeze do |exclude|
    Array(exclude).to_set
  end
  prop :resource
  prop :view
  prop :host_component

  delegate_missing_to :@host_component

  def after_initialize
    filter_actions unless @custom_list

    # Hydrate each action action with the record when rendering a list on row controls
    if @as_row_control
      @actions.each do |action|
        action.hydrate(resource: @resource, record: @resource.record) if action.respond_to?(:hydrate)
      end
    end
  end

  def render?
    @actions.present?
  end

  def filter_actions
    @actions = @actions.dup

    if @exclude.any?
      @actions.reject! { |action| @exclude.include?(action.class) }
    end

    if @include.any?
      @actions.select! { |action| @include.include?(action.class) }
    end
  end

  private

  def icon(icon)
    svg icon, class: "h-5 shrink-0 mr-1 inline pointer-events-none"
  end

  def render_item(action)
    case action
    when Avo::Divider
      render Avo::DividerComponent.new(action.label)
    when Avo::BaseAction
      render_action_link(action)
    when defined?(Avo::Advanced::Resources::Controls::Action) && Avo::Advanced::Resources::Controls::Action
      render_action_link(action.action, icon: action.icon)
    when defined?(Avo::Advanced::Resources::Controls::LinkTo) && Avo::Advanced::Resources::Controls::LinkTo
      link_to action.args[:path],
        class: action.args.delete(:class) || "flex items-center px-4 py-3 w-full text-black font-semibold text-sm hover:bg-primary-100",
        **action.args.except(:path, :label, :icon) do
          raw("#{icon(action.args[:icon])} #{action.args[:label]}")
        end
    end
  end

  private

  def render_action_link(action, icon: nil)
    link_to action.link_arguments(resource: @resource, arguments: action.arguments).first,
      data: action_data_attributes(action),
      title: action.action_name,
      class: action_css_class(action) do
        raw("#{icon(icon || action.icon)} #{action.action_name}")
      end
  end

  def action_data_attributes(action)
    {
      action_name: action.action_name,
      "turbo-frame": Avo::MODAL_FRAME_ID,
      action: "click->actions-picker#visitAction",
      "actions-picker-target": action.standalone ? "standaloneAction" : "resourceAction",
      disabled: action.disabled?,
      turbo_prefetch: false,
      enabled_classes: "text-black",
      disabled_classes: "text-gray-500"
    }
  end

  def action_css_class(action)
    helpers.class_names("flex items-center px-4 py-3 w-full font-semibold text-sm hover:bg-primary-100", {
      "text-gray-500": action.disabled?,
      "text-black": action.enabled?,
    })
  end
end