Class: Weft::Component

Inherits:
Arbre::Component
  • Object
show all
Extended by:
Registry::Eligibility
Includes:
Weft::Context::Interception, Weft::Context::Traversal, DSL::Actions, DSL::Containers, DSL::Inclusions, DSL::Params, DSL::Recoveries, DSL::Triggers, DSL::Updates
Defined in:
lib/weft/component.rb

Overview

Base class for all Weft components. Extends Arbre::Component with:

  • Attribute DSL for declaring wire state
  • Convention-based DOM ID and partial URL
  • Auto-registration with the global Registry

Constant Summary collapse

SCALAR_ID_CLASSES =

Value classes whose instances may suffix a DOM id. An allowlist: arrays, hashes, and rich objects stringify to selector-hostile junk ("member-roster-[]" breaks querySelector), so only honest scalars ride.

[String, Symbol, Numeric, TrueClass, FalseClass].freeze

Class Attribute Summary collapse

Attributes included from DSL::Params

#params

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Registry::Eligibility

abstract!, routable!, routable?

Methods included from Weft::Context::Interception

#insert_tag

Methods included from Weft::Context::Traversal

#closest, #closest!, #enclosing, #enclosing!

Methods included from DSL::Containers

behavior_for, included

Methods included from DSL::Actions

included

Methods included from DSL::Updates

included

Methods included from DSL::Inclusions

included

Methods included from DSL::Triggers

included

Methods included from DSL::Recoveries

included

Methods included from DSL::Params

included, #serializable_params, warned_collisions

Constructor Details

#initializeComponent

Params resolve at construction, not build: the context (which carries the wire source and any staged hand-off) is the constructor's one argument, and resolving here makes params available even before super in user build bodies — the "compute chrome from params, then super" pattern needs that.



147
148
149
150
# File 'lib/weft/component.rb', line 147

def initialize(*)
  super
  @params = assembled_params
end

Class Attribute Details

.component_pathObject

Class-level path override (string or proc). Inherited by subclasses.



40
41
42
43
44
45
46
# File 'lib/weft/component.rb', line 40

def component_path
  if instance_variable_defined?(:@component_path)
    @component_path
  elsif superclass.respond_to?(:component_path)
    superclass.component_path
  end
end

Class Method Details

.inferred_routable?Boolean

Inferred routability from declared state, ignoring any explicit override (see Weft::Registry::Eligibility#routable?). A component is independently addressable when it declares interactive behavior — params, actions, refresh triggers, or push config. Pure presentational components (none of those) register but are never served. Subclasses fall back to this when they have no override of their own, so an abstract parent does not disable concrete children.

Returns:

  • (Boolean)


68
69
70
# File 'lib/weft/component.rb', line 68

def inferred_routable?
  params.any? || actions.any? || refresh_triggers.any? || !push_config.nil?
end

.inherited(subclass) ⇒ Object



72
73
74
75
# File 'lib/weft/component.rb', line 72

def inherited(subclass)
  super
  Weft.registry.register(subclass)
end

.render(**wire_params) ⇒ Object

Render this component as an HTML string, outside any Arbre DSL context. The kwargs are pseudo-wire: exactly what a request's query string would carry. Used by the Router for partial responses, and available to users for testing, REPL exploration, or any standalone rendering need.

StatCard.render(status: "shipped")  # => "<div id=\"...\">...</div>"


83
84
85
86
87
88
# File 'lib/weft/component.rb', line 83

def render(**wire_params)
  klass = self
  Weft::Context.new({}, nil, wire_params: wire_params) do
    insert_tag(klass)
  end.to_s
end

.resolved_component_pathObject

Resolves the actual path string for this component class. An explicit class-level override (string or proc) wins; otherwise the configured default proc derives one from the class name (see default_component_path).



53
54
55
56
57
58
59
# File 'lib/weft/component.rb', line 53

def resolved_component_path
  case (path = component_path)
  when Proc then path.call(self)
  when String then path
  else default_component_path
  end
end

.weft_dom_id_for(params = {}) ⇒ Object

Compute the would-be DOM ID for an instance of this class given a plain params hash, without instantiating. Single source of truth for the convention; the instance method delegates, and the Router falls back to this when it can't construct an instance to ask. The primary value suffixes only when it's a non-blank scalar — nil, "", and non-scalar values all derive the same bare class id, so a component's identity is stable across the ways "no value" arrives.



102
103
104
105
106
# File 'lib/weft/component.rb', line 102

def weft_dom_id_for(params = {})
  base = name.underscore.tr("/", "-").tr("_", "-")
  primary_value = params.respond_to?(:values) ? params.values.first : nil
  identity_suffix?(primary_value) ? "#{base}-#{primary_value}" : base
end

Instance Method Details

#build(attributes = {}) ⇒ Object



152
153
154
155
156
157
158
159
160
# File 'lib/weft/component.rb', line 152

def build(attributes = {})
  apply_received_fallback(attributes) unless arbre_context.respond_to?(:take_received!)
  warn_declared_chrome_collisions(attributes)
  super
  self.id = weft_dom_id
  claim_dom_slot!
  apply_refresh_attrs
  apply_push_attrs
end

#weft_dom_idObject

Convention-based DOM ID: dasherized class name + primary wire-param value.



175
176
177
# File 'lib/weft/component.rb', line 175

def weft_dom_id
  self.class.weft_dom_id_for(serializable_params)
end

#weft_url(**overrides) ⇒ Object

URL to this component's Weft route with current params as query string. Pass overrides to change specific param values in the URL.

weft_url                          # => "/_components/orders_panel?status=shipped&page=1"
weft_url(page: 2)                 # => "/_components/orders_panel?status=shipped&page=2"
weft_url(status: nil, page: 1)    # => "/_components/orders_panel?page=1"


168
169
170
171
172
# File 'lib/weft/component.rb', line 168

def weft_url(**overrides)
  path = self.class.resolved_component_path
  query = serializable_params.merge(overrides).compact
  query.empty? ? path : "#{path}?#{URI.encode_www_form(query)}"
end