Module: Vident2::Component

Extended by:
ActiveSupport::Concern
Includes:
Tailwind
Included in:
Phlex::HTML, ViewComponent::Base
Defined in:
lib/vident2/component.rb

Overview

Composition root for Vident 2.0 components: props, DSL receiver, singular/plural parsers, add_stimulus_* mutators, and the render pipeline glue.

Constant Summary collapse

SINGULAR_PARSERS =

Singular parsers return a pre-built value object; pre-built input passes through unchanged.

{
  controllers: :stimulus_controller,
  actions:     :stimulus_action,
  targets:     :stimulus_target,
  outlets:     :stimulus_outlet,
  values:      :stimulus_value,
  params:      :stimulus_param,
  class_maps:  :stimulus_class
}.freeze
PLURAL_PARSERS =

Plural parsers return a Collection (exposes ‘#to_h` for `data:` splatting). Inputs: Symbol / String / Array (= singular parser’s arg tuple) / Hash (keyed: one pair each) / pre-built Value or Collection (pass-through).

{
  controllers: :stimulus_controllers,
  actions:     :stimulus_actions,
  targets:     :stimulus_targets,
  outlets:     :stimulus_outlets,
  values:      :stimulus_values,
  params:      :stimulus_params,
  class_maps:  :stimulus_classes
}.freeze
MUTATOR_METHODS =

Mutators. One call = one entry: Array input is the singular parser’s arg tuple, NOT splatted across multiple mutator calls.

{
  controllers: :add_stimulus_controllers,
  actions:     :add_stimulus_actions,
  targets:     :add_stimulus_targets,
  outlets:     :add_stimulus_outlets,
  values:      :add_stimulus_values,
  params:      :add_stimulus_params,
  class_maps:  :add_stimulus_classes
}.freeze
SINGULAR_NAMES =

Kind name → ‘stimulus_<singular>` suffix (used by child_element).

{
  controllers: :controller,
  actions:     :action,
  targets:     :target,
  outlets:     :outlet,
  values:      :value,
  params:      :param,
  class_maps:  :class
}.freeze

Instance Method Summary collapse

Methods included from Tailwind

#tailwind_merge_available?, #tailwind_merger

Instance Method Details

#after_component_initializeObject

User hook: runs after the Draft is built but before seal.



202
203
# File 'lib/vident2/component.rb', line 202

def after_component_initialize
end

#child_element(tag_name, stimulus_controllers: nil, stimulus_targets: nil, stimulus_actions: nil, stimulus_outlets: nil, stimulus_values: nil, stimulus_params: nil, stimulus_classes: nil, stimulus_controller: nil, stimulus_target: nil, stimulus_action: nil, stimulus_outlet: nil, stimulus_value: nil, stimulus_param: nil, stimulus_class: nil, **options, &block) ⇒ Object

Emit a child element with stimulus_* kwargs folded into data-* attrs. Plural kwargs must be Enumerable. Adapter provides the tag emission (‘generate_child_element`).



320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/vident2/component.rb', line 320

def child_element(
  tag_name,
  stimulus_controllers: nil,
  stimulus_targets: nil,
  stimulus_actions: nil,
  stimulus_outlets: nil,
  stimulus_values: nil,
  stimulus_params: nil,
  stimulus_classes: nil,
  stimulus_controller: nil,
  stimulus_target: nil,
  stimulus_action: nil,
  stimulus_outlet: nil,
  stimulus_value: nil,
  stimulus_param: nil,
  stimulus_class: nil,
  **options,
  &block
)
  inputs = {
    controllers: [stimulus_controllers, stimulus_controller],
    actions:     [stimulus_actions, stimulus_action],
    targets:     [stimulus_targets, stimulus_target],
    outlets:     [stimulus_outlets, stimulus_outlet],
    values:      [stimulus_values, stimulus_value],
    params:      [stimulus_params, stimulus_param],
    class_maps:  [stimulus_classes, stimulus_class]
  }

  data_attrs = {}
  ::Vident2::Internals::Registry.each do |kind|
    plural, singular = inputs.fetch(kind.name)
    child_element_check_plural!(plural, singular, kind)
    coll = child_element_build_collection(kind, plural, singular)
    data_attrs.merge!(coll.to_h) unless coll.empty?
  end

  generate_child_element(tag_name, data_attrs, options, &block)
end

#class_list_for_stimulus_classes(*names) ⇒ Object

