vident 1.0.1 → 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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -0
  3. data/README.md +49 -18
  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/vident/internals/action_builder.rb +97 -0
  22. data/lib/vident/internals/attribute_writer.rb +17 -0
  23. data/lib/vident/internals/class_list_builder.rb +62 -0
  24. data/lib/vident/internals/declaration.rb +13 -0
  25. data/lib/vident/internals/declarations.rb +64 -0
  26. data/lib/vident/internals/draft.rb +47 -0
  27. data/lib/vident/internals/dsl.rb +172 -0
  28. data/lib/vident/internals/plan.rb +9 -0
  29. data/lib/vident/internals/registry.rb +37 -0
  30. data/lib/vident/internals/resolver.rb +316 -0
  31. data/lib/vident/internals/target_builder.rb +23 -0
  32. data/lib/vident/stable_id.rb +3 -3
  33. data/lib/vident/stimulus/action.rb +127 -0
  34. data/lib/vident/stimulus/base.rb +26 -0
  35. data/lib/vident/stimulus/class_map.rb +57 -0
  36. data/lib/vident/stimulus/collection.rb +40 -0
  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/vident/stimulus/outlet.rb +93 -0
  42. data/lib/vident/stimulus/param.rb +56 -0
  43. data/lib/vident/stimulus/target.rb +48 -0
  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 +133 -21
  51. data/skills/vident/api-reference.md +662 -0
  52. data/skills/vident/examples.md +505 -0
  53. metadata +40 -28
  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 -87
  57. data/lib/vident/component_class_lists.rb +0 -34
  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 -119
  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
@@ -1,19 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/string/inflections"
4
+
3
5
  module Vident
4
6
  module Stimulus
5
- # Vident's internal naming conventions for per-primitive wiring — the
6
- # `add_stimulus_<plural>` mutator method, the `@stimulus_<plural>` prop
7
- # ivar, and the `@stimulus_<plural>_collection` parsed-collection ivar.
8
- # Mixed in by the consumers that need these helpers. Kept off `Primitive`
9
- # so the primitive stays a clean domain value object and doesn't carry
10
- # the implementation details of its consumers.
11
7
  module Naming
12
- def mutator_method(primitive) = :"add_stimulus_#{primitive.name}"
8
+ module_function
13
9
 
14
- def prop_ivar(primitive) = :"@stimulus_#{primitive.name}"
10
+ def stimulize_path(path)
11
+ path.to_s.split("/").map(&:dasherize).join("--")
12
+ end
15
13
 
16
- def collection_ivar(primitive) = :"@stimulus_#{primitive.name}_collection"
14
+ def js_name(name)
15
+ name.to_s.camelize(:lower)
16
+ end
17
17
  end
18
18
  end
