class ClassVariants::Instance
def base(klass = nil, &block)
def base(klass = nil, &block) raise ArgumentError, "Use of positional argument and code block is not supported" if klass && block_given? if block_given? with_slots(&block).each do |slot| @bases << slot end else @bases << {slot: :default, class: klass} end end
def defaults(**options)
def defaults(**options) @defaults = options end
def dup
def dup self.class.new.tap do |copy| copy.instance_variable_set(:@bases, @bases.dup) copy.instance_variable_set(:@variants, @variants.dup) copy.instance_variable_set(:@defaults, @defaults.dup) end end
def expand_compound_variants(compound_variants)
def expand_compound_variants(compound_variants) compound_variants.map do |compound_variant| compound_variant.merge(slot: :default) end end
def expand_variants(variants)
def expand_variants(variants) variants.flat_map do |property, values| case values when String {property.to_s.delete_prefix("!").to_sym => !property.to_s.start_with?("!"), :class => values, :slot => :default} else values.map do |key, value| {property => key, :class => value, :slot => :default} end end end end
def initialize(...)
def initialize(...) @bases = [] @variants = [] @defaults = {} merge(...) end
def merge(**options, &block)
def merge(**options, &block) raise ArgumentError, "Use of hash config and code block is not supported" if !options.empty? && block_given? (base = options.fetch(:base, nil)) && @bases << {class: base, slot: :default} @variants += [ expand_variants(options.fetch(:variants, {})), expand_compound_variants(options.fetch(:compound_variants, [])) ].inject(:+) @defaults.merge!(options.fetch(:defaults, {})) instance_eval(&block) if block_given? self end
def render(slot = :default, **overrides)
def render(slot = :default, **overrides) classes = overrides.delete(:class) result = [] # Start with our default classes @bases.each do |base| result << base[:class] if base[:slot] == slot end # Then merge the passed in overrides on top of the defaults criteria = @defaults.merge(overrides) @variants.each do |candidate| next unless candidate[:slot] == slot if (candidate.keys - [:class, :slot]).all? { |key| criteria[key] == candidate[key] } result << candidate[:class] end end # add the passed in classes to the result result << classes # Compact out any nil values we may have dug up result.compact! # Return the final token list with_classess_processor(result.join(" ")) end
def slot(name = :default, **options)
def slot(name = :default, **options) raise ArgumentError, "class option is required" unless options.key?(:class) @slots << options.merge(slot: name) end
def variant(**options, &block)
def variant(**options, &block) raise ArgumentError, "Use of class option and code block is not supported" if options.key?(:class) && block_given? if block_given? with_slots(&block).each do |slot| @variants << options.merge(slot) end else @variants << options.merge(slot: :default) end end
def with_classess_processor(classes)
def with_classess_processor(classes) if ClassVariants.configuration.process_classes_with.respond_to?(:call) ClassVariants.configuration.process_classes_with.call(classes) else classes end end
def with_slots
def with_slots @slots = [] yield @slots end