h('Logo') * ->l(Sidebar::make()->items(['Dashboard', 'Settings'])) * ->c('Content') * ->f('') * ->render(); */ class Layout implements Htmlable, Renderable { protected string $variant; protected array $attributes = []; protected string $path = ''; // Hierarchical path (e.g., "L-" for nested in Left) protected array $header = []; protected array $left = []; protected array $content = []; protected array $right = []; protected array $footer = []; public function __construct(string $variant = 'HCF', string $path = '') { $this->variant = strtoupper($variant); $this->path = $path; } /** * Create a new layout instance */ public static function make(string $variant = 'HCF', string $path = ''): static { return new static($variant, $path); } /** * Get the slot ID for a given slot letter */ protected function slotId(string $slot): string { return $this->path.$slot; } /** * Add to the Header slot */ public function h(mixed ...$items): static { foreach ($items as $item) { $this->header[] = $item; } return $this; } /** * Add to the Left slot */ public function l(mixed ...$items): static { foreach ($items as $item) { $this->left[] = $item; } return $this; } /** * Add to the Content slot */ public function c(mixed ...$items): static { foreach ($items as $item) { $this->content[] = $item; } return $this; } /** * Add to the Right slot */ public function r(mixed ...$items): static { foreach ($items as $item) { $this->right[] = $item; } return $this; } /** * Add to the Footer slot */ public function f(mixed ...$items): static { foreach ($items as $item) { $this->footer[] = $item; } return $this; } /** * Alias methods for readability (variadic) */ public function addHeader(mixed ...$items): static { return $this->h(...$items); } public function addLeft(mixed ...$items): static { return $this->l(...$items); } public function addContent(mixed ...$items): static { return $this->c(...$items); } public function addRight(mixed ...$items): static { return $this->r(...$items); } public function addFooter(mixed ...$items): static { return $this->f(...$items); } /** * Set HTML attributes on the layout container */ public function attributes(array $attributes): static { $this->attributes = array_merge($this->attributes, $attributes); return $this; } /** * Add a CSS class */ public function class(string $class): static { $existing = $this->attributes['class'] ?? ''; $this->attributes['class'] = trim($existing.' '.$class); return $this; } /** * Check if variant includes a slot */ protected function has(string $slot): bool { return str_contains($this->variant, strtoupper($slot)); } /** * Render all items in a slot with indexed data attributes */ protected function renderSlot(array $items, string $slot): string { $html = ''; foreach ($items as $index => $item) { $itemId = $this->slotId($slot).'-'.$index; $resolved = $this->resolveItem($item, $slot); $html .= '