vident 0.13.1 → 1.0.0.alpha2

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.
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ # Builds a hash of Stimulus data attributes from collections of stimulus objects
5
+ # Handles merging multiple actions, targets, outlets, values, and classes
6
+ # into the final data-* attributes needed for HTML elements
7
+ class StimulusDataAttributeBuilder
8
+ def initialize(controllers: [], actions: [], targets: [], outlets: [], values: [], classes: [])
9
+ @controllers = Array(controllers)
10
+ @actions = Array(actions)
11
+ @targets = Array(targets)
12
+ @outlets = Array(outlets)
13
+ @values = Array(values)
14
+ @classes = Array(classes)
15
+ end
16
+
17
+ # Build the final data attributes hash
18
+ def build
19
+ {
20
+ **merged_controllers,
21
+ **merged_actions,
22
+ **merged_targets,
23
+ **merged_outlets,
24
+ **merged_values,
25
+ **merged_classes
26
+ }.transform_keys(&:to_s).compact
27
+ end
28
+
29
+ private
30
+
31
+ def merged_controllers
32
+ return {} if @controllers.empty?
33
+
34
+ if @controllers.first.is_a?(StimulusControllerCollection)
35
+ StimulusControllerCollection.merge(*@controllers).to_h
36
+ else
37
+ StimulusControllerCollection.new(@controllers).to_h
38
+ end
39
+ end
40
+
41
+ def merged_actions
42
+ return {} if @actions.empty?
43
+
44
+ if @actions.first.is_a?(StimulusActionCollection)
45
+ StimulusActionCollection.merge(*@actions).to_h
46
+ else
47
+ StimulusActionCollection.new(@actions).to_h
48
+ end
49
+ end
50
+
51
+ def merged_targets
52
+ return {} if @targets.empty?
53
+
54
+ if @targets.first.is_a?(StimulusTargetCollection)
55
+ StimulusTargetCollection.merge(*@targets).to_h
56
+ else
57
+ StimulusTargetCollection.new(@targets).to_h
58
+ end
59
+ end
60
+
61
+ def merged_outlets
62
+ return {} if @outlets.empty?
63
+
64
+ if @outlets.first.is_a?(StimulusOutletCollection)
65
+ StimulusOutletCollection.merge(*@outlets).to_h
66
+ else
67
+ StimulusOutletCollection.new(@outlets).to_h
68
+ end
69
+ end
70
+
71
+ def merged_values
72
+ return {} if @values.empty?
73
+
74
+ if @values.first.is_a?(StimulusValueCollection)
75
+ StimulusValueCollection.merge(*@values).to_h
76
+ else
77
+ StimulusValueCollection.new(@values).to_h
78
+ end
79
+ end
80
+
81
+ def merged_classes
82
+ return {} if @classes.empty?
83
+
84
+ if @classes.first.is_a?(StimulusClassCollection)
85
+ StimulusClassCollection.merge(*@classes).to_h
86
+ else
87
+ StimulusClassCollection.new(@classes).to_h
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ module StimulusHelper
5
+ extend ActiveSupport::Concern
6
+
7
+ class_methods do
8
+ def stimulus(&block)
9
+ # Initialize stimulus builder if not already present
10
+ if @stimulus_builder.nil?
11
+ @stimulus_builder = StimulusBuilder.new
12
+ @inheritance_merged = false
13
+ end
14
+
15
+ # Ensure inheritance is applied
16
+ ensure_inheritance_merged
17
+
18
+ # Execute the new block to add/merge new attributes
19
+ @stimulus_builder.instance_eval(&block)
20
+ end
21
+
22
+ def stimulus_dsl_attributes(component_instance)
23
+ # If no stimulus blocks have been defined on this class, check parent
24
+ if @stimulus_builder.nil? && superclass.respond_to?(:stimulus_dsl_attributes)
25
+ return superclass.stimulus_dsl_attributes(component_instance)
26
+ end
27
+
28
+ # Ensure inheritance is applied at access time
29
+ ensure_inheritance_merged
30
+
31
+ @stimulus_builder&.to_attributes(component_instance) || {}
32
+ end
33
+
34
+ private
35
+
36
+ def ensure_inheritance_merged
37
+ return if @inheritance_merged || @stimulus_builder.nil?
38
+
39
+ if superclass.respond_to?(:stimulus_dsl_builder, true)
40
+ parent_builder = superclass.send(:stimulus_dsl_builder)
41
+ if parent_builder
42
+ @stimulus_builder.merge_with(parent_builder)
43
+ end
44
+ end
45
+ @inheritance_merged = true
46
+ end
47
+
48
+ protected
49
+
50
+ def stimulus_dsl_builder
51
+ @stimulus_builder
52
+ end
53
+ end
54
+
55
+ # Instance method to get DSL attributes for this component instance
56
+ def stimulus_dsl_attributes
57
+ self.class.stimulus_dsl_attributes(self)
58
+ end
59
+
60
+ # Instance method to resolve prop-mapped values at runtime
61
+ def resolve_values_from_props(prop_names)
62
+ return {} if prop_names.empty?
63
+
64
+ resolved = {}
65
+ prop_names.each do |name|
66
+ # Map from instance variable if it exists
67
+ if instance_variable_defined?("@#{name}")
68
+ resolved[name] = instance_variable_get("@#{name}")
69
+ end
70
+ end
71
+ resolved
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ class StimulusOutlet < StimulusAttributeBase
5
+ attr_reader :controller, :outlet_name, :selector
6
+
7
+ def initialize(*args, implied_controller:, component_id: nil)
8
+ @component_id = component_id
9
+ super(*args, implied_controller: implied_controller)
10
+ end
11
+
12
+ def to_s
13
+ @selector
14
+ end
15
+
16
+ def data_attribute_name
17
+ "#{@controller}-#{@outlet_name}-outlet"
18
+ end
19
+
20
+ def data_attribute_value
21
+ @selector
22
+ end
23
+
24
+ private
25
+
26
+ def parse_arguments(*args)
27
+ case args.size
28
+ when 1
29
+ parse_single_argument(args[0])
30
+ when 2
31
+ parse_two_arguments(args[0], args[1])
32
+ when 3
33
+ parse_three_arguments(args[0], args[1], args[2])
34
+ else
35
+ raise ArgumentError, "Invalid number of arguments: #{args.size}"
36
+ end
37
+ end
38
+
39
+ def parse_single_argument(arg)
40
+ @controller = implied_controller_name
41
+ if arg.is_a?(Symbol)
42
+ # Single symbol: outlet name on implied controller with auto-generated selector
43
+ outlet_identifier = arg.to_s.dasherize
44
+ @outlet_name = outlet_identifier
45
+ @selector = build_outlet_selector(outlet_identifier)
46
+ elsif arg.is_a?(String)
47
+ # Single string: outlet identifier with auto-generated selector
48
+ @outlet_name = arg.dasherize
49
+ @selector = build_outlet_selector(arg)
50
+ elsif arg.is_a?(Array) && arg.size == 2
51
+ # Array format: [outlet_identifier, css_selector]
52
+ @outlet_name = arg[0].to_s.dasherize
53
+ @selector = arg[1]
54
+ elsif arg.respond_to?(:stimulus_identifier)
55
+ # Component with stimulus_identifier
56
+ identifier = arg.stimulus_identifier
57
+ @outlet_name = identifier
58
+ @selector = build_outlet_selector(identifier)
59
+ elsif arg.respond_to?(:implied_controller_name)
60
+ # RootComponent with implied_controller_name
61
+ identifier = arg.implied_controller_name
62
+ @outlet_name = identifier
63
+ @selector = build_outlet_selector(identifier)
64
+ else
65
+ raise ArgumentError, "Invalid argument type: #{arg.class}"
66
+ end
67
+ end
68
+
69
+ def parse_two_arguments(arg1, arg2)
70
+ if arg1.is_a?(Symbol) && arg2.is_a?(String)
71
+ # outlet name on implied controller + custom selector
72
+ @controller = implied_controller_name
73
+ @outlet_name = arg1.to_s.dasherize
74
+ @selector = arg2
75
+ else
76
+ raise ArgumentError, "Invalid argument types: #{arg1.class}, #{arg2.class}"
77
+ end
78
+ end
79
+
80
+ def parse_three_arguments(controller, outlet_name, selector)
81
+ if controller.is_a?(String) && outlet_name.is_a?(Symbol) && selector.is_a?(String)
82
+ # controller path + outlet name + selector
83
+ @controller = stimulize_path(controller)
84
+ @outlet_name = outlet_name.to_s.dasherize
85
+ @selector = selector
86
+ else
87
+ raise ArgumentError, "Invalid argument types: #{controller.class}, #{outlet_name.class}, #{selector.class}"
88
+ end
89
+ end
90
+
91
+ # Build outlet selector following the same pattern as RootComponent
92
+ def build_outlet_selector(outlet_selector)
93
+ prefix = @component_id ? "##{@component_id} " : ""
94
+ "#{prefix}[data-controller~=#{outlet_selector}]"
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ class StimulusOutletCollection < StimulusCollectionBase
5
+ def to_h
6
+ return {} if items.empty?
7
+
8
+ merged = {}
9
+ items.each do |outlet|
10
+ merged.merge!(outlet.to_h)
11
+ end
12
+ merged
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ class StimulusTarget < StimulusAttributeBase
5
+ attr_reader :controller, :name
6
+
7
+ def to_s
8
+ @name
9
+ end
10
+
11
+ # Returns the data attribute name for this target
12
+ def data_attribute_name
13
+ "#{@controller}-target"
14
+ end
15
+
16
+ # Returns the target name value for the data attribute
17
+ def data_attribute_value
18
+ @name
19
+ end
20
+
21
+ private
22
+
23
+ def parse_arguments(*args)
24
+ case args.size
25
+ when 1
26
+ parse_single_argument(args[0])
27
+ when 2
28
+ parse_two_arguments(args[0], args[1])
29
+ else
30
+ raise ArgumentError, "Invalid number of arguments: #{args.size}"
31
+ end
32
+ end
33
+
34
+ def parse_single_argument(arg)
35
+ @controller = implied_controller_name
36
+ if arg.is_a?(Symbol)
37
+ # 1 symbol arg, name of target on implied controller
38
+ @name = js_name(arg)
39
+ elsif arg.is_a?(String)
40
+ # 1 string arg, assume it's a target name on implied controller
41
+ @name = arg
42
+ else
43
+ raise ArgumentError, "Invalid argument type: #{arg.class}"
44
+ end
45
+ end
46
+
47
+ def parse_two_arguments(part1, part2)
48
+ if part1.is_a?(String) && part2.is_a?(Symbol)
49
+ # 1 string arg, 1 symbol = controller + target
50
+ @controller = stimulize_path(part1)
51
+ @name = js_name(part2)
52
+ else
53
+ raise ArgumentError, "Invalid argument types: #{part1.class}, #{part2.class}"
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ class StimulusTargetCollection < StimulusCollectionBase
5
+ def to_h
6
+ return {} if items.empty?
7
+
8
+ merged = {}
9
+ items.each do |target|
10
+ target.to_h.each do |key, value|
11
+ merged[key] = if merged.key?(key)
12
+ # Merge space-separated values for same target attribute
13
+ "#{merged[key]} #{value}"
14
+ else
15
+ value
16
+ end
17
+ end
18
+ end
19
+ merged
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Vident
6
+ class StimulusValue < StimulusAttributeBase
7
+ attr_reader :controller, :value_name, :value
8
+
9
+ def to_s
10
+ @value.to_s
11
+ end
12
+
13
+ def data_attribute_name
14
+ "#{@controller}-#{@value_name}-value"
15
+ end
16
+
17
+ def data_attribute_value
18
+ @value
19
+ end
20
+
21
+ private
22
+
23
+ def parse_arguments(*args)
24
+ case args.size
25
+ when 2
26
+ parse_two_arguments(args[0], args[1])
27
+ when 3
28
+ parse_three_arguments(args[0], args[1], args[2])
29
+ else
30
+ raise ArgumentError, "Invalid number of arguments: #{args.size} (#{args.inspect}). Did you pass an array of hashes?"
31
+ end
32
+ end
33
+
34
+ def parse_two_arguments(value_name, value)
35
+ if value_name.is_a?(Symbol)
36
+ # value name on implied controller + value
37
+ @controller = implied_controller_name
38
+ @value_name = value_name.to_s.dasherize
39
+ @value = serialize_value(value)
40
+ else
41
+ raise ArgumentError, "Invalid argument types: #{value_name.class}, #{value.class}"
42
+ end
43
+ end
44
+
45
+ def parse_three_arguments(controller, value_name, value)
46
+ if controller.is_a?(String) && value_name.is_a?(Symbol)
47
+ # controller + value name + value
48
+ @controller = stimulize_path(controller)
49
+ @value_name = value_name.to_s.dasherize
50
+ @value = serialize_value(value)
51
+ else
52
+ raise ArgumentError, "Invalid argument types: #{controller.class}, #{value_name.class}, #{value.class}"
53
+ end
54
+ end
55
+
56
+ def serialize_value(value)
57
+ case value
58
+ when Array, Hash
59
+ value.to_json
60
+ when TrueClass, FalseClass
61
+ value.to_s
62
+ when Numeric
63
+ value.to_s
64
+ else
65
+ value.to_s
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ class StimulusValueCollection < StimulusCollectionBase
5
+ def to_h
6
+ return {} if items.empty?
7
+
8
+ merged = {}
9
+ items.each do |value|
10
+ merged.merge!(value.to_h)
11
+ end
12
+ merged
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ module TagHelper
5
+ # Generate a tag with the given name and options, including stimulus data attributes
6
+ def tag(
7
+ tag_name,
8
+ stimulus_controllers: nil,
9
+ stimulus_targets: nil,
10
+ stimulus_actions: nil,
11
+ stimulus_outlets: nil,
12
+ stimulus_values: nil,
13
+ stimulus_classes: nil,
14
+ stimulus_controller: nil,
15
+ stimulus_target: nil,
16
+ stimulus_action: nil,
17
+ stimulus_outlet: nil,
18
+ stimulus_value: nil,
19
+ stimulus_class: nil,
20
+ **options,
21
+ &block
22
+ )
23
+ # Ensure the plural attributes are actually enumerables
24
+ tag_attribute_must_be_collection!(stimulus_controllers, "stimulus_controllers")
25
+ tag_attribute_must_be_collection!(stimulus_targets, "stimulus_targets")
26
+ tag_attribute_must_be_collection!(stimulus_actions, "stimulus_actions")
27
+ tag_attribute_must_be_collection!(stimulus_outlets, "stimulus_outlets")
28
+ tag_attribute_must_be_collection!(stimulus_values, "stimulus_values")
29
+ tag_attribute_must_be_collection!(stimulus_classes, "stimulus_classes")
30
+
31
+ stimulus_controllers_collection = send(:stimulus_controllers, *tag_wrap_single_stimulus_attribute(stimulus_controllers, stimulus_controller))
32
+ stimulus_targets_collection = send(:stimulus_targets, *tag_wrap_single_stimulus_attribute(stimulus_targets, stimulus_target))
33
+ stimulus_actions_collection = send(:stimulus_actions, *tag_wrap_single_stimulus_attribute(stimulus_actions, stimulus_action))
34
+ stimulus_outlets_collection = send(:stimulus_outlets, *tag_wrap_single_stimulus_attribute(stimulus_outlets, stimulus_outlet))
35
+ stimulus_values_collection = send(:stimulus_values, stimulus_values || stimulus_value)
36
+ stimulus_classes_collection = send(:stimulus_classes, stimulus_classes || stimulus_class)
37
+
38
+ stimulus_data_attributes = StimulusDataAttributeBuilder.new(
39
+ controllers: stimulus_controllers_collection,
40
+ actions: stimulus_actions_collection,
41
+ targets: stimulus_targets_collection,
42
+ outlets: stimulus_outlets_collection,
43
+ values: stimulus_values_collection,
44
+ classes: stimulus_classes_collection
45
+ ).build
46
+ generate_tag(tag_name, stimulus_data_attributes, options, &block)
47
+ end
48
+
49
+ private
50
+
51
+ def tag_attribute_must_be_collection!(collection, name)
52
+ return unless collection
53
+ raise ArgumentError, "'#{name}:' must be an enumerable. Did you mean '#{name.to_s.singularize}:'?" unless collection.is_a?(Enumerable)
54
+ end
55
+
56
+ def tag_wrap_single_stimulus_attribute(plural, singular)
57
+ plural || (singular ? Array.wrap(singular) : nil)
58
+ end
59
+
60
+ def generate_tag(tag_name, stimulus_data_attributes, options, &block)
61
+ raise NoMethodError, "Not implemented"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ # Adds Tailwind CSS class merging functionality to components
5
+ # This module provides methods to create and manage TailwindMerge::Merger instances
6
+ module Tailwind
7
+ # Get or create a thread-safe Tailwind merger instance
8
+ def tailwind_merger
9
+ return unless tailwind_merge_available?
10
+
11
+ return @tailwind_merger if defined?(@tailwind_merger)
12
+
13
+ @tailwind_merger = Thread.current[:vident_tailwind_merger] ||= ::TailwindMerge::Merger.new
14
+ end
15
+
16
+ # Check if TailwindMerge gem is available
17
+ def tailwind_merge_available?
18
+ defined?(::TailwindMerge::Merger) && ::TailwindMerge::Merger.respond_to?(:new)
19
+ rescue NameError
20
+ false
21
+ end
22
+ end
23
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vident
4
- VERSION = "0.13.1"
4
+ VERSION = "1.0.0.alpha2"
5
5
 
