Added Flexbox
This commit is contained in:
parent
82cfa6c80d
commit
f3fe8934ef
72
examples/FlexLayout.php
Normal file
72
examples/FlexLayout.php
Normal 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();
|
||||||
@ -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);
|
||||||
|
|||||||
@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user