vident 1.0.2 → 2.0.1
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 +50 -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 +46 -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
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "registry"
|
|
4
|
+
|
|
5
|
+
module Vident
|
|
6
|
+
module Internals
|
|
7
|
+
module AttributeWriter
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
def call(plan)
|
|
11
|
+
Registry::KINDS.reduce({}) do |acc, kind|
|
|
12
|
+
acc.merge(kind.value_class.to_data_hash(plan.public_send(kind.name)))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -2,24 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
require "set"
|
|
4
4
|
|
|
5
|
-
module
|
|
5
|
+
module Vident
|
|
6
6
|
module Internals
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# Tiers (render order, left -> right):
|
|
12
|
-
# 1. component_name — always first.
|
|
13
|
-
# 2-5. Priority cascade (only the highest non-nil wins):
|
|
14
|
-
# root_element_classes (instance override) <
|
|
15
|
-
# root_element_attributes[:classes] <
|
|
16
|
-
# root_element(class:) from template <
|
|
17
|
-
# html_options[:class] from prop
|
|
18
|
-
# 6. classes: prop — ALWAYS appended, even when tier 5 is present.
|
|
19
|
-
#
|
|
20
|
-
# Plus: per-kind StimulusClassMap entries whose name is in
|
|
21
|
-
# `stimulus_class_names` are appended as CSS. Tailwind merge runs last
|
|
22
|
-
# if the merger is passed.
|
|
7
|
+
# Builds the root element's CSS class list with a 6-tier precedence cascade.
|
|
8
|
+
# Tiers (left-to-right): component_name, then the highest-priority non-nil of
|
|
9
|
+
# root_element_classes / root_element_attributes[:classes] / root_element(class:) /
|
|
10
|
+
# html_options[:class], then classes: prop (always appended), then stimulus class maps.
|
|
23
11
|
module ClassListBuilder
|
|
24
12
|
CLASSNAME_SEPARATOR = " "
|
|
25
13
|
|
|
@@ -39,7 +27,6 @@ module Vident2
|
|
|
39
27
|
parts = []
|
|
40
28
|
parts << component_name if component_name
|
|
41
29
|
|
|
42
|
-
# Priority cascade: top-down, first non-nil wins.
|
|
43
30
|
if html_options_class
|
|
44
31
|
parts.concat(Array.wrap(html_options_class))
|
|
45
32
|
elsif root_element_html_class
|
|
@@ -50,8 +37,6 @@ module Vident2
|
|
|
50
37
|
parts.concat(Array.wrap(root_element_classes))
|
|
51
38
|
end
|
|
52
39
|
|
|
53
|
-
# `classes:` prop: always appended, even when something in the
|
|
54
|
-
# cascade already contributed.
|
|
55
40
|
parts.concat(Array.wrap(classes_prop)) if classes_prop
|
|
56
41
|
|
|
57
42
|
parts.compact!
|
|
@@ -68,8 +53,6 @@ module Vident2
|
|
|
68
53
|
tailwind_merger ? tailwind_merger.merge(joined) : joined
|
|
69
54
|
end
|
|
70
55
|
|
|
71
|
-
# Pick ClassMap entries whose `name` matches any of the requested
|
|
72
|
-
# Symbols/Strings (dasherized to match the ClassMap's canonical form).
|
|
73
56
|
def stimulus_class_css(class_maps, names)
|
|
74
57
|
names_set = names.map { |n| n.to_s.dasherize }.to_set
|
|
75
58
|
class_maps.select { |cm| names_set.include?(cm.name) }.map(&:css)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vident
|
|
4
|
+
module Internals
|
|
5
|
+
# One unresolved DSL entry; the Resolver parses it into typed Stimulus
|
|
6
|
+
# value objects at instance init time.
|
|
7
|
+
Declaration = Data.define(:args, :when_proc, :meta) do
|
|
8
|
+
def self.of(*args, when_proc: nil, **meta)
|
|
9
|
+
new(args: args.freeze, when_proc: when_proc, meta: meta.freeze)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -2,19 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "declaration"
|
|
4
4
|
|
|
5
|
-
module
|
|
5
|
+
module Vident
|
|
6
6
|
module Internals
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
|
|
11
|
-
# them into `Stimulus::*` value objects at instance init, not here.
|
|
12
|
-
#
|
|
13
|
-
# Keyed kinds (values, params, class_maps, outlets) use `(key, entry)`
|
|
14
|
-
# pairs to let a later block's same-key entry replace an earlier one.
|
|
15
|
-
# Positional kinds (controllers, actions, targets) are flat arrays;
|
|
16
|
-
# later blocks append.
|
|
17
|
-
Declarations = Data.define(
|
|
7
|
+
# Frozen per-class aggregate of raw `Declaration` entries from `stimulus do` blocks.
|
|
8
|
+
# Keyed kinds use `[key, Declaration]` pairs so later same-key entries replace earlier ones;
|
|
9
|
+
# positional kinds are flat arrays where later blocks append.
|
|
10
|
+
class Declarations < Data.define(
|
|
18
11
|
:controllers,
|
|
19
12
|
:actions,
|
|
20
13
|
:targets,
|
|
@@ -23,7 +16,7 @@ module Vident2
|
|
|
23
16
|
:params,
|
|
24
17
|
:class_maps,
|
|
25
18
|
:values_from_props
|
|
26
|
-
)
|
|
19
|
+
)
|
|
27
20
|
EMPTY_ARRAY = [].freeze
|
|
28
21
|
|
|
29
22
|
def self.empty = @empty ||= new(
|
|
@@ -43,9 +36,6 @@ module Vident2
|
|
|
43
36
|
!class_maps.empty? || !values_from_props.empty?
|
|
44
37
|
end
|
|
45
38
|
|
|
46
|
-
# Merge two Declarations, treating `self` as parent and `other` as
|
|
47
|
-
# child. Positional kinds concat (parent first, then child).
|
|
48
|
-
# Keyed kinds last-wins on matching key.
|
|
49
39
|
def merge(other)
|
|
50
40
|
self.class.new(
|
|
51
41
|
controllers: concat_positional(controllers, other.controllers),
|
|
@@ -63,8 +53,6 @@ module Vident2
|
|
|
63
53
|
|
|
64
54
|
def concat_positional(a, b) = (a + b).freeze
|
|
65
55
|
|
|
66
|
-
# Keyed entries are `[key, Declaration]` tuples; last write on a
|
|
67
|
-
# given key wins, insertion order otherwise preserved.
|
|
68
56
|
def merge_keyed(a, b)
|
|
69
57
|
merged = {}
|
|
70
58
|
a.each { |(k, d)| merged[k] = d }
|
|
@@ -3,16 +3,9 @@
|
|
|
3
3
|
require_relative "registry"
|
|
4
4
|
require_relative "plan"
|
|
5
5
|
|
|
6
|
-
module
|
|
6
|
+
module Vident
|
|
7
7
|
module Internals
|
|
8
|
-
#
|
|
9
|
-
# Per-instance mutable working copy. Seven Arrays, one per Registry
|
|
10
|
-
# kind. `add_<kind>(value_or_values)` mutators are the single sanctioned
|
|
11
|
-
# seam for cross-instance mutation (outlet-host pattern) and for
|
|
12
|
-
# `add_stimulus_*` calls from `after_component_initialize`.
|
|
13
|
-
#
|
|
14
|
-
# After `seal!` the Draft is closed — any further `add_*` raises
|
|
15
|
-
# `Vident2::StateError`. The sealed Plan is a frozen Data.define snapshot.
|
|
8
|
+
# Per-instance mutable accumulator; seals into a frozen Plan once rendering begins.
|
|
16
9
|
class Draft
|
|
17
10
|
def initialize(**collections)
|
|
18
11
|
@collections = Registry.names.to_h { |name| [name, []] }
|
|
@@ -21,12 +14,8 @@ module Vident2
|
|
|
21
14
|
end
|
|
22
15
|
|
|
23
16
|
Registry.each do |kind|
|
|
24
|
-
# reader
|
|
25
17
|
define_method(kind.name) { @collections[kind.name] }
|
|
26
18
|
|
|
27
|
-
# mutator: one call = one logical add. Array input concats all
|
|
28
|
-
# elements as pre-parsed values; a single non-Array value appends
|
|
29
|
-
# as one entry.
|
|
30
19
|
define_method(:"add_#{kind.name}") do |value_or_values|
|
|
31
20
|
raise_if_sealed!
|
|
32
21
|
Array(value_or_values).each { |v| @collections[kind.name] << v }
|
|
@@ -36,8 +25,6 @@ module Vident2
|
|
|
36
25
|
|
|
37
26
|
def sealed? = @sealed
|
|
38
27
|
|
|
39
|
-
# Freeze the working copy and snapshot as a frozen Plan. Idempotent:
|
|
40
|
-
# subsequent calls return the memoised Plan.
|
|
41
28
|
def seal!
|
|
42
29
|
return @plan if @sealed
|
|
43
30
|
@sealed = true
|
|
@@ -52,7 +39,7 @@ module Vident2
|
|
|
52
39
|
|
|
53
40
|
def raise_if_sealed!
|
|
54
41
|
return unless @sealed
|
|
55
|
-
raise ::
|
|
42
|
+
raise ::Vident::StateError,
|
|
56
43
|
"cannot modify stimulus attributes after rendering has begun"
|
|
57
44
|
end
|
|
58
45
|
end
|
|
@@ -5,15 +5,10 @@ require_relative "declarations"
|
|
|
5
5
|
require_relative "action_builder"
|
|
6
6
|
require_relative "target_builder"
|
|
7
7
|
|
|
8
|
-
module
|
|
8
|
+
module Vident
|
|
9
9
|
module Internals
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# call as one or more `Declaration` raw entries; `finalize` folds
|
|
13
|
-
# them into a frozen `Declarations` aggregate.
|
|
14
|
-
#
|
|
15
|
-
# Parsing into `Stimulus::*` value objects is deferred to the
|
|
16
|
-
# Resolver — this class stores only raw argument tuples.
|
|
10
|
+
# Block receiver for `stimulus do ... end`. Records raw Declaration entries;
|
|
11
|
+
# parsing into typed Stimulus value objects is deferred to the Resolver.
|
|
17
12
|
class DSL
|
|
18
13
|
attr_reader :caller_location
|
|
19
14
|
|
|
@@ -31,9 +26,6 @@ module Vident2
|
|
|
31
26
|
|
|
32
27
|
# ---- plural (kwargs) forms --------------------------------------
|
|
33
28
|
|
|
34
|
-
# Each arg becomes one controller entry. An Array arg is splatted
|
|
35
|
-
# into positional args for a single controller (e.g. a tuple
|
|
36
|
-
# `[path, {as: :alias}]`); anything else is treated as a path.
|
|
37
29
|
def controllers(*args)
|
|
38
30
|
args.each do |arg|
|
|
39
31
|
case arg
|
|
@@ -46,10 +38,6 @@ module Vident2
|
|
|
46
38
|
self
|
|
47
39
|
end
|
|
48
40
|
|
|
49
|
-
# Array in the plural form splats into the singular parser (matching
|
|
50
|
-
# V1's plural→singular forwarding) so `actions [:click, :handle]`
|
|
51
|
-
# records a single Action entry with event+method rather than two
|
|
52
|
-
# separate Actions.
|
|
53
41
|
def actions(*names)
|
|
54
42
|
names.each do |name|
|
|
55
43
|
case name
|
|
@@ -89,10 +77,7 @@ module Vident2
|
|
|
89
77
|
self
|
|
90
78
|
end
|
|
91
79
|
|
|
92
|
-
#
|
|
93
|
-
# that can't be a Ruby kwarg) plus kwargs. Order: positional first,
|
|
94
|
-
# kwargs after — last-write wins on duplicates per the keyed merge
|
|
95
|
-
# rule.
|
|
80
|
+
# Positional Hash arg supports keys like `"admin--users"` that can't be Ruby kwargs.
|
|
96
81
|
def outlets(positional = nil, **hash)
|
|
97
82
|
if positional.is_a?(Hash)
|
|
98
83
|
positional.each { |k, v| record_keyed(@outlets, k, v) }
|
|
@@ -110,31 +95,23 @@ module Vident2
|
|
|
110
95
|
|
|
111
96
|
# ---- singular forms --------------------------------------------
|
|
112
97
|
|
|
113
|
-
# Optional `as: :alias` captured in meta for the Resolver.
|
|
114
98
|
def controller(*args, **meta)
|
|
115
99
|
@controllers << Declaration.of(*args, **meta)
|
|
116
100
|
self
|
|
117
101
|
end
|
|
118
102
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
# If no chain methods are called, the raw args pass through unchanged.
|
|
122
|
-
def action(*args)
|
|
123
|
-
builder = ActionBuilder.new(*args)
|
|
103
|
+
def action(*args, **meta)
|
|
104
|
+
builder = ActionBuilder.new(*args, **meta)
|
|
124
105
|
@actions << builder
|
|
125
106
|
builder
|
|
126
107
|
end
|
|
127
108
|
|
|
128
|
-
# Returns a `TargetBuilder` so users can chain `.when { ... }` for
|
|
129
|
-
# conditional inclusion.
|
|
130
109
|
def target(*args)
|
|
131
110
|
builder = TargetBuilder.new(*args)
|
|
132
111
|
@targets << builder
|
|
133
112
|
builder
|
|
134
113
|
end
|
|
135
114
|
|
|
136
|
-
# `value(:url, "x")`, `value(:url, -> { ... })`,
|
|
137
|
-
# `value(:count, static: 0)`, `value(:clicked_count, from_prop: true)`.
|
|
138
115
|
def value(name, *args, **meta)
|
|
139
116
|
entry = [name, Declaration.of(*args, **meta)]
|
|
140
117
|
replace_or_append(@values, entry)
|
|
@@ -161,8 +138,6 @@ module Vident2
|
|
|
161
138
|
|
|
162
139
|
# ---- folding ----------------------------------------------------
|
|
163
140
|
|
|
164
|
-
# Returns a frozen Declarations snapshot of what this block
|
|
165
|
-
# received. Called once the block finishes executing.
|
|
166
141
|
def to_declarations
|
|
167
142
|
Declarations.new(
|
|
168
143
|
controllers: @controllers.dup.freeze,
|
|
@@ -183,7 +158,6 @@ module Vident2
|
|
|
183
158
|
replace_or_append(bucket, entry)
|
|
184
159
|
end
|
|
185
160
|
|
|
186
|
-
# Last-write wins on matching key, insertion order otherwise.
|
|
187
161
|
def replace_or_append(bucket, entry)
|
|
188
162
|
key = entry.first
|
|
189
163
|
idx = bucket.index { |(k, _)| k == key }
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../stimulus/controller"
|
|
4
|
+
require_relative "../stimulus/action"
|
|
5
|
+
require_relative "../stimulus/target"
|
|
6
|
+
require_relative "../stimulus/outlet"
|
|
7
|
+
require_relative "../stimulus/value"
|
|
8
|
+
require_relative "../stimulus/param"
|
|
9
|
+
require_relative "../stimulus/class_map"
|
|
10
|
+
|
|
11
|
+
module Vident
|
|
12
|
+
module Internals
|
|
13
|
+
module Registry
|
|
14
|
+
Kind = Data.define(:name, :plural_name, :singular_name, :value_class, :keyed) do
|
|
15
|
+
alias_method :keyed?, :keyed
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
KINDS = [
|
|
19
|
+
Kind.new(:controllers, :controllers, :controller, Vident::Stimulus::Controller, false),
|
|
20
|
+
Kind.new(:actions, :actions, :action, Vident::Stimulus::Action, false),
|
|
21
|
+
Kind.new(:targets, :targets, :target, Vident::Stimulus::Target, false),
|
|
22
|
+
Kind.new(:outlets, :outlets, :outlet, Vident::Stimulus::Outlet, true),
|
|
23
|
+
Kind.new(:values, :values, :value, Vident::Stimulus::Value, true),
|
|
24
|
+
Kind.new(:params, :params, :param, Vident::Stimulus::Param, true),
|
|
25
|
+
Kind.new(:class_maps, :classes, :class, Vident::Stimulus::ClassMap, true)
|
|
26
|
+
].freeze
|
|
27
|
+
|
|
28
|
+
BY_NAME = KINDS.to_h { |k| [k.name, k] }.freeze
|
|
29
|
+
|
|
30
|
+
def self.fetch(name) = BY_NAME.fetch(name)
|
|
31
|
+
|
|
32
|
+
def self.each(&block) = KINDS.each(&block)
|
|
33
|
+
|
|
34
|
+
def self.names = BY_NAME.keys
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|