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 +4 -4
- data/README.md +5 -5
- data/app/javascript/tailmix/finder.js +8 -5
- data/examples/modal_component.rb +52 -23
- data/lib/tailmix/configuration.rb +13 -0
- data/lib/tailmix/definition/contexts/actions/element_builder.rb +2 -2
- data/lib/tailmix/definition/contexts/dimension_builder.rb +27 -9
- data/lib/tailmix/definition/contexts/element_builder.rb +3 -3
- data/lib/tailmix/definition/contexts/variant_builder.rb +35 -0
- data/lib/tailmix/definition/merger.rb +11 -18
- data/lib/tailmix/definition/result.rb +52 -5
- data/lib/tailmix/dev/docs.rb +8 -2
- data/lib/tailmix/dev/tools.rb +4 -0
- data/lib/tailmix/dsl.rb +35 -0
- data/lib/tailmix/html/attributes.rb +34 -11
- data/lib/tailmix/html/data_map.rb +7 -5
- data/lib/tailmix/html/selector.rb +19 -0
- data/lib/tailmix/runtime/context.rb +20 -12
- data/lib/tailmix/version.rb +1 -1
- data/lib/tailmix.rb +9 -43
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6eba946c8714d0699a6d9476c1c7b31f323418fdbf6fbc449bb96fb5ffe85f2
|
|
4
|
+
data.tar.gz: 44460ef22c33497ff09002ef084691dafdedf19be942685faf8f578155f98351
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
- `
|
|
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
|
-
|
|
63
|
-
|
|
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
|
-
|
|
75
|
-
|
|
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
|
-
*
|
|
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 = `[
|
|
9
|
+
const selector = `[data-tailmix-${name}]`;
|
|
10
|
+
|
|
11
|
+
if (rootElement.matches(selector)) {
|
|
12
|
+
return rootElement;
|
|
13
|
+
}
|
|
11
14
|
return rootElement.querySelector(selector);
|
|
12
15
|
}
|
data/examples/modal_component.rb
CHANGED
|
@@ -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
|
-
|
|
13
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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:
|
|
99
|
-
#
|
|
101
|
+
# - true:
|
|
102
|
+
# - classes : "visible opacity-100"
|
|
103
|
+
# - false:
|
|
104
|
+
# - classes : "invisible opacity-0"
|
|
100
105
|
# - size (default: :md)
|
|
101
|
-
# - :sm:
|
|
102
|
-
#
|
|
103
|
-
#
|
|
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
|
-
|
|
180
|
+
modal = ModalComponent.new(size: :lg, open: true)
|
|
171
181
|
# modal.lock!
|
|
172
|
-
|
|
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
|
-
#
|
|
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,
|
|
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
|
-
|
|
4
|
-
attr_reader :options
|
|
3
|
+
require_relative "variant_builder"
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
@dimensions[name.to_sym] =
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
data/lib/tailmix/dev/docs.rb
CHANGED
|
@@ -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[:
|
|
47
|
-
output << " - #{
|
|
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
|
data/lib/tailmix/dev/tools.rb
CHANGED
data/lib/tailmix/dsl.rb
ADDED
|
@@ -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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
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.
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
|
|
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:
|
|
65
|
+
data_map: attributes.data,
|
|
55
66
|
root_definition: @definition,
|
|
56
67
|
component: @component_instance
|
|
57
68
|
)
|
|
58
69
|
|
|
59
|
-
|
|
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
|
data/lib/tailmix/version.rb
CHANGED
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
|
|
49
|
-
@
|
|
15
|
+
def configuration
|
|
16
|
+
@configuration ||= Configuration.new
|
|
50
17
|
end
|
|
51
18
|
|
|
52
|
-
def
|
|
53
|
-
|
|
19
|
+
def configure
|
|
20
|
+
yield(configuration)
|
|
54
21
|
end
|
|
55
22
|
end
|
|
56
23
|
|
|
57
24
|
def self.included(base)
|
|
58
|
-
base.extend(
|
|
25
|
+
base.extend(DSL)
|
|
59
26
|
end
|
|
60
27
|
|
|
61
|
-
def tailmix(
|
|
62
|
-
|
|
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.
|
|
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-
|
|
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
|