6
6
  # Shared version for all vident gems
7
7
  def self.version
data/lib/vident.rb CHANGED
@@ -1,12 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ if ENV["COVERAGE"]
4
+ require "simplecov"
5
+ SimpleCov.command_name "Vident"
6
+ SimpleCov.root File.expand_path("..", __dir__)
7
+ SimpleCov.start do
8
+ add_filter "/test/"
9
+ add_filter "/tmp/"
10
+ add_filter "/bin/"
11
+ add_filter "/lib/vident/engine.rb"
12
+ end
13
+ end
14
+
3
15
  require "active_support"
4
16
  require "active_support/concern"
17
+ require "literal"
18
+
5
19
  require "vident/version"
6
- require "vident/base"
20
+ require "vident/tailwind"
21
+
22
+ require "vident/stimulus_attribute_base"
23
+ require "vident/stimulus_controller"
24
+ require "vident/stimulus_action"
25
+ require "vident/stimulus_target"
26
+ require "vident/stimulus_outlet"
27
+ require "vident/stimulus_value"
28
+ require "vident/stimulus_class"
29
+
30
+ require "vident/stimulus_collection_base"
31
+ require "vident/stimulus_controller_collection"
32
+ require "vident/stimulus_action_collection"
33
+ require "vident/stimulus_target_collection"
34
+ require "vident/stimulus_outlet_collection"
35
+ require "vident/stimulus_value_collection"
36
+ require "vident/stimulus_class_collection"
37
+
38
+ require "vident/stimulus_attributes"
39
+ require "vident/stimulus_data_attribute_builder"
40
+
41
+ require "vident/tag_helper"
7
42
  require "vident/stable_id"
