diff --git a/CHANGELOG.md b/CHANGELOG.md index ba10f4b..9008f1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## Unreleased +- Add support for merging ([#23](https://github.com/avo-hq/class_variants/pull/23)) + ## 1.0.0 (2024-11-13) - Add support for slots ([#15](https://github.com/avo-hq/class_variants/pull/15)) - Allow passing additional classes when render ([#17](https://github.com/avo-hq/class_variants/pull/17)) diff --git a/lib/class_variants/instance.rb b/lib/class_variants/instance.rb index 55c170e..e6e4a7b 100644 --- a/lib/class_variants/instance.rb +++ b/lib/class_variants/instance.rb @@ -1,20 +1,36 @@ module ClassVariants class Instance - def initialize(**options, &block) + def initialize(...) + @bases = [] + @variants = [] + @defaults = {} + + merge(...) + end + + def merge(**options, &block) raise ArgumentError, "Use of hash config and code block is not supported" if !options.empty? && block_given? - @base = options.empty? ? {} : {default: options.fetch(:base, nil)} - @variants = expand_variants(options.fetch(:variants, {})) + expand_compound_variants(options.fetch(:compound_variants, [])) - @defaults = options.fetch(:defaults, {}) + (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) classes = overrides.delete(:class) + result = [] # Start with our default classes - result = [@base[slot]] + @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) @@ -44,10 +60,10 @@ def base(klass = nil, &block) if block_given? with_slots(&block).each do |slot| - @base[slot[:slot]] = slot[:class] + @bases << slot end else - @base[:default] = klass + @bases << {slot: :default, class: klass} end end diff --git a/test/merge_test.rb b/test/merge_test.rb new file mode 100644 index 0000000..7db792d --- /dev/null +++ b/test/merge_test.rb @@ -0,0 +1,71 @@ +require "test_helper" + +class MergeTest < Minitest::Test + def test_hash_merge + cv = ClassVariants.build( + base: "rounded", + variants: { + color: { + primary: "bg-blue-500", + secondary: "bg-purple-500", + success: "bg-green-500" + } + }, + default: { + color: :primary + } + ) + cv.merge( + base: "border", + variants: { + color: { + primary: "bg-blue-700", + secondary: "bg-purple-700" + } + }, + defaults: { + color: :secondary + } + ) + + assert_equal "rounded border bg-purple-500 bg-purple-700", cv.render + end + + def test_block_merge + cv = ClassVariants.build do + base do + slot :root, class: "rounded" + slot :title, class: "font-bold" + end + + variant variant: :outlined do + slot :root, class: "border-red-700" + slot :title, class: "text-red-700" + end + + variant variant: :filled do + slot :root, class: "bg-red-100" + slot :title, class: "text-red-900" + end + + defaults variant: :outlined + end + + cv.merge do + base do + slot :root, class: "mb-4" + slot :title, class: "mb-1" + end + + variant variant: :filled do + slot :root, class: "dark:bg-red-800" + slot :title, class: "dark:text-red-50" + end + + defaults variant: :filled + end + + assert_equal "rounded mb-4 bg-red-100 dark:bg-red-800", cv.render(:root) + assert_equal "font-bold mb-1 text-red-900 dark:text-red-50", cv.render(:title) + end +end