tailmix 0.2.0 → 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/.rubocop.yml +3 -0
- data/README.md +147 -85
- data/app/javascript/tailmix/finder.js +15 -0
- data/app/javascript/tailmix/index.js +7 -0
- data/app/javascript/tailmix/mutator.js +28 -0
- data/app/javascript/tailmix/runner.js +7 -0
- data/app/javascript/tailmix/stimulus_adapter.js +37 -0
- data/examples/_modal_component.arb +36 -0
- data/examples/modal_component.rb +209 -0
- data/lib/generators/tailmix/install_generator.rb +19 -0
- data/lib/tailmix/configuration.rb +13 -0
- data/lib/tailmix/definition/context_builder.rb +39 -0
- data/lib/tailmix/definition/contexts/action_builder.rb +31 -0
- data/lib/tailmix/definition/contexts/actions/element_builder.rb +30 -0
- data/lib/tailmix/definition/contexts/attribute_builder.rb +21 -0
- data/lib/tailmix/definition/contexts/dimension_builder.rb +34 -0
- data/lib/tailmix/definition/contexts/element_builder.rb +41 -0
- data/lib/tailmix/definition/contexts/stimulus_builder.rb +101 -0
- data/lib/tailmix/definition/contexts/variant_builder.rb +35 -0
- data/lib/tailmix/definition/merger.rb +86 -0
- data/lib/tailmix/definition/result.rb +78 -0
- data/lib/tailmix/definition.rb +11 -0
- data/lib/tailmix/dev/docs.rb +88 -0
- data/lib/tailmix/dev/stimulus_generator.rb +124 -0
- data/lib/tailmix/dev/tools.rb +30 -0
- data/lib/tailmix/dsl.rb +35 -0
- data/lib/tailmix/engine.rb +17 -0
- data/lib/tailmix/html/attributes.rb +94 -0
- data/lib/tailmix/html/class_list.rb +79 -0
- data/lib/tailmix/html/data_map.rb +97 -0
- data/lib/tailmix/html/selector.rb +19 -0
- data/lib/tailmix/html/stimulus_builder.rb +65 -0
- data/lib/tailmix/runtime/action.rb +51 -0
- data/lib/tailmix/runtime/context.rb +74 -0
- data/lib/tailmix/runtime/facade_builder.rb +23 -0
- data/lib/tailmix/runtime/stimulus/compiler.rb +59 -0
- data/lib/tailmix/runtime.rb +14 -0
- data/lib/tailmix/version.rb +1 -1
- data/lib/tailmix.rb +18 -23
- metadata +37 -12
- data/examples/interactive_component.rb +0 -42
- data/examples/status_badge_component.rb +0 -44
- data/lib/tailmix/action.rb +0 -27
- data/lib/tailmix/dimension.rb +0 -18
- data/lib/tailmix/element.rb +0 -24
- data/lib/tailmix/manager.rb +0 -86
- data/lib/tailmix/part.rb +0 -39
- data/lib/tailmix/resolver.rb +0 -28
- data/lib/tailmix/schema.rb +0 -41
- data/lib/tailmix/utils.rb +0 -15
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Tailmix
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
|
8
|
+
def add_javascript
|
|
9
|
+
say "Pinning Tailmix JavaScript", :green
|
|
10
|
+
append_to_file "config/importmap.rb", <<~RUBY
|
|
11
|
+
pin "tailmix", to: "tailmix/index.js"
|
|
12
|
+
RUBY
|
|
13
|
+
|
|
14
|
+
say "Adding Tailmix to asset manifest", :green
|
|
15
|
+
append_to_file "app/assets/config/manifest.js", "\n//= link tailmix/index.js.js\n"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
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
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "contexts/action_builder"
|
|
4
|
+
require_relative "contexts/element_builder"
|
|
5
|
+
|
|
6
|
+
module Tailmix
|
|
7
|
+
module Definition
|
|
8
|
+
class ContextBuilder
|
|
9
|
+
attr_reader :elements, :actions
|
|
10
|
+
|
|
11
|
+
def initialize
|
|
12
|
+
@elements = {}
|
|
13
|
+
@actions = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def element(name, base_classes = "", &block)
|
|
17
|
+
builder = Contexts::ElementBuilder.new(name)
|
|
18
|
+
builder.attributes.classes(base_classes.split)
|
|
19
|
+
|
|
20
|
+
builder.instance_eval(&block) if block
|
|
21
|
+
|
|
22
|
+
@elements[name.to_sym] = builder
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def action(name, method:, &block)
|
|
26
|
+
builder = Contexts::ActionBuilder.new(method)
|
|
27
|
+
builder.instance_eval(&block) if block
|
|
28
|
+
@actions[name.to_sym] = builder
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def build_definition
|
|
32
|
+
Definition::Result::Context.new(
|
|
33
|
+
elements: @elements.transform_values(&:build_definition).freeze,
|
|
34
|
+
actions: @actions.transform_values(&:build_definition).freeze
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "actions/element_builder"
|
|
4
|
+
|
|
5
|
+
module Tailmix
|
|
6
|
+
module Definition
|
|
7
|
+
module Contexts
|
|
8
|
+
class ActionBuilder
|
|
9
|
+
def initialize(method)
|
|
10
|
+
@method = method
|
|
11
|
+
@mutations = {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def element(name, &block)
|
|
15
|
+
builder = Actions::ElementBuilder.new(@method)
|
|
16
|
+
builder.instance_eval(&block)
|
|
17
|
+
|
|
18
|
+
commands = builder.build_commands
|
|
19
|
+
@mutations[name.to_sym] = commands unless commands.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def build_definition
|
|
23
|
+
Definition::Result::Action.new(
|
|
24
|
+
action: @method,
|
|
25
|
+
mutations: @mutations.freeze
|
|
26
|
+
)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tailmix
|
|
4
|
+
module Definition
|
|
5
|
+
module Contexts
|
|
6
|
+
module Actions
|
|
7
|
+
class ElementBuilder
|
|
8
|
+
def initialize(default_method)
|
|
9
|
+
@default_method = default_method
|
|
10
|
+
@commands = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def classes(classes_string, options = {})
|
|
14
|
+
method = options.fetch(:method, @default_method)
|
|
15
|
+
@commands << { field: :classes, method: method, payload: classes_string }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def data(data_hash)
|
|
19
|
+
operation = data_hash.delete(:method) || @default_method
|
|
20
|
+
@commands << { field: :data, method: operation, payload: data_hash }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def build_commands
|
|
24
|
+
@commands
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tailmix
|
|
4
|
+
module Definition
|
|
5
|
+
module Contexts
|
|
6
|
+
class AttributeBuilder
|
|
7
|
+
def initialize
|
|
8
|
+
@classes = []
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def classes(*list)
|
|
12
|
+
@classes.concat(list.flatten.map(&:to_s))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def build_definition
|
|
16
|
+
Definition::Result::Attributes.new(classes: @classes.freeze)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "variant_builder"
|
|
4
|
+
|
|
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
|
|
24
|
+
|
|
25
|
+
def build_dimension
|
|
26
|
+
{
|
|
27
|
+
default: @default,
|
|
28
|
+
variants: @variants.freeze
|
|
29
|
+
}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "attribute_builder"
|
|
4
|
+
require_relative "stimulus_builder"
|
|
5
|
+
require_relative "dimension_builder"
|
|
6
|
+
|
|
7
|
+
module Tailmix
|
|
8
|
+
module Definition
|
|
9
|
+
module Contexts
|
|
10
|
+
class ElementBuilder
|
|
11
|
+
def initialize(name)
|
|
12
|
+
@name = name
|
|
13
|
+
@dimensions = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def attributes
|
|
17
|
+
@attributes_builder ||= AttributeBuilder.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def stimulus
|
|
21
|
+
@stimulus_builder ||= StimulusBuilder.new
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def dimension(name, default: nil, &block)
|
|
25
|
+
builder = Contexts::DimensionBuilder.new(default: default)
|
|
26
|
+
builder.instance_eval(&block)
|
|
27
|
+
@dimensions[name.to_sym] = builder.build_dimension
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def build_definition
|
|
31
|
+
Definition::Result::Element.new(
|
|
32
|
+
name: @name,
|
|
33
|
+
attributes: attributes.build_definition,
|
|
34
|
+
stimulus: stimulus.build_definition,
|
|
35
|
+
dimensions: @dimensions.freeze
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tailmix
|
|
4
|
+
module Definition
|
|
5
|
+
module Contexts
|
|
6
|
+
class StimulusBuilder
|
|
7
|
+
attr_reader :definitions
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@definitions = []
|
|
11
|
+
@current_context = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def controller(name)
|
|
15
|
+
@definitions << { type: :controller, name: name }
|
|
16
|
+
context(name)
|
|
17
|
+
end
|
|
18
|
+
alias_method :ctr, :controller
|
|
19
|
+
|
|
20
|
+
def context(name)
|
|
21
|
+
@current_context = name.to_s
|
|
22
|
+
self
|
|
23
|
+
end
|
|
24
|
+
alias_method :ctx, :context
|
|
25
|
+
|
|
26
|
+
def target(target_name)
|
|
27
|
+
raise "A controller context must be set..." unless @current_context
|
|
28
|
+
@definitions << { type: :target, controller: @current_context, name: target_name }
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def action(*args)
|
|
33
|
+
ensure_context!
|
|
34
|
+
|
|
35
|
+
action_data = case args.first
|
|
36
|
+
when String
|
|
37
|
+
{ type: :raw, content: args.first }
|
|
38
|
+
when Hash
|
|
39
|
+
{ type: :hash, content: args.first }
|
|
40
|
+
else
|
|
41
|
+
{ type: :tuple, content: args }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
@definitions << { type: :action, controller: @current_context, data: action_data }
|
|
45
|
+
self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def value(value_name, value: nil, call: nil, method: nil)
|
|
49
|
+
ensure_context!
|
|
50
|
+
|
|
51
|
+
source = if !value.nil?
|
|
52
|
+
{ type: :literal, content: value }
|
|
53
|
+
elsif call.is_a?(Proc)
|
|
54
|
+
{ type: :proc, content: call }
|
|
55
|
+
elsif method.is_a?(Symbol) || method.is_a?(String)
|
|
56
|
+
{ type: :method, content: method.to_sym }
|
|
57
|
+
else
|
|
58
|
+
raise ArgumentError, "You must provide one of value:, call:, or method: keyword arguments."
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@definitions << {
|
|
62
|
+
type: :value,
|
|
63
|
+
controller: @current_context,
|
|
64
|
+
name: value_name,
|
|
65
|
+
source: source
|
|
66
|
+
}
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def action_payload(action_name, as: nil)
|
|
71
|
+
ensure_context!
|
|
72
|
+
value_name = as || "#{action_name}_action"
|
|
73
|
+
|
|
74
|
+
@definitions << {
|
|
75
|
+
type: :action_payload,
|
|
76
|
+
controller: @current_context,
|
|
77
|
+
action_name: action_name.to_sym,
|
|
78
|
+
value_name: value_name.to_sym
|
|
79
|
+
}
|
|
80
|
+
self
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def param(params_hash)
|
|
84
|
+
ensure_context!
|
|
85
|
+
@definitions << { type: :param, controller: @current_context, params: params_hash }
|
|
86
|
+
self
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def build_definition
|
|
90
|
+
Definition::Result::Stimulus.new(definitions: @definitions.freeze)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def ensure_context!
|
|
96
|
+
raise "A controller context must be set via .controller() or .context() before this call." unless @current_context
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
@@ -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
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tailmix
|
|
4
|
+
module Definition
|
|
5
|
+
# A service object responsible for deep-merging two tailmix definitions.
|
|
6
|
+
class Merger
|
|
7
|
+
def self.call(parent_def, child_def)
|
|
8
|
+
new(parent_def, child_def).merge
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(parent_def, child_def)
|
|
12
|
+
@parent_def = parent_def
|
|
13
|
+
@child_def = child_def
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def merge
|
|
17
|
+
Result::Context.new(
|
|
18
|
+
elements: merged_elements,
|
|
19
|
+
actions: merged_actions
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def merged_actions
|
|
26
|
+
# For actions, child definitions completely override parent definitions.
|
|
27
|
+
@parent_def.actions.merge(@child_def.actions)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def merged_elements
|
|
31
|
+
all_element_keys = (@parent_def.elements.keys | @child_def.elements.keys)
|
|
32
|
+
|
|
33
|
+
all_element_keys.each_with_object({}) do |key, h|
|
|
34
|
+
parent_element = @parent_def.elements[key]
|
|
35
|
+
child_element = @child_def.elements[key]
|
|
36
|
+
|
|
37
|
+
h[key] = if parent_element && child_element
|
|
38
|
+
merge_element(parent_element, child_element)
|
|
39
|
+
else
|
|
40
|
+
child_element || parent_element
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def merge_element(parent_el, child_el)
|
|
46
|
+
Result::Element.new(
|
|
47
|
+
name: parent_el.name,
|
|
48
|
+
attributes: merge_attributes(parent_el.attributes, child_el.attributes),
|
|
49
|
+
dimensions: merge_dimensions(parent_el.dimensions, child_el.dimensions),
|
|
50
|
+
stimulus: merge_stimulus(parent_el.stimulus, child_el.stimulus)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def merge_attributes(parent_attrs, child_attrs)
|
|
55
|
+
# Combine base classes, ensuring no duplicates.
|
|
56
|
+
combined_classes = (parent_attrs.classes + child_attrs.classes).uniq
|
|
57
|
+
Result::Attributes.new(classes: combined_classes)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def merge_stimulus(parent_stimulus, child_stimulus)
|
|
61
|
+
# Combine stimulus definitions.
|
|
62
|
+
combined_definitions = parent_stimulus.definitions + child_stimulus.definitions
|
|
63
|
+
Result::Stimulus.new(definitions: combined_definitions)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def merge_dimensions(parent_dims, child_dims)
|
|
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, {}))
|
|
75
|
+
|
|
76
|
+
default = child_val.key?(:default) ? child_val[:default] : parent_val[:default]
|
|
77
|
+
|
|
78
|
+
merged[key] = { default: default, variants: merged_variants }
|
|
79
|
+
else
|
|
80
|
+
merged[key] = parent_val || child_val
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tailmix
|
|
4
|
+
module Definition
|
|
5
|
+
module Result
|
|
6
|
+
Context = Struct.new(:elements, :actions, keyword_init: true) do
|
|
7
|
+
def to_h
|
|
8
|
+
{
|
|
9
|
+
elements: elements.transform_values(&:to_h),
|
|
10
|
+
actions: actions.transform_values(&:to_h)
|
|
11
|
+
}
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Element = Struct.new(:name, :attributes, :dimensions, :stimulus, keyword_init: true) do
|
|
16
|
+
def to_h
|
|
17
|
+
{
|
|
18
|
+
name: name,
|
|
19
|
+
attributes: attributes.to_h,
|
|
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,
|
|
32
|
+
stimulus: stimulus.to_h
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
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
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tailmix
|
|
4
|
+
module Dev
|
|
5
|
+
class Docs
|
|
6
|
+
|
|
7
|
+
def initialize(tools)
|
|
8
|
+
@tools = tools
|
|
9
|
+
@definition = tools.definition
|
|
10
|
+
@component_class_name = tools.component_class
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def generate
|
|
14
|
+
output = ["== Tailmix Docs for #{@component_class_name} =="]
|
|
15
|
+
|
|
16
|
+
signature = generate_signature
|
|
17
|
+
output << "Signature: `initialize(#{signature})`" unless signature.empty?
|
|
18
|
+
output << ""
|
|
19
|
+
|
|
20
|
+
output << generate_dimensions_docs
|
|
21
|
+
output << ""
|
|
22
|
+
output << generate_actions_docs
|
|
23
|
+
output << ""
|
|
24
|
+
output << generate_stimulus_docs
|
|
25
|
+
|
|
26
|
+
output.join("\n")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def generate_signature
|
|
32
|
+
all_dimensions
|
|
33
|
+
.map { |name, config| "#{name}: #{config[:default].inspect}" if config.key?(:default) }
|
|
34
|
+
.compact
|
|
35
|
+
.join(", ")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def generate_dimensions_docs
|
|
39
|
+
output = []
|
|
40
|
+
|
|
41
|
+
if all_dimensions.any?
|
|
42
|
+
output << "Dimensions:"
|
|
43
|
+
all_dimensions.each do |dim_name, config|
|
|
44
|
+
default_info = config[:default] ? "(default: #{config[:default].inspect})" : ""
|
|
45
|
+
output << " - #{dim_name} #{default_info}"
|
|
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?
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
output << "No dimensions defined."
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
output.join("\n")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def generate_actions_docs
|
|
64
|
+
output = []
|
|
65
|
+
actions = @definition.actions
|
|
66
|
+
|
|
67
|
+
if actions.any?
|
|
68
|
+
output << "Actions:"
|
|
69
|
+
actions.keys.each do |action_name|
|
|
70
|
+
output << " - :#{action_name}"
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
output << "No actions defined."
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
output.join("\n")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def generate_stimulus_docs
|
|
80
|
+
@tools.stimulus.scaffold(show_docs: true)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def all_dimensions
|
|
84
|
+
@_all_dimensions ||= @definition.elements.values.flat_map(&:dimensions).reduce({}, :merge)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|