app/lib/primer/attributes_helper.rb



# frozen_string_literal: true

module Primer
  # :nodoc:
  module AttributesHelper
    PLURAL_ARIA_ATTRIBUTES = %i[describedby labelledby].freeze
    PLURAL_DATA_ATTRIBUTES = %i[target targets].freeze

    def aria(val, system_arguments)
      system_arguments[:"aria-#{val}"] || system_arguments.dig(:aria, val.to_sym)
    end

    def data(val, system_arguments)
      system_arguments[:"data-#{val}"] || system_arguments.dig(:data, val.to_sym)
    end

    # Merges hashes that contain "aria-*" keys and nested aria: hashes. Removes keys from
    # each hash and returns them in the new hash.
    #
    # Eg. merge_aria({ "aria-disabled": "true" }, { aria: { invalid: "true" } })
    #     => { disabled: "true", invalid: "true" }
    #
    # Certain aria attributes can contain multiple values separated by spaces. merge_aria
    # will combine these plural attributes into a composite string.
    #
    # Eg. merge_aria({ "aria-labelledby": "foo" }, { aria: { labelledby: "bar" } })
    #     => { labelledby: "foo bar" }
    #
    # It's designed to be used to normalize and merge aria information from system_arguments
    # hashes. Consider using this pattern in component initializers:
    #
    # @system_arguments[:aria] = merge_aria(
    #   @system_arguments,
    #   { aria: { labelled_by: id } }
    # )
    def merge_aria(*hashes)
      merge_prefixed_attribute_hashes(
        *hashes, prefix: :aria, plural_keys: PLURAL_ARIA_ATTRIBUTES
      )
    end

    # Merges hashes that contain "data-*" keys and nested data: hashes. Removes keys from
    # each hash and returns them in the new hash.
    #
    # Eg. merge_data({ "data-foo": "true" }, { data: { bar: "true" } })
    #     => { foo: "true", bar: "true" }
    #
    # Certain data attributes can contain multiple values separated by spaces. merge_data
    # will combine these plural attributes into a composite string.
    #
    # Eg. merge_data({ "data-target": "foo" }, { data: { target: "bar" } })
    #     => { target: "foo bar" }
    #
    # It's designed to be used to normalize and merge data information from system_arguments
    # hashes. Consider using this pattern in component initializers:
    #
    # @system_arguments[:data] = merge_aria(
    #   @system_arguments,
    #   { data: { foo: "bar" } }
    # )
    def merge_data(*hashes)
      merge_prefixed_attribute_hashes(
        *hashes, prefix: :data, plural_keys: PLURAL_DATA_ATTRIBUTES
      )
    end

    def merge_prefixed_attribute_hashes(*hashes, prefix:, plural_keys:)
      {}.tap do |result|
        hashes.each do |hash|
          next unless hash

          prefix_hash = hash.delete(prefix) || {}

          prefix_hash.each_pair do |key, val|
            result[key] =
              if plural_keys.include?(key)
                [*(result[key] || "").split, val].join(" ").strip
              else
                val
              end
          end

          hash.delete_if do |key, val|
            key_s = key.to_s

            if key.start_with?("#{prefix}-")
              bare_key = key_s.sub("#{prefix}-", "").to_sym

              result[bare_key] =
                if plural_keys.include?(bare_key)
                  [*(result[bare_key] || "").split, val].join(" ").strip
                else
                  val
                end

              true
            else
              false
            end
          end
        end
      end
    end
  end
end