19
19
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Vident
4
+ module Stimulus
5
+ Null = ::Vident::StimulusNull
6
+ end
7
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "literal"
4
+ require_relative "naming"
5
+ require_relative "base"
6
+ require_relative "controller"
7
+
8
+ module Vident
9
+ module Stimulus
10
+ # `data-<ctrl>-<name>-outlet` fragment. `selector` is the CSS selector
11
+ # the Stimulus runtime uses to resolve the outlet on the page.
12
+ class Outlet < Base
13
+ prop :controller, Controller
14
+ prop :name, String
15
+ prop :selector, String
16
+
17
+ def self.parse(*args, implied:, component_id: nil)
18
+ case args
19
+ in [Symbol => sym]
20
+ name = sym.to_s.dasherize
21
+ new(
22
+ controller: implied,
23
+ name: name,
24
+ selector: auto_selector(name, component_id: component_id)
25
+ )
26
+ in [String => str]
27
+ name = str.dasherize
28
+ new(
29
+ controller: implied,
30
+ name: name,
31
+ selector: auto_selector(name, component_id: component_id)
32
+ )
33
+ in [[identifier, selector]]
34
+ new(
35
+ controller: implied,
36
+ name: identifier.to_s.dasherize,
37
+ selector: selector
38
+ )
39
+ in [Symbol => sym, String => sel]
40
+ new(
41
+ controller: implied,
42
+ name: sym.to_s.dasherize,
43
+ selector: sel
44
+ )
45
+ in [String => id_or_name, String => sel]
46
+ new(
47
+ controller: implied,
48
+ name: id_or_name.dasherize,
49
+ selector: sel
50
+ )
51
+ in [String => ctrl_path, Symbol => sym, String => sel]
52
+ new(
53
+ controller: Controller.parse(ctrl_path, implied: implied),
54
+ name: sym.to_s.dasherize,
55
+ selector: sel
56
+ )
57
+ in [obj] if obj.respond_to?(:stimulus_identifier)
58
+ ident = obj.stimulus_identifier
59
+ new(
60
+ controller: implied,
61
+ name: ident,
62
+ selector: auto_selector(ident, component_id: component_id)
63
+ )
64
+ else
65
+ raise ::Vident::ParseError, "Outlet.parse: invalid arguments #{args.inspect}"
66
+ end
67
+ end
68
+
69
+ def self.auto_selector(outlet_identifier, component_id:)
70
+ prefix = component_id ? "##{css_escape_ident(component_id)} " : ""
71
+ "#{prefix}[data-controller~=#{outlet_identifier}]"
72
+ end
73
+
74
+ # CSS-escapes anything outside the bare identifier alphabet
75
+ # (`A-Za-z0-9_-`) using the `\HH ` hex form (with trailing space
76
+ # delimiter). Bare `\<char>` works for many punctuation cases but
77
+ # not for whitespace, parens, or non-ASCII — the hex form is
78
+ # always valid per CSS Syntax §4.3.7.
79
+ def self.css_escape_ident(id)
80
+ id.to_s.gsub(/[^A-Za-z0-9_-]/) { |c| "\\#{c.ord.to_s(16)} " }
81
+ end
82
+
83
+ def to_s = selector
84
+
85
+ def data_attribute_key = :"#{controller.name}-#{name}-outlet"
86
+
87
+ def to_data_pair = [data_attribute_key, selector]
88
+
89
+ def to_h = {data_attribute_key => selector}
90
+ alias_method :to_hash, :to_h
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "literal"
5
+ require_relative "naming"
6
+ require_relative "base"
7
+ require_relative "null"
8
+ require_relative "controller"
9
+
10
+ module Vident
11
+ module Stimulus
12
+ # `data-<ctrl>-<name>-param` fragment — same shape as Value, distinct
13
+ # semantics on the JS side (read via `event.params.<camel>`).
14
+ class Param < Base
15
+ prop :controller, Controller
16
+ prop :name, String
17
+ prop :serialized, String
18
+
19
+ def self.parse(*args, implied:, component_id: nil)
20
+ case args
21
+ in [Symbol => name_sym, raw]
22
+ new(
23
+ controller: implied,
24
+ name: name_sym.to_s.dasherize,
25
+ serialized: serialize(raw)
26
+ )
27
+ in [String => ctrl_path, Symbol => name_sym, raw]
28
+ new(
29
+ controller: Controller.parse(ctrl_path, implied: implied),
30
+ name: name_sym.to_s.dasherize,
31
+ serialized: serialize(raw)
32
+ )
33
+ else
34
+ raise ::Vident::ParseError, "Param.parse: invalid arguments #{args.inspect}"
35
+ end
36
+ end
37
+
38
+ def self.serialize(raw)
39
+ raise ::Vident::ParseError, "Param.serialize: nil is not serializable — filter nil upstream" if raw.nil?
40
+ case raw
41
+ when Array, Hash then raw.to_json
42
+ else raw.to_s
43
+ end
44
+ end
45
+
46
+ def to_s = serialized
47
+
48
+ def data_attribute_key = :"#{controller.name}-#{name}-param"
49
+
50
+ def to_data_pair = [data_attribute_key, serialized]
51
+
52
+ def to_h = {data_attribute_key => serialized}
53
+ alias_method :to_hash, :to_h
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "literal"
4
+ require_relative "naming"
5
+ require_relative "base"
6
+ require_relative "controller"
7
+
8
+ module Vident
9
+ module Stimulus
10
+ # `data-<ctrl>-target` fragment.
11
+ class Target < Base
12
+ prop :controller, Controller
13
+ prop :name, String
14
+
15
+ def self.parse(*args, implied:, component_id: nil)
16
+ case args
17
+ in [Symbol => sym]
18
+ new(controller: implied, name: Naming.js_name(sym))
19
+ in [String => str]
20
+ new(controller: implied, name: str)
21
+ in [String => ctrl_path, Symbol => sym]
22
+ new(
23
+ controller: Controller.parse(ctrl_path, implied: implied),
24
+ name: Naming.js_name(sym)
25
+ )
26
+ else
27
+ raise ::Vident::ParseError, "Target.parse: invalid arguments #{args.inspect}"
28
+ end
29
+ end
30
+
31
+ def to_s = name
32
+
33
+ def data_attribute_key = :"#{controller.name}-target"
34
+
35
+ def to_data_pair = [data_attribute_key, name]
36
+
37
+ def to_h = {data_attribute_key => name}
38
+ alias_method :to_hash, :to_h
39
+
40
+ def self.to_data_hash(targets)
41
+ targets.each_with_object({}) do |t, acc|
42
+ key, value = t.to_data_pair
43
+ acc[key] = acc.key?(key) ? "#{acc[key]} #{value}" : value
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "literal"
5
+ require_relative "naming"
6
+ require_relative "base"
7
+ require_relative "controller"
8
+ require_relative "null"
9
+
10
+ module Vident
11
+ module Stimulus
12
+ # `data-<ctrl>-<name>-value` fragment. Holds the serialised form
13
+ # (always a String). Only `nil` is rejected — `false`, blank strings,
14
+ # and empty collections emit their serialised form.
15
+ class Value < Base
16
+ prop :controller, Controller
17
+ prop :name, String
18
+ prop :serialized, String
19
+
20
+ def self.parse(*args, implied:, component_id: nil)
21
+ case args
22
+ in [Symbol => name_sym, raw]
23
+ new(
24
+ controller: implied,
25
+ name: name_sym.to_s.dasherize,
26
+ serialized: serialize(raw)
27
+ )
28
+ in [String => ctrl_path, Symbol => name_sym, raw]
29
+ new(
30
+ controller: Controller.parse(ctrl_path, implied: implied),
31
+ name: name_sym.to_s.dasherize,
32
+ serialized: serialize(raw)
33
+ )
34
+ else
35
+ raise ::Vident::ParseError, "Value.parse: invalid arguments #{args.inspect}"
36
+ end
37
+ end
38
+
39
+ def self.serialize(raw)
40
+ raise ::Vident::ParseError, "Value.serialize: nil is not serializable — filter nil upstream" if raw.nil?
41
+ case raw
42
+ when Array, Hash then raw.to_json
43
+ else raw.to_s
44
+ end
45
+ end
46
+
47
+ def to_s = serialized
48
+
49
+ def data_attribute_key = :"#{controller.name}-#{name}-value"
50
+
51
+ def to_data_pair = [data_attribute_key, serialized]
52
+
53
+ def to_h = {data_attribute_key => serialized}
54
+ alias_method :to_hash, :to_h
55
+ end
56
+ end
57
+ end
@@ -9,13 +9,9 @@ module Vident
9
9
  # A bare `nil` (static or returned from a proc) omits the attribute entirely so
