# typed: false # frozen_string_literal: true require "dry-initializer" module Ariadne class << self def generate_id SecureRandom.hex(6) end end # :nodoc: class BaseComponent < ViewComponentContrib::Base include Ariadne::ViewComponent::HTMLAttrs include ViewComponentContrib::StyleVariants include ViewComponentContrib::TranslationHelper include Ariadne::ViewComponent::StyleVariants include Ariadne::AttributesHelper include Ariadne::ViewHelper extend Dry::Initializer[undefined: false] accepts_html_attributes ACCEPT_ANYTHING = lambda { |static_content = nil, &block| next static_content if static_content.present? view_context.capture { block&.call } } # classes is an array of CSS classes style_config.postprocess_with do |classes| Ariadne::ViewComponents.tailwind_merger.merge(classes) end class << self def component_name @component_name ||= name.sub(/::Component$/, "").underscore end def component_id(&block) @component_id ||= block || proc { self.class.component_name.delete_prefix("ui/").gsub(/[^a-z0-9]+/, "-") } end def stimulus_name # @tag stimulus-id @stimulus_name ||= component_name.gsub(/[^a-z0-9]+/, "-") end def i18n_scope @i18n_scope ||= component_name.split("/") end def translate(key, scope: nil, **options) I18n.t(key, **options, scope: [*i18n_scope, *Array.wrap(scope)]) end end self.i18n_namespace = "ariadne" # Support relative component names within components def component(name, ...) return super unless name.starts_with?(".") full_name = Pathname.new(File.join(self.class.component_name, name)).cleanpath.to_s super(full_name, ...) end def component_id @component_id ||= instance_eval(&self.class.component_id) end def class_for(name) # @tag isolated-styles # stripping away UI and Component modulename = self.class.name.split("::")[1...-1].join("--").downcase "ariadne-#{modulename}-#{name}" end def in_turbo_frame(**options) Ariadne::Turbo::Frame::Component.new(component: self, **options) end def in_turbo_stream(action: "update", **options) Ariadne::Turbo::StreamAction::Component.new(component: self, action: action, **options) end def options @options ||= self.class.dry_initializer.attributes(self) end def html_attributes tag.attributes(html_attrs.except(:class)) end def styles end def validate_aria_label!(html_attrs) aria_label = aria(html_attrs, "label") aria_labelledby = aria(html_attrs, "labelledby") raise ArgumentError, "`aria-label` or `aria-labelledby` is required." if aria_label.blank? && aria_labelledby.blank? end # The scope here is defined within the component's namespace delegate :translate, to: :class delegate :stimulus_name, to: :class end end