app/pb_kits/playbook/pb_icon/icon.rb



# frozen_string_literal: true

require "open-uri"
require "json"

module Playbook
  module PbIcon
    class Icon < Playbook::KitBase
      prop :border, type: Playbook::Props::Boolean,
                    default: false
      prop :fixed_width, type: Playbook::Props::Boolean,
                         default: true
      prop :flip, type: Playbook::Props::Enum,
                  values: ["horizontal", "vertical", "both", nil],
                  default: nil
      prop :icon
      prop :custom_icon, type: Playbook::Props::String,
                         default: nil
      prop :inverse, type: Playbook::Props::Boolean,
                     default: false
      prop :list_item, type: Playbook::Props::Boolean,
                       default: false
      prop :pull, type: Playbook::Props::Enum,
                  values: ["left", "right", nil],
                  default: nil
      prop :pulse, type: Playbook::Props::Boolean,
                   default: false
      prop :rotation, type: Playbook::Props::Enum,
                      values: [90, 180, 270, nil],
                      default: nil
      prop :size, type: Playbook::Props::Enum,
                  values: ["lg", "xs", "sm", "1x", "2x", "3x", "4x", "5x", "6x", "7x", "8x", "9x", "10x", nil],
                  default: nil
      prop :font_style, type: Playbook::Props::Enum,
                        values: %w[far fas fab fak],
                        default: "far"
      prop :spin, type: Playbook::Props::Boolean,
                  default: false
      prop :color, type: Playbook::Props::String
      prop :tabindex

      def valid_emoji?
        emoji_regex = /\p{Emoji}/
        emoji_regex.match?(icon)
      end

      def classname
        generate_classname(
          "pb_icon_kit",
          color_class,
          font_style_class,
          icon_class,
          border_class,
          fixed_width_class,
          flip_class,
          inverse_class,
          list_item_class,
          pull_class,
          pulse_class,
          rotation_class,
          size_class,
          spin_class,
          separator: " "
        )
      end

      def custom_icon_classname
        generate_classname(
          "pb_icon_kit",
          border_class,
          color_class,
          fixed_width_class,
          flip_class,
          inverse_class,
          list_item_class,
          pull_class,
          pulse_class,
          rotation_class,
          size_class,
          spin_class,
          separator: " "
        )
      end

      def icon_alias_map
        return unless Rails.application.config.respond_to?(:icon_alias_path)

        base_path = Rails.application.config.icon_alias_path
        json = File.read(Rails.root.join(base_path))
        JSON.parse(json)["aliases"].freeze
      end

      def asset_path
        return unless Rails.application.config.respond_to?(:icon_path)

        base_path = Rails.application.config.icon_path
        resolved_icon = resolve_alias(icon)
        icon_path = Dir.glob(Rails.root.join(base_path, "**", "#{resolved_icon}.svg")).first
        icon_path if icon_path && File.exist?(icon_path)
      end

      def render_svg
        doc = Nokogiri::XML(URI.open(asset_path || icon || custom_icon)) # rubocop:disable Security/Open
        svg = doc.at_css "svg"

        unless svg
          return "" # Return an empty string if SVG element is not found
        end

        svg["class"] = %w[pb_custom_icon svg-inline--fa].concat([object.custom_icon_classname]).join(" ")
        svg["id"] = object.id
        svg["data"] = object.data
        svg["aria"] = object.aria
        svg["height"] = "auto"
        svg["width"] = "auto"
        svg["tabindex"] = object.tabindex
        fill_color = object.color || "currentColor"
        doc.at_css("path")["fill"] = fill_color
        raw doc
      rescue OpenURI::HTTPError, StandardError
        # Handle any exceptions and return an empty string
        ""
      end

      def is_svg?
        (icon || custom_icon.to_s).include?(".svg") || asset_path.present?
      end

    private

      def resolve_alias(icon)
        return icon unless icon_alias_map

        aliases = icon_alias_map[icon]
        return icon unless aliases

        if aliases.is_a?(Array)
          aliases.find { |alias_name| file_exists?(alias_name) } || icon
        else
          aliases
        end
      end

      def file_exists?(alias_name)
        base_path = Rails.application.config.icon_path
        File.exist?(Dir.glob(Rails.root.join(base_path, "**", "#{alias_name}.svg")).first)
      end

      def svg_size
        size.nil? ? "1x" : size
      end

      def border_class
        prefix = is_svg? ? "svg_border" : "fa-border"
        border ? prefix : nil
      end

      def fixed_width_class
        prefix = is_svg? ? "svg_fw" : "fa-fw"
        fixed_width ? prefix : nil
      end

      def icon_class
        icon ? "fa-#{icon}" : nil
      end

      def inverse_class
        class_name = is_svg? ? "svg_inverse" : "fa-inverse"
        inverse ? class_name : nil
      end

      def list_item_class
        class_name = is_svg? ? "svg_li" : "fa-li"
        list_item ? class_name : nil
      end

      def flip_class
        prefix = is_svg? ? "flip_" : "fa-flip-"
        case flip
        when "horizontal"
          "#{prefix}horizontal"
        when "vertical"
          "#{prefix}vertical"
        when "both"
          "#{prefix}horizontal #{prefix}vertical"
        end
      end

      def pull_class
        class_name = is_svg? ? "pull_#{pull}" : "fa-pull-#{pull}"
        pull ? class_name : nil
      end

      def pulse_class
        class_name = is_svg? ? "pulse" : "fa-pulse"
        pulse ? class_name : nil
      end

      def rotation_class
        class_name = is_svg? ? "rotate_#{rotation}" : "fa-rotate-#{rotation}"
        rotation ? class_name : nil
      end

      def size_class
        class_name = is_svg? ? "svg_#{size}" : "fa-#{size}"
        size ? class_name : nil
      end

      def font_style_class
        font_style ? font_style.to_s : "far"
      end

      def spin_class
        class_name = is_svg? ? "spin" : "fa-spin"
        spin ? class_name : nil
      end

      def color_class
        color ? "color_#{color}" : nil
      end
    end
  end
end