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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +43 -0
  3. data/README.md +45 -17
  4. data/lib/vident/caching.rb +4 -110
  5. data/lib/vident/capabilities/caching.rb +98 -0
  6. data/lib/vident/capabilities/child_element_rendering.rb +92 -0
  7. data/lib/vident/capabilities/class_list_building.rb +23 -0
  8. data/lib/vident/capabilities/declarable.rb +39 -0
  9. data/lib/vident/capabilities/identifiable.rb +54 -0
  10. data/lib/vident/capabilities/inspectable.rb +17 -0
  11. data/lib/vident/capabilities/root_element_rendering.rb +31 -0
  12. data/lib/vident/capabilities/stimulus_data_emitting.rb +98 -0
  13. data/lib/vident/capabilities/stimulus_declaring.rb +79 -0
  14. data/lib/vident/capabilities/stimulus_draft.rb +51 -0
  15. data/lib/vident/capabilities/stimulus_mutation.rb +60 -0
  16. data/lib/vident/capabilities/stimulus_parsing.rb +144 -0
  17. data/lib/vident/capabilities/tailwind.rb +18 -0
  18. data/lib/vident/component.rb +14 -76
  19. data/lib/vident/engine.rb +6 -5
  20. data/lib/vident/error.rb +16 -0
  21. data/lib/{vident2 → vident}/internals/action_builder.rb +18 -22
  22. data/lib/vident/internals/attribute_writer.rb +17 -0
  23. data/lib/{vident2 → vident}/internals/class_list_builder.rb +5 -22
  24. data/lib/vident/internals/declaration.rb +13 -0
  25. data/lib/{vident2 → vident}/internals/declarations.rb +6 -18
  26. data/lib/{vident2 → vident}/internals/draft.rb +3 -16
  27. data/lib/{vident2 → vident}/internals/dsl.rb +6 -32
  28. data/lib/vident/internals/plan.rb +9 -0
  29. data/lib/vident/internals/registry.rb +37 -0
  30. data/lib/{vident2 → vident}/internals/resolver.rb +101 -91
  31. data/lib/{vident2 → vident}/internals/target_builder.rb +1 -7
  32. data/lib/vident/stable_id.rb +3 -3
  33. data/lib/{vident2 → vident}/stimulus/action.rb +11 -24
  34. data/lib/vident/stimulus/base.rb +26 -0
  35. data/lib/{vident2 → vident}/stimulus/class_map.rb +6 -18
  36. data/lib/{vident2 → vident}/stimulus/collection.rb +6 -8
  37. data/lib/vident/stimulus/combinable.rb +30 -0
  38. data/lib/vident/stimulus/controller.rb +45 -0
  39. data/lib/vident/stimulus/naming.rb +9 -9
  40. data/lib/vident/stimulus/null.rb +7 -0
  41. data/lib/{vident2 → vident}/stimulus/outlet.rb +12 -32
  42. data/lib/{vident2 → vident}/stimulus/param.rb +5 -11
  43. data/lib/{vident2 → vident}/stimulus/target.rb +5 -14
  44. data/lib/vident/stimulus/value.rb +57 -0
  45. data/lib/vident/stimulus_null.rb +4 -8
  46. data/lib/vident/tailwind.rb +4 -17
  47. data/lib/vident/types.rb +28 -0
  48. data/lib/vident/version.rb +1 -6
  49. data/lib/vident.rb +44 -36
  50. data/skills/vident/SKILL.md +122 -19
  51. data/skills/vident/api-reference.md +259 -115
  52. data/skills/vident/examples.md +23 -10
  53. metadata +38 -60
  54. data/lib/vident/child_element_helper.rb +0 -64
  55. data/lib/vident/class_list_builder.rb +0 -112
  56. data/lib/vident/component_attribute_resolver.rb +0 -106
  57. data/lib/vident/component_class_lists.rb +0 -37
  58. data/lib/vident/stimulus/primitive.rb +0 -38
  59. data/lib/vident/stimulus.rb +0 -31
  60. data/lib/vident/stimulus_action.rb +0 -133
  61. data/lib/vident/stimulus_action_collection.rb +0 -11
  62. data/lib/vident/stimulus_attribute_base.rb +0 -67
  63. data/lib/vident/stimulus_attributes.rb +0 -129
  64. data/lib/vident/stimulus_builder.rb +0 -136
  65. data/lib/vident/stimulus_class.rb +0 -59
  66. data/lib/vident/stimulus_class_collection.rb +0 -11
  67. data/lib/vident/stimulus_collection_base.rb +0 -51
  68. data/lib/vident/stimulus_component.rb +0 -75
  69. data/lib/vident/stimulus_controller.rb +0 -41
  70. data/lib/vident/stimulus_controller_collection.rb +0 -14
  71. data/lib/vident/stimulus_data_attribute_builder.rb +0 -32
  72. data/lib/vident/stimulus_helper.rb +0 -66
  73. data/lib/vident/stimulus_outlet.rb +0 -90
  74. data/lib/vident/stimulus_outlet_collection.rb +0 -11
  75. data/lib/vident/stimulus_param.rb +0 -42
  76. data/lib/vident/stimulus_param_collection.rb +0 -11
  77. data/lib/vident/stimulus_target.rb +0 -47
  78. data/lib/vident/stimulus_target_collection.rb +0 -18
  79. data/lib/vident/stimulus_value.rb +0 -39
  80. data/lib/vident/stimulus_value_collection.rb +0 -11
  81. data/lib/vident2/caching.rb +0 -93
  82. data/lib/vident2/component.rb +0 -538
  83. data/lib/vident2/engine.rb +0 -18
  84. data/lib/vident2/error.rb +0 -30
  85. data/lib/vident2/internals/attribute_writer.rb +0 -22
  86. data/lib/vident2/internals/declaration.rb +0 -17
  87. data/lib/vident2/internals/plan.rb +0 -12
  88. data/lib/vident2/internals/registry.rb +0 -41
  89. data/lib/vident2/phlex/html.rb +0 -84
  90. data/lib/vident2/phlex.rb +0 -9
  91. data/lib/vident2/stimulus/controller.rb +0 -59
  92. data/lib/vident2/stimulus/naming.rb +0 -26
  93. data/lib/vident2/stimulus/null.rb +0 -16
  94. data/lib/vident2/stimulus/value.rb +0 -77
  95. data/lib/vident2/tailwind.rb +0 -19
  96. data/lib/vident2/version.rb +0 -5
  97. data/lib/vident2/view_component/base.rb +0 -124
  98. data/lib/vident2/view_component.rb +0 -9
  99. data/lib/vident2.rb +0 -50
