tailmix 0.4.5 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6092dfded3153a41a28c7ca05e13fdbd52836c86b92576606aaafaabba030d12
4
- data.tar.gz: 2abf62eb8efb4a6fa6e7bc42da9a55e7e09610c9ad4a688af9030fb5a94c2d26
3
+ metadata.gz: f6eba946c8714d0699a6d9476c1c7b31f323418fdbf6fbc449bb96fb5ffe85f2
4
+ data.tar.gz: 44460ef22c33497ff09002ef084691dafdedf19be942685faf8f578155f98351
5
5
  SHA512:
6
- metadata.gz: ba691f44a9ae33dab521b918f89858cbd2d4d08ae867330845ff403764a24ae63c87eafc1a20b8b73b5a1b9c1c4762673398f0b4c79427186bb42e96e4d48f40
7
- data.tar.gz: b7bb32552b876c9c59d3c2e82450e3200fe4fe3aa4267c4b6ef94f1151836bbfa10f74a10ac80f7044a0fe2fefd69cf8f8d0a0717d9749abe8dc8b1f99e2d42e
6
+ metadata.gz: 587453b63976416198477d8b38c02ed24e7c3d48972464fa926d2ee0cad42737f324dede5e3dac92d78e584ba71cfb6552b1be7f4eb94485816a598e9d3fe5c3
7
+ data.tar.gz: 2d61fe96e8c2bf7ba9dc94876381bf0f5202dd0d22dd4aafe25ada2e977182b9693ae13d1f42c5d985980196ed1382587e9995fd5c614b925bab46f3159dec9f
data/README.md CHANGED
@@ -40,7 +40,7 @@ You define your component's appearance using a simple `tailmix do ... end` DSL i
40
40
 
41
41
  - `element :name, "base classes"`: Defines a logical part of your component (e.g., `:wrapper`, `:panel`, `:icon`).
42
42
  - `dimension :name, default: :value`: Defines a variant or "dimension" (e.g., `size` or `color`).
43
- - `option :value, "classes"`: Defines the classes for a specific variant option.
43
+ - `variant :value, "classes"`: Defines the classes for a specific variant.
44
44
  - `action :name, method: :add | :toggle | :remove`: Defines a named set of UI mutations that can be applied on the server (`.apply!`) or passed to the client (`action_payload`).
45
45
  - `stimulus`: A powerful nested DSL for declaratively describing Stimulus `data-*` attributes.
46
46
 
@@ -59,8 +59,8 @@ class ModalComponent
59
59
  tailmix do
60
60
  element :base, "fixed inset-0 z-50 flex items-center justify-center" do
61
61
  dimension :open, default: false do
62
- option true, "visible opacity-100"
63
- option false, "invisible opacity-0"
62
+ variant true, "visible opacity-100"
63
+ variant false, "invisible opacity-0"
64
64
  end
65
65
  stimulus.controller("modal")
66
66
  end
@@ -71,8 +71,8 @@ class ModalComponent
71
71
 
72
72
  element :panel, "relative bg-white rounded-lg shadow-xl" do
73
73
  dimension :size, default: :md do
74
- option :sm, "w-full max-w-sm p-4"
75
- option :md, "w-full max-w-md p-6"
74
+ variant :sm, "w-full max-w-sm p-4"
75
+ variant :md, "w-full max-w-md p-6"
76
76
  end
77
77
  end
78
78
 
@@ -1,12 +1,15 @@
1
- const SELECTOR_ATTRIBUTE = "data-tm-el";
2
-
3
1
  /**
4
- * find element [data-tm-el="..."]
2
+ * Finds an element by the new Tailmix selector convention.
3
+ * e.g., [data-tailmix-panel]
5
4
  * @param {HTMLElement} rootElement
6
- * @param {string} name
5
+ * @param {string} name - The logical name of the element (e.g., "panel").
7
6
  * @returns {HTMLElement|null}
8
7
  */
9
8
  export function findElement(rootElement, name) {
10
- const selector = `[${SELECTOR_ATTRIBUTE}="${name}"]`;
9
+ const selector = `[data-tailmix-${name}]`;
10
+
11
+ if (rootElement.matches(selector)) {
12
+ return rootElement;
13
+ }
11
14
  return rootElement.querySelector(selector);
12
15
  }
