Module: ViewComponent::SlotableV2

Extended by:
ActiveSupport::Concern
Included in:
Base
Defined in:
lib/view_component/slotable_v2.rb

Constant Summary collapse

RESERVED_NAMES =
{
  singular: %i[content render].freeze,
  plural: %i[contents renders].freeze
}.freeze

Instance Method Summary collapse

Instance Method Details

#get_slot(slot_name) ⇒ Object



278
279
280
281
282
283
284
285
286
287
288
289
290
291
# File 'lib/view_component/slotable_v2.rb', line 278

def get_slot(slot_name)
  content unless content_evaluated? # ensure content is loaded so slots will be defined

  slot = self.class.registered_slots[slot_name]
  @__vc_set_slots ||= {}

  if @__vc_set_slots[slot_name]
    return @__vc_set_slots[slot_name]
  end

  if slot[:collection]
    []
  end
end

#set_slot(slot_name, slot_definition = nil, *args, &block) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
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
# File 'lib/view_component/slotable_v2.rb', line 293

def set_slot(slot_name, slot_definition = nil, *args, &block)
  slot_definition ||= self.class.registered_slots[slot_name]
  slot = SlotV2.new(self)

  # Passing the block to the sub-component wrapper like this has two
  # benefits:
  #
  # 1. If this is a `content_area` style sub-component, we will render the
  # block via the `slot`
  #
  # 2. Since we've to pass block content to components when calling
  # `render`, evaluating the block here would require us to call
  # `view_context.capture` twice, which is slower
  slot.__vc_content_block = block if block

  # If class
  if slot_definition[:renderable]
    slot.__vc_component_instance = slot_definition[:renderable].new(*args)
  # If class name as a string
  elsif slot_definition[:renderable_class_name]
    slot.__vc_component_instance =
      self.class.const_get(slot_definition[:renderable_class_name]).new(*args)
  # If passed a lambda
  elsif slot_definition[:renderable_function]
    # Use `bind(self)` to ensure lambda is executed in the context of the
    # current component. This is necessary to allow the lambda to access helper
    # methods like `content_tag` as well as parent component state.
    renderable_function = slot_definition[:renderable_function].bind(self)
    renderable_value =
      if block
        renderable_function.call(*args) do |*rargs|
          view_context.capture(*rargs, &block)
        end
      else
        renderable_function.call(*args)
      end

    # Function calls can return components, so if it's a component handle it specially
    if renderable_value.respond_to?(:render_in)
      slot.__vc_component_instance = renderable_value
    else
      slot.__vc_content = renderable_value
    end
  end

  @__vc_set_slots ||= {}

  if slot_definition[:collection]
    @__vc_set_slots[slot_name] ||= []
    @__vc_set_slots[slot_name].push(slot)
  else
    @__vc_set_slots[slot_name] = slot
  end

  slot
end