Added Flexbox

This commit is contained in:
Thomas Peterson 2025-10-22 18:44:38 +02:00
parent 82cfa6c80d
commit f3fe8934ef
3 changed files with 196 additions and 3 deletions

72
examples/FlexLayout.php Normal file
View File

@ -0,0 +1,72 @@
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PHPNative\Framework\Application;
use PHPNative\Ui\Widget\Container;
use PHPNative\Ui\Widget\Label;
// Check PHP version
if (PHP_VERSION_ID < 80100) {
die("This demo requires PHP 8.1+ for Fiber support.\nYour version: " . PHP_VERSION . "\n");
}
$app = new Application('Flexbox Layout Demo', 800, 600);
// Main container with padding
$mainContainer = new Container(style: 'p-4 bg-gray-100');
// Example 1: Flex Row with basis
$row1 = new Container(style: 'flex flex-row bg-white m-2 p-2');
$box1 = new Container(style: 'basis-1/3 bg-blue-500 p-4');
$label1 = new Label(text: '33% Basis', style: 'text-white');
$box1->addComponent($label1);
$box2 = new Container(style: 'basis-1/3 bg-green-500 p-4');
$label2 = new Label(text: '33% Basis', style: 'text-white');
$box2->addComponent($label2);
$box3 = new Container(style: 'basis-1/3 bg-red-500 p-4');
$label3 = new Label(text: '33% Basis', style: 'text-white');
$box3->addComponent($label3);
$row1->addComponent($box1);
$row1->addComponent($box2);
$row1->addComponent($box3);
// Example 2: Flex Row with flex-grow
$row2 = new Container(style: 'flex flex-row bg-white m-2 p-2');
$fixedBox = new Container(style: 'w-200 bg-purple-500 p-4');
$fixedLabel = new Label(text: 'Fixed 200px', style: 'text-white');
$fixedBox->addComponent($fixedLabel);
$growBox = new Container(style: 'flex-1 bg-yellow-500 p-4');
$growLabel = new Label(text: 'Flex Grow (rest)', style: 'text-black');
$growBox->addComponent($growLabel);
$row2->addComponent($fixedBox);
$row2->addComponent($growBox);
// Example 3: Flex Column
$col1 = new Container(style: 'flex flex-col bg-white m-2 p-2 h-200');
$colBox1 = new Container(style: 'basis-1/2 bg-cyan-500 p-4');
$colLabel1 = new Label(text: '50% Height', style: 'text-white');
$colBox1->addComponent($colLabel1);
$colBox2 = new Container(style: 'basis-1/2 bg-orange-500 p-4');
$colLabel2 = new Label(text: '50% Height', style: 'text-white');
$colBox2->addComponent($colLabel2);
$col1->addComponent($colBox1);
$col1->addComponent($colBox2);
// Add all examples to main container
$mainContainer->addComponent($row1);
$mainContainer->addComponent($row2);
$mainContainer->addComponent($col1);
$app->setRoot($mainContainer);
$app->run();

View File

@ -10,12 +10,19 @@ if (PHP_VERSION_ID < 80100) {
} }
$app = new \PHPNative\Framework\Application('ToDo Demo', 700, 500); $app = new \PHPNative\Framework\Application('ToDo Demo', 700, 500);
$container = new \PHPNative\Ui\Widget\Container(style: 'm-10 p-10 rounded-xl bg-lime-200'); $container = new \PHPNative\Ui\Widget\Container(style: 'm-10 p-10 flex-row rounded-xl bg-lime-200');
$containerMenu = new \PHPNative\Ui\Widget\Container(style: 'bg-lime-700'); $containerContent = new \PHPNative\Ui\Widget\Container(style: 'flex-1 bg-lime-700');
$label = new Label( $labelContent = new Label(
text: 'Todo App', text: 'Todo App',
style: 'text-xl2 text-red-500', style: 'text-xl2 text-red-500',
); );
$containerContent->addComponent($labelContent);
$container->addComponent($containerContent);
$containerMenu = new \PHPNative\Ui\Widget\Container(style: 'w-200 bg-lime-200');
$label = new Label(
text: 'Menu App',
style: 'text-red-500',
);
$containerMenu->addComponent($label); $containerMenu->addComponent($label);
$container->addComponent($containerMenu); $container->addComponent($containerMenu);
$app->setRoot($container); $app->setRoot($container);

View File

@ -2,11 +2,125 @@
namespace PHPNative\Ui\Widget; namespace PHPNative\Ui\Widget;
use PHPNative\Framework\TextRenderer;
use PHPNative\Tailwind\Style\Basis;
use PHPNative\Tailwind\Style\DirectionEnum;
use PHPNative\Tailwind\Style\Flex;
use PHPNative\Tailwind\Style\FlexTypeEnum;
use PHPNative\Tailwind\Style\Height;
use PHPNative\Tailwind\Style\Unit;
use PHPNative\Tailwind\Style\Width;
use PHPNative\Ui\Component; use PHPNative\Ui\Component;
use PHPNative\Ui\Viewport;
class Container extends Component class Container extends Component
{ {
public function __construct( public function __construct(
public string $style = '', public string $style = '',
) {} ) {}
public function layout(null|TextRenderer $textRenderer = null): void
{
// Call parent to compute styles and setup viewports
parent::layout($textRenderer);
// Check if this container has flex layout
if (!isset($this->computedStyles[Flex::class])) {
return;
}
$flex = $this->computedStyles[Flex::class];
$this->layoutChildren($flex, $textRenderer);
}
private function layoutChildren(Flex $flex, null|TextRenderer $textRenderer): void
{
if (empty($this->children)) {
return;
}
$isRow = $flex->direction === DirectionEnum::row;
$availableSpace = $isRow ? $this->contentViewport->width : $this->contentViewport->height;
// First pass: calculate fixed sizes and count flex-grow items
$childSizes = [];
$flexGrowCount = 0;
$usedSpace = 0;
foreach ($this->children as $index => $child) {
// Parse child styles to get basis, width, height, and flex
$childStyles = \PHPNative\Tailwind\StyleParser::parse($child->style)->getValidStyles(
\PHPNative\Tailwind\Style\MediaQueryEnum::normal,
\PHPNative\Tailwind\Style\StateEnum::normal,
);
$childFlex = $childStyles[Flex::class] ?? null;
$basis = $childStyles[Basis::class] ?? null;
$width = $childStyles[Width::class] ?? null;
$height = $childStyles[Height::class] ?? null;
$size = 0;
// Check if child has flex-grow
if ($childFlex && $childFlex->type !== FlexTypeEnum::none) {
$flexGrowCount++;
$childSizes[$index] = ['size' => 0, 'flexGrow' => true];
} else {
// Calculate fixed size from basis, width, or height
if ($basis) {
$size = $this->calculateSize($basis, $availableSpace);
} elseif ($isRow && $width) {
$size = $this->calculateSize($width, $availableSpace);
} elseif (!$isRow && $height) {
$size = $this->calculateSize($height, $availableSpace);
}
$usedSpace += $size;
$childSizes[$index] = ['size' => $size, 'flexGrow' => false];
}
}
// Calculate remaining space for flex-grow items
$remainingSpace = max(0, $availableSpace - $usedSpace);
$flexGrowSize = $flexGrowCount > 0 ? $remainingSpace / $flexGrowCount : 0;
// Second pass: assign sizes and position children
$currentPosition = $isRow ? $this->contentViewport->x : $this->contentViewport->y;
foreach ($this->children as $index => $child) {
$childSize = $childSizes[$index];
$size = $childSize['flexGrow'] ? $flexGrowSize : $childSize['size'];
// Create viewport for child
$childViewport = new Viewport();
if ($isRow) {
// Flex row
$childViewport->x = $currentPosition;
$childViewport->y = $this->contentViewport->y;
$childViewport->width = $size;
$childViewport->height = $this->contentViewport->height;
$currentPosition += $size;
} else {
// Flex column
$childViewport->x = $this->contentViewport->x;
$childViewport->y = $currentPosition;
$childViewport->width = $this->contentViewport->width;
$childViewport->height = $size;
$currentPosition += $size;
}
$child->setViewport($childViewport);
$child->layout($textRenderer);
}
}
private function calculateSize(Width|Height|Basis $style, float $availableSpace): float
{
return match ($style->unit) {
Unit::Pixel => (float) $style->value,
Unit::Point => (float) $style->value,
Unit::Percent => ($availableSpace * $style->value) / 100,
};
}
} }