@@ -9,8 +9,8 @@ class ModalComponent
9
9
  tailmix do
10
10
  element :base, "fixed inset-0 z-50 flex items-center justify-center" do
11
11
  dimension :open, default: true do
12
- option true, "visible opacity-100"
13
- option false, "invisible opacity-0"
12
+ variant true, "visible opacity-100"
13
+ variant false, "invisible opacity-0"
14
14
  end
15
15
  stimulus.controller("modal").action_payload(:toggle, as: :toggle_data)
16
16
  end
@@ -21,9 +21,12 @@ class ModalComponent
21
21
 
22
22
  element :panel, "relative bg-white rounded-lg shadow-xl transition-transform transform" do
23
23
  dimension :size, default: :md do
24
- option :sm, "w-full max-w-sm p-4"
25
- option :md, "w-full max-w-md p-6"
26
- option :lg, "w-full max-w-lg p-8"
24
+ variant :sm, "w-full max-w-sm p-4" do
25
+ classes "dark:text-slate-400", group: :dark_mode
26
+ classes "one two"
27
+ end
28
+ variant :md, "w-full max-w-md p-6"
29
+ variant :lg, "w-full max-w-lg p-8"
27
30
  end
28
31
  stimulus.context("modal").target("panel")
29
32
  end
@@ -81,12 +84,12 @@ class ModalComponent
81
84
  end
82
85
 
83
86
  puts "-" * 100
84
- puts ModalComponent.dev.docs
85
- puts ""
86
- puts "Scaffolds:"
87
- puts ""
88
- puts ModalComponent.dev.stimulus.scaffold
89
- puts ""
87
+ # puts ModalComponent.dev.docs
88
+ # puts ""
89
+ # puts "Scaffolds:"
90
+ # puts ""
91
+ # puts ModalComponent.dev.stimulus.scaffold
92
+ # puts ""
90
93
 
91
94
  # >>>
92
95
  #
@@ -95,12 +98,19 @@ puts ""
95
98
  #
96
99
  # Dimensions:
97
100
  # - open (default: true)
98
- # - true: "visible opacity-100"
99
- # - false: "invisible opacity-0"
101
+ # - true:
102
+ # - classes : "visible opacity-100"
103
+ # - false:
104
+ # - classes : "invisible opacity-0"
100
105
  # - size (default: :md)
101
- # - :sm: "w-full max-w-sm p-4"
102
- # - :md: "w-full max-w-md p-6"
103
- # - :lg: "w-full max-w-lg p-8"
106
+ # - :sm:
107
+ # - classes : "w-full max-w-sm p-4"
108
+ # - classes (group: :dark_mode): "dark:text-slate-400"
109
+ # - classes : "one two"
110
+ # - :md:
111
+ # - classes : "w-full max-w-md p-6"
112
+ # - :lg:
113
+ # - classes : "w-full max-w-lg p-8"
104
114
  #
105
115
  # Actions:
106
116
  # - :toggle
@@ -167,14 +177,33 @@ puts ""
167
177
 
168
178
 
169
179
 
170
- # modal = ModalComponent.new(size: :lg, open: true)
180
+ modal = ModalComponent.new(size: :lg, open: true)
171
181
  # modal.lock!
172
- # ui = modal.ui
182
+ ui = modal.ui
183
+
184
+
185
+ def stringify_keys(obj)
186
+ case obj
187
+ when Hash
188
+ obj.transform_keys(&:to_s).transform_values { |v| stringify_keys(v) }
189
+ when Array
190
+ obj.map { |v| stringify_keys(v) }
191
+ else
192
+ obj
193
+ end
194
+ end
173
195
 
174
196
  # puts "Definition:"
175
- # pp ModalComponent.tailmix_definition
176
- # pp ModalComponent.tailmix_definition.to_h
177
- # ui.overlay.each do |key, value|
178
- # puts "#{key} :-> #{value.inspect.to_s[0, 75]}..."
179
- # end
197
+ # puts JSON.pretty_generate(stringify_keys(ModalComponent.tailmix_definition.to_h))
180
198
  # ui.action(:lock).apply!
