headerContainer = new Container('flex flex-row bg-gray-200 border-b-2 border-gray-400'); $this->addComponent($this->headerContainer); // Create body container (scrollable) $this->bodyContainer = new Container('flex flex-col overflow-auto flex-1'); $this->addComponent($this->bodyContainer); } /** * Define columns * * @param array $columns Array of column definitions ['key' => string, 'title' => string, 'width' => int|null] */ public function setColumns(array $columns): void { $this->columns = $columns; $this->headerContainer->clearChildren(); foreach ($columns as $column) { $title = $column['title'] ?? $column['key']; $width = $column['width'] ?? null; $style = 'px-4 py-2 font-bold border-r border-gray-300'; if ($width) { $style .= ' w-' . ((int)($width / 4)); } else { $style .= ' flex-1'; } $headerLabel = new Label($title, $style); $this->headerContainer->addComponent($headerLabel); } } /** * Set table data * * @param array $data Array of row data (associative arrays) */ public function setData(array $data): void { $this->rows = $data; $this->bodyContainer->clearChildren(); foreach ($data as $rowIndex => $row) { $this->addRow($row, $rowIndex); } } /** * Add a single row */ private function addRow(array $rowData, int $rowIndex): void { $isSelected = $rowIndex === $this->selectedRowIndex; $rowStyle = 'flex flex-row border-b border-gray-200 hover:bg-gray-100'; if ($isSelected) { $rowStyle .= ' bg-blue-100'; } $rowContainer = new Container($rowStyle); foreach ($this->columns as $column) { $key = $column['key']; $value = $rowData[$key] ?? ''; $width = $column['width'] ?? null; $cellStyle = 'px-4 py-2 border-r border-gray-300'; if ($width) { $cellStyle .= ' w-' . ((int)($width / 4)); } else { $cellStyle .= ' flex-1'; } $cellLabel = new Label((string)$value, $cellStyle); $rowContainer->addComponent($cellLabel); } // Make row clickable $clickHandler = new class($rowContainer, $rowIndex, $this) extends Container { private int $rowIndex; private Table $table; public function __construct(Container $rowContainer, int $rowIndex, Table $table) { $this->rowIndex = $rowIndex; $this->table = $table; parent::__construct(''); $this->addComponent($rowContainer); } public function handleMouseClick(float $mouseX, float $mouseY, int $button): bool { // Check if click is within row bounds if ( $mouseX >= $this->viewport->x && $mouseX <= ($this->viewport->x + $this->viewport->width) && $mouseY >= $this->viewport->y && $mouseY <= ($this->viewport->y + $this->viewport->height) ) { $this->table->selectRow($this->rowIndex); return true; } return parent::handleMouseClick($mouseX, $mouseY, $button); } }; $this->bodyContainer->addComponent($clickHandler); } /** * Select a row */ public function selectRow(int $rowIndex): void { $this->selectedRowIndex = $rowIndex; // Trigger callback if ($this->onRowSelect !== null) { ($this->onRowSelect)($rowIndex, $this->rows[$rowIndex] ?? null); } // Re-render rows to update selection $this->setData($this->rows); } /** * Set row select callback */ public function setOnRowSelect(callable $callback): void { $this->onRowSelect = $callback; } /** * Get selected row index */ public function getSelectedRowIndex(): ?int { return $this->selectedRowIndex; } /** * Get selected row data */ public function getSelectedRow(): ?array { return $this->selectedRowIndex !== null ? ($this->rows[$this->selectedRowIndex] ?? null) : null; } }