8
- require "vident/attributes/not_typed"
9
- require "vident/root_component"
43
+ require "vident/class_list_builder"
44
+
45
+ require "vident/stimulus_component"
46
+ require "vident/component_class_lists"
47
+ require "vident/component_attribute_resolver"
48
+ require "vident/stimulus_builder"
49
+ require "vident/stimulus_helper"
50
+
10
51
  require "vident/component"
11
52
 
12
53
  require "vident/engine" if defined?(Rails)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vident
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.1
4
+ version: 1.0.0.alpha2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Ierodiaconou
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-06-26 00:00:00.000000000 Z
10
+ date: 2025-07-08 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: railties
@@ -49,6 +49,26 @@ dependencies:
49
49
  - - "<"
50
50
  - !ruby/object:Gem::Version
51
51
  version: '9'
52
+ - !ruby/object:Gem::Dependency
53
+ name: literal
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '1.0'
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '2'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ - - "<"
70
+ - !ruby/object:Gem::Version
71
+ version: '2'
52
72
  description: Vident makes using Stimulus with your `ViewComponent` or `Phlex` view
53
73
  components as easy as writing Ruby. Vident is the base of your design system implementation,
54
74
  which provides helpers for working with Stimulus. For component libraries with ViewComponent
@@ -63,13 +83,34 @@ files:
63
83
  - LICENSE.txt