199
+
200
+ ModalComponent.dev.elements.each do |element_name|
201
+ element = ui.send(element_name)
202
+ puts element_name
203
+ element.each_attribute do |attribute|
204
+ attribute.each do |key, value|
205
+ puts " #{key} :-> #{value}"
206
+ end
207
+ puts ""
208
+ end
209
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tailmix
4
+ # Stores the configuration for the Tailmix gem.
5
+ class Configuration
6
+ attr_accessor :element_selector_attribute, :dev_mode_attributes
7
+
8
+ def initialize
9
+ @element_selector_attribute = nil
10
+ @dev_mode_attributes = defined?(Rails) && Rails.env.development?
11
+ end
12
+ end
13
+ end
@@ -10,13 +10,13 @@ module Tailmix
10
10
  @commands = []
11
11
  end
12
12
 
13
- def classes(classes_string, method: @default_method)
13
+ def classes(classes_string, options = {})
14
+ method = options.fetch(:method, @default_method)
14
15
  @commands << { field: :classes, method: method, payload: classes_string }
15
16
  end
16
17
 
17
18
  def data(data_hash)
18
19
  operation = data_hash.delete(:method) || @default_method
19
-
20
20
  @commands << { field: :data, method: operation, payload: data_hash }
21
21
  end
22
22
 
@@ -1,16 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class DimensionBuilder
4
- attr_reader :options
3
+ require_relative "variant_builder"
5
4
 
6
- def initialize(default: nil)
7
- @options = { options: {}, default: default }
8
- end
5
+ module Tailmix
6
+ module Definition
7
+ module Contexts
8
+ class DimensionBuilder
9
+ def initialize(default: nil)
10
+ @variants = {}
11
+ @default = default
12
+ end
13
+
14
+ def variant(name, classes = "", data: {}, aria: {}, &block)
15
+ builder = VariantBuilder.new
16
+ builder.classes(classes) if classes && !classes.empty?
17
+ builder.data(data)
18
+ builder.aria(aria)
19
+
20
+ builder.instance_eval(&block) if block
21
+
22
+ @variants[name] = builder.build_variant
23
+ end
9
24
 
10
- def option(value, classes, default: false)
11
- @options[:options][value] = classes.split
12
- if default && @options[:default].nil?
13
- @options[:default] = value
25
+ def build_dimension
26
+ {
27
+ default: @default,
28
+ variants: @variants.freeze
29
+ }
30
+ end
31
+ end
14
32
  end
15
33
  end
16
34
  end
@@ -22,9 +22,9 @@ module Tailmix
22
22
  end
23
23
 
24
24
  def dimension(name, default: nil, &block)
25
- dimension = DimensionBuilder.new(default: default)
26
- dimension.instance_eval(&block)
27
- @dimensions[name.to_sym] = dimension.options
25
+ builder = Contexts::DimensionBuilder.new(default: default)
26
+ builder.instance_eval(&block)
27
+ @dimensions[name.to_sym] = builder.build_dimension
28
28
  end
29
29
 
