vident 1.0.2 → 2.0.0
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/CHANGELOG.md +43 -0
- data/README.md +45 -17
- data/lib/vident/caching.rb +4 -110
- data/lib/vident/capabilities/caching.rb +98 -0
- data/lib/vident/capabilities/child_element_rendering.rb +92 -0
- data/lib/vident/capabilities/class_list_building.rb +23 -0
- data/lib/vident/capabilities/declarable.rb +39 -0
- data/lib/vident/capabilities/identifiable.rb +54 -0
- data/lib/vident/capabilities/inspectable.rb +17 -0
- data/lib/vident/capabilities/root_element_rendering.rb +31 -0
- data/lib/vident/capabilities/stimulus_data_emitting.rb +98 -0
- data/lib/vident/capabilities/stimulus_declaring.rb +79 -0
- data/lib/vident/capabilities/stimulus_draft.rb +51 -0
- data/lib/vident/capabilities/stimulus_mutation.rb +60 -0
- data/lib/vident/capabilities/stimulus_parsing.rb +144 -0
- data/lib/vident/capabilities/tailwind.rb +18 -0
- data/lib/vident/component.rb +14 -76
- data/lib/vident/engine.rb +6 -5
- data/lib/vident/error.rb +16 -0
- data/lib/{vident2 → vident}/internals/action_builder.rb +18 -22
- data/lib/vident/internals/attribute_writer.rb +17 -0
- data/lib/{vident2 → vident}/internals/class_list_builder.rb +5 -22
- data/lib/vident/internals/declaration.rb +13 -0
- data/lib/{vident2 → vident}/internals/declarations.rb +6 -18
- data/lib/{vident2 → vident}/internals/draft.rb +3 -16
- data/lib/{vident2 → vident}/internals/dsl.rb +6 -32
- data/lib/vident/internals/plan.rb +9 -0
- data/lib/vident/internals/registry.rb +37 -0
- data/lib/{vident2 → vident}/internals/resolver.rb +101 -91
- data/lib/{vident2 → vident}/internals/target_builder.rb +1 -7
- data/lib/vident/stable_id.rb +3 -3
- data/lib/{vident2 → vident}/stimulus/action.rb +11 -24
- data/lib/vident/stimulus/base.rb +26 -0
- data/lib/{vident2 → vident}/stimulus/class_map.rb +6 -18
- data/lib/{vident2 → vident}/stimulus/collection.rb +6 -8
- data/lib/vident/stimulus/combinable.rb +30 -0
- data/lib/vident/stimulus/controller.rb +45 -0
- data/lib/vident/stimulus/naming.rb +9 -9
- data/lib/vident/stimulus/null.rb +7 -0
- data/lib/{vident2 → vident}/stimulus/outlet.rb +12 -32
- data/lib/{vident2 → vident}/stimulus/param.rb +5 -11
- data/lib/{vident2 → vident}/stimulus/target.rb +5 -14
- data/lib/vident/stimulus/value.rb +57 -0
- data/lib/vident/stimulus_null.rb +4 -8
- data/lib/vident/tailwind.rb +4 -17
- data/lib/vident/types.rb +28 -0
- data/lib/vident/version.rb +1 -6
- data/lib/vident.rb +44 -36
- data/skills/vident/SKILL.md +122 -19
- data/skills/vident/api-reference.md +259 -115
- data/skills/vident/examples.md +23 -10
- metadata +38 -60
- data/lib/vident/child_element_helper.rb +0 -64
- data/lib/vident/class_list_builder.rb +0 -112
- data/lib/vident/component_attribute_resolver.rb +0 -106
- data/lib/vident/component_class_lists.rb +0 -37
- data/lib/vident/stimulus/primitive.rb +0 -38
- data/lib/vident/stimulus.rb +0 -31
- data/lib/vident/stimulus_action.rb +0 -133
- data/lib/vident/stimulus_action_collection.rb +0 -11
- data/lib/vident/stimulus_attribute_base.rb +0 -67
- data/lib/vident/stimulus_attributes.rb +0 -129
- data/lib/vident/stimulus_builder.rb +0 -136
- data/lib/vident/stimulus_class.rb +0 -59
- data/lib/vident/stimulus_class_collection.rb +0 -11
- data/lib/vident/stimulus_collection_base.rb +0 -51
- data/lib/vident/stimulus_component.rb +0 -75
- data/lib/vident/stimulus_controller.rb +0 -41
- data/lib/vident/stimulus_controller_collection.rb +0 -14
- data/lib/vident/stimulus_data_attribute_builder.rb +0 -32
- data/lib/vident/stimulus_helper.rb +0 -66
- data/lib/vident/stimulus_outlet.rb +0 -90
- data/lib/vident/stimulus_outlet_collection.rb +0 -11
- data/lib/vident/stimulus_param.rb +0 -42
- data/lib/vident/stimulus_param_collection.rb +0 -11
- data/lib/vident/stimulus_target.rb +0 -47
- data/lib/vident/stimulus_target_collection.rb +0 -18
- data/lib/vident/stimulus_value.rb +0 -39
- data/lib/vident/stimulus_value_collection.rb +0 -11
- data/lib/vident2/caching.rb +0 -93
- data/lib/vident2/component.rb +0 -538
- data/lib/vident2/engine.rb +0 -18
- data/lib/vident2/error.rb +0 -30
- data/lib/vident2/internals/attribute_writer.rb +0 -22
- data/lib/vident2/internals/declaration.rb +0 -17
- data/lib/vident2/internals/plan.rb +0 -12
- data/lib/vident2/internals/registry.rb +0 -41
- data/lib/vident2/phlex/html.rb +0 -84
- data/lib/vident2/phlex.rb +0 -9
- data/lib/vident2/stimulus/controller.rb +0 -59
- data/lib/vident2/stimulus/naming.rb +0 -26
- data/lib/vident2/stimulus/null.rb +0 -16
- data/lib/vident2/stimulus/value.rb +0 -77
- data/lib/vident2/tailwind.rb +0 -19
- data/lib/vident2/version.rb +0 -5
- data/lib/vident2/view_component/base.rb +0 -124
- data/lib/vident2/view_component.rb +0 -9
- data/lib/vident2.rb +0 -50
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vident
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stephen Ierodiaconou
|
|
@@ -86,72 +86,50 @@ files:
|
|
|
86
86
|
- lib/generators/vident/install/templates/vident.rb
|
|
87
87
|
- lib/vident.rb
|
|
88
88
|
- lib/vident/caching.rb
|
|
89
|
-
- lib/vident/
|
|
90
|
-
- lib/vident/
|
|
89
|
+
- lib/vident/capabilities/caching.rb
|
|
90
|
+
- lib/vident/capabilities/child_element_rendering.rb
|
|
91
|
+
- lib/vident/capabilities/class_list_building.rb
|
|
92
|
+
- lib/vident/capabilities/declarable.rb
|
|
93
|
+
- lib/vident/capabilities/identifiable.rb
|
|
94
|
+
- lib/vident/capabilities/inspectable.rb
|
|
95
|
+
- lib/vident/capabilities/root_element_rendering.rb
|
|
96
|
+
- lib/vident/capabilities/stimulus_data_emitting.rb
|
|
97
|
+
- lib/vident/capabilities/stimulus_declaring.rb
|
|
98
|
+
- lib/vident/capabilities/stimulus_draft.rb
|
|
99
|
+
- lib/vident/capabilities/stimulus_mutation.rb
|
|
100
|
+
- lib/vident/capabilities/stimulus_parsing.rb
|
|
101
|
+
- lib/vident/capabilities/tailwind.rb
|
|
91
102
|
- lib/vident/component.rb
|
|
92
|
-
- lib/vident/component_attribute_resolver.rb
|
|
93
|
-
- lib/vident/component_class_lists.rb
|
|
94
103
|
- lib/vident/engine.rb
|
|
104
|
+
- lib/vident/error.rb
|
|
105
|
+
- lib/vident/internals/action_builder.rb
|
|
106
|
+
- lib/vident/internals/attribute_writer.rb
|
|
107
|
+
- lib/vident/internals/class_list_builder.rb
|
|
108
|
+
- lib/vident/internals/declaration.rb
|
|
109
|
+
- lib/vident/internals/declarations.rb
|
|
110
|
+
- lib/vident/internals/draft.rb
|
|
111
|
+
- lib/vident/internals/dsl.rb
|
|
112
|
+
- lib/vident/internals/plan.rb
|
|
113
|
+
- lib/vident/internals/registry.rb
|
|
114
|
+
- lib/vident/internals/resolver.rb
|
|
115
|
+
- lib/vident/internals/target_builder.rb
|
|
95
116
|
- lib/vident/stable_id.rb
|
|
96
|
-
- lib/vident/stimulus.rb
|
|
117
|
+
- lib/vident/stimulus/action.rb
|
|
118
|
+
- lib/vident/stimulus/base.rb
|
|
119
|
+
- lib/vident/stimulus/class_map.rb
|
|
120
|
+
- lib/vident/stimulus/collection.rb
|
|
121
|
+
- lib/vident/stimulus/combinable.rb
|
|
122
|
+
- lib/vident/stimulus/controller.rb
|
|
97
123
|
- lib/vident/stimulus/naming.rb
|
|
98
|
-
- lib/vident/stimulus/
|
|
99
|
-
- lib/vident/
|
|
100
|
-
- lib/vident/
|
|
101
|
-
- lib/vident/
|
|
102
|
-
- lib/vident/
|
|
103
|
-
- lib/vident/stimulus_builder.rb
|
|
104
|
-
- lib/vident/stimulus_class.rb
|
|
105
|
-
- lib/vident/stimulus_class_collection.rb
|
|
106
|
-
- lib/vident/stimulus_collection_base.rb
|
|
107
|
-
- lib/vident/stimulus_component.rb
|
|
108
|
-
- lib/vident/stimulus_controller.rb
|
|
109
|
-
- lib/vident/stimulus_controller_collection.rb
|
|
110
|
-
- lib/vident/stimulus_data_attribute_builder.rb
|
|
111
|
-
- lib/vident/stimulus_helper.rb
|
|
124
|
+
- lib/vident/stimulus/null.rb
|
|
125
|
+
- lib/vident/stimulus/outlet.rb
|
|
126
|
+
- lib/vident/stimulus/param.rb
|
|
127
|
+
- lib/vident/stimulus/target.rb
|
|
128
|
+
- lib/vident/stimulus/value.rb
|
|
112
129
|
- lib/vident/stimulus_null.rb
|
|
113
|
-
- lib/vident/stimulus_outlet.rb
|
|
114
|
-
- lib/vident/stimulus_outlet_collection.rb
|
|
115
|
-
- lib/vident/stimulus_param.rb
|
|
116
|
-
- lib/vident/stimulus_param_collection.rb
|
|
117
|
-
- lib/vident/stimulus_target.rb
|
|
118
|
-
- lib/vident/stimulus_target_collection.rb
|
|
119
|
-
- lib/vident/stimulus_value.rb
|
|
120
|
-
- lib/vident/stimulus_value_collection.rb
|
|
121
130
|
- lib/vident/tailwind.rb
|
|
131
|
+
- lib/vident/types.rb
|
|
122
132
|
- lib/vident/version.rb
|
|
123
|
-
- lib/vident2.rb
|
|
124
|
-
- lib/vident2/caching.rb
|
|
125
|
-
- lib/vident2/component.rb
|
|
126
|
-
- lib/vident2/engine.rb
|
|
127
|
-
- lib/vident2/error.rb
|
|
128
|
-
- lib/vident2/internals/action_builder.rb
|
|
129
|
-
- lib/vident2/internals/attribute_writer.rb
|
|
130
|
-
- lib/vident2/internals/class_list_builder.rb
|
|
131
|
-
- lib/vident2/internals/declaration.rb
|
|
132
|
-
- lib/vident2/internals/declarations.rb
|
|
133
|
-
- lib/vident2/internals/draft.rb
|
|
134
|
-
- lib/vident2/internals/dsl.rb
|
|
135
|
-
- lib/vident2/internals/plan.rb
|
|
136
|
-
- lib/vident2/internals/registry.rb
|
|
137
|
-
- lib/vident2/internals/resolver.rb
|
|
138
|
-
- lib/vident2/internals/target_builder.rb
|
|
139
|
-
- lib/vident2/phlex.rb
|
|
140
|
-
- lib/vident2/phlex/html.rb
|
|
141
|
-
- lib/vident2/stimulus/action.rb
|
|
142
|
-
- lib/vident2/stimulus/class_map.rb
|
|
143
|
-
- lib/vident2/stimulus/collection.rb
|
|
144
|
-
- lib/vident2/stimulus/controller.rb
|
|
145
|
-
- lib/vident2/stimulus/naming.rb
|
|
146
|
-
- lib/vident2/stimulus/null.rb
|
|
147
|
-
- lib/vident2/stimulus/outlet.rb
|
|
148
|
-
- lib/vident2/stimulus/param.rb
|
|
149
|
-
- lib/vident2/stimulus/target.rb
|
|
150
|
-
- lib/vident2/stimulus/value.rb
|
|
151
|
-
- lib/vident2/tailwind.rb
|
|
152
|
-
- lib/vident2/version.rb
|
|
153
|
-
- lib/vident2/view_component.rb
|
|
154
|
-
- lib/vident2/view_component/base.rb
|
|
155
133
|
- skills/vident/SKILL.md
|
|
156
134
|
- skills/vident/api-reference.md
|
|
157
135
|
- skills/vident/examples.md
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vident
|
|
4
|
-
module ChildElementHelper
|
|
5
|
-
# Explicit kwargs (14 of them, 1 plural + 1 singular per primitive) are the
|
|
6
|
-
# public API — keep them so call-sites get typo-checking and IDE support.
|
|
7
|
-
# The body is registry-driven via `Stimulus::PRIMITIVES`.
|
|
8
|
-
def child_element(
|
|
9
|
-
tag_name,
|
|
10
|
-
stimulus_controllers: nil,
|
|
11
|
-
stimulus_targets: nil,
|
|
12
|
-
stimulus_actions: nil,
|
|
13
|
-
stimulus_outlets: nil,
|
|
14
|
-
stimulus_values: nil,
|
|
15
|
-
stimulus_params: nil,
|
|
16
|
-
stimulus_classes: nil,
|
|
17
|
-
stimulus_controller: nil,
|
|
18
|
-
stimulus_target: nil,
|
|
19
|
-
stimulus_action: nil,
|
|
20
|
-
stimulus_outlet: nil,
|
|
21
|
-
stimulus_value: nil,
|
|
22
|
-
stimulus_param: nil,
|
|
23
|
-
stimulus_class: nil,
|
|
24
|
-
**options,
|
|
25
|
-
&block
|
|
26
|
-
)
|
|
27
|
-
inputs = {
|
|
28
|
-
controllers: [stimulus_controllers, stimulus_controller],
|
|
29
|
-
actions: [stimulus_actions, stimulus_action],
|
|
30
|
-
targets: [stimulus_targets, stimulus_target],
|
|
31
|
-
outlets: [stimulus_outlets, stimulus_outlet],
|
|
32
|
-
values: [stimulus_values, stimulus_value],
|
|
33
|
-
params: [stimulus_params, stimulus_param],
|
|
34
|
-
classes: [stimulus_classes, stimulus_class]
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
collections = Stimulus::PRIMITIVES.to_h do |primitive|
|
|
38
|
-
plural, singular = inputs.fetch(primitive.name)
|
|
39
|
-
child_element_attribute_must_be_collection!(plural, primitive.key.to_s)
|
|
40
|
-
args = primitive.keyed? ? [plural || singular] : child_element_wrap_single_stimulus_attribute(plural, singular)
|
|
41
|
-
[primitive.name, send(primitive.key, *Array.wrap(args))]
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
data_attrs = StimulusDataAttributeBuilder.new(**collections).build
|
|
45
|
-
generate_child_element(tag_name, data_attrs, options, &block)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
def child_element_attribute_must_be_collection!(collection, name)
|
|
51
|
-
return unless collection
|
|
52
|
-
raise ArgumentError, "'#{name}:' must be an enumerable. Did you mean '#{name.to_s.singularize}:'?" unless collection.is_a?(Enumerable)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def child_element_wrap_single_stimulus_attribute(plural, singular)
|
|
56
|
-
return plural if plural
|
|
57
|
-
singular.nil? ? nil : [singular]
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def generate_child_element(tag_name, stimulus_data_attributes, options, &block)
|
|
61
|
-
raise NoMethodError, "Not implemented"
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "set"
|
|
4
|
-
|
|
5
|
-
module Vident
|
|
6
|
-
class ClassListBuilder
|
|
7
|
-
CLASSNAME_SEPARATOR = " "
|
|
8
|
-
|
|
9
|
-
# If the HTML "class" option is provided, it is taken in order of precedence of source.
|
|
10
|
-
# The order of precedence is:
|
|
11
|
-
# lowest | root_element_classes => whatever is returned
|
|
12
|
-
# ....... | root_element_attributes => the `html_options[:class]` value
|
|
13
|
-
# ....... | root_element(class: ...) => the `class` value of the arguments passed to the root element
|
|
14
|
-
# highest | render MyComponent.new(html_options: { class: ... }) => the `html_options[:class]` value
|
|
15
|
-
# The "classes" prop on the component on the other hand is used to add additional classes to the component.
|
|
16
|
-
# eg: render MyComponent.new(classes: "my-additional-class another-class")
|
|
17
|
-
def initialize(tailwind_merger: nil, component_name: nil, root_element_attributes_classes: nil, root_element_classes: nil, root_element_html_class: nil, html_class: nil, additional_classes: nil)
|
|
18
|
-
@class_list = component_name ? [component_name] : []
|
|
19
|
-
@class_list.concat(Array.wrap(root_element_classes)) if root_element_classes && !root_element_attributes_classes && !root_element_html_class && !html_class
|
|
20
|
-
@class_list.concat(Array.wrap(root_element_attributes_classes)) if root_element_attributes_classes && !root_element_html_class && !html_class
|
|
21
|
-
@class_list.concat(Array.wrap(root_element_html_class)) if root_element_html_class && !html_class
|
|
22
|
-
@class_list.concat(Array.wrap(html_class)) if html_class
|
|
23
|
-
@class_list.concat(Array.wrap(additional_classes)) if additional_classes
|
|
24
|
-
@class_list.compact!
|
|
25
|
-
|
|
26
|
-
@tailwind_merger = tailwind_merger
|
|
27
|
-
|
|
28
|
-
if @tailwind_merger && !defined?(::TailwindMerge::Merger)
|
|
29
|
-
raise LoadError, "TailwindMerge gem is required when using tailwind_merger:. Add 'gem \"tailwind_merge\"' to your Gemfile."
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Main method to build a final class list from multiple sources
|
|
34
|
-
# @param class_lists [Array<String, Array, StimulusClass, nil>] Multiple class sources to merge
|
|
35
|
-
# @param stimulus_class_names [Array<Symbol, String>] Optional names of stimulus classes to include
|
|
36
|
-
# @return [String, nil] Final space-separated class string or nil if no classes
|
|
37
|
-
def build(extra_classes = nil, stimulus_class_names: [])
|
|
38
|
-
class_list = @class_list + Array.wrap(extra_classes).compact
|
|
39
|
-
flattened_classes = flatten_and_normalize_classes(class_list, stimulus_class_names)
|
|
40
|
-
return nil if flattened_classes.empty?
|
|
41
|
-
|
|
42
|
-
deduplicated_classes = dedupe_classes(flattened_classes)
|
|
43
|
-
return nil if deduplicated_classes.blank?
|
|
44
|
-
|
|
45
|
-
class_string = deduplicated_classes.join(CLASSNAME_SEPARATOR)
|
|
46
|
-
|
|
47
|
-
if @tailwind_merger
|
|
48
|
-
dedupe_with_tailwind(class_string)
|
|
49
|
-
else
|
|
50
|
-
class_string
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
private
|
|
55
|
-
|
|
56
|
-
# Flatten and normalize all input class sources
|
|
57
|
-
def flatten_and_normalize_classes(class_lists, stimulus_class_names)
|
|
58
|
-
stimulus_class_names_set = stimulus_class_names.map { |name| name.to_s.dasherize }.to_set
|
|
59
|
-
|
|
60
|
-
class_lists.compact.flat_map do |class_source|
|
|
61
|
-
case class_source
|
|
62
|
-
when String
|
|
63
|
-
class_source.split(CLASSNAME_SEPARATOR).reject(&:empty?)
|
|
64
|
-
when Array
|
|
65
|
-
class_source.flat_map { |item| normalize_single_class_item(item, stimulus_class_names_set) }
|
|
66
|
-
else
|
|
67
|
-
normalize_single_class_item(class_source, stimulus_class_names_set)
|
|
68
|
-
end
|
|
69
|
-
end.compact
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Normalize a single class item (could be string, StimulusClass, object with to_s, etc.)
|
|
73
|
-
def normalize_single_class_item(item, stimulus_class_names_set)
|
|
74
|
-
return [] if item.blank?
|
|
75
|
-
|
|
76
|
-
# Handle StimulusClass instances
|
|
77
|
-
if stimulus_class_instance?(item)
|
|
78
|
-
# Only include if the class name matches one of the requested names
|
|
79
|
-
# If stimulus_class_names_set is empty, exclude all stimulus classes
|
|
80
|
-
if stimulus_class_names_set.present? && stimulus_class_names_set.include?(item.class_name)
|
|
81
|
-
class_value = item.to_s
|
|
82
|
-
class_value.include?(CLASSNAME_SEPARATOR) ?
|
|
83
|
-
class_value.split(CLASSNAME_SEPARATOR).reject(&:empty?) :
|
|
84
|
-
[class_value]
|
|
85
|
-
else
|
|
86
|
-
[]
|
|
87
|
-
end
|
|
88
|
-
else
|
|
89
|
-
# Handle regular strings and other objects
|
|
90
|
-
item_string = item.to_s
|
|
91
|
-
item_string.include?(CLASSNAME_SEPARATOR) ?
|
|
92
|
-
item_string.split(CLASSNAME_SEPARATOR).reject(&:empty?) :
|
|
93
|
-
[item_string]
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Check if an item is a StimulusClass instance
|
|
98
|
-
def stimulus_class_instance?(item)
|
|
99
|
-
item.respond_to?(:class_name) && item.respond_to?(:to_s)
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# Deduplicate classes while preserving order (first occurrence wins)
|
|
103
|
-
def dedupe_classes(class_array)
|
|
104
|
-
class_array.reject(&:blank?).uniq
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# Merge classes using Tailwind CSS merge
|
|
108
|
-
def dedupe_with_tailwind(class_string)
|
|
109
|
-
@tailwind_merger.merge(class_string)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vident
|
|
4
|
-
module ComponentAttributeResolver
|
|
5
|
-
include Stimulus::Naming
|
|
6
|
-
|
|
7
|
-
private
|
|
8
|
-
|
|
9
|
-
# Prepare attributes set at initialization. The DSL's static entries are
|
|
10
|
-
# merged in here so user-land `after_component_initialize` mutators append
|
|
11
|
-
# after them (preserving DSL-first ordering). DSL procs are NOT resolved
|
|
12
|
-
# here — they run at render time via `resolve_stimulus_attributes_at_render_time`
|
|
13
|
-
# so they can reach `helpers` / `view_context`.
|
|
14
|
-
def prepare_component_attributes
|
|
15
|
-
prepare_stimulus_collections
|
|
16
|
-
|
|
17
|
-
add_stimulus_attributes_from_dsl(phase: :static)
|
|
18
|
-
|
|
19
|
-
extra = root_element_attributes
|
|
20
|
-
@html_options = (extra[:html_options] || {}).merge(@html_options) if extra.key?(:html_options)
|
|
21
|
-
@root_element_attributes_classes = extra[:classes]
|
|
22
|
-
@root_element_attributes_id = extra[:id] || id
|
|
23
|
-
@element_tag = extra[:element_tag] if extra.key?(:element_tag)
|
|
24
|
-
|
|
25
|
-
Stimulus::PRIMITIVES.each do |primitive|
|
|
26
|
-
send(mutator_method(primitive), extra[primitive.key]) if extra.key?(primitive.key)
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
@stimulus_proc_attributes_resolved = false
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Render-phase: resolve DSL proc entries against the component instance,
|
|
33
|
-
# now that `helpers` / `view_context` are wired. Triggered by Phlex's
|
|
34
|
-
# `before_template` and ViewComponent's `before_render`. Idempotent —
|
|
35
|
-
# `stimulus_data_attributes` also calls this as a safety net.
|
|
36
|
-
def resolve_stimulus_attributes_at_render_time
|
|
37
|
-
return if @stimulus_proc_attributes_resolved
|
|
38
|
-
@stimulus_proc_attributes_resolved = true
|
|
39
|
-
|
|
40
|
-
add_stimulus_attributes_from_dsl(phase: :procs)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def resolve_root_element_attributes_before_render(root_element_html_options = nil)
|
|
44
|
-
extra = root_element_html_options || {}
|
|
45
|
-
|
|
46
|
-
# Options set on component at render time take precedence over attributes set by methods on the component
|
|
47
|
-
# or attributes passed to root_element in the template
|
|
48
|
-
final_attributes = {
|
|
49
|
-
data: stimulus_data_attributes # Lowest precedence
|
|
50
|
-
}
|
|
51
|
-
if root_element_html_options.present? # Mid precedence
|
|
52
|
-
root_element_tag_html_options_merge(final_attributes, root_element_html_options)
|
|
53
|
-
end
|
|
54
|
-
if @html_options.present? # Highest precedence
|
|
55
|
-
root_element_tag_html_options_merge(final_attributes, @html_options)
|
|
56
|
-
end
|
|
57
|
-
final_attributes[:class] = render_classes(extra[:class])
|
|
58
|
-
final_attributes[:id] = (extra[:id] || @root_element_attributes_id) unless final_attributes.key?(:id)
|
|
59
|
-
final_attributes
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def root_element_tag_html_options_merge(final_attributes, other_html_options)
|
|
63
|
-
if other_html_options[:data].present?
|
|
64
|
-
final_attributes[:data].merge!(other_html_options[:data])
|
|
65
|
-
end
|
|
66
|
-
final_attributes.merge!(other_html_options.except(:data))
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Run DSL attributes through their `add_stimulus_*` mutators. `phase:` is
|
|
70
|
-
# forwarded to the builder: `:static` skips procs (init-time), `:procs`
|
|
71
|
-
# skips non-procs (render-time), `:all` resolves everything.
|
|
72
|
-
# `values_from_props` is a sidecar on values, resolved at instance
|
|
73
|
-
# render time (only during the static phase since it has no procs).
|
|
74
|
-
def add_stimulus_attributes_from_dsl(phase: :all)
|
|
75
|
-
dsl_attrs = self.class.stimulus_dsl_attributes(self, phase:)
|
|
76
|
-
return if dsl_attrs.empty?
|
|
77
|
-
|
|
78
|
-
Stimulus::PRIMITIVES.each do |primitive|
|
|
79
|
-
value = dsl_attrs[primitive.key]
|
|
80
|
-
send(mutator_method(primitive), value) if value
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
if dsl_attrs[:stimulus_values_from_props]
|
|
84
|
-
resolved_values = resolve_values_from_props(dsl_attrs[:stimulus_values_from_props])
|
|
85
|
-
add_stimulus_values(resolved_values) unless resolved_values.empty?
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Seed the collection ivars from each prop's raw value.
|
|
90
|
-
def prepare_stimulus_collections
|
|
91
|
-
Stimulus::PRIMITIVES.each do |primitive|
|
|
92
|
-
raw = instance_variable_get(prop_ivar(primitive))
|
|
93
|
-
collection = send(primitive.key, *Array.wrap(raw))
|
|
94
|
-
instance_variable_set(collection_ivar(primitive), collection)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
@stimulus_outlet_host.add_stimulus_outlets(self) if @stimulus_outlet_host
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def stimulus_data_attributes
|
|
101
|
-
resolve_stimulus_attributes_at_render_time
|
|
102
|
-
collections = Stimulus::PRIMITIVES.to_h { |primitive| [primitive.name, instance_variable_get(collection_ivar(primitive))] }
|
|
103
|
-
StimulusDataAttributeBuilder.new(**collections).build
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vident
|
|
4
|
-
module ComponentClassLists
|
|
5
|
-
# Generates the full list of HTML classes for the component
|
|
6
|
-
def render_classes(root_element_html_class = nil) = class_list_builder(root_element_html_class).build
|
|
7
|
-
|
|
8
|
-
# Getter for a stimulus classes list so can be used in view to set initial state on SSR
|
|
9
|
-
# Returns a String of classes that can be used in a `class` attribute.
|
|
10
|
-
def class_list_for_stimulus_classes(*names)
|
|
11
|
-
# DSL proc entries are resolved lazily at render time; trigger them now
|
|
12
|
-
# so procs that use only instance state work from ERB/template.
|
|
13
|
-
resolve_stimulus_attributes_at_render_time if respond_to?(:resolve_stimulus_attributes_at_render_time, true)
|
|
14
|
-
ClassListBuilder.new(tailwind_merger:).build(
|
|
15
|
-
@stimulus_classes_collection&.to_a,
|
|
16
|
-
stimulus_class_names: names
|
|
17
|
-
) || ""
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
|
|
22
|
-
# Not memoised: the per-thread TailwindMerger is the only expensive piece
|
|
23
|
-
# and it's already cached; the builder itself just copies a few ivars.
|
|
24
|
-
# Memoising here would latch the first caller's `root_element_html_class:`.
|
|
25
|
-
def class_list_builder(root_element_html_class = nil)
|
|
26
|
-
ClassListBuilder.new(
|
|
27
|
-
tailwind_merger:,
|
|
28
|
-
component_name:,
|
|
29
|
-
root_element_attributes_classes: @root_element_attributes_classes,
|
|
30
|
-
root_element_classes:,
|
|
31
|
-
root_element_html_class:,
|
|
32
|
-
additional_classes: @classes,
|
|
33
|
-
html_class: @html_options&.fetch(:class, nil)
|
|
34
|
-
)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vident
|
|
4
|
-
module Stimulus
|
|
5
|
-
# A Stimulus primitive kind: name, plus the Value/Collection classes that
|
|
6
|
-
# back it. Two concrete subclasses distinguish how the primitive behaves
|
|
7
|
-
# when a Hash is passed to the plural parser:
|
|
8
|
-
#
|
|
9
|
-
# - `Keyed` — `{a: 1, b: 2}` expands to one value object per pair.
|
|
10
|
-
# Used for values / params / classes / outlets.
|
|
11
|
-
# - `Positional` — `{...}` is a single-arg descriptor (e.g. Action's
|
|
12
|
-
# `{event:, method:, ...}` short form).
|
|
13
|
-
# Used for controllers / actions / targets.
|
|
14
|
-
class Primitive < ::Data.define(:name, :value_class, :collection_class)
|
|
15
|
-
# Short forms. `name` (Data field) is the plural — `:values`; `plural`
|
|
16
|
-
# is an alias for symmetry with `singular`.
|
|
17
|
-
alias_method :plural, :name
|
|
18
|
-
def singular = name.to_s.singularize.to_sym
|
|
19
|
-
|
|
20
|
-
# The primitive's key in Vident's attribute namespace. Used both as the
|
|
21
|
-
# parser method name (`def stimulus_values(...)`) and as the hash key
|
|
22
|
-
# in DSL attrs / component props / `root_element_attributes` — the same
|
|
23
|
-
# Symbol serves all three roles.
|
|
24
|
-
def key = :"stimulus_#{name}"
|
|
25
|
-
def singular_key = :"stimulus_#{singular}"
|
|
26
|
-
|
|
27
|
-
def keyed? = raise NotImplementedError
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
class KeyedPrimitive < Primitive
|
|
31
|
-
def keyed? = true
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
class PositionalPrimitive < Primitive
|
|
35
|
-
def keyed? = false
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
data/lib/vident/stimulus.rb
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vident
|
|
4
|
-
module Stimulus
|
|
5
|
-
# Registry of primitive kinds. Add an entry (paired with a Value/Collection
|
|
6
|
-
# class pair) to extend; plural parsers, mutators, and the data-attribute
|
|
7
|
-
# builder pick it up. Array order = data attribute emission order.
|
|
8
|
-
PRIMITIVES = [
|
|
9
|
-
PositionalPrimitive.new(:controllers, StimulusController, StimulusControllerCollection),
|
|
10
|
-
PositionalPrimitive.new(:actions, StimulusAction, StimulusActionCollection),
|
|
11
|
-
PositionalPrimitive.new(:targets, StimulusTarget, StimulusTargetCollection),
|
|
12
|
-
KeyedPrimitive.new(:outlets, StimulusOutlet, StimulusOutletCollection),
|
|
13
|
-
KeyedPrimitive.new(:values, StimulusValue, StimulusValueCollection),
|
|
14
|
-
KeyedPrimitive.new(:params, StimulusParam, StimulusParamCollection),
|
|
15
|
-
KeyedPrimitive.new(:classes, StimulusClass, StimulusClassCollection)
|
|
16
|
-
].freeze
|
|
17
|
-
|
|
18
|
-
PRIMITIVES_BY_NAME = PRIMITIVES.to_h { |primitive| [primitive.name, primitive] }.freeze
|
|
19
|
-
|
|
20
|
-
class << self
|
|
21
|
-
def primitive(name)
|
|
22
|
-
PRIMITIVES_BY_NAME[name] or
|
|
23
|
-
raise ArgumentError, "Unknown stimulus primitive #{name.inspect}; valid: #{PRIMITIVES_BY_NAME.keys.inspect}"
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def each(&block) = PRIMITIVES.each(&block)
|
|
27
|
-
|
|
28
|
-
def names = PRIMITIVES.map(&:name)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Vident
|
|
4
|
-
class StimulusAction < StimulusAttributeBase
|
|
5
|
-
# https://stimulus.hotwired.dev/reference/actions#options
|
|
6
|
-
VALID_OPTIONS = [:once, :prevent, :stop, :passive, :"!passive", :capture, :self].freeze
|
|
7
|
-
|
|
8
|
-
# Typed descriptor for modifiers (`:once`/`:prevent`/etc., keyboard filter,
|
|
9
|
-
# `@window`) that the plain Array form can't express. Hash input to the
|
|
10
|
-
# parsers (`{event:, method:, ...}`) is desugared into one of these.
|
|
11
|
-
class Descriptor < ::Literal::Data
|
|
12
|
-
prop :method, _Union(Symbol, String)
|
|
13
|
-
prop :event, _Nilable(_Union(Symbol, String)), default: nil
|
|
14
|
-
prop :controller, _Nilable(String), default: nil
|
|
15
|
-
prop :options, _Array(Symbol), default: -> { [] }
|
|
16
|
-
prop :keyboard, _Nilable(String), default: nil
|
|
17
|
-
prop :window, _Boolean, default: false
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
attr_reader :event, :controller, :action, :options, :keyboard, :window
|
|
21
|
-
|
|
22
|
-
def initialize(*args, implied_controller: nil)
|
|
23
|
-
@options = []
|
|
24
|
-
@keyboard = nil
|
|
25
|
-
@window = false
|
|
26
|
-
super
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def to_s
|
|
30
|
-
head =
|
|
31
|
-
if @event
|
|
32
|
-
ev = @event.to_s
|
|
33
|
-
ev = "#{ev}.#{@keyboard}" if @keyboard
|
|
34
|
-
ev = "#{ev}#{@options.map { |o| ":#{o}" }.join}" if @options.any?
|
|
35
|
-
ev = "#{ev}@window" if @window
|
|
36
|
-
"#{ev}->"
|
|
37
|
-
else
|
|
38
|
-
""
|
|
39
|
-
end
|
|
40
|
-
"#{head}#{@controller}##{@action}"
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def data_attribute_name = "action"
|
|
44
|
-
|
|
45
|
-
def data_attribute_value = to_s
|
|
46
|
-
|
|
47
|
-
private
|
|
48
|
-
|
|
49
|
-
def parse_arguments(*args)
|
|
50
|
-
part1, part2, part3 = args
|
|
51
|
-
|
|
52
|
-
case args.size
|
|
53
|
-
when 1
|
|
54
|
-
parse_single_argument(part1)
|
|
55
|
-
when 2
|
|
56
|
-
parse_two_arguments(part1, part2)
|
|
57
|
-
when 3
|
|
58
|
-
parse_three_arguments(part1, part2, part3)
|
|
59
|
-
else
|
|
60
|
-
raise ArgumentError, "Invalid number of 'action' arguments: #{args.size}"
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def parse_single_argument(arg)
|
|
65
|
-
case arg
|
|
66
|
-
when Descriptor then apply_descriptor(arg)
|
|
67
|
-
when Hash then apply_descriptor(Descriptor.new(**arg))
|
|
68
|
-
when Symbol
|
|
69
|
-
@event = nil
|
|
70
|
-
@controller = implied_controller_name
|
|
71
|
-
@action = js_name(arg)
|
|
72
|
-
when String then parse_qualified_action_string(arg)
|
|
73
|
-
else raise ArgumentError, "Invalid 'action' argument type (1): #{arg.class}"
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# (:event, :method) or ("controller/path", :method)
|
|
78
|
-
def parse_two_arguments(part1, part2)
|
|
79
|
-
if part1.is_a?(Symbol) && part2.is_a?(Symbol)
|
|
80
|
-
@event = part1.to_s
|
|
81
|
-
@controller = implied_controller_name
|
|
82
|
-
@action = js_name(part2)
|
|
83
|
-
elsif part1.is_a?(String) && part2.is_a?(Symbol)
|
|
84
|
-
@event = nil
|
|
85
|
-
@controller = stimulize_path(part1)
|
|
86
|
-
@action = js_name(part2)
|
|
87
|
-
else
|
|
88
|
-
raise ArgumentError, "Invalid 'action' argument types (2): #{part1.class}, #{part2.class}"
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# (:event, "controller/path", :method)
|
|
93
|
-
def parse_three_arguments(part1, part2, part3)
|
|
94
|
-
if part1.is_a?(Symbol) && part2.is_a?(String) && part3.is_a?(Symbol)
|
|
95
|
-
@event = part1.to_s
|
|
96
|
-
@controller = stimulize_path(part2)
|
|
97
|
-
@action = js_name(part3)
|
|
98
|
-
else
|
|
99
|
-
raise ArgumentError, "Invalid 'action' argument types (3): #{part1.class}, #{part2.class}, #{part3.class}"
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
def apply_descriptor(d)
|
|
104
|
-
invalid = d.options - VALID_OPTIONS
|
|
105
|
-
unless invalid.empty?
|
|
106
|
-
raise ArgumentError,
|
|
107
|
-
"Invalid action option(s) #{invalid.inspect}. Valid: #{VALID_OPTIONS.inspect}"
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
@event = d.event&.to_s
|
|
111
|
-
@controller = d.controller ? stimulize_path(d.controller) : implied_controller_name
|
|
112
|
-
@action = d.method.is_a?(Symbol) ? js_name(d.method) : d.method.to_s
|
|
113
|
-
@options = d.options
|
|
114
|
-
@keyboard = d.keyboard
|
|
115
|
-
@window = d.window
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def parse_qualified_action_string(action_string)
|
|
119
|
-
if action_string.include?("->")
|
|
120
|
-
event_part, controller_action = action_string.split("->", 2)
|
|
121
|
-
@event = event_part
|
|
122
|
-
controller_part, action_part = controller_action.split("#", 2)
|
|
123
|
-
@controller = controller_part
|
|
124
|
-
@action = action_part
|
|
125
|
-
else
|
|
126
|
-
@event = nil
|
|
127
|
-
controller_part, action_part = action_string.split("#", 2)
|
|
128
|
-
@controller = controller_part
|
|
129
|
-
@action = action_part
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
end
|