@@ -1,90 +0,0 @@
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 = @selector
13
-
14
- def data_attribute_name = "#{@controller}-#{@outlet_name}-outlet"
15
-
16
- def data_attribute_value = @selector
17
-
18
- private
19
-
20
- def parse_arguments(*args)
21
- case args.size
22
- when 1
23
- parse_single_argument(args[0])
24
- when 2
25
- parse_two_arguments(args[0], args[1])
26
- when 3
27
- parse_three_arguments(args[0], args[1], args[2])
28
- else
29
- raise ArgumentError, "Invalid number of arguments: #{args.size}"
30
- end
31
- end
32
-
33
- def parse_single_argument(arg)
34
- @controller = implied_controller_name
35
- if arg.is_a?(Symbol)
36
- # Single symbol: outlet name on implied controller with auto-generated selector
37
- outlet_identifier = arg.to_s.dasherize
38
- @outlet_name = outlet_identifier
39
- @selector = build_outlet_selector(outlet_identifier)
40
- elsif arg.is_a?(String)
41
- # Single string: outlet identifier with auto-generated selector
42
- @outlet_name = arg.dasherize
43
- @selector = build_outlet_selector(arg)
44
- elsif arg.is_a?(Array) && arg.size == 2
45
- # Array format: [outlet_identifier, css_selector]
46
- @outlet_name = arg[0].to_s.dasherize
47
- @selector = arg[1]
48
- elsif arg.respond_to?(:stimulus_identifier)
49
- # Component with stimulus_identifier
50
- identifier = arg.stimulus_identifier
51
- @outlet_name = identifier
52
- @selector = build_outlet_selector(identifier)
53
- elsif arg.respond_to?(:implied_controller_name)
54
- # RootComponent with implied_controller_name
55
- identifier = arg.implied_controller_name
56
- @outlet_name = identifier
57
- @selector = build_outlet_selector(identifier)
58
- else
59
- raise ArgumentError, "Invalid argument type: #{arg.class}"
60
- end
61
- end
62
-
63
- def parse_two_arguments(arg1, arg2)
64
- if (arg1.is_a?(Symbol) || arg1.is_a?(String)) && arg2.is_a?(String)
65
- @controller = implied_controller_name
66
- @outlet_name = arg1.to_s.dasherize
67
- @selector = arg2
68
- else
69
- raise ArgumentError, "Invalid argument types: #{arg1.class}, #{arg2.class}"
70
- end
71
- end
72
-
73
- def parse_three_arguments(controller, outlet_name, selector)
74
- if controller.is_a?(String) && outlet_name.is_a?(Symbol) && selector.is_a?(String)
75
- # controller path + outlet name + selector
76
- @controller = stimulize_path(controller)
77
- @outlet_name = outlet_name.to_s.dasherize
78
- @selector = selector
79
- else
80
- raise ArgumentError, "Invalid argument types: #{controller.class}, #{outlet_name.class}, #{selector.class}"
81
- end
82
- end
83
-
84
- # Build outlet selector following the same pattern as RootComponent
85
- def build_outlet_selector(outlet_selector)
86
- prefix = @component_id ? "##{@component_id} " : ""
87
- "#{prefix}[data-controller~=#{outlet_selector}]"
88
- end
89
- end
90
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- class StimulusOutletCollection < StimulusCollectionBase
5
- def to_h
6
- return {} if items.empty?
7
-
8
- items.each_with_object({}) { |outlet, merged| merged.merge!(outlet.to_h) }
9
- end
10
- end
11
- end
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- # `data-<controller>-<name>-param="..."` — readable on the JS side as
5
- # `event.params.<camelName>`. Element-scoped: every action on the element
6
- # sees the same params.
7
- class StimulusParam < StimulusAttributeBase
8
- attr_reader :controller, :param_name, :value
9
-
10
- def to_s = @value.to_s
11
-
12
- def data_attribute_name = "#{@controller}-#{@param_name}-param"
13
-
14
- def data_attribute_value = @value
15
-
16
- private
17
-
18
- def parse_arguments(*args)
19
- case args.size
20
- when 2 then parse_two_arguments(*args)
21
- when 3 then parse_three_arguments(*args)
22
- else raise ArgumentError, "Invalid number of arguments: #{args.size} (#{args.inspect}). Did you pass an array of hashes?"
23
- end
24
- end
25
-
26
- def parse_two_arguments(param_name, value)
27
- raise ArgumentError, "Invalid argument types: #{param_name.class}, #{value.class}" unless param_name.is_a?(Symbol)
28
- @controller = implied_controller_name
29
- @param_name = param_name.to_s.dasherize
30
- @value = serialize_value(value)
31
- end
32
-
33
- def parse_three_arguments(controller, param_name, value)
34
- unless controller.is_a?(String) && param_name.is_a?(Symbol)
35
- raise ArgumentError, "Invalid argument types: #{controller.class}, #{param_name.class}, #{value.class}"
36
- end
37
- @controller = stimulize_path(controller)
38
- @param_name = param_name.to_s.dasherize
39
- @value = serialize_value(value)
40
- end
41
- end
42
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- class StimulusParamCollection < StimulusCollectionBase
5
- def to_h
6
- return {} if items.empty?
7
-
8
- items.each_with_object({}) { |param, merged| merged.merge!(param.to_h) }
9
- end
10
- end
11
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- class StimulusTarget < StimulusAttributeBase
5
- attr_reader :controller, :name
6
-
7
- def to_s = @name
8
-
9
- # Returns the data attribute name for this target
10
- def data_attribute_name = "#{@controller}-target"
11
-
12
- # Returns the target name value for the data attribute
13
- def data_attribute_value = @name
14
-
15
- private
16
-
17
- def parse_arguments(*args)
18
- case args.size
19
- when 1
20
- parse_single_argument(args[0])
21
- when 2
22
- parse_two_arguments(args[0], args[1])
23
- else
24
- raise ArgumentError, "Invalid number of arguments: #{args.size}"
25
- end
26
- end
27
-
28
- def parse_single_argument(arg)
29
- @controller = implied_controller_name
30
- @name = case arg
31
- when Symbol then js_name(arg) # name of target on implied controller
32
- when String then arg # target name on implied controller
33
- else raise ArgumentError, "Invalid argument type: #{arg.class}"
34
- end
35
- end
36
-
37
- def parse_two_arguments(part1, part2)
38
- if part1.is_a?(String) && part2.is_a?(Symbol)
39
- # 1 string arg, 1 symbol = controller + target
40
- @controller = stimulize_path(part1)
41
- @name = js_name(part2)
42
- else
43
- raise ArgumentError, "Invalid argument types: #{part1.class}, #{part2.class}"
44
- end
45
- end
46
- end
47
- end
@@ -1,18 +0,0 @@
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
- # Merge space-separated values for same target attribute
12
- merged[key] = merged.key?(key) ? "#{merged[key]} #{value}" : value
13
- end
14
- end
15
- merged
16
- end
17
- end
18
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- class StimulusValue < StimulusAttributeBase
5
- attr_reader :controller, :value_name, :value
6
-
7
- def to_s = @value.to_s
8
-
9
- def data_attribute_name = "#{@controller}-#{@value_name}-value"
10
-
11
- def data_attribute_value = @value
12
-
13
- private
14
-
15
- def parse_arguments(*args)
16
- case args.size
17
- when 2 then parse_two_arguments(*args)
18
- when 3 then parse_three_arguments(*args)
19
- else raise ArgumentError, "Invalid number of arguments: #{args.size} (#{args.inspect}). Did you pass an array of hashes?"
20
- end
21
- end
22
-
23
- def parse_two_arguments(value_name, value)
24
- raise ArgumentError, "Invalid argument types: #{value_name.class}, #{value.class}" unless value_name.is_a?(Symbol)
25
- @controller = implied_controller_name
26
- @value_name = value_name.to_s.dasherize
27
- @value = serialize_value(value)
28
- end
29
-
30
- def parse_three_arguments(controller, value_name, value)
31
- unless controller.is_a?(String) && value_name.is_a?(Symbol)
32
- raise ArgumentError, "Invalid argument types: #{controller.class}, #{value_name.class}, #{value.class}"
33
- end
34
- @controller = stimulize_path(controller)
35
- @value_name = value_name.to_s.dasherize
36
- @value = serialize_value(value)
37
- end
38
- end
39
- end
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Vident
4
- class StimulusValueCollection < StimulusCollectionBase
5
- def to_h
6
- return {} if items.empty?
7
-
8
- items.each_with_object({}) { |value, merged| merged.merge!(value.to_h) }
9
- end
10
- end
11
- end
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "digest/sha1"
4
-
5
- module Vident2
6
- # Fragment-caching opt-in. Include into a component to get `cacheable?`,
7
- # `cache_key`, and the `with_cache_key` / `depends_on` class helpers.
8
- #
9
- # `cache_component_modified_time` lives on the adapter base class
10
- # (Phlex: `.rb` mtime; VC: sidecar template + `.rb` mtime).
11
- module Caching
12
- extend ActiveSupport::Concern
13
-
14
- class_methods do
15
- def inherited(subclass)
16
- subclass.instance_variable_set(:@named_cache_key_attributes, @named_cache_key_attributes&.clone)
17
- super
18
- end
19
-
20
- def with_cache_key(*attrs, name: :_collection)
21
- attrs << :component_modified_time
22
- attrs << :to_h if respond_to?(:to_h)
23
- named_cache_key_includes(name, *attrs.uniq)
24
- end
25
-
26
- attr_reader :named_cache_key_attributes
27
-
28
- def depends_on(*klasses)
29
- @component_dependencies ||= []
30
- @component_dependencies += klasses
31
- end
32
-
33
- attr_reader :component_dependencies
34
-
35
- def component_modified_time
36
- return @component_modified_time if defined?(::Rails) && ::Rails.env.production? && @component_modified_time
37
-
38
- raise StandardError, "Must implement cache_component_modified_time" unless respond_to?(:cache_component_modified_time)
39
-
40
- deps = component_dependencies&.map(&:component_modified_time)&.join("-") || ""
41
- @component_modified_time = deps + cache_component_modified_time
42
- end
43
-
44
- private
45
-
46
- def named_cache_key_includes(name, *attrs)
47
- define_cache_key_method unless @named_cache_key_attributes
48
- @named_cache_key_attributes ||= {}
49
- @named_cache_key_attributes[name] = attrs
50
- end
51
-
52
- def define_cache_key_method
53
- define_method :cache_key do |n = :_collection|
54
- @cache_key ||= {}
55
- return @cache_key[n] if @cache_key.key?(n)
56
- generate_cache_key(n)
57
- @cache_key[n]
58
- end
59
- end
60
- end
61
-
62
- def component_modified_time = self.class.component_modified_time
63
-
64
- def cacheable? = respond_to?(:cache_key)
65
-
66
- def cache_key_modifier = ENV["RAILS_CACHE_ID"]
67
-
68
- def cache_keys_for_sources(key_attributes)
69
- sources = key_attributes.flat_map { |n| n.is_a?(Proc) ? instance_eval(&n) : send(n) }
70
- sources.compact.filter_map { |item| generate_item_cache_key_from(item) unless item == self }
71
- end
72
-
73
- def generate_item_cache_key_from(item)
74
- if item.respond_to? :cache_key_with_version
75
- item.cache_key_with_version
76
- elsif item.respond_to? :cache_key
77
- item.cache_key
78
- elsif item.is_a?(String)
79
- Digest::SHA1.hexdigest(item)
80
- else
81
- Digest::SHA1.hexdigest(Marshal.dump(item))
82
- end
83
- end
84
-
85
- def generate_cache_key(index)
86
- key_attributes = self.class.named_cache_key_attributes[index]
87
- return nil unless key_attributes
88
- key = "#{self.class.name}/#{cache_keys_for_sources(key_attributes).join("/")}"
89
- raise StandardError, "Cache key for key #{key} is blank!" if key.blank?
90
- @cache_key[index] = cache_key_modifier.present? ? "#{key}/#{cache_key_modifier}" : key
91
- end
92
- end
93
- end