blocks work as usual. * * Usage: * RsvColumnLayout::split('1:2') * ->column(function () { ?> *

Add timetable

* ... * column(function () { ?> *
* ... * output(); */ class RsvColumnLayout { /** @var list Column content, in left-to-right order. */ private array $columns = []; /** @var list 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 .= '
' . $this->capture($render) . '
'; } return '
' . $cols . '
'; } 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(); } }