Files
Reservair/modules/Layout/RsvColumnLayout.php
2026-06-12 16:05:14 +02:00

79 lines
2.1 KiB
PHP

<?php
namespace Reservair\Layout;
/**
* Fluent builder for multi-column admin page layouts.
*
* Lets a page declare the shape it wants (e.g. a two-column split) without
* committing to any markup or styling. Each column's content is supplied as a
* callable that echoes — inline HTML and <script> blocks work as usual.
*
* Usage:
* RsvColumnLayout::split('1:2')
* ->column(function () { ?>
* <h2>Add timetable</h2>
* ...
* <?php })
* ->column(function () { ?>
* <div id="availability_table"></div>
* ...
* <?php })
* ->output();
*/
class RsvColumnLayout
{
/** @var list<callable():void> Column content, in left-to-right order. */
private array $columns = [];
/** @var list<int> Relative column weights, parsed from the ratio. */
private array $weights;
private function __construct(string $ratio)
{
$this->weights = array_map('intval', explode(':', $ratio));
}
/**
* Side-by-side columns that stack on narrow screens.
*
* @param string $ratio Relative column widths, e.g. '1:1', '1:2'.
*/
public static function split(string $ratio = '1:1'): static
{
return new static($ratio);
}
/** Adds the next column. $render echoes the column's content. */
public function column(callable $render): static
{
$this->columns[] = $render;
return $this;
}
public function render(): string
{
$cols = '';
foreach ($this->columns as $i => $render) {
$grow = $this->weights[$i] ?? end($this->weights) ?: 1;
$cols .= '<div class="rsv-col" style="--rsv-col-grow:' . (int) $grow . '">'
. $this->capture($render)
. '</div>';
}
return '<div class="rsv-cols">' . $cols . '</div>';
}
public function output(): void
{
echo $this->render();
}
/** Runs an echoing callable and returns what it printed. */
private function capture(callable $render): string
{
ob_start();
$render();
return ob_get_clean();
}
}