File size: 5.36Kb
<?php
namespace Illuminate\View\Concerns;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Arr;
use Illuminate\Support\HtmlString;
use Illuminate\View\ComponentSlot;
trait ManagesComponents
{
/**
* The components being rendered.
*
* @var array
*/
protected $componentStack = [];
/**
* The original data passed to the component.
*
* @var array
*/
protected $componentData = [];
/**
* The component data for the component that is currently being rendered.
*
* @var array
*/
protected $currentComponentData = [];
/**
* The slot contents for the component.
*
* @var array
*/
protected $slots = [];
/**
* The names of the slots being rendered.
*
* @var array
*/
protected $slotStack = [];
/**
* Start a component rendering process.
*
* @param \Illuminate\Contracts\View\View|\Illuminate\Contracts\Support\Htmlable|\Closure|string $view
* @param array $data
* @return void
*/
public function startComponent($view, array $data = [])
{
if (ob_start()) {
$this->componentStack[] = $view;
$this->componentData[$this->currentComponent()] = $data;
$this->slots[$this->currentComponent()] = [];
}
}
/**
* Get the first view that actually exists from the given list, and start a component.
*
* @param array $names
* @param array $data
* @return void
*/
public function startComponentFirst(array $names, array $data = [])
{
$name = Arr::first($names, function ($item) {
return $this->exists($item);
});
$this->startComponent($name, $data);
}
/**
* Render the current component.
*
* @return string
*/
public function renderComponent()
{
$view = array_pop($this->componentStack);
$this->currentComponentData = array_merge(
$previousComponentData = $this->currentComponentData,
$data = $this->componentData()
);
try {
$view = value($view, $data);
if ($view instanceof View) {
return $view->with($data)->render();
} elseif ($view instanceof Htmlable) {
return $view->toHtml();
} else {
return $this->make($view, $data)->render();
}
} finally {
$this->currentComponentData = $previousComponentData;
}
}
/**
* Get the data for the given component.
*
* @return array
*/
protected function componentData()
{
$defaultSlot = new HtmlString(trim(ob_get_clean()));
$slots = array_merge([
'__default' => $defaultSlot,
], $this->slots[count($this->componentStack)]);
return array_merge(
$this->componentData[count($this->componentStack)],
['slot' => $defaultSlot],
$this->slots[count($this->componentStack)],
['__laravel_slots' => $slots]
);
}
/**
* Get an item from the component data that exists above the current component.
*
* @param string $key
* @param mixed $default
* @return mixed|null
*/
public function getConsumableComponentData($key, $default = null)
{
if (array_key_exists($key, $this->currentComponentData)) {
return $this->currentComponentData[$key];
}
$currentComponent = count($this->componentStack);
if ($currentComponent === 0) {
return value($default);
}
for ($i = $currentComponent - 1; $i >= 0; $i--) {
$data = $this->componentData[$i] ?? [];
if (array_key_exists($key, $data)) {
return $data[$key];
}
}
return value($default);
}
/**
* Start the slot rendering process.
*
* @param string $name
* @param string|null $content
* @param array $attributes
* @return void
*/
public function slot($name, $content = null, $attributes = [])
{
if (func_num_args() === 2 || $content !== null) {
$this->slots[$this->currentComponent()][$name] = $content;
} elseif (ob_start()) {
$this->slots[$this->currentComponent()][$name] = '';
$this->slotStack[$this->currentComponent()][] = [$name, $attributes];
}
}
/**
* Save the slot content for rendering.
*
* @return void
*/
public function endSlot()
{
last($this->componentStack);
$currentSlot = array_pop(
$this->slotStack[$this->currentComponent()]
);
[$currentName, $currentAttributes] = $currentSlot;
$this->slots[$this->currentComponent()][$currentName] = new ComponentSlot(
trim(ob_get_clean()), $currentAttributes
);
}
/**
* Get the index for the current component.
*
* @return int
*/
protected function currentComponent()
{
return count($this->componentStack) - 1;
}
/**
* Flush all of the component state.
*
* @return void
*/
protected function flushComponents()
{
$this->componentStack = [];
$this->componentData = [];
$this->currentComponentData = [];
}
}