172 lines
4.9 KiB
PHP
172 lines
4.9 KiB
PHP
<?php
|
|
|
|
namespace PHPNative\Ui\Widget;
|
|
|
|
use PHPNative\Framework\TextRenderer;
|
|
|
|
class Table extends Container
|
|
{
|
|
private array $columns = [];
|
|
private array $rows = [];
|
|
private Container $headerContainer;
|
|
private Container $bodyContainer;
|
|
private ?int $selectedRowIndex = null;
|
|
private $onRowSelect = null;
|
|
|
|
public function __construct(
|
|
string $style = '',
|
|
) {
|
|
parent::__construct('flex flex-col overflow-auto ' . $style);
|
|
|
|
// Create header container
|
|
$this->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;
|
|
}
|
|
}
|