10
10
  # Stimulus uses its per-type default. Reach for this sentinel only when you need
11
11
  # an explicit JS `null`.
12
- StimulusNull = Object.new
13
- def StimulusNull.inspect
14
- "Vident::StimulusNull"
15
- end
12
+ StimulusNull = Object.new.tap do |s|
13
+ def s.inspect = "Vident::StimulusNull"
16
14
 
17
- def StimulusNull.to_s
18
- "null"
19
- end
20
- StimulusNull.freeze
15
+ def s.to_s = "null"
16
+ end.freeze
21
17
  end
@@ -1,21 +1,8 @@
1
1
  # frozen_string_literal: true
2
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)
3
+ require_relative "capabilities/tailwind"
12
4
 
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
- end
20
- end
5
+ module Vident
6
+ # Top-level alias for the mixin at `Vident::Capabilities::Tailwind`.
7
+ Tailwind = Capabilities::Tailwind
21
8
  end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "literal"
4
+ require_relative "stimulus/controller"
5
+ require_relative "stimulus/action"
6
+ require_relative "stimulus/target"
7
+ require_relative "stimulus/outlet"
8
+ require_relative "stimulus/value"
9
+ require_relative "stimulus/param"
10
+ require_relative "stimulus/class_map"
11
+
12
+ module Vident
13
+ # Canonical Literal type unions for the seven `stimulus_*:` props.
14
+ # The same objects are used internally by `Vident::Capabilities::Declarable`
15
+ # for the built-in props; exposed here so user components can reference
16
+ # them when adding matching props of their own.
17
+ module Types
18
+ extend Literal::Types
19
+
20
+ StimulusControllers = _Array(_Union(String, Symbol, ::Vident::Stimulus::Controller))
21
+ StimulusActions = _Array(_Union(String, Symbol, Array, Hash, ::Vident::Stimulus::Action))
22
+ StimulusTargets = _Array(_Union(String, Symbol, Array, ::Vident::Stimulus::Target))
23
+ StimulusOutlets = _Array(_Union(String, Symbol, Array, ::Vident::Stimulus::Outlet))
24
+ StimulusValues = _Union(_Hash(Symbol, _Any), Array, ::Vident::Stimulus::Value)
25
+ StimulusParams = _Union(_Hash(Symbol, _Any), Array, ::Vident::Stimulus::Param)
26
+ StimulusClasses = _Union(_Hash(Symbol, _Any), Array, ::Vident::Stimulus::ClassMap)
27
+ end
28
+ end
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Vident
4
- VERSION = "1.0.1"
5
-
6
- # Shared version for all vident gems
7
- def self.version
8
- VERSION
9
- end
4
+ VERSION = "2.0.0"
10
5
  end