64
84
  - README.md
65
85
  - lib/vident.rb
66
- - lib/vident/attributes/not_typed.rb
67
- - lib/vident/base.rb
68
86
  - lib/vident/caching.rb
87
+ - lib/vident/class_list_builder.rb
69
88
  - lib/vident/component.rb
89
+ - lib/vident/component_attribute_resolver.rb
90
+ - lib/vident/component_class_lists.rb
70
91
  - lib/vident/engine.rb
71
- - lib/vident/root_component.rb
72
92
  - lib/vident/stable_id.rb
93
+ - lib/vident/stimulus_action.rb
94
+ - lib/vident/stimulus_action_collection.rb
95
+ - lib/vident/stimulus_attribute_base.rb
96
+ - lib/vident/stimulus_attributes.rb
97
+ - lib/vident/stimulus_builder.rb
98
+ - lib/vident/stimulus_class.rb
99
+ - lib/vident/stimulus_class_collection.rb
100
+ - lib/vident/stimulus_collection_base.rb
101
+ - lib/vident/stimulus_component.rb
102
+ - lib/vident/stimulus_controller.rb
103
+ - lib/vident/stimulus_controller_collection.rb
104
+ - lib/vident/stimulus_data_attribute_builder.rb
105
+ - lib/vident/stimulus_helper.rb
106
+ - lib/vident/stimulus_outlet.rb
107
+ - lib/vident/stimulus_outlet_collection.rb
108
+ - lib/vident/stimulus_target.rb
109
+ - lib/vident/stimulus_target_collection.rb
110
+ - lib/vident/stimulus_value.rb
111
+ - lib/vident/stimulus_value_collection.rb
112
+ - lib/vident/tag_helper.rb
113
+ - lib/vident/tailwind.rb
73
114
  - lib/vident/version.rb
74
115
  homepage: https://github.com/stevegeek/vident
75
116
  licenses: