Backup
This commit is contained in:
parent
bf986acb49
commit
3fa5276aba
@ -4,7 +4,7 @@ namespace PHPNative\Ui\Widget;
|
|||||||
|
|
||||||
class FileBrowser extends Container
|
class FileBrowser extends Container
|
||||||
{
|
{
|
||||||
private Table $fileTable;
|
private VirtualTable $fileTable;
|
||||||
private Label $pathLabel;
|
private Label $pathLabel;
|
||||||
private string $currentPath;
|
private string $currentPath;
|
||||||
private $onFileSelect = null;
|
private $onFileSelect = null;
|
||||||
@ -17,9 +17,8 @@ class FileBrowser extends Container
|
|||||||
|
|
||||||
public function __construct(string $initialPath = '.', bool $isRemote = false, string $style = '')
|
public function __construct(string $initialPath = '.', bool $isRemote = false, string $style = '')
|
||||||
{
|
{
|
||||||
// Root-Container füllt die verfügbare Höhe im Eltern-Layout (flex-1),
|
// 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
|
// und enthält eine VirtualTable für performantes Scrollen.
|
||||||
// ihr Body sinnvoll scrollen kann.
|
|
||||||
parent::__construct('w-full flex flex-col flex-1 gap-2 ' . $style);
|
parent::__construct('w-full flex flex-col flex-1 gap-2 ' . $style);
|
||||||
|
|
||||||
$this->currentPath = $initialPath;
|
$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->pathLabel = new Label($initialPath, 'px-3 py-2 bg-gray-200 text-black rounded text-sm font-mono');
|
||||||
$this->addComponent($this->pathLabel);
|
$this->addComponent($this->pathLabel);
|
||||||
|
|
||||||
// File table, die den verbleibenden Platz im FileBrowser nutzt.
|
// VirtualTable nutzt Paging, um bei vielen Einträgen nur
|
||||||
// Das eigentliche Scrollen passiert im Body-Container der Table
|
// einen Teil der Zeilen im UI zu halten – das macht Scrollen
|
||||||
// (overflow-auto dort).
|
// deutlich flüssiger bei großen Verzeichnissen.
|
||||||
$this->fileTable = new Table(' flex-1');
|
$this->fileTable = new VirtualTable(' flex-1');
|
||||||
$this->fileTable->setColumns([
|
$this->fileTable->setColumns([
|
||||||
['key' => 'type', 'title' => 'Typ', 'width' => 60],
|
['key' => 'type', 'title' => 'Typ', 'width' => 60],
|
||||||
['key' => 'name', 'title' => 'Name'],
|
['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