Backup
This commit is contained in:
parent
bf986acb49
commit
3fa5276aba
@ -4,7 +4,7 @@ namespace PHPNative\Ui\Widget;
|
||||
|
||||
class FileBrowser extends Container
|
||||
{
|
||||
private Table $fileTable;
|
||||
private VirtualTable $fileTable;
|
||||
private Label $pathLabel;
|
||||
private string $currentPath;
|
||||
private $onFileSelect = null;
|
||||
@ -17,9 +17,8 @@ class FileBrowser extends Container
|
||||
|
||||
public function __construct(string $initialPath = '.', bool $isRemote = false, string $style = '')
|
||||
{
|
||||
// Root-Container füllt die verfügbare Höhe im Eltern-Layout (flex-1),
|
||||
// damit die Tabelle unten eine klar begrenzte Höhe bekommt und
|
||||
// ihr Body sinnvoll scrollen kann.
|
||||
// Root-Container füllt die verfügbare Höhe im Eltern-Layout (flex-1)
|
||||
// und enthält eine VirtualTable für performantes Scrollen.
|
||||
parent::__construct('w-full flex flex-col flex-1 gap-2 ' . $style);
|
||||
|
||||
$this->currentPath = $initialPath;
|
||||
@ -29,10 +28,10 @@ class FileBrowser extends Container
|
||||
$this->pathLabel = new Label($initialPath, 'px-3 py-2 bg-gray-200 text-black rounded text-sm font-mono');
|
||||
$this->addComponent($this->pathLabel);
|
||||
|
||||
// File table, die den verbleibenden Platz im FileBrowser nutzt.
|
||||
// Das eigentliche Scrollen passiert im Body-Container der Table
|
||||
// (overflow-auto dort).
|
||||
$this->fileTable = new Table(' flex-1');
|
||||
// VirtualTable nutzt Paging, um bei vielen Einträgen nur
|
||||
// einen Teil der Zeilen im UI zu halten – das macht Scrollen
|
||||
// deutlich flüssiger bei großen Verzeichnissen.
|
||||
$this->fileTable = new VirtualTable(' flex-1');
|
||||
$this->fileTable->setColumns([
|
||||
['key' => 'type', 'title' => 'Typ', 'width' => 60],
|
||||
['key' => 'name', 'title' => 'Name'],
|
||||
|
||||
164
src/Ui/Widget/VirtualTable.php
Normal file
164
src/Ui/Widget/VirtualTable.php
Normal file
@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Ui\Widget;
|
||||
|
||||
use PHPNative\Framework\TextRenderer;
|
||||
|
||||
class VirtualTable extends Container
|
||||
{
|
||||
private Table $innerTable;
|
||||
private array $rows = [];
|
||||
private array $columns = [];
|
||||
private int $pageSize = 200;
|
||||
private int $currentPage = 0;
|
||||
private int $totalPages = 1;
|
||||
private $onRowSelect = null;
|
||||
private Label $pageInfoLabel;
|
||||
private Button $prevButton;
|
||||
private Button $nextButton;
|
||||
private bool $prevEnabled = false;
|
||||
private bool $nextEnabled = false;
|
||||
|
||||
public function __construct(string $style = '')
|
||||
{
|
||||
parent::__construct('flex flex-col w-full' . $style);
|
||||
|
||||
$this->innerTable = new Table(' flex-1');
|
||||
$this->addComponent($this->innerTable);
|
||||
|
||||
$pagination = new Container('flex flex-row items-center justify-end gap-2 mt-1');
|
||||
|
||||
$this->pageInfoLabel = new Label('', 'text-xs text-gray-600');
|
||||
|
||||
$this->prevButton = new Button('←', 'px-2 py-1 text-xs bg-gray-200 rounded hover:bg-gray-300');
|
||||
$this->nextButton = new Button('→', 'px-2 py-1 text-xs bg-gray-200 rounded hover:bg-gray-300');
|
||||
|
||||
$virtualTable = $this;
|
||||
$this->prevButton->setOnClick(function () use ($virtualTable) {
|
||||
$virtualTable->goToPreviousPage();
|
||||
});
|
||||
$this->nextButton->setOnClick(function () use ($virtualTable) {
|
||||
$virtualTable->goToNextPage();
|
||||
});
|
||||
|
||||
$pagination->addComponent($this->pageInfoLabel);
|
||||
$pagination->addComponent($this->prevButton);
|
||||
$pagination->addComponent($this->nextButton);
|
||||
|
||||
$this->addComponent($pagination);
|
||||
}
|
||||
|
||||
public function layout(null|TextRenderer $textRenderer = null): void
|
||||
{
|
||||
parent::layout($textRenderer);
|
||||
$this->updatePaginationLabel();
|
||||
}
|
||||
|
||||
public function setColumns(array $columns): void
|
||||
{
|
||||
$this->columns = $columns;
|
||||
$this->innerTable->setColumns($columns);
|
||||
}
|
||||
|
||||
public function setData(array $data): void
|
||||
{
|
||||
$this->rows = array_values($data);
|
||||
$this->currentPage = 0;
|
||||
$this->recalculateTotalPages();
|
||||
$this->updatePageData();
|
||||
}
|
||||
|
||||
public function setOnRowSelect(callable $callback): void
|
||||
{
|
||||
$this->onRowSelect = $callback;
|
||||
|
||||
$virtualTable = $this;
|
||||
$this->innerTable->setOnRowSelect(function ($index, $row) use ($virtualTable) {
|
||||
if ($virtualTable->onRowSelect !== null) {
|
||||
$globalIndex = ($virtualTable->currentPage * $virtualTable->pageSize) + $index;
|
||||
($virtualTable->onRowSelect)($globalIndex, $row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function getSelectedRow(): null|array
|
||||
{
|
||||
$selected = $this->innerTable->getSelectedRow();
|
||||
if ($selected === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $selected;
|
||||
}
|
||||
|
||||
private function recalculateTotalPages(): void
|
||||
{
|
||||
$rowCount = count($this->rows);
|
||||
$this->totalPages = max(1, (int) ceil($rowCount / $this->pageSize));
|
||||
if ($this->currentPage >= $this->totalPages) {
|
||||
$this->currentPage = $this->totalPages - 1;
|
||||
}
|
||||
}
|
||||
|
||||
private function updatePageData(): void
|
||||
{
|
||||
$offset = $this->currentPage * $this->pageSize;
|
||||
$pageRows = array_slice($this->rows, $offset, $this->pageSize);
|
||||
$this->innerTable->setData($pageRows, false);
|
||||
$this->updatePaginationLabel();
|
||||
}
|
||||
|
||||
private function updatePaginationLabel(): void
|
||||
{
|
||||
$totalRows = count($this->rows);
|
||||
if ($totalRows === 0) {
|
||||
$this->pageInfoLabel->setText('Keine Einträge');
|
||||
$this->prevEnabled = false;
|
||||
$this->nextEnabled = false;
|
||||
$this->prevButton->setStyle('px-2 py-1 text-xs bg-gray-100 rounded text-gray-400');
|
||||
$this->nextButton->setStyle('px-2 py-1 text-xs bg-gray-100 rounded text-gray-400');
|
||||
return;
|
||||
}
|
||||
|
||||
$start = ($this->currentPage * $this->pageSize) + 1;
|
||||
$end = min($totalRows, ($this->currentPage + 1) * $this->pageSize);
|
||||
|
||||
$this->pageInfoLabel->setText(sprintf('Zeige %d–%d von %d', $start, $end, $totalRows));
|
||||
|
||||
$this->prevEnabled = $this->currentPage > 0;
|
||||
$this->nextEnabled = ($this->currentPage + 1) < $this->totalPages;
|
||||
|
||||
$this->prevButton->setStyle(
|
||||
$this->prevEnabled
|
||||
? 'px-2 py-1 text-xs bg-gray-200 rounded hover:bg-gray-300'
|
||||
: 'px-2 py-1 text-xs bg-gray-100 rounded text-gray-400'
|
||||
);
|
||||
$this->nextButton->setStyle(
|
||||
$this->nextEnabled
|
||||
? 'px-2 py-1 text-xs bg-gray-200 rounded hover:bg-gray-300'
|
||||
: 'px-2 py-1 text-xs bg-gray-100 rounded text-gray-400'
|
||||
);
|
||||
}
|
||||
|
||||
private function goToPreviousPage(): void
|
||||
{
|
||||
if ($this->currentPage <= 0 || !$this->prevEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->currentPage--;
|
||||
$this->updatePageData();
|
||||
$this->markDirty(true);
|
||||
}
|
||||
|
||||
private function goToNextPage(): void
|
||||
{
|
||||
if (($this->currentPage + 1) >= $this->totalPages || !$this->nextEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->currentPage++;
|
||||
$this->updatePageData();
|
||||
$this->markDirty(true);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user