vident 1.0.1 → 1.0.2
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 +11 -0
- data/README.md +4 -1
- data/lib/vident/component_attribute_resolver.rb +27 -8
- data/lib/vident/component_class_lists.rb +3 -0
- data/lib/vident/stimulus_builder.rb +28 -11
- data/lib/vident/stimulus_helper.rb +4 -4
- data/lib/vident/version.rb +1 -1
- data/lib/vident2/caching.rb +93 -0
- data/lib/vident2/component.rb +538 -0
- data/lib/vident2/engine.rb +18 -0
- data/lib/vident2/error.rb +30 -0
- data/lib/vident2/internals/action_builder.rb +101 -0
- data/lib/vident2/internals/attribute_writer.rb +22 -0
- data/lib/vident2/internals/class_list_builder.rb +79 -0
- data/lib/vident2/internals/declaration.rb +17 -0
- data/lib/vident2/internals/declarations.rb +76 -0
- data/lib/vident2/internals/draft.rb +60 -0
- data/lib/vident2/internals/dsl.rb +198 -0
- data/lib/vident2/internals/plan.rb +12 -0
- data/lib/vident2/internals/registry.rb +41 -0
- data/lib/vident2/internals/resolver.rb +306 -0
- data/lib/vident2/internals/target_builder.rb +29 -0
- data/lib/vident2/phlex/html.rb +84 -0
- data/lib/vident2/phlex.rb +9 -0
- data/lib/vident2/stimulus/action.rb +140 -0
- data/lib/vident2/stimulus/class_map.rb +69 -0
- data/lib/vident2/stimulus/collection.rb +42 -0
- data/lib/vident2/stimulus/controller.rb +59 -0
- data/lib/vident2/stimulus/naming.rb +26 -0
- data/lib/vident2/stimulus/null.rb +16 -0
- data/lib/vident2/stimulus/outlet.rb +113 -0
- data/lib/vident2/stimulus/param.rb +62 -0
- data/lib/vident2/stimulus/target.rb +57 -0
- data/lib/vident2/stimulus/value.rb +77 -0
- data/lib/vident2/tailwind.rb +19 -0
- data/lib/vident2/version.rb +5 -0
- data/lib/vident2/view_component/base.rb +124 -0
- data/lib/vident2/view_component.rb +9 -0
- data/lib/vident2.rb +50 -0
- data/skills/vident/SKILL.md +11 -2
- data/skills/vident/api-reference.md +518 -0
- data/skills/vident/examples.md +492 -0
- metadata +35 -1
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
# Vident API reference
|
|
2
|
+
|
|
3
|
+
Public surface of `vident`, `vident-view_component`, and `vident-phlex`, verified against
|
|
4
|
+
the current code in `lib/vident/`. Every method's argument shapes, return shape, and
|
|
5
|
+
raise-conditions are documented here — SKILL.md is the tutorial; this file is the spec.
|
|
6
|
+
|
|
7
|
+
If something is missing, it isn't public. `lib/vident/*.rb` is the source of truth.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Base classes
|
|
12
|
+
|
|
13
|
+
### `Vident::ViewComponent::Base < ::ViewComponent::Base`
|
|
14
|
+
|
|
15
|
+
Inherits everything from `::ViewComponent::Base` and includes `Vident::Component`.
|
|
16
|
+
File: `lib/vident/view_component/base.rb`.
|
|
17
|
+
|
|
18
|
+
Adds:
|
|
19
|
+
|
|
20
|
+
- `root_element(**overrides, &block)` — renders the component's root tag. `overrides`
|
|
21
|
+
are passed as HTML options (merged with `root_element_attributes`, `html_options`,
|
|
22
|
+
class precedence rules from SKILL.md §4). Self-closing tags (`:area`, `:br`, `:col`,
|
|
23
|
+
`:embed`, `:hr`, `:img`, `:input`, `:link`, `:meta`, `:param`, `:source`, `:track`,
|
|
24
|
+
`:wbr`) are emitted without children.
|
|
25
|
+
- 14 `as_stimulus_*` helpers — return an HTML-safe `String` of raw `data-*` attributes
|
|
26
|
+
suitable for embedding inside an HTML tag in ERB. Signatures match the corresponding
|
|
27
|
+
`stimulus_*` method (see section 4):
|
|
28
|
+
- Plural: `as_stimulus_controllers`, `as_stimulus_actions`, `as_stimulus_targets`,
|
|
29
|
+
`as_stimulus_outlets`, `as_stimulus_values`, `as_stimulus_params`, `as_stimulus_classes`.
|
|
30
|
+
- Singular: `as_stimulus_controller`, `as_stimulus_action`, `as_stimulus_target`,
|
|
31
|
+
`as_stimulus_outlet`, `as_stimulus_value`, `as_stimulus_param`, `as_stimulus_class`.
|
|
32
|
+
- Class-level cache support: `template_path`, `component_path`, `components_base_path`,
|
|
33
|
+
`cache_component_modified_time`, `cache_sidecar_view_modified_time`,
|
|
34
|
+
`cache_rb_component_modified_time` — used by `Vident::Caching` to chain
|
|
35
|
+
template mtimes into a component's cache key.
|
|
36
|
+
|
|
37
|
+
### `Vident::Phlex::HTML < ::Phlex::HTML`
|
|
38
|
+
|
|
39
|
+
Includes `Vident::Component`. File: `lib/vident/phlex/html.rb`.
|
|
40
|
+
|
|
41
|
+
Adds:
|
|
42
|
+
|
|
43
|
+
- `root_element(**overrides, &block)` — Phlex equivalent. Dispatches to the tag method
|
|
44
|
+
named by `root_element_tag_type` (default `:div`). The block runs first, then the tag
|
|
45
|
+
wraps the captured content so DSL methods called inside the block see resolved state
|
|
46
|
+
before outer tag options are computed.
|
|
47
|
+
- Tag whitelist — `check_valid_html_tag!` enforces `STANDARD_ELEMENTS + VOID_ELEMENTS`
|
|
48
|
+
(see file for the full set). Passing an unknown tag to `element_tag:` or to
|
|
49
|
+
`child_element` raises `ArgumentError`.
|
|
50
|
+
- Source-file tracking — the class-level `inherited` hook records each subclass's source
|
|
51
|
+
file in `component_source_file_path` so `Vident::Caching` can pick up an mtime.
|
|
52
|
+
- No `as_stimulus_*` helpers — Phlex has its own tag DSL; use `child_element` or spread
|
|
53
|
+
`data: { **component.stimulus_target(:name) }` inline.
|
|
54
|
+
|
|
55
|
+
### `Vident::Component` (module)
|
|
56
|
+
|
|
57
|
+
Included into both base classes. File: `lib/vident/component.rb`.
|
|
58
|
+
|
|
59
|
+
Public class methods:
|
|
60
|
+
|
|
61
|
+
- `prop_names` — `Array(Symbol)`, list of every declared prop (including inherited).
|
|
62
|
+
|
|
63
|
+
Public instance methods:
|
|
64
|
+
|
|
65
|
+
- `after_component_initialize` — empty override hook. Runs after props are assigned and
|
|
66
|
+
Vident has prepared its stimulus collections. Do not override `after_initialize` unless
|
|
67
|
+
you `super` — Literal calls it to wire everything up.
|
|
68
|
+
- `root_element_classes` — override to return `String | Array(String) | nil`. Lower
|
|
69
|
+
precedence than `html_options[:class]` and `root_element_attributes[:classes]`
|
|
70
|
+
(see SKILL.md §4).
|
|
71
|
+
- `root_element_attributes` — override to return a Hash. Accepted keys (all optional):
|
|
72
|
+
`:element_tag` (Symbol), `:html_options` (Hash), `:id` (String), `:classes`
|
|
73
|
+
(String | Array), and any of the seven `stimulus_<plural>:` / `stimulus_<singular>:`
|
|
74
|
+
keys documented in section 5.
|
|
75
|
+
- `clone(overrides = {})` — returns a new instance, `self.class.new(**to_h.merge(**overrides))`.
|
|
76
|
+
- `inspect(klass_name = "Component")` — formatted debug string with every prop.
|
|
77
|
+
- `id` — `String`, auto-generated from `StableId` if `@id` was nil. The generated form
|
|
78
|
+
is `"#{component_name}-#{StableId.next_id_in_sequence}"`.
|
|
79
|
+
- `prop_names` — instance-method alias for the class method.
|
|
80
|
+
|
|
81
|
+
Not public (override at your own risk, used internally):
|
|
82
|
+
|
|
83
|
+
- `root_element(&block)` — raises in the base; the ViewComponent / Phlex subclasses
|
|
84
|
+
implement it.
|
|
85
|
+
- `root_element_tag_type` — returns `@element_tag || :div`.
|
|
86
|
+
- `random_id` — memoised generator (cached per instance).
|
|
87
|
+
|
|
88
|
+
### Built-in props (every component)
|
|
89
|
+
|
|
90
|
+
From `Vident::Component` (`lib/vident/component.rb`):
|
|
91
|
+
|
|
92
|
+
| Prop | Type | Default | Notes |
|
|
93
|
+
| -------------- | ----------------------------------- | -------------- | ------------------------------------------------ |
|
|
94
|
+
| `element_tag` | `Symbol` | `:div` | Root HTML tag. |
|
|
95
|
+
| `id` | `_Nilable(String)` | auto | Auto-generated via `StableId` when not provided. |
|
|
96
|
+
| `classes` | `_Union(String, _Array(String))` | `[]` | Appended on top of all other class sources. |
|
|
97
|
+
| `html_options` | `Hash` | `{}` | Merged onto root; highest class-source precedence. |
|
|
98
|
+
|
|
99
|
+
From `Vident::StimulusComponent` (`lib/vident/stimulus_component.rb`):
|
|
100
|
+
|
|
101
|
+
| Prop | Type | Default |
|
|
102
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------ |
|
|
103
|
+
| `stimulus_controllers` | `_Array(_Union(String, Symbol, StimulusController, StimulusControllerCollection))` | `[default_controller_path]` unless `no_stimulus_controller`, else `[]` |
|
|
104
|
+
| `stimulus_actions` | `_Array(_Union(String, Symbol, Array, Hash, StimulusAction, StimulusAction::Descriptor, StimulusActionCollection))` | `[]` |
|
|
105
|
+
| `stimulus_targets` | `_Array(_Union(String, Symbol, Array, Hash, StimulusTarget, StimulusTargetCollection))` | `[]` |
|
|
106
|
+
| `stimulus_outlets` | `_Array(_Union(String, Symbol, StimulusOutlet, StimulusOutletCollection))` | `[]` |
|
|
107
|
+
| `stimulus_outlet_host` | `_Nilable(Vident::Component)` | `nil` |
|
|
108
|
+
| `stimulus_values` | `_Union(_Hash(Symbol, _Any), Array, StimulusValue, StimulusValueCollection)` | `{}` |
|
|
109
|
+
| `stimulus_params` | `_Union(_Hash(Symbol, _Any), Array, StimulusParam, StimulusParamCollection)` | `{}` |
|
|
110
|
+
| `stimulus_classes` | `_Union(_Hash(Symbol, String), Array, StimulusClass, StimulusClassCollection)` | `{}` |
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 2. Class-level DSL
|
|
115
|
+
|
|
116
|
+
All of these live on `Vident::Component`'s class body (via included modules).
|
|
117
|
+
|
|
118
|
+
- `prop(name, type, **literal_options)` — from the Literal gem. See
|
|
119
|
+
https://literal.fun/ for option details. `default:` may be a callable (`lambda/proc`)
|
|
120
|
+
or an immediate value; callable is required when the default is non-frozen (hash, array).
|
|
121
|
+
- `no_stimulus_controller` — sets a class ivar that drops the implied controller from
|
|
122
|
+
the `stimulus_controllers` default. Use when the component is purely presentational
|
|
123
|
+
and needs no paired `_controller.js`.
|
|
124
|
+
- `stimulus_controller?` — `Boolean`, `true` by default; becomes `false` after a
|
|
125
|
+
`no_stimulus_controller` declaration.
|
|
126
|
+
- `stimulus_identifier_path` — the `name.underscore` of the class (e.g.
|
|
127
|
+
`"dashboard/release_card_component"`). Falls back to `"anonymous_component"` for
|
|
128
|
+
anonymous classes.
|
|
129
|
+
- `stimulus_identifier` — `stimulize_path(stimulus_identifier_path)` — the kebab-cased
|
|
130
|
+
identifier (`"dashboard--release-card-component"`). Also available as an instance method.
|
|
131
|
+
- `component_name` — memoised alias for `stimulus_identifier`. Also available as an
|
|
132
|
+
instance method. Used as the first class on the root element and as the outlet name
|
|
133
|
+
seed.
|
|
134
|
+
- `stimulus_scoped_event(event)` — `Symbol` of the form `:"<component_name>:<jsName>"`.
|
|
135
|
+
E.g. `FooComponent.stimulus_scoped_event(:data_ready)` →
|
|
136
|
+
`:"foo-component:dataReady"`. Also an instance method.
|
|
137
|
+
- `stimulus_scoped_event_on_window(event)` — same, with `@window` suffix. Also an
|
|
138
|
+
instance method.
|
|
139
|
+
- `stimulus(&block)` — the DSL entry point. Opens a `Vident::StimulusBuilder` block
|
|
140
|
+
evaluator. See section 3.
|
|
141
|
+
|
|
142
|
+
Not intended for application code:
|
|
143
|
+
|
|
144
|
+
- `stimulus_dsl_attributes(component_instance)` — returns the DSL's emitted attribute
|
|
145
|
+
hash for a specific instance (so procs resolve against it).
|
|
146
|
+
- `stimulus_dsl_builder` — the builder accessor; `protected`, used only by inheritance
|
|
147
|
+
merging.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 3. `stimulus do ... end` block
|
|
152
|
+
|
|
153
|
+
Evaluated by `Vident::StimulusBuilder` (`lib/vident/stimulus_builder.rb`). Multiple
|
|
154
|
+
`stimulus do` blocks on the same class accumulate. A subclass's blocks are merged with
|
|
155
|
+
every parent's blocks on first access (subclass entries appended to positional kinds;
|
|
156
|
+
subclass wins on conflicts for keyed kinds).
|
|
157
|
+
|
|
158
|
+
Every DSL method returns `self` so calls chain — but there's no real reason to chain
|
|
159
|
+
inside a `do ... end` block. All methods accept procs anywhere a value is expected;
|
|
160
|
+
procs are evaluated via `instance_exec` on the component instance at render time.
|
|
161
|
+
|
|
162
|
+
### Methods on the builder
|
|
163
|
+
|
|
164
|
+
- `actions(*entries)` — positional. Each entry is one of:
|
|
165
|
+
- `Symbol` → `implied#<jsSymbol>`
|
|
166
|
+
- `[Symbol, Symbol]` → `<event>-><implied>#<jsMethod>`
|
|
167
|
+
- `[Symbol, String, Symbol]` → `<event>-><stimulized-path>#<jsMethod>`
|
|
168
|
+
- `String` containing `#` → parsed literally (pass-through); `event->ctrl#method`
|
|
169
|
+
or `ctrl#method` supported.
|
|
170
|
+
- `Hash` (desugared to a `Descriptor`) — keys: `:method` (required), `:event`,
|
|
171
|
+
`:controller`, `:options` (`Array<Symbol>`), `:keyboard` (`String`),
|
|
172
|
+
`:window` (`Boolean`). See section 4.2 for the `options:` whitelist.
|
|
173
|
+
- `Vident::StimulusAction::Descriptor` — typed equivalent of the Hash form.
|
|
174
|
+
- `Proc` — evaluated at render time; `nil` / `false` return drops the entry.
|
|
175
|
+
- `targets(*entries)` — positional. Each entry is one of:
|
|
176
|
+
- `Symbol` → target on the implied controller
|
|
177
|
+
- `String` → pass-through target name
|
|
178
|
+
- `[String, Symbol]` → target on the named cross-controller
|
|
179
|
+
- `Proc` — evaluated at render time; `nil` drops the entry.
|
|
180
|
+
- `values(**kvs)` — keyed. Values may be `String`/`Number`/`Boolean` (stringified),
|
|
181
|
+
`Array`/`Hash` (JSON-serialised), `Vident::StimulusNull` (emits literal `"null"`),
|
|
182
|
+
or a `Proc` resolving to any of the above. A resolved `nil` omits the attribute.
|
|
183
|
+
- `params(**kvs)` — keyed. Same serialisation rules as `values`.
|
|
184
|
+
- `classes(**kvs)` — keyed. Value is `String` or `Array(String)`; array joined with
|
|
185
|
+
single space. A `Proc` may resolve to either. A resolved `nil` omits the attribute.
|
|
186
|
+
- `outlets(positional_hash = nil, **kvs)` — keyed. Value is a `String` CSS selector.
|
|
187
|
+
`outlets({"admin--users" => ".sel"})` accepts a positional Hash so identifiers
|
|
188
|
+
containing `--` (not valid Ruby kwarg keys) work. Procs are **not** supported here
|
|
189
|
+
— the builder skips proc resolution for outlets; pass cross-controller outlets via
|
|
190
|
+
the `stimulus_outlets:` prop or `root_element_attributes` instead.
|
|
191
|
+
- `values_from_props(*prop_names)` — keyed, sidecar to `values`. Mirrors each prop's
|
|
192
|
+
current `@ivar` value at render time. Prop names are Symbols.
|
|
193
|
+
|
|
194
|
+
No `controllers` method exists in the DSL. Controllers are set via the `stimulus_controllers:`
|
|
195
|
+
prop, `root_element_attributes[:stimulus_controllers]`, or `no_stimulus_controller`.
|
|
196
|
+
|
|
197
|
+
### What the builder emits
|
|
198
|
+
|
|
199
|
+
`to_attributes(component_instance)` returns a Hash keyed by `:stimulus_actions`,
|
|
200
|
+
`:stimulus_targets`, `:stimulus_values`, `:stimulus_params`, `:stimulus_classes`,
|
|
201
|
+
`:stimulus_outlets`, plus `:stimulus_values_from_props` (an Array of prop-name Symbols)
|
|
202
|
+
if `values_from_props` was used. Only primitives with entries are included.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 4. Instance-level Stimulus helpers (`Vident::StimulusAttributes`)
|
|
207
|
+
|
|
208
|
+
Included into every component via `StimulusComponent`. File:
|
|
209
|
+
`lib/vident/stimulus_attributes.rb`.
|
|
210
|
+
|
|
211
|
+
### 4.1 Plural parsers `stimulus_<plural>(*args)`
|
|
212
|
+
|
|
213
|
+
Seven methods: `stimulus_controllers`, `stimulus_actions`, `stimulus_targets`,
|
|
214
|
+
`stimulus_outlets`, `stimulus_values`, `stimulus_params`, `stimulus_classes`.
|
|
215
|
+
|
|
216
|
+
Each returns a collection object (`StimulusActionCollection`, etc.) whose `#to_h`
|
|
217
|
+
serialises to a `Hash` of `data-*` keys → values. Arg handling per input:
|
|
218
|
+
|
|
219
|
+
- no args or all-blank → empty collection
|
|
220
|
+
- single pre-built collection → returned as-is
|
|
221
|
+
- `Array` → splatted into the singular builder
|
|
222
|
+
- `Hash` (for **keyed** primitives: outlets, values, params, classes)
|
|
223
|
+
→ expanded per-pair, each pair becomes one value object
|
|
224
|
+
- `Hash` (for **positional** primitives: controllers, actions, targets)
|
|
225
|
+
→ passed as a single-arg descriptor (Action's `{event:, method:, ...}` form)
|
|
226
|
+
- pre-built value object → preserved
|
|
227
|
+
|
|
228
|
+
### 4.2 Singular builders `stimulus_<singular>(*args)`
|
|
229
|
+
|
|
230
|
+
Each singular builder accepts the set of argument shapes its `parse_arguments`
|
|
231
|
+
implementation supports. Raises `ArgumentError` on unsupported shape or arity.
|
|
232
|
+
|
|
233
|
+
- `stimulus_controller(*)` — 0 or 1 arg. 0 args returns the implied controller; 1 arg
|
|
234
|
+
is a controller path `String`/`Symbol`.
|
|
235
|
+
- `stimulus_action(*)` — 1/2/3 args. See `StimulusAction::parse_arguments` for all
|
|
236
|
+
accepted forms. `options:` whitelist (raises otherwise):
|
|
237
|
+
`[:once, :prevent, :stop, :passive, :"!passive", :capture, :self]`.
|
|
238
|
+
- `stimulus_target(*)` — 1 or 2 args. `(Symbol)` / `(String)` → implied controller;
|
|
239
|
+
`(String, Symbol)` → cross-controller + name.
|
|
240
|
+
- `stimulus_outlet(*)` — 1/2/3 args.
|
|
241
|
+
- `(Symbol)` or `(String)` → identifier, auto-generated selector
|
|
242
|
+
`"#<component_id> [data-controller~=<identifier>]"`.
|
|
243
|
+
- `(Array[identifier, selector])` — explicit selector.
|
|
244
|
+
- `(component_instance)` — instance responding to `#stimulus_identifier` or
|
|
245
|
+
`#implied_controller_name`; auto-selector built from its identifier.
|
|
246
|
+
- `(String|Symbol, String)` → outlet-name + selector on implied controller.
|
|
247
|
+
- `(String, Symbol, String)` → cross-controller + outlet-name + selector.
|
|
248
|
+
- `stimulus_value(name, value)` or `stimulus_value(controller_path, name, value)` —
|
|
249
|
+
2 or 3 args.
|
|
250
|
+
- `stimulus_param(name, value)` or `stimulus_param(controller_path, name, value)` —
|
|
251
|
+
2 or 3 args.
|
|
252
|
+
- `stimulus_class(name, classes)` or `stimulus_class(controller_path, name, classes)` —
|
|
253
|
+
2 or 3 args; `classes` is `String` or `Array(String)`.
|
|
254
|
+
|
|
255
|
+
### 4.3 Mutators `add_stimulus_<plural>(input)`
|
|
256
|
+
|
|
257
|
+
Seven methods, one per primitive. Merge new attributes into the per-kind collection
|
|
258
|
+
ivar (e.g. `@stimulus_actions_collection`). Typical use: inside
|
|
259
|
+
`after_component_initialize`, compute runtime attributes and add them.
|
|
260
|
+
|
|
261
|
+
**Splat asymmetry vs DSL.** The DSL's `actions [:click, :handle]` treats the Array as
|
|
262
|
+
one action descriptor (event + method). The mutator `add_stimulus_actions([:click, :handle])`
|
|
263
|
+
splats the Array and treats it as two separate symbol actions. To pass an
|
|
264
|
+
Array-shaped single action through the mutator, wrap with a pre-built value
|
|
265
|
+
(`stimulus_action(:click, :handle)`) or double-wrap (`[[:click, :handle]]`).
|
|
266
|
+
|
|
267
|
+
### 4.4 Value serialisation (`StimulusAttributeBase#serialize_value`)
|
|
268
|
+
|
|
269
|
+
`Array` and `Hash` → JSON. Everything else → `to_s`. `Vident::StimulusNull.to_s`
|
|
270
|
+
returns the literal string `"null"`. A `nil` reaches the DSL/prop layer and
|
|
271
|
+
is dropped by `StimulusBuilder#resolve_hash_filtering_nil` before serialisation, so
|
|
272
|
+
the data attribute is omitted (not emitted as empty).
|
|
273
|
+
|
|
274
|
+
### 4.5 Name-shaping helpers
|
|
275
|
+
|
|
276
|
+
- `StimulusAttributeBase.stimulize_path(path)` — `"admin/users"` → `"admin--users"`;
|
|
277
|
+
each path segment is `dasherize`d and segments joined with `--`.
|
|
278
|
+
- `StimulusAttributeBase.js_name(name)` — `camelize(:lower)`; `:my_thing` → `"myThing"`.
|
|
279
|
+
|
|
280
|
+
Both also available as private instance methods on any `StimulusAttributeBase` subclass.
|
|
281
|
+
|
|
282
|
+
### 4.6 Scoped events
|
|
283
|
+
|
|
284
|
+
- Class method `stimulus_scoped_event(event)` — returns `Symbol`
|
|
285
|
+
`:"<component_name>:<jsName>"`. **Call on the dispatcher's class**, not on the
|
|
286
|
+
listener's.
|
|
287
|
+
- Class method `stimulus_scoped_event_on_window(event)` — same with `@window` suffix.
|
|
288
|
+
- Both also exist as instance methods that delegate to the class method.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 5. `root_element_attributes` accepted keys
|
|
293
|
+
|
|
294
|
+
Override `root_element_attributes` (instance method on your component) to return any
|
|
295
|
+
subset of:
|
|
296
|
+
|
|
297
|
+
| Key | Type | Effect |
|
|
298
|
+
| --------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
299
|
+
| `:element_tag` | `Symbol` | Overrides the root element tag. |
|
|
300
|
+
| `:html_options` | `Hash` | Merged onto the root; highest-precedence source for `:class`. |
|
|
301
|
+
| `:classes` | `String \| Array(String)` | Second-highest `:class` source (see SKILL.md §4). |
|
|
302
|
+
| `:id` | `String` | Sets the root element's id. |
|
|
303
|
+
| `:stimulus_controllers`| Same shape as `stimulus_controllers` prop | Merged into the controllers collection. |
|
|
304
|
+
| `:stimulus_actions` | Same shape as `stimulus_actions` prop | Merged into the actions collection. |
|
|
305
|
+
| `:stimulus_targets` | Same shape as `stimulus_targets` prop | Merged into the targets collection. |
|
|
306
|
+
| `:stimulus_outlets` | Same shape as `stimulus_outlets` prop | Merged into the outlets collection. |
|
|
307
|
+
| `:stimulus_values` | Same shape as `stimulus_values` prop | Merged into the values collection. |
|
|
308
|
+
| `:stimulus_params` | Same shape as `stimulus_params` prop | Merged into the params collection. |
|
|
309
|
+
| `:stimulus_classes` | Same shape as `stimulus_classes` prop | Merged into the classes collection. |
|
|
310
|
+
|
|
311
|
+
Precedence (lower wins if both set):
|
|
312
|
+
|
|
313
|
+
1. `stimulus do` DSL
|
|
314
|
+
2. `stimulus_*` props (the `render Foo.new(stimulus_actions: ...)` path)
|
|
315
|
+
3. `root_element_attributes` return value
|
|
316
|
+
4. `add_stimulus_*` called after those (e.g. in `after_component_initialize`)
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## 6. `child_element`
|
|
321
|
+
|
|
322
|
+
Renders a single child tag with `stimulus_*` kwargs compiled into `data-*` attributes.
|
|
323
|
+
|
|
324
|
+
```ruby
|
|
325
|
+
def child_element(tag_name,
|
|
326
|
+
stimulus_controllers: nil, stimulus_controller: nil,
|
|
327
|
+
stimulus_actions: nil, stimulus_action: nil,
|
|
328
|
+
stimulus_targets: nil, stimulus_target: nil,
|
|
329
|
+
stimulus_outlets: nil, stimulus_outlet: nil,
|
|
330
|
+
stimulus_values: nil, stimulus_value: nil,
|
|
331
|
+
stimulus_params: nil, stimulus_param: nil,
|
|
332
|
+
stimulus_classes: nil, stimulus_class: nil,
|
|
333
|
+
**options, &block)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
- Plural kwargs take an `Enumerable`; passing a non-Enumerable raises
|
|
337
|
+
`ArgumentError` with a message pointing at the singular name.
|
|
338
|
+
- Singular kwargs take a single entry.
|
|
339
|
+
- `**options` passes through as HTML options.
|
|
340
|
+
- For ViewComponent's renderer, self-closing tags are emitted without the block.
|
|
341
|
+
- For Phlex's renderer, the tag name is validated against
|
|
342
|
+
`Vident::Phlex::HTML::VALID_TAGS`; unknown tags raise `ArgumentError`.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## 7. `Vident::StimulusBuilder` primitives
|
|
347
|
+
|
|
348
|
+
For use in advanced cases (passing typed descriptors across components, building
|
|
349
|
+
reusable shared helpers). File: `lib/vident/stimulus_action.rb`.
|
|
350
|
+
|
|
351
|
+
### `Vident::StimulusAction::Descriptor`
|
|
352
|
+
|
|
353
|
+
A `::Literal::Data` value object with the same shape as the Hash form accepted by
|
|
354
|
+
`actions`:
|
|
355
|
+
|
|
356
|
+
| Prop | Type | Default |
|
|
357
|
+
| ------------- | ------------------------------------- | ------- |
|
|
358
|
+
| `method` | `_Union(Symbol, String)` | — |
|
|
359
|
+
| `event` | `_Nilable(_Union(Symbol, String))` | `nil` |
|
|
360
|
+
| `controller` | `_Nilable(String)` | `nil` |
|
|
361
|
+
| `options` | `_Array(Symbol)` | `[]` |
|
|
362
|
+
| `keyboard` | `_Nilable(String)` | `nil` |
|
|
363
|
+
| `window` | `_Boolean` | `false` |
|
|
364
|
+
|
|
365
|
+
### `Vident::StimulusNull`
|
|
366
|
+
|
|
367
|
+
Frozen singleton object. `inspect` → `"Vident::StimulusNull"`; `to_s` → `"null"`.
|
|
368
|
+
See SKILL.md §1.4 for the usage contract.
|
|
369
|
+
|
|
370
|
+
### Collection classes
|
|
371
|
+
|
|
372
|
+
Each primitive has a `StimulusXCollection < StimulusCollectionBase`:
|
|
373
|
+
|
|
374
|
+
- Base methods: `<<(item)`, `to_a`, `to_h` (abstract; each subclass implements),
|
|
375
|
+
`empty?`, `any?`, `merge(*others)`, `self.merge(*collections)`.
|
|
376
|
+
- `StimulusActionCollection#to_h` → `{action: "…"}` with entries joined by space.
|
|
377
|
+
- `StimulusControllerCollection#to_h` → `{controller: "…"}` with non-empty entries
|
|
378
|
+
joined by space.
|
|
379
|
+
- `StimulusTargetCollection#to_h` → one key per controller-target attribute;
|
|
380
|
+
multiple targets on the same controller are joined with a single space.
|
|
381
|
+
- `StimulusValueCollection`, `StimulusParamCollection`, `StimulusClassCollection` →
|
|
382
|
+
merged per-data-attribute hash.
|
|
383
|
+
- `StimulusOutletCollection` → same.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 8. `Vident::Caching`
|
|
388
|
+
|
|
389
|
+
Opt-in: `include Vident::Caching` + `with_cache_key(...)` in the component class.
|
|
390
|
+
File: `lib/vident/caching.rb`.
|
|
391
|
+
|
|
392
|
+
### Class methods
|
|
393
|
+
|
|
394
|
+
- `with_cache_key(*attrs, name: :_collection)` — declares which attributes feed into
|
|
395
|
+
`cache_key`. The call appends `:component_modified_time` and `:to_h` (when
|
|
396
|
+
available) to the given attrs, then calls `named_cache_key_includes(name, *attrs.uniq)`.
|
|
397
|
+
- `depends_on(*klasses)` — chains other Vident components' `component_modified_time`
|
|
398
|
+
into this class's `component_modified_time`, so sub-component edits bust the
|
|
399
|
+
parent's cache.
|
|
400
|
+
- `component_modified_time` — memoised in `Rails.env.production?`, otherwise recomputed
|
|
401
|
+
on every call. Raises `StandardError` if the host class has no
|
|
402
|
+
`cache_component_modified_time` (base classes provide it).
|
|
403
|
+
|
|
404
|
+
### Instance methods
|
|
405
|
+
|
|
406
|
+
- `component_modified_time` — delegates to the class method.
|
|
407
|
+
- `cacheable?` — `respond_to?(:cache_key)`.
|
|
408
|
+
- `cache_key` — defined when `with_cache_key` has been called; returns
|
|
409
|
+
`"#{class.name}/#{cache_keys_for_sources(...).join("/")}"`, optionally suffixed with
|
|
410
|
+
`ENV["RAILS_CACHE_ID"]`. Raises `StandardError` if the computed key is blank.
|
|
411
|
+
- `cache_key_modifier` — returns `ENV["RAILS_CACHE_ID"]` (may be nil).
|
|
412
|
+
|
|
413
|
+
`with_cache_key` without any attrs is valid — the call still appends
|
|
414
|
+
`:component_modified_time` and `:to_h`, so the cache key reflects the template mtime
|
|
415
|
+
plus the component's full prop hash.
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## 9. `Vident::StableId`
|
|
420
|
+
|
|
421
|
+
File: `lib/vident/stable_id.rb`.
|
|
422
|
+
|
|
423
|
+
### Errors
|
|
424
|
+
|
|
425
|
+
- `Vident::StableId::GeneratorNotSetError` — raised by `STRICT` when no per-thread
|
|
426
|
+
sequence generator is set.
|
|
427
|
+
- `Vident::StableId::StrategyNotConfiguredError` — raised when any component calls
|
|
428
|
+
`next_id_in_sequence` before `StableId.strategy=` has been set.
|
|
429
|
+
|
|
430
|
+
### Strategies (both callables accepting `(generator_or_nil) -> String`)
|
|
431
|
+
|
|
432
|
+
- `STRICT` — raises `GeneratorNotSetError` if the generator is nil. Use in
|
|
433
|
+
development/production paired with the `before_action` seed in `ApplicationController`.
|
|
434
|
+
- `RANDOM_FALLBACK` — returns `Random.hex(16)` when the generator is nil; otherwise
|
|
435
|
+
returns `generator.next.join("-")`. Use in test/previews/jobs/mailers.
|
|
436
|
+
|
|
437
|
+
### Class methods
|
|
438
|
+
|
|
439
|
+
- `strategy` / `strategy=` — get/set the configured callable.
|
|
440
|
+
- `set_current_sequence_generator(seed:)` — seeds a per-thread generator. Raises
|
|
441
|
+
`ArgumentError` on `seed: nil`. Seed is MD5-hashed then fed to `Random.new`, so any
|
|
442
|
+
`String`-coercible seed works.
|
|
443
|
+
- `clear_current_sequence_generator` — clears the per-thread generator.
|
|
444
|
+
- `with_sequence_generator(seed:) { ... }` — scoped seed for a block (used by jobs,
|
|
445
|
+
mailers, Metal endpoints). Restores the previous generator on exit.
|
|
446
|
+
- `next_id_in_sequence` — delegates to the configured `strategy`.
|
|
447
|
+
|
|
448
|
+
### Installation
|
|
449
|
+
|
|
450
|
+
`bin/rails generate vident:install` (file:
|
|
451
|
+
`lib/generators/vident/install/install_generator.rb`):
|
|
452
|
+
|
|
453
|
+
1. Writes `config/initializers/vident.rb` setting `strategy` to `RANDOM_FALLBACK` in
|
|
454
|
+
test and `STRICT` everywhere else.
|
|
455
|
+
2. Injects `before_action` + `after_action` into `ApplicationController` (idempotent —
|
|
456
|
+
skips if a previous install patched it).
|
|
457
|
+
3. Copies `skills/vident/SKILL.md` from the gem to `.claude/skills/vident/SKILL.md`
|
|
458
|
+
in the host app (skipped if already present).
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## 10. `Vident::Tailwind`
|
|
463
|
+
|
|
464
|
+
Included into every component. File: `lib/vident/tailwind.rb`.
|
|
465
|
+
|
|
466
|
+
- `tailwind_merger` — returns a thread-cached `::TailwindMerge::Merger` instance if
|
|
467
|
+
the `tailwind_merge` gem is loaded; otherwise returns `nil`.
|
|
468
|
+
- `tailwind_merge_available?` — `true` iff `::TailwindMerge::Merger` is defined.
|
|
469
|
+
|
|
470
|
+
`Vident::ClassListBuilder` invokes `tailwind_merger.merge(class_string)` automatically
|
|
471
|
+
at the final stage of its `build(...)` call when a merger is provided. No per-component
|
|
472
|
+
opt-in is required beyond adding the gem to the Gemfile.
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## 11. `class_list_for_stimulus_classes`
|
|
477
|
+
|
|
478
|
+
Instance method on every component. File: `lib/vident/component_class_lists.rb`.
|
|
479
|
+
|
|
480
|
+
```ruby
|
|
481
|
+
class_list_for_stimulus_classes(*names) -> String
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
Returns the resolved `data-*-class` values for the named stimulus-class entries,
|
|
485
|
+
deduplicated and (when `tailwind_merger` is available) Tailwind-merged. Intended
|
|
486
|
+
for inlining into `class=` on SSR so the first render has the same visual state the
|
|
487
|
+
JS controller will toggle on/off.
|
|
488
|
+
|
|
489
|
+
Names may be `Symbol` or `String`; both are normalised via `dasherize`.
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## 12. Rails engine hooks
|
|
494
|
+
|
|
495
|
+
- `Vident::Engine` (`lib/vident/engine.rb`) — autoloaded when Rails is defined.
|
|
496
|
+
Loads `Vident::Generators::InstallGenerator`.
|
|
497
|
+
- `Vident::Phlex::Engine`, `Vident::ViewComponent::Engine` — thin Rails::Engine
|
|
498
|
+
subclasses from the sub-gems; no explicit initializer.
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## 13. What's not in the public API
|
|
503
|
+
|
|
504
|
+
The following show up in `lib/vident/*.rb` but are explicitly internal:
|
|
505
|
+
|
|
506
|
+
- `Vident::Stimulus::PRIMITIVES`, `Vident::Stimulus::Primitive`,
|
|
507
|
+
`Vident::Stimulus::KeyedPrimitive`, `Vident::Stimulus::PositionalPrimitive`,
|
|
508
|
+
`Vident::Stimulus::Naming` — the registry that drives every plural parser,
|
|
509
|
+
mutator, and DSL primitive. Don't rely on these modules in application code.
|
|
510
|
+
- `Vident::StimulusDataAttributeBuilder` — used internally by `root_element_attributes`
|
|
511
|
+
resolution and `child_element`. Takes a collection-per-primitive kwarg hash and
|
|
512
|
+
merges their `to_h` outputs.
|
|
513
|
+
- `Vident::ClassListBuilder` — invoked internally by `ComponentClassLists#render_classes`.
|
|
514
|
+
- `Vident::ComponentAttributeResolver`, `Vident::ComponentClassLists`,
|
|
515
|
+
`Vident::StimulusHelper`, `Vident::ChildElementHelper`, `Vident::StimulusComponent` —
|
|
516
|
+
included into components; their private/internal methods are not API.
|
|
517
|
+
- `Vident::StimulusComponent.stimulus_identifier_from_path(path)` — still callable
|
|
518
|
+
but kept only as a back-compat shim for `StimulusAttributeBase.stimulize_path`.
|