data/lib/vident.rb CHANGED
@@ -5,46 +5,54 @@ require "active_support/concern"
5
5
  require "literal"
6
6
 
7
7
  require "vident/version"
8
- require "vident/tailwind"
9
8
 
10
- require "vident/stimulus_null"
11
- require "vident/stimulus_attribute_base"
12
- require "vident/stimulus_controller"
13
- require "vident/stimulus_action"
14
- require "vident/stimulus_target"
15
- require "vident/stimulus_outlet"
16
- require "vident/stimulus_value"
17
- require "vident/stimulus_param"
18
- require "vident/stimulus_class"
19
-
20
- require "vident/stimulus_collection_base"
21
- require "vident/stimulus_controller_collection"
22
- require "vident/stimulus_action_collection"
23
- require "vident/stimulus_target_collection"
24
- require "vident/stimulus_outlet_collection"
25
- require "vident/stimulus_value_collection"
26
- require "vident/stimulus_param_collection"
27
- require "vident/stimulus_class_collection"
28
-
29
- require "vident/stimulus/primitive"
30
- require "vident/stimulus/naming"
31
- require "vident/stimulus"
9
+ module Vident
10
+ end
32
11
 
33
- require "vident/stimulus_attributes"
34
- require "vident/stimulus_data_attribute_builder"
35
-
36
- require "vident/child_element_helper"
12
+ require "vident/engine" if defined?(Rails::Engine)
13
+ require "vident/error"
37
14
  require "vident/stable_id"
38
- require "vident/class_list_builder"
15
+ require "vident/stimulus_null"
39
16
 
40
- require "vident/stimulus_component"
41
- require "vident/component_class_lists"
42
- require "vident/component_attribute_resolver"
43
- require "vident/stimulus_builder"
44
- require "vident/stimulus_helper"
17
+ require "vident/stimulus/naming"
18
+ require "vident/stimulus/null"
19
+ require "vident/stimulus/controller"
20
+ require "vident/stimulus/action"
21
+ require "vident/stimulus/target"
22
+ require "vident/stimulus/outlet"
23
+ require "vident/stimulus/value"
24
+ require "vident/stimulus/param"
25
+ require "vident/stimulus/class_map"
26
+ require "vident/stimulus/collection"
27
+ require "vident/types"
28
+ require "vident/internals/registry"
29
+ require "vident/internals/declaration"
30
+ require "vident/internals/declarations"
31
+ require "vident/internals/dsl"
32
+ require "vident/internals/draft"
33
+ require "vident/internals/plan"
34
+ require "vident/internals/resolver"
35
+ require "vident/internals/attribute_writer"
36
+ require "vident/internals/class_list_builder"
37
+
38
+ require "vident/capabilities/tailwind"
39
+ require "vident/capabilities/caching"
40
+ require "vident/capabilities/declarable"
41
+ require "vident/capabilities/identifiable"
42
+ require "vident/capabilities/stimulus_declaring"
43
+ require "vident/capabilities/stimulus_parsing"
44
+ require "vident/capabilities/stimulus_mutation"
45
+ require "vident/capabilities/stimulus_draft"
46
+ require "vident/capabilities/stimulus_data_emitting"
47
+ require "vident/capabilities/class_list_building"
48
+ require "vident/capabilities/root_element_rendering"
49
+ require "vident/capabilities/child_element_rendering"
50
+ require "vident/capabilities/inspectable"
45
51
 
46
- require "vident/component"
52
+ require "vident/tailwind"
53
+ require "vident/caching"
47
54
 
48
- require "vident/engine" if defined?(Rails)
55
+ require "vident/component"
49
56
 
50
- module Vident; end
57
+ require "vident/phlex"
58
+ require "vident/view_component"