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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +54 -0
- data/README.md +49 -18
- 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/vident/internals/action_builder.rb +97 -0
- data/lib/vident/internals/attribute_writer.rb +17 -0
- data/lib/vident/internals/class_list_builder.rb +62 -0
- data/lib/vident/internals/declaration.rb +13 -0
- data/lib/vident/internals/declarations.rb +64 -0
- data/lib/vident/internals/draft.rb +47 -0
- data/lib/vident/internals/dsl.rb +172 -0
- data/lib/vident/internals/plan.rb +9 -0
- data/lib/vident/internals/registry.rb +37 -0
- data/lib/vident/internals/resolver.rb +316 -0
- data/lib/vident/internals/target_builder.rb +23 -0
- data/lib/vident/stable_id.rb +3 -3
- data/lib/vident/stimulus/action.rb +127 -0
- data/lib/vident/stimulus/base.rb +26 -0
- data/lib/vident/stimulus/class_map.rb +57 -0
- data/lib/vident/stimulus/collection.rb +40 -0
- 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/vident/stimulus/outlet.rb +93 -0
- data/lib/vident/stimulus/param.rb +56 -0
- data/lib/vident/stimulus/target.rb +48 -0
- 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 +44 -36
- data/skills/vident/SKILL.md +133 -21
- data/skills/vident/api-reference.md +662 -0
- data/skills/vident/examples.md +505 -0
- metadata +40 -28
- 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 -87
- data/lib/vident/component_class_lists.rb +0 -34
- 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 -119
- 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
|
@@ -0,0 +1,662 @@
|
|
|
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
|
+
- `with(overrides = {})` — returns a new instance, `self.class.new(**to_h.merge(overrides))`. `clone(overrides = {})` is a backward-compat alias.
|
|
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::Component` via the `StimulusDeclaring` / `StimulusParsing` capability mixins:
|
|
100
|
+
|
|
101
|
+
| Prop | Type | Default |
|
|
102
|
+
| ----------------------- | --------------------------------- | ------------------------------------ |
|
|
103
|
+
| `stimulus_controllers` | `Vident::Types::StimulusControllers` | `[default_controller_path]` unless `no_stimulus_controller`, else `[]` |
|
|
104
|
+
| `stimulus_actions` | `Vident::Types::StimulusActions` | `[]` |
|
|
105
|
+
| `stimulus_targets` | `Vident::Types::StimulusTargets` | `[]` |
|
|
106
|
+
| `stimulus_outlets` | `Vident::Types::StimulusOutlets` | `[]` |
|
|
107
|
+
| `stimulus_outlet_host` | `_Nilable(Vident::Component)` | `nil` |
|
|
108
|
+
| `stimulus_values` | `Vident::Types::StimulusValues` | `{}` |
|
|
109
|
+
| `stimulus_params` | `Vident::Types::StimulusParams` | `{}` |
|
|
110
|
+
| `stimulus_classes` | `Vident::Types::StimulusClasses` | `{}` |
|
|
111
|
+
|
|
112
|
+
`Vident::Types::*` are the canonical Literal type unions for each prop kind (file: `lib/vident/types.rb`). The unions are:
|
|
113
|
+
|
|
114
|
+
- `StimulusControllers` → `_Array(_Union(String, Symbol, Vident::Stimulus::Controller))`
|
|
115
|
+
- `StimulusActions` → `_Array(_Union(String, Symbol, Array, Hash, Vident::Stimulus::Action))`
|
|
116
|
+
- `StimulusTargets` → `_Array(_Union(String, Symbol, Array, Vident::Stimulus::Target))`
|
|
117
|
+
- `StimulusOutlets` → `_Array(_Union(String, Symbol, Array, Vident::Stimulus::Outlet))`
|
|
118
|
+
- `StimulusValues` → `_Union(_Hash(Symbol, _Any), Array, Vident::Stimulus::Value)`
|
|
119
|
+
- `StimulusParams` → `_Union(_Hash(Symbol, _Any), Array, Vident::Stimulus::Param)`
|
|
120
|
+
- `StimulusClasses` → `_Union(_Hash(Symbol, _Any), Array, Vident::Stimulus::ClassMap)`
|
|
121
|
+
|
|
122
|
+
Exposed publicly so user components can reuse them when adding matching props:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
class MyComponent < Vident::ViewComponent::Base
|
|
126
|
+
prop :extra_actions, Vident::Types::StimulusActions, default: -> { [] }
|
|
127
|
+
end
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 2. Class-level DSL
|
|
133
|
+
|
|
134
|
+
All of these live on `Vident::Component`'s class body (via included modules).
|
|
135
|
+
|
|
136
|
+
- `prop(name, type, **literal_options)` — from the Literal gem. See
|
|
137
|
+
https://literal.fun/ for option details. `default:` may be a callable (`lambda/proc`)
|
|
138
|
+
or an immediate value; callable is required when the default is non-frozen (hash, array).
|
|
139
|
+
- `no_stimulus_controller` — sets a class ivar that drops the implied controller from
|
|
140
|
+
the `stimulus_controllers` default. Use when the component is purely presentational
|
|
141
|
+
and needs no paired `_controller.js`. Inherited by subclasses.
|
|
142
|
+
- `has_stimulus_controller` — the inverse: re-enables the implied controller on a
|
|
143
|
+
subclass whose parent declared `no_stimulus_controller`. Idempotent; order relative
|
|
144
|
+
to `stimulus do` blocks does not matter.
|
|
145
|
+
- `stimulus_controller?` — `Boolean`, `true` by default; becomes `false` after a
|
|
146
|
+
`no_stimulus_controller` declaration and `true` again after `has_stimulus_controller`.
|
|
147
|
+
- `stimulus_identifier_path` — the `name.underscore` of the class (e.g.
|
|
148
|
+
`"dashboard/release_card_component"`). Falls back to `"anonymous_component"` for
|
|
149
|
+
anonymous classes.
|
|
150
|
+
- `stimulus_identifier` — `stimulize_path(stimulus_identifier_path)` — the kebab-cased
|
|
151
|
+
identifier (`"dashboard--release-card-component"`). Also available as an instance method.
|
|
152
|
+
- `component_name` — memoised alias for `stimulus_identifier`. Also available as an
|
|
153
|
+
instance method. Used as the first class on the root element and as the outlet name
|
|
154
|
+
seed.
|
|
155
|
+
- `stimulus_scoped_event(event)` — `Symbol` of the form `:"<component_name>:<jsName>"`.
|
|
156
|
+
E.g. `FooComponent.stimulus_scoped_event(:data_ready)` →
|
|
157
|
+
`:"foo-component:dataReady"`. Also an instance method.
|
|
158
|
+
- `stimulus_scoped_event_on_window(event)` — same, with `@window` suffix. Also an
|
|
159
|
+
instance method.
|
|
160
|
+
- `stimulus(&block)` — the DSL entry point. Opens a `Vident::Internals::DSL` block
|
|
161
|
+
evaluator. See section 3.
|
|
162
|
+
|
|
163
|
+
Not intended for application code:
|
|
164
|
+
|
|
165
|
+
- `declarations` — frozen `Vident::Internals::Declarations` aggregate (own + inherited);
|
|
166
|
+
`protected`, consumed by the Resolver at render time.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## 3. `stimulus do ... end` block
|
|
171
|
+
|
|
172
|
+
Evaluated by `Vident::Internals::DSL` (`lib/vident/internals/dsl.rb`). Multiple
|
|
173
|
+
`stimulus do` blocks on the same class accumulate. A subclass's blocks are merged with
|
|
174
|
+
every parent's blocks on first access (subclass entries appended to positional kinds;
|
|
175
|
+
subclass wins on conflicts for keyed kinds).
|
|
176
|
+
|
|
177
|
+
Every DSL method returns `self` (for the singular primitives `action`/`target`, the
|
|
178
|
+
fluent **builder** is returned instead — chain methods also return self). All DSL
|
|
179
|
+
entries may use a `Proc` anywhere a value is expected; procs are evaluated via
|
|
180
|
+
`instance_exec` on the component instance at render time (or at `after_initialize`
|
|
181
|
+
for purely static entries).
|
|
182
|
+
|
|
183
|
+
### Methods on the builder
|
|
184
|
+
|
|
185
|
+
**Controllers.** Primary form is the singular `controller`; plural `controllers`
|
|
186
|
+
accumulates paths without the `as:` alias.
|
|
187
|
+
|
|
188
|
+
- `controller(path, as: alias_sym = nil)` — declare a cross-controller path on
|
|
189
|
+
the root element. Optional `as:` registers an alias looked up by
|
|
190
|
+
`action(...).on_controller(alias_sym)` / `on_controller: alias_sym`. Paths
|
|
191
|
+
may be `String` (`"admin/users"`) or `Symbol` (`:admin_users`).
|
|
192
|
+
- `controllers(*paths)` — one entry per path. Array entries splat into the
|
|
193
|
+
singular parser (so `[path, as: sym]` tuples work when building
|
|
194
|
+
programmatically).
|
|
195
|
+
- `no_stimulus_controller` — class-level, **not** inside the block. Suppresses
|
|
196
|
+
the implied controller. Raises `Vident::DeclarationError` if any DSL entries
|
|
197
|
+
were subsequently added.
|
|
198
|
+
|
|
199
|
+
**Actions.** Primary form is the singular `action(*args, **meta)` which returns
|
|
200
|
+
an `Internals::ActionBuilder`. Chain methods pre-applied via kwargs are equivalent
|
|
201
|
+
to calling the setters explicitly.
|
|
202
|
+
|
|
203
|
+
- `action(*args, **meta) -> ActionBuilder` — builder state:
|
|
204
|
+
- Positional `*args` shapes (`base_descriptor` pattern-matches):
|
|
205
|
+
- `(Symbol)` → method on implied (no event)
|
|
206
|
+
- `(Symbol, Symbol)` → `(event, method)` on implied
|
|
207
|
+
- `(Symbol, String, Symbol)` → `(event, controller_path, method)`
|
|
208
|
+
- `(Hash)` → full descriptor (`:method`, `:event`, `:controller`, `:options`, `:keyboard`, `:window`)
|
|
209
|
+
- Kwargs `**meta` (equivalent to the fluent chain methods):
|
|
210
|
+
- `on:` (Symbol/String) → event
|
|
211
|
+
- `call_method:` (Symbol/String) → override the method name
|
|
212
|
+
- `modifier:` (Symbol or Array) → Stimulus options whitelist (see §4.2)
|
|
213
|
+
- `keyboard:` (String) → `keydown.<key>` filter suffix
|
|
214
|
+
- `window:` (Boolean) → `@window` suffix
|
|
215
|
+
- `on_controller:` (Symbol) → resolve against a `controller ..., as: sym` alias
|
|
216
|
+
- `when:` (Proc / callable) → render-time predicate; `false`/`nil` drops the entry
|
|
217
|
+
- Unknown kwargs raise `ArgumentError`.
|
|
218
|
+
- Chain methods on the returned builder: `.on(event)`, `.call_method(name)`,
|
|
219
|
+
`.modifier(*opts)`, `.keyboard(str)`, `.window`, `.on_controller(sym)`,
|
|
220
|
+
`.when(callable = nil, &block)`. Each returns the builder.
|
|
221
|
+
- `actions(*entries)` — legacy plural form, still accepted. Each entry is one of:
|
|
222
|
+
- `Symbol` → `implied#<jsSymbol>`
|
|
223
|
+
- `[Symbol, Symbol]` → `<event>-><implied>#<jsMethod>`
|
|
224
|
+
- `[Symbol, String, Symbol]` → `<event>-><stimulized-path>#<jsMethod>`
|
|
225
|
+
- `String` containing `#` → parsed literally (pass-through).
|
|
226
|
+
- `Hash` → descriptor keys as above.
|
|
227
|
+
- `Proc` → evaluated at render time; `nil`/`false` drops.
|
|
228
|
+
|
|
229
|
+
**Alias resolution.** When an action descriptor's `:controller` is a `Symbol`, the
|
|
230
|
+
resolver looks it up in the class's declared alias map (`controller X, as: sym`
|
|
231
|
+
entries) and substitutes the full path before parsing. Unknown alias →
|
|
232
|
+
`Vident::DeclarationError`. Alias resolution also runs on runtime inputs
|
|
233
|
+
(`stimulus_actions:` prop, `root_element_attributes[:stimulus_actions]`) that
|
|
234
|
+
carry a Hash with a `Symbol` `:controller`.
|
|
235
|
+
|
|
236
|
+
**Targets.** Singular `target` returns a `TargetBuilder` whose only chain method
|
|
237
|
+
is `.when`; plural `targets` accepts the same positional shapes as before.
|
|
238
|
+
|
|
239
|
+
- `target(*args) -> TargetBuilder` — chain `.when(callable = nil, &block)` for
|
|
240
|
+
conditional inclusion; without a chain, the builder passes `*args` through.
|
|
241
|
+
- `targets(*entries)` — each entry is one of:
|
|
242
|
+
- `Symbol` → target on the implied controller
|
|
243
|
+
- `String` → pass-through target name
|
|
244
|
+
- `[String, Symbol]` → target on the named cross-controller
|
|
245
|
+
- `Proc` — `nil` return drops the entry.
|
|
246
|
+
|
|
247
|
+
**Keyed primitives.** `values`, `params`, `classes`, `outlets` keep the plural
|
|
248
|
+
kwargs form and add singular `value`/`param`/`class_map`/`outlet` that take
|
|
249
|
+
`(name, *args, **meta)`:
|
|
250
|
+
|
|
251
|
+
- `values(**kvs)` / `value(name, *args, **meta)` — keyed. Values may be
|
|
252
|
+
`String`/`Number`/`Boolean` (stringified), `Array`/`Hash` (JSON-serialised),
|
|
253
|
+
`Vident::StimulusNull` (emits literal `"null"`), or a `Proc` resolving to
|
|
254
|
+
any of the above. A resolved `nil` omits the attribute. Singular supports
|
|
255
|
+
`value :count, static: 0` and `value :clicked_count, from_prop: true` meta
|
|
256
|
+
forms.
|
|
257
|
+
- `params(**kvs)` / `param(name, *args, **meta)` — same serialisation rules.
|
|
258
|
+
- `classes(**kvs)` / `class_map(name, *args, **meta)` — value is `String` or
|
|
259
|
+
`Array(String)`; array joined with single space.
|
|
260
|
+
- `outlets(positional_hash = nil, **kvs)` / `outlet(name, *args, **meta)` —
|
|
261
|
+
value is a `String` CSS selector, a `Proc` returning one, or a pre-built
|
|
262
|
+
outlet value object. `outlets({"admin--users" => ".sel"})` accepts a
|
|
263
|
+
positional Hash so identifiers containing `--` (not valid Ruby kwarg keys)
|
|
264
|
+
work.
|
|
265
|
+
- `values_from_props(*prop_names)` — keyed, sidecar to `values`. Mirrors each
|
|
266
|
+
prop's current `@ivar` value at render time. Prop names are Symbols.
|
|
267
|
+
|
|
268
|
+
### What the builder emits
|
|
269
|
+
|
|
270
|
+
`to_declarations` (called on the `Vident::Internals::DSL` instance when the block
|
|
271
|
+
closes) returns a frozen `Vident::Internals::Declarations` struct. The struct is a
|
|
272
|
+
`Data.define(...)` value object with these fields — all frozen arrays:
|
|
273
|
+
|
|
274
|
+
| Field | Content |
|
|
275
|
+
| ------------------ | -------------------------------------------------------------------------- |
|
|
276
|
+
| `controllers` | `Array` of `Declaration` entries (each wraps a path + optional `as:` alias). |
|
|
277
|
+
| `actions` | `Array` of `Declaration` entries, one per `action(...)` call. |
|
|
278
|
+
| `targets` | `Array` of `Declaration` entries, one per `target(...)` call. |
|
|
279
|
+
| `outlets` | `Array` of `[key, Declaration]` pairs (keyed; last-write-wins on same key).|
|
|
280
|
+
| `values` | `Array` of `[key, Declaration]` pairs. |
|
|
281
|
+
| `params` | `Array` of `[key, Declaration]` pairs. |
|
|
282
|
+
| `class_maps` | `Array` of `[key, Declaration]` pairs. |
|
|
283
|
+
| `values_from_props`| `Array(Symbol)` — prop names listed via `values_from_props`. |
|
|
284
|
+
|
|
285
|
+
The struct supports `merge(other)` (subclass block merged over superclass) and
|
|
286
|
+
`any?`. Entries remain as raw `Declaration` tuples — parsing into
|
|
287
|
+
`Vident::Stimulus::*` value objects is deferred to the Resolver at render time.
|
|
288
|
+
Application code does not call `to_declarations` directly.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 4. Instance-level Stimulus helpers (`Vident::Capabilities::StimulusParsing`)
|
|
293
|
+
|
|
294
|
+
Included into every component via `Vident::Component`. File:
|
|
295
|
+
`lib/vident/capabilities/stimulus_parsing.rb`.
|
|
296
|
+
|
|
297
|
+
### 4.1 Plural parsers `stimulus_<plural>(*args)`
|
|
298
|
+
|
|
299
|
+
Seven methods: `stimulus_controllers`, `stimulus_actions`, `stimulus_targets`,
|
|
300
|
+
`stimulus_outlets`, `stimulus_values`, `stimulus_params`, `stimulus_classes`.
|
|
301
|
+
|
|
302
|
+
Each returns a collection object (`Vident::Stimulus::Collection`, etc.) whose `#to_h`
|
|
303
|
+
serialises to a `Hash` of `data-*` keys → values. Arg handling per input:
|
|
304
|
+
|
|
305
|
+
- no args or all-blank → empty collection
|
|
306
|
+
- single pre-built collection → returned as-is
|
|
307
|
+
- `Array` → splatted into the singular builder
|
|
308
|
+
- `Hash` (for **keyed** primitives: outlets, values, params, classes)
|
|
309
|
+
→ expanded per-pair, each pair becomes one value object
|
|
310
|
+
- `Hash` (for **positional** primitives: controllers, actions, targets)
|
|
311
|
+
→ passed as a single-arg descriptor (Action's `{event:, method:, ...}` form)
|
|
312
|
+
- pre-built value object → preserved
|
|
313
|
+
|
|
314
|
+
### 4.2 Singular builders `stimulus_<singular>(*args)`
|
|
315
|
+
|
|
316
|
+
Each singular builder delegates to the corresponding value class's `.parse(*args, implied:, component_id:)`
|
|
317
|
+
class method. Raises `ArgumentError` (or `Vident::ParseError`) on unsupported shape or arity.
|
|
318
|
+
|
|
319
|
+
- `stimulus_controller(*)` — 0 or 1 arg. 0 args returns the implied controller; 1 arg
|
|
320
|
+
is a controller path `String`/`Symbol`.
|
|
321
|
+
- `stimulus_action(*)` — 1/2/3 args. See `Vident::Stimulus::Action.parse` for all
|
|
322
|
+
accepted forms. `options:` whitelist (raises otherwise):
|
|
323
|
+
`[:once, :prevent, :stop, :passive, :"!passive", :capture, :self]`.
|
|
324
|
+
- `stimulus_target(*)` — 1 or 2 args. `(Symbol)` / `(String)` → implied controller;
|
|
325
|
+
`(String, Symbol)` → cross-controller + name.
|
|
326
|
+
- `stimulus_outlet(*)` — 1/2/3 args.
|
|
327
|
+
- `(Symbol)` or `(String)` → identifier, auto-generated selector
|
|
328
|
+
`"#<component_id> [data-controller~=<identifier>]"`.
|
|
329
|
+
- `(Array[identifier, selector])` — explicit selector.
|
|
330
|
+
- `(component_instance)` — instance responding to `#stimulus_identifier` or
|
|
331
|
+
`#implied_controller_name`; auto-selector built from its identifier.
|
|
332
|
+
- `(String|Symbol, String)` → outlet-name + selector on implied controller.
|
|
333
|
+
- `(String, Symbol, String)` → cross-controller + outlet-name + selector.
|
|
334
|
+
- `stimulus_value(name, value)` or `stimulus_value(controller_path, name, value)` —
|
|
335
|
+
2 or 3 args.
|
|
336
|
+
- `stimulus_param(name, value)` or `stimulus_param(controller_path, name, value)` —
|
|
337
|
+
2 or 3 args.
|
|
338
|
+
- `stimulus_class(name, classes)` or `stimulus_class(controller_path, name, classes)` —
|
|
339
|
+
2 or 3 args; `classes` is `String` or `Array(String)`.
|
|
340
|
+
|
|
341
|
+
### 4.3 Mutators `add_stimulus_<plural>(input)`
|
|
342
|
+
|
|
343
|
+
Seven methods, one per primitive. Merge new attributes into the per-kind collection
|
|
344
|
+
ivar (e.g. `@stimulus_actions_collection`). Typical use: inside
|
|
345
|
+
`after_component_initialize`, compute runtime attributes and add them.
|
|
346
|
+
|
|
347
|
+
**Array input is one entry.** `add_stimulus_actions([:click, :handle])` treats the
|
|
348
|
+
Array as *one* action descriptor (event + method pair), matching the DSL's
|
|
349
|
+
`actions [:click, :handle]` semantics. The V1 splat asymmetry — where the mutator
|
|
350
|
+
treated the Array as two separate symbol actions — was fixed in V2. To pass a
|
|
351
|
+
pre-built action object, construct it first:
|
|
352
|
+
`add_stimulus_actions(stimulus_action(:click, :handle))`.
|
|
353
|
+
|
|
354
|
+
### 4.4 Value serialisation
|
|
355
|
+
|
|
356
|
+
`Array` and `Hash` → JSON. Everything else → `to_s`. `Vident::StimulusNull.to_s`
|
|
357
|
+
returns the literal string `"null"`. A `nil` reaches the DSL/prop layer and
|
|
358
|
+
is dropped by the Resolver before serialisation, so the data attribute is omitted
|
|
359
|
+
(not emitted as empty).
|
|
360
|
+
|
|
361
|
+
### 4.5 Name-shaping helpers
|
|
362
|
+
|
|
363
|
+
`Vident::Stimulus::Naming` is a `module_function` module — call its methods directly:
|
|
364
|
+
|
|
365
|
+
- `Vident::Stimulus::Naming.stimulize_path(path)` — `"admin/users"` → `"admin--users"`;
|
|
366
|
+
each path segment is `dasherize`d and segments joined with `--`.
|
|
367
|
+
- `Vident::Stimulus::Naming.js_name(name)` — `camelize(:lower)`; `:my_thing` → `"myThing"`.
|
|
368
|
+
|
|
369
|
+
### 4.6 Scoped events
|
|
370
|
+
|
|
371
|
+
- Class method `stimulus_scoped_event(event)` — returns `Symbol`
|
|
372
|
+
`:"<component_name>:<jsName>"`. **Call on the dispatcher's class**, not on the
|
|
373
|
+
listener's.
|
|
374
|
+
- Class method `stimulus_scoped_event_on_window(event)` — same with `@window` suffix.
|
|
375
|
+
- Both also exist as instance methods that delegate to the class method.
|
|
376
|
+
|
|
377
|
+
### 4.7 Class-level builders
|
|
378
|
+
|
|
379
|
+
Class methods parallel to the instance singulars, useful when you need a
|
|
380
|
+
Stimulus value object without a component instance (Turbo-Stream partials,
|
|
381
|
+
JSON responses, system-test selectors).
|
|
382
|
+
|
|
383
|
+
- `MyComponent.stimulus_controller` — no args; returns the implied `Vident::Stimulus::Controller`.
|
|
384
|
+
- `MyComponent.stimulus_target(Symbol|String)` — returns `Vident::Stimulus::Target`.
|
|
385
|
+
- `MyComponent.stimulus_action(*args)` — same grammar as the instance singular, but cross-controller forms (`[String, Symbol]`, `[Symbol, String, Symbol]`) raise `Vident::ParseError`.
|
|
386
|
+
- `MyComponent.stimulus_value(name, value)` — two-arg form only; the three-arg cross-controller form raises.
|
|
387
|
+
- `MyComponent.stimulus_param(name, value)` — same constraint.
|
|
388
|
+
- `MyComponent.stimulus_class(name, css)` — same constraint.
|
|
389
|
+
- `MyComponent.stimulus_outlet(name, selector)` — **selector required**; single-arg auto-selector form raises `Vident::ParseError` (no `component_id` at class level). For cross-controller outlets, call `Vident::Stimulus::Outlet.parse(...)` directly.
|
|
390
|
+
|
|
391
|
+
Class-level output matches instance-level where both apply:
|
|
392
|
+
|
|
393
|
+
```ruby
|
|
394
|
+
ButtonComponent.stimulus_target(:submit).to_h ==
|
|
395
|
+
ButtonComponent.new.stimulus_target(:submit).to_h # => true
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
The implied controller is memoised per-class on the singleton; subclasses inherit the identifier path but get their own memo.
|
|
399
|
+
|
|
400
|
+
### 4.8 Root-element composition helpers
|
|
401
|
+
|
|
402
|
+
Two instance methods that return what `root_element(...)` would emit — for components that render their root tag via a third-party helper (e.g. `InlineSvg::inline_svg_tag`).
|
|
403
|
+
|
|
404
|
+
- `root_element_class_list(extra_classes = nil)` — returns a `String`. Applies the full 6-tier class cascade (`component_name`, `root_element_classes`, `root_element_attributes[:classes]`, `html_options[:class]`, `@classes` prop, then `extra_classes`) plus Tailwind-merging.
|
|
405
|
+
- `root_element_data_attributes` — returns a `Hash` with Symbol keys. Seals the Draft into a Plan (idempotent) and runs the AttributeWriter, yielding the same `data-controller` / `data-action` / `data-*-target` / etc. hash that `root_element(...)` would emit.
|
|
406
|
+
|
|
407
|
+
```ruby
|
|
408
|
+
def svg_attributes
|
|
409
|
+
{
|
|
410
|
+
id: @id,
|
|
411
|
+
class: root_element_class_list,
|
|
412
|
+
data: root_element_data_attributes
|
|
413
|
+
}
|
|
414
|
+
end
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Both honour `no_stimulus_controller` (no `data-controller` in the hash; component-identifier CSS class still emitted, matching `root_element`).
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## 5. `root_element_attributes` accepted keys
|
|
422
|
+
|
|
423
|
+
Override `root_element_attributes` (instance method on your component) to return any
|
|
424
|
+
subset of:
|
|
425
|
+
|
|
426
|
+
| Key | Type | Effect |
|
|
427
|
+
| --------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
428
|
+
| `:element_tag` | `Symbol` | Overrides the root element tag. |
|
|
429
|
+
| `:html_options` | `Hash` | Merged onto the root; highest-precedence source for `:class`. |
|
|
430
|
+
| `:classes` | `String \| Array(String)` | Second-highest `:class` source (see SKILL.md §4). |
|
|
431
|
+
| `:id` | `String` | Sets the root element's id. |
|
|
432
|
+
| `:stimulus_controllers`| Same shape as `stimulus_controllers` prop | Merged into the controllers collection. |
|
|
433
|
+
| `:stimulus_actions` | Same shape as `stimulus_actions` prop | Merged into the actions collection. |
|
|
434
|
+
| `:stimulus_targets` | Same shape as `stimulus_targets` prop | Merged into the targets collection. |
|
|
435
|
+
| `:stimulus_outlets` | Same shape as `stimulus_outlets` prop | Merged into the outlets collection. |
|
|
436
|
+
| `:stimulus_values` | Same shape as `stimulus_values` prop | Merged into the values collection. |
|
|
437
|
+
| `:stimulus_params` | Same shape as `stimulus_params` prop | Merged into the params collection. |
|
|
438
|
+
| `:stimulus_classes` | Same shape as `stimulus_classes` prop | Merged into the classes collection. |
|
|
439
|
+
|
|
440
|
+
Precedence (lower wins if both set):
|
|
441
|
+
|
|
442
|
+
1. `stimulus do` DSL
|
|
443
|
+
2. `stimulus_*` props (the `render Foo.new(stimulus_actions: ...)` path)
|
|
444
|
+
3. `root_element_attributes` return value
|
|
445
|
+
4. `add_stimulus_*` called after those (e.g. in `after_component_initialize`)
|
|
446
|
+
|
|
447
|
+
---
|
|
448
|
+
|
|
449
|
+
## 6. `child_element`
|
|
450
|
+
|
|
451
|
+
Renders a single child tag with `stimulus_*` kwargs compiled into `data-*` attributes.
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
def child_element(tag_name,
|
|
455
|
+
stimulus_controllers: nil, stimulus_controller: nil,
|
|
456
|
+
stimulus_actions: nil, stimulus_action: nil,
|
|
457
|
+
stimulus_targets: nil, stimulus_target: nil,
|
|
458
|
+
stimulus_outlets: nil, stimulus_outlet: nil,
|
|
459
|
+
stimulus_values: nil, stimulus_value: nil,
|
|
460
|
+
stimulus_params: nil, stimulus_param: nil,
|
|
461
|
+
stimulus_classes: nil, stimulus_class: nil,
|
|
462
|
+
**options, &block)
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
- Plural kwargs take an `Enumerable`; passing a non-Enumerable raises
|
|
466
|
+
`ArgumentError` with a message pointing at the singular name.
|
|
467
|
+
- Singular kwargs take a single entry.
|
|
468
|
+
- `**options` passes through as HTML options.
|
|
469
|
+
- For ViewComponent's renderer, self-closing tags are emitted without the block.
|
|
470
|
+
- For Phlex's renderer, the tag name is validated against
|
|
471
|
+
`Vident::Phlex::HTML::VALID_TAGS`; unknown tags raise `ArgumentError`.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## 7. `Vident::Internals::DSL` primitives
|
|
476
|
+
|
|
477
|
+
For use in advanced cases (passing typed descriptors across components, building
|
|
478
|
+
reusable shared helpers). Value classes live under `lib/vident/stimulus/`.
|
|
479
|
+
|
|
480
|
+
### Hash descriptor form
|
|
481
|
+
|
|
482
|
+
There is no separate `Descriptor` class in V2. The Hash form accepted by `actions`
|
|
483
|
+
(and `stimulus_actions:`) is parsed directly into `Vident::Stimulus::Action`. Accepted keys:
|
|
484
|
+
|
|
485
|
+
| Key | Type | Default |
|
|
486
|
+
| ------------- | ------------------------------------- | ------- |
|
|
487
|
+
| `method:` | `Symbol \| String` | required |
|
|
488
|
+
| `event:` | `Symbol \| String \| nil` | `nil` |
|
|
489
|
+
| `controller:` | `String \| nil` | `nil` |
|
|
490
|
+
| `options:` | `Array(Symbol)` — see §4.2 whitelist | `[]` |
|
|
491
|
+
| `keyboard:` | `String \| nil` | `nil` |
|
|
492
|
+
| `window:` | `Boolean` | `false` |
|
|
493
|
+
|
|
494
|
+
### `Vident::StimulusNull`
|
|
495
|
+
|
|
496
|
+
Frozen singleton object. `inspect` → `"Vident::StimulusNull"`; `to_s` → `"null"`.
|
|
497
|
+
See SKILL.md §1.4 for the usage contract.
|
|
498
|
+
|
|
499
|
+
### Collection class
|
|
500
|
+
|
|
501
|
+
All primitive kinds share one parametric class: `Vident::Stimulus::Collection`,
|
|
502
|
+
parametrised on a `Kind` record from `Vident::Internals::Registry`.
|
|
503
|
+
|
|
504
|
+
- Methods: `each`, `to_a`, `size`, `length`, `empty?`, `any?`, `to_h`, `to_hash`,
|
|
505
|
+
`merge(other)` (single same-kind Collection; raises `ArgumentError` on mismatch).
|
|
506
|
+
- `#to_h` shape per kind:
|
|
507
|
+
- `actions` → `{action: "…"}` with entries joined by space.
|
|
508
|
+
- `controllers` → `{controller: "…"}` with non-empty entries joined by space.
|
|
509
|
+
- `targets` → one key per controller-target attribute; multiple targets on
|
|
510
|
+
the same controller joined with a single space.
|
|
511
|
+
- `values`, `params`, `class_maps` → merged per-data-attribute Hash.
|
|
512
|
+
- `outlets` → same.
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
## 8. `Vident::Caching`
|
|
517
|
+
|
|
518
|
+
Opt-in: `include Vident::Caching` + `with_cache_key(...)` in the component class.
|
|
519
|
+
File: `lib/vident/caching.rb`.
|
|
520
|
+
|
|
521
|
+
### Class methods
|
|
522
|
+
|
|
523
|
+
- `with_cache_key(*attrs, name: :_collection)` — declares which attributes feed into
|
|
524
|
+
`cache_key`. The call appends `:component_modified_time` and `:to_h` (when
|
|
525
|
+
available) to the given attrs, then calls `named_cache_key_includes(name, *attrs.uniq)`.
|
|
526
|
+
- `depends_on(*klasses)` — chains other Vident components' `component_modified_time`
|
|
527
|
+
into this class's `component_modified_time`, so sub-component edits bust the
|
|
528
|
+
parent's cache.
|
|
529
|
+
- `component_modified_time` — memoised in `Rails.env.production?`, otherwise recomputed
|
|
530
|
+
on every call. Raises `Vident::ConfigurationError` if the host class has no
|
|
531
|
+
`cache_component_modified_time` (base classes provide it).
|
|
532
|
+
|
|
533
|
+
### Instance methods
|
|
534
|
+
|
|
535
|
+
- `component_modified_time` — delegates to the class method.
|
|
536
|
+
- `cacheable?` — `respond_to?(:cache_key)`.
|
|
537
|
+
- `cache_key` — defined when `with_cache_key` has been called; returns
|
|
538
|
+
`"#{class.name}/#{cache_keys_for_sources(...).join("/")}"`, optionally suffixed with
|
|
539
|
+
`ENV["RAILS_CACHE_ID"]`. Raises `Vident::ConfigurationError` if the computed key is blank.
|
|
540
|
+
- `cache_key_modifier` — returns `ENV["RAILS_CACHE_ID"]` (may be nil).
|
|
541
|
+
|
|
542
|
+
`with_cache_key` without any attrs is valid — the call still appends
|
|
543
|
+
`:component_modified_time` and `:to_h`, so the cache key reflects the template mtime
|
|
544
|
+
plus the component's full prop hash.
|
|
545
|
+
|
|
546
|
+
### Fragment-caching the render: `cache_component`
|
|
547
|
+
|
|
548
|
+
Available on both adapter base classes (`Vident::Phlex::HTML` and `Vident::ViewComponent::Base`). Wraps a block of render output with Rails.cache using the Vident-computed `cache_key`:
|
|
549
|
+
|
|
550
|
+
- `cache_component(*extra_keys, **options, &block)` — on Phlex, delegates to `Phlex::SGML#cache([cache_key, *extra_keys], **options, &block)`. On ViewComponent, uses `Rails.cache.fetch([cache_key, *extra_keys], **options) { capture(&block) }`.
|
|
551
|
+
- Raises `Vident::ConfigurationError` if the component is not cacheable (no `with_cache_key` declared).
|
|
552
|
+
- `extra_keys` let the caller add per-render state to the cache key without modifying `with_cache_key`.
|
|
553
|
+
- Phlex usage: inside `view_template`. ViewComponent usage: inside a `def call` method; sidecar ERB templates can use Rails' native `<% cache cache_key do %> ... <% end %>` instead.
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## 9. `Vident::StableId`
|
|
558
|
+
|
|
559
|
+
File: `lib/vident/stable_id.rb`.
|
|
560
|
+
|
|
561
|
+
### Errors
|
|
562
|
+
|
|
563
|
+
- `Vident::StableId::GeneratorNotSetError` — raised by `STRICT` when no per-thread
|
|
564
|
+
sequence generator is set.
|
|
565
|
+
- `Vident::StableId::StrategyNotConfiguredError` — raised when any component calls
|
|
566
|
+
`next_id_in_sequence` before `StableId.strategy=` has been set.
|
|
567
|
+
|
|
568
|
+
### Strategies (both callables accepting `(generator_or_nil) -> String`)
|
|
569
|
+
|
|
570
|
+
- `STRICT` — raises `GeneratorNotSetError` if the generator is nil. Use in
|
|
571
|
+
development/production paired with the `before_action` seed in `ApplicationController`.
|
|
572
|
+
- `RANDOM_FALLBACK` — returns `Random.hex(16)` when the generator is nil; otherwise
|
|
573
|
+
returns `generator.next.join("-")`. Use in test/previews/jobs/mailers.
|
|
574
|
+
|
|
575
|
+
### Class methods
|
|
576
|
+
|
|
577
|
+
- `strategy` / `strategy=` — get/set the configured callable.
|
|
578
|
+
- `set_current_sequence_generator(seed:)` — seeds a per-thread generator. Raises
|
|
579
|
+
`ArgumentError` on `seed: nil`. Seed is MD5-hashed then fed to `Random.new`, so any
|
|
580
|
+
`String`-coercible seed works.
|
|
581
|
+
- `clear_current_sequence_generator` — clears the per-thread generator.
|
|
582
|
+
- `with_sequence_generator(seed:) { ... }` — scoped seed for a block (used by jobs,
|
|
583
|
+
mailers, Metal endpoints). Restores the previous generator on exit.
|
|
584
|
+
- `next_id_in_sequence` — delegates to the configured `strategy`.
|
|
585
|
+
|
|
586
|
+
### Installation
|
|
587
|
+
|
|
588
|
+
`bin/rails generate vident:install` (file:
|
|
589
|
+
`lib/generators/vident/install/install_generator.rb`):
|
|
590
|
+
|
|
591
|
+
1. Writes `config/initializers/vident.rb` setting `strategy` to `RANDOM_FALLBACK` in
|
|
592
|
+
test and `STRICT` everywhere else.
|
|
593
|
+
2. Injects `before_action` + `after_action` into `ApplicationController` (idempotent —
|
|
594
|
+
skips if a previous install patched it).
|
|
595
|
+
3. Copies `skills/vident/SKILL.md` from the gem to `.claude/skills/vident/SKILL.md`
|
|
596
|
+
in the host app (skipped if already present).
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
## 10. `Vident::Tailwind`
|
|
601
|
+
|
|
602
|
+
Included into every component. File: `lib/vident/tailwind.rb`.
|
|
603
|
+
|
|
604
|
+
- `tailwind_merger` — returns a thread-cached `::TailwindMerge::Merger` instance if
|
|
605
|
+
the `tailwind_merge` gem is loaded; otherwise returns `nil`.
|
|
606
|
+
- `tailwind_merge_available?` — `true` iff `::TailwindMerge::Merger` is defined.
|
|
607
|
+
|
|
608
|
+
`Vident::Internals::ClassListBuilder` invokes `tailwind_merger.merge(class_string)` automatically
|
|
609
|
+
at the final stage of its `call(...)` when a merger is provided. No per-component
|
|
610
|
+
opt-in is required beyond adding the gem to the Gemfile.
|
|
611
|
+
|
|
612
|
+
---
|
|
613
|
+
|
|
614
|
+
## 11. `class_list_for_stimulus_classes`
|
|
615
|
+
|
|
616
|
+
Instance method on every component. File: `lib/vident/capabilities/class_list_building.rb`.
|
|
617
|
+
|
|
618
|
+
```ruby
|
|
619
|
+
class_list_for_stimulus_classes(*names) -> String
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
Returns the resolved `data-*-class` values for the named stimulus-class entries,
|
|
623
|
+
deduplicated and (when `tailwind_merger` is available) Tailwind-merged. Intended
|
|
624
|
+
for inlining into `class=` on SSR so the first render has the same visual state the
|
|
625
|
+
JS controller will toggle on/off.
|
|
626
|
+
|
|
627
|
+
Names may be `Symbol` or `String`; both are normalised via `dasherize`.
|
|
628
|
+
|
|
629
|
+
---
|
|
630
|
+
|
|
631
|
+
## 12. Rails engine hooks
|
|
632
|
+
|
|
633
|
+
- `Vident::Engine` (`lib/vident/engine.rb`) — autoloaded when Rails is defined.
|
|
634
|
+
Registers Zeitwerk inflections for Vident's non-standard file names (`"dsl"` →
|
|
635
|
+
`"DSL"`, `"html"` → `"HTML"`) so `Vident::Internals::DSL` and
|
|
636
|
+
`Vident::Phlex::HTML` resolve correctly. It does not load any generators at
|
|
637
|
+
engine init — `Vident::Generators::InstallGenerator` is autoloaded on demand
|
|
638
|
+
when `bin/rails generate vident:install` is invoked.
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
642
|
+
## 13. What's not in the public API
|
|
643
|
+
|
|
644
|
+
The following show up in `lib/vident/` but are explicitly internal:
|
|
645
|
+
|
|
646
|
+
- `Vident::Internals::Registry::KINDS` / `Vident::Internals::Registry::Kind` — the
|
|
647
|
+
registry that drives every plural parser, mutator, and DSL primitive. Don't rely on
|
|
648
|
+
these in application code.
|
|
649
|
+
- `Vident::Stimulus::Naming` — pure naming helpers (`stimulize_path`, `js_name`)
|
|
650
|
+
consumed by value classes. The two `module_function` methods documented in §4.5
|
|
651
|
+
are callable directly (`Vident::Stimulus::Naming.stimulize_path(...)`) but the
|
|
652
|
+
module itself is not designed for subclassing or further extension.
|
|
653
|
+
- `Vident::Internals::AttributeWriter` — used internally by `root_element_attributes`
|
|
654
|
+
resolution and `child_element`. Takes a collection-per-primitive kwarg hash and
|
|
655
|
+
merges their `to_h` outputs.
|
|
656
|
+
- `Vident::Internals::ClassListBuilder` — invoked internally by
|
|
657
|
+
`Vident::Capabilities::ClassListBuilding#class_list_for_stimulus_classes`.
|
|
658
|
+
- `Vident::Capabilities::ChildElementRendering`, `Vident::Capabilities::RootElementRendering`,
|
|
659
|
+
`Vident::Capabilities::StimulusMutation`, `Vident::Capabilities::StimulusDraft` —
|
|
660
|
+
included into components; their private/internal methods are not API.
|
|
661
|
+
- `Vident::Stimulus::Naming.stimulize_path(path)` — the canonical path → identifier
|
|
662
|
+
helper. (V1's `stimulus_identifier_from_path` on `Vident::Component` was removed in V2.)
|