SSR helper: resolved ClassMap entries matching ‘names` as a space-joined String. Tailwind-merged if available. `“”` on miss.



303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/vident2/component.rb', line 303

def class_list_for_stimulus_classes(*names)
  resolve_stimulus_attributes_at_render_time
  plan = seal_draft
  maps = plan.class_maps
  return "" if maps.empty? || names.empty?

  result = ::Vident2::Internals::ClassListBuilder.call(
    stimulus_classes: maps,
    stimulus_class_names: names,
    tailwind_merger: tailwind_merger
  )
  result || ""
end

#clone(overrides = {}) ⇒ Object

Fresh instance with current props merged with overrides.



173
174
175
# File 'lib/vident2/component.rb', line 173

def clone(overrides = {})
  self.class.new(**to_h.merge(overrides))
end

#component_nameObject



146
# File 'lib/vident2/component.rb', line 146

def component_name = self.class.component_name

#generate_child_element(tag_name, stimulus_data_attributes, options, &block) ⇒ Object

tag guard. VC: content_tag / tag.

Raises:

  • (NoMethodError)


534
535
536
# File 'lib/vident2/component.rb', line 534

def generate_child_element(tag_name, stimulus_data_attributes, options, &block)
  raise NoMethodError, "adapter must implement generate_child_element"
end

#idObject

Auto-id: ‘<component-name>-<stable-id>`. `.presence` is intentional — blank string falls through to auto-generation.



156
157
158
# File 'lib/vident2/component.rb', line 156

def id
  @id.presence || random_id
end

#inspect(klass_name = "Component") ⇒ Object

Custom format kept for tooling / specs that regex-match output.



178
179
180
181
# File 'lib/vident2/component.rb', line 178

def inspect(klass_name = "Component")
  attr_text = to_h.map { |k, v| "#{k}=#{v.inspect}" }.join(", ")
  "#<#{self.class.name}<Vident::#{klass_name}> #{attr_text}>"
end

#outlet_idObject

Stable ‘[identifier, “#<id>”]` pair for connecting an outlet to this instance.



168
169
170
# File 'lib/vident2/component.rb', line 168

def outlet_id
  @outlet_id ||= [stimulus_identifier, "##{id}"]
end

#prop_namesObject



145
# File 'lib/vident2/component.rb', line 145

def prop_names = self.class.prop_names

#random_idObject

Auto-generated id, independent of the ‘id:` prop. Useful for ARIA references that need to be stable per-instance.



162
163
164
# File 'lib/vident2/component.rb', line 162

def random_id
  @__vident2_auto_id ||= "#{component_name}-#{::Vident2::StableId.next_id_in_sequence}"
end

#resolve_stimulus_attributes_at_render_timeObject

Resolve DSL proc entries deferred at ‘after_initialize`. Called by the adapter’s ‘before_template` / `before_render`; `seal_draft` and `class_list_for_stimulus_classes` call it as safety nets.

Flag set before the guards so a sealed Draft can’t trap us in a loop where every subsequent call re-takes the sealed branch.



381
382
383
384
385
386
387
388
389
# File 'lib/vident2/component.rb', line 381

def resolve_stimulus_attributes_at_render_time
  return if @__vident2_procs_resolved
  @__vident2_procs_resolved = true
  # Nil = test double. Sealed = someone consumed the Draft already.
  return if @__vident2_draft.nil? || @__vident2_draft.sealed?
  ::Vident2::Internals::Resolver.resolve_procs_into(
    @__vident2_draft, self.class.declarations, self
  )
end

#rootObject

Dispatches to the adapter-specific ‘root_element` on subclasses (Phlex / ViewComponent). Keep as `def` not `alias_method` so Ruby’s dynamic dispatch finds the subclass override.



528
529
530
# File 'lib/vident2/component.rb', line 528

def root(...)
  root_element(...)
end

#root_element(**overrides, &block) ⇒ Object

Raises:

  • (NoMethodError)


521
522
523
# File 'lib/vident2/component.rb', line 521

def root_element(**overrides, &block)
  raise NoMethodError, "subclass must implement root_element"
end

#root_element_attributesObject

User override: extra attrs for the root. ‘stimulus_*:` keys APPEND into the Draft (same as props).



193
# File 'lib/vident2/component.rb', line 193

def root_element_attributes = {}

#root_element_classesObject

User override: instance-level extra classes for the root (one tier of ClassListBuilder’s cascade). Return nil for no contribution.



197
198
199
# File 'lib/vident2/component.rb', line 197

def root_element_classes
  nil
end

#stimulus_identifierObject



147
# File 'lib/vident2/component.rb', line 147

def stimulus_identifier = self.class.stimulus_identifier

#stimulus_scoped_event(event) ⇒ Object



151
# File 'lib/vident2/component.rb', line 151

def stimulus_scoped_event(event) = self.class.stimulus_scoped_event(event)

#stimulus_scoped_event_on_window(event) ⇒ Object



152
# File 'lib/vident2/component.rb', line 152

def stimulus_scoped_event_on_window(event) = self.class.stimulus_scoped_event_on_window(event)