30
30
  def build_definition
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tailmix
4
+ module Definition
5
+ module Contexts
6
+ class VariantBuilder
7
+ def initialize
8
+ @class_groups = []
9
+ @data = {}
10
+ @aria = {}
11
+ end
12
+
13
+ def classes(class_string, options = {})
14
+ @class_groups << { classes: class_string.to_s.split, options: options }
15
+ end
16
+
17
+ def data(hash)
18
+ @data.merge!(hash)
19
+ end
20
+
21
+ def aria(hash)
22
+ @aria.merge!(hash)
23
+ end
24
+
25
+ def build_variant
26
+ Definition::Result::Variant.new(
27
+ class_groups: @class_groups.freeze,
28
+ data: @data.freeze,
29
+ aria: @aria.freeze
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -63,28 +63,21 @@ module Tailmix
63
63
  Result::Stimulus.new(definitions: combined_definitions)
64
64
  end
65
65
 
66
- private
67
-
68
66
  def merge_dimensions(parent_dims, child_dims)
69
- deep_merge(parent_dims, child_dims) do |key, parent_val, child_val|
70
- if key == :options && parent_val.is_a?(Hash) && child_val.is_a?(Hash)
71
- parent_val.merge(child_val)
72
- else
73
- child_val
74
- end
75
- end
76
- end
67
+ all_keys = parent_dims.keys | child_dims.keys
68
+
69
+ all_keys.each_with_object({}) do |key, merged|
70
+ parent_val = parent_dims[key]
71
+ child_val = child_dims[key]
72
+
73
+ if parent_val && child_val
74
+ merged_variants = parent_val.fetch(:variants, {}).merge(child_val.fetch(:variants, {}))
77
75
 
78
- def deep_merge(parent_hash, child_hash, &block)
79
- child_hash.each_with_object(parent_hash.dup) do |(key, child_val), new_hash|
80
- parent_val = new_hash[key]
76
+ default = child_val.key?(:default) ? child_val[:default] : parent_val[:default]
81
77
 
82
- new_hash[key] = if parent_val.is_a?(Hash) && child_val.is_a?(Hash)
83
- deep_merge(parent_val, child_val, &block)
84
- elsif block_given? && new_hash.key?(key)
85
- block.call(key, parent_val, child_val)
78
+ merged[key] = { default: default, variants: merged_variants }
86
79
  else
87
- child_val
80
+ merged[key] = parent_val || child_val
88
81
  end
89
82
  end
90
83
  end
@@ -17,15 +17,62 @@ module Tailmix
17
17
  {
18
18
  name: name,
19
19
  attributes: attributes.to_h,
20
- dimensions: dimensions,
20
+ dimensions: dimensions.transform_values do |dimension|
21
+ dimension.transform_values do |value|
22
+ case value
23
+ when Variant
24
+ value.to_h
25
+ when Hash
26
+ value.transform_values { |v| v.respond_to?(:to_h) ? v.to_h : v }
27
+ else
28
+ value
29
+ end
30
+ end
31
+ end,
21
32
  stimulus: stimulus.to_h
22
33
  }
23
34
  end
24
35
  end
25
36
 
26
- Attributes = Struct.new(:classes, keyword_init: true)
27
- Stimulus = Struct.new(:definitions, keyword_init: true)
28
- Action = Struct.new(:action, :mutations, keyword_init: true)
37
+ Variant = Struct.new(:class_groups, :data, :aria, keyword_init: true) do
38
+ def classes
39
+ class_groups.flat_map { |group| group[:classes] }
40
+ end
41
+
42
+ def to_h
43
+ {
44
+ classes: classes,
45
+ class_groups: class_groups,
46
+ data: data,
47
+ aria: aria
48
+ }
49
+ end
50
+ end
51
+
52
+ Attributes = Struct.new(:classes, keyword_init: true) do
53
+ def to_h
54
+ {
55
+ classes: classes
56
+ }
57
+ end
58
+ end
59
+
60
+ Stimulus = Struct.new(:definitions, keyword_init: true) do
61
+ def to_h
62
+ {
63
+ definitions: definitions
64
+ }
65
+ end
66
+ end
67
+
68
+ Action = Struct.new(:action, :mutations, keyword_init: true) do
69
+ def to_h
70
+ {
71
+ action: action,
72
+ mutations: mutations
73
+ }
74
+ end
75
+ end
29
76
  end
30
77
  end
31
- end
78
+ end
@@ -43,8 +43,14 @@ module Tailmix
43
43
  all_dimensions.each do |dim_name, config|
44
44
  default_info = config[:default] ? "(default: #{config[:default].inspect})" : ""
45
45
  output << " - #{dim_name} #{default_info}"
46
- config[:options].each do |option_key, option_value|
47
- output << " - #{option_key.inspect}: \"#{option_value.join(' ')}\""
46
+ config[:variants].each do |variant_name, variant_def|
47
+ output << " - #{variant_name.inspect}:"
48
+ variant_def.class_groups.each do |group|
49
+ label = group[:options][:group] ? "(group: :#{group[:options][:group]})" : ""
50
+ output << " - classes #{label}: \"#{group[:classes].join(' ')}\""
51
+ end
52
+ output << " - data: #{variant_def.data.inspect}" if variant_def.data.any?
53
+ output << " - aria: #{variant_def.aria.inspect}" if variant_def.aria.any?
48
54
  end
49
55
  end
50
56
  else
@@ -21,6 +21,10 @@ module Tailmix
21
21
  def stimulus
22
22
  StimulusGenerator.new(@definition, @component_class.name)
23
23
  end
24
+
25
+ def elements
26
+ @definition.elements.values.map(&:name)
27
+ end
24
28
  end
25
29
  end
26
30
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "definition/context_builder"
4
+ require_relative "definition/merger"
5
+ require_relative "dev/tools"
6
+
7
+ module Tailmix
8
+ # The main DSL for defining component styles and behaviors.
9
+ # This module is extended into any class that includes Tailmix.
10
+ module DSL
11
+ def tailmix(&block)
12
+ child_context = Definition::ContextBuilder.new
13
+ child_context.instance_eval(&block)
14
+ child_definition = child_context.build_definition
15
+
16
+ if superclass.respond_to?(:tailmix_definition) && (parent_definition = superclass.tailmix_definition)
17
+ @tailmix_definition = Definition::Merger.call(parent_definition, child_definition)
18
+ else
19
+ @tailmix_definition = child_definition
20
+ end
21
+ end
22
+
23
+ def tailmix_definition
24
+ @tailmix_definition || raise(Error, "Tailmix definition not found in #{name}")
25
+ end
26
+
27
+ def tailmix_facade_class
28
+ @_tailmix_facade_class ||= Runtime::FacadeBuilder.build(tailmix_definition)
29
+ end
30
+
31
+ def dev
32
+ Dev::Tools.new(self)
33
+ end
34
+ end
35
+ end
@@ -3,18 +3,30 @@
3
3
  require "erb"
4
4
  require_relative "class_list"
5
5
  require_relative "data_map"
6
+ require_relative "selector"
6
7
 
7
8
  module Tailmix
8
9
  module HTML
9
10
  class Attributes < Hash
10
- attr_reader :element_name
11
+ attr_reader :element_name, :variant_string
11
12
 
12
- def initialize(initial_hash = {}, element_name: nil)
13
+ def initialize(initial_hash = {}, element_name: nil, variant_string: "")
13
14
  @element_name = element_name
15
+ @variant_string = variant_string
14
16
  super()
15
- self[:class] = ClassList.new
16
- self[:data] = DataMap.new
17
- merge!(initial_hash)
17
+
18
+ attrs_to_merge = initial_hash.dup
19
+
20
+ initial_classes = attrs_to_merge.delete(:class)
21
+ initial_data = attrs_to_merge.delete(:data)
22
+ initial_aria = attrs_to_merge.delete(:aria)
23
+
24
+ self[:class] = ClassList.new(initial_classes)
25
+ self[:data] = DataMap.new("data", initial_data || {})
26
+ self[:aria] = DataMap.new("aria", initial_aria || {})
27
+ self[:tailmix] = Selector.new(element_name, variant_string)
28
+
29
+ merge!(attrs_to_merge)
18
30
  end
19
31
 
20
32
  def each(&block)
@@ -22,15 +34,14 @@ module Tailmix
22
34
  end
23
35
 
24
36
  def to_h
25
- final_attrs = select { |k, _| !%i[class data].include?(k.to_sym) }
37
+ final_attrs = select { |k, _| !%i[class data aria tailmix].include?(k.to_sym) }
38
+
26
39
  class_string = self[:class].to_s
27
40
  final_attrs[:class] = class_string unless class_string.empty?
28
- final_attrs.merge!(self[:data].to_h)
29
41
 
30
- selector_attr = Tailmix.configuration.element_selector_attribute
31
- if selector_attr && @element_name
32
- final_attrs[selector_attr] = @element_name
33
- end
42
+ final_attrs.merge!(self[:data].to_h)
43
+ final_attrs.merge!(self[:aria].to_h)
44
+ final_attrs.merge!(self[:tailmix].to_h)
34
45
 
35
46
  final_attrs
36
47
  end
@@ -48,10 +59,18 @@ module Tailmix
48
59
  self[:data]
49
60
  end
50
61
 
62
+ def aria
63
+ self[:aria]
64
+ end
65
+
51
66
  def stimulus
52
67
  data.stimulus
53
68
  end
54
69
 
70
+ def tailmix
71
+ self[:tailmix]
72
+ end
73
+
55
74
  def toggle(class_names)
56
75
  classes.toggle(class_names)
57
76
  self
@@ -66,6 +85,10 @@ module Tailmix
66
85
  classes.remove(class_names)
67
86
  self
68
87
  end
88
+
89
+ def each_attribute(&block)
90
+ [ classes: classes, data: data.to_h, aria: aria.to_h, tailmix: tailmix.to_h ].each(&block)
91
+ end
69
92
  end
70
93
  end
71
94
  end
@@ -9,12 +9,14 @@ module Tailmix
9
9
  class DataMap
10
10
  MERGEABLE_LIST_ATTRIBUTES = %i[controller action target].freeze
11
11
 
12
- def initialize(initial_data = {})
12
+ def initialize(prefix, initial_data = {})
13
+ @prefix = prefix
13
14
  @data = {}
14
15
  merge!(initial_data)
15
16
  end
16
17
 
17
18
  def stimulus
19
+ raise "Stimulus builder is only available for data attributes" unless @prefix == "data"
18
20
  StimulusBuilder.new(self)
19
21
  end
20
22
 
@@ -26,7 +28,7 @@ module Tailmix
26
28
  key = key.to_sym
27
29
  if value.is_a?(Hash) && @data[key].is_a?(Hash)
28
30
  @data[key].merge!(value)
29
- elsif MERGEABLE_LIST_ATTRIBUTES.include?(key)
31
+ elsif @prefix == "data" && MERGEABLE_LIST_ATTRIBUTES.include?(key)
30
32
  add_to_set(key, value)
31
33
  else
32
34
  @data[key] = value
@@ -67,15 +69,15 @@ module Tailmix
67
69
  end
68
70
 
69
71
  def to_h
70
- flatten_data_hash(@data)
72
+ flatten_data_hash(@data, @prefix)
71
73
  end
72
74
 
73
75
  private
74
76
 
75
- def flatten_data_hash(hash, prefix = "data", accumulator = {})
77
+ def flatten_data_hash(hash, prefix, accumulator = {})
76
78
  hash.each do |key, value|
77
79
  current_key = "#{prefix}-#{key.to_s.tr('_', '-')}"
78
- if key.to_s.end_with?("_value")
80
+ if @prefix == "data" && key.to_s.end_with?("_value")
79
81
  serialized_value = case value
80
82
  when Hash, Array then value.to_json
81
83
  else value
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tailmix
4
+ module HTML
5
+ class Selector
6
+ def initialize(element_name, variant_string)
7
+ @element_name = element_name
8
+ @variant_string = variant_string
9
+ end
10
+
11
+ def to_h
12
+ return {} unless @element_name
13
+
14
+ key = "data-tailmix-#{@element_name}"
15
+ { key => @variant_string }
16
+ end
17
+ end
18
+ end
19
+ end
@@ -38,28 +38,36 @@ module Tailmix
38
38
 
39
39
  def build_attributes_for(element_name, dimensions)
40
40
  element_def = @definition.elements.fetch(element_name)
41
- initial_classes = element_def.attributes.classes
42
- class_list = HTML::ClassList.new(initial_classes)
43
41
 
44
- element_def.dimensions.each do |name, dim|
45
- value = dimensions.fetch(name, dim[:default])
42
+ active_dimensions = dimensions.slice(*element_def.dimensions.keys)
43
+ variant_string = active_dimensions.map { |k, v| "#{k}:#{v}" }.join(",")
44
+
45
+ attributes = HTML::Attributes.new(
46
+ { class: element_def.attributes.classes },
47
+ element_name: element_def.name,
48
+ variant_string: variant_string,
49
+ )
50
+
51
+ element_def.dimensions.each do |name, dim_def|
52
+ value = dimensions.fetch(name, dim_def[:default])
46
53
  next if value.nil?
47
- classes_for_option = dim.fetch(:options, {}).fetch(value, nil)
48
- class_list.add(classes_for_option)
54
+
55
+ variant_def = dim_def.fetch(:variants, {}).fetch(value, nil)
56
+ next unless variant_def
57
+
58
+ attributes.classes.add(variant_def.classes)
59
+ attributes.data.merge!(variant_def.data)
60
+ attributes.aria.merge!(variant_def.aria)
49
61
  end
50
62
 
51
- data_map = HTML::DataMap.new
52
63
  Stimulus::Compiler.call(
53
64
  definition: element_def.stimulus,
54
- data_map: data_map,
65
+ data_map: attributes.data,
55
66
  root_definition: @definition,
56
67
  component: @component_instance
57
68
  )
58
69
 
59
- HTML::Attributes.new(
60
- { class: class_list, data: data_map },
61
- element_name: element_def.name
62
- )
70
+ attributes
63
71
  end
64
72
  end
65
73
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tailmix
4
- VERSION = "0.4.5"
4
+ VERSION = "0.4.6"
5
5
  end
data/lib/tailmix.rb CHANGED
@@ -1,66 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "tailmix/version"
4
+ require_relative "tailmix/configuration"
5
+ require_relative "tailmix/dsl"
4
6
  require_relative "tailmix/definition"
5
7
  require_relative "tailmix/runtime"
6
- require_relative "tailmix/dev/tools"
7
8
 
8
9
  module Tailmix
9
10
  class Error < StandardError; end
10
11
 
11
12
  class << self
12
13
  attr_writer :configuration
13
- end
14
-
15
- def self.configuration
16
- @configuration ||= Configuration.new
17
- end
18
-
19
- def self.configure
20
- yield(configuration)
21
- end
22
-
23
- class Configuration
24
- attr_accessor :element_selector_attribute
25
-
26
- def initialize
27
- element_selector_attribute = nil
28
- end
29
- end
30
-
31
- module ClassMethods
32
- def tailmix(&block)
33
- child_context = Definition::ContextBuilder.new
34
- child_context.instance_eval(&block)
35
- child_definition = child_context.build_definition
36
-
37
- if superclass.respond_to?(:tailmix_definition) && (parent_definition = superclass.tailmix_definition)
38
- @tailmix_definition = Definition::Merger.call(parent_definition, child_definition)
39
- else
40
- @tailmix_definition = child_definition
41
- end
42
- end
43
-
44
- def tailmix_definition
45
- @tailmix_definition || raise(Error, "Tailmix definition not found in #{name}")
46
- end
47
14
 
48
- def tailmix_facade_class
49
- @_tailmix_facade_class ||= Runtime::FacadeBuilder.build(tailmix_definition)
15
+ def configuration
16
+ @configuration ||= Configuration.new
50
17
  end
51
18
 
52
- def dev
53
- Dev::Tools.new(self)
19
+ def configure
20
+ yield(configuration)
54
21
  end
55
22
  end
56
23
 
57
24
  def self.included(base)
58
- base.extend(ClassMethods)
25
+ base.extend(DSL)
59
26
  end
60
27
 
61
- def tailmix(options = {})
62
- facade_class = self.class.tailmix_facade_class
63
- facade_class.new(self, self.class.tailmix_definition, options)
28
+ def tailmix(dimensions = {})
29
+ self.class.tailmix_facade_class.new(self, self.class.tailmix_definition, dimensions)
64
30
  end
65
31
  end
66
32
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tailmix
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Fokin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-08-23 00:00:00.000000000 Z
11
+ date: 2025-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -77,6 +77,7 @@ files:
77
77
  - examples/modal_component.rb
78
78
  - lib/generators/tailmix/install_generator.rb
79
79
  - lib/tailmix.rb
80
+ - lib/tailmix/configuration.rb
80
81
  - lib/tailmix/definition.rb
81
82
  - lib/tailmix/definition/context_builder.rb
82
83
  - lib/tailmix/definition/contexts/action_builder.rb
@@ -85,15 +86,18 @@ files:
85
86
  - lib/tailmix/definition/contexts/dimension_builder.rb
86
87
  - lib/tailmix/definition/contexts/element_builder.rb
87
88
  - lib/tailmix/definition/contexts/stimulus_builder.rb
89
+ - lib/tailmix/definition/contexts/variant_builder.rb
88
90
  - lib/tailmix/definition/merger.rb
89
91
  - lib/tailmix/definition/result.rb
90
92
  - lib/tailmix/dev/docs.rb
91
93
  - lib/tailmix/dev/stimulus_generator.rb
92
94
  - lib/tailmix/dev/tools.rb
95
+ - lib/tailmix/dsl.rb
93
96
  - lib/tailmix/engine.rb
94
97
  - lib/tailmix/html/attributes.rb
95
98
  - lib/tailmix/html/class_list.rb
96
99
  - lib/tailmix/html/data_map.rb
100
+ - lib/tailmix/html/selector.rb
97
101
  - lib/tailmix/html/stimulus_builder.rb
98
102
  - lib/tailmix/runtime.rb
99
103
  - lib/tailmix/runtime/action.rb