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);
|
||||
$container = new \PHPNative\Ui\Widget\Container(style: 'm-10 p-10 rounded-xl bg-lime-200');
|
||||
$containerMenu = new \PHPNative\Ui\Widget\Container(style: 'bg-lime-700');
|
||||
$label = new Label(
|
||||
$container = new \PHPNative\Ui\Widget\Container(style: 'm-10 p-10 flex-row rounded-xl bg-lime-200');
|
||||
$containerContent = new \PHPNative\Ui\Widget\Container(style: 'flex-1 bg-lime-700');
|
||||
$labelContent = new Label(
|
||||
text: 'Todo App',
|
||||
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);
|
||||
$container->addComponent($containerMenu);
|
||||
$app->setRoot($container);
|
||||
|
||||
@ -2,11 +2,125 @@
|
||||
|
||||
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\Viewport;
|
||||
|
||||
class Container extends Component
|
||||
{
|
||||
public function __construct(
|
||||
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