First Commit
This commit is contained in:
commit
949cd47c96
51
.gitignore
vendored
Normal file
51
.gitignore
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
# Composer
|
||||
/vendor/
|
||||
composer.phar
|
||||
composer.lock
|
||||
|
||||
# PHP
|
||||
*.log
|
||||
*.cache
|
||||
.phpunit.result.cache
|
||||
|
||||
# IDE & Editor
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# System Files
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
desktop.ini
|
||||
|
||||
# Build & Distribution
|
||||
/build/
|
||||
/dist/
|
||||
*.phar
|
||||
|
||||
# Environment
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Temporary Files
|
||||
/tmp/
|
||||
/temp/
|
||||
*.tmp
|
||||
|
||||
# Logs
|
||||
/logs/
|
||||
*.log
|
||||
|
||||
# Coverage & Testing
|
||||
/coverage/
|
||||
.phpunit.cache
|
||||
|
||||
# OS
|
||||
.Trash-*
|
||||
.nfs*
|
||||
22
composer.json
Normal file
22
composer.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "phpnative/framework",
|
||||
"type": "library",
|
||||
"require": {
|
||||
"ext-parallel": "^1.2",
|
||||
"monolog/monolog": "^3.9",
|
||||
"php": "^8.4",
|
||||
"symfony/var-dumper": "^7.3"
|
||||
},
|
||||
"license": "MIT",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPNative\\": "src/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Thomas Peterson",
|
||||
"email": "info@thomas-peterson.de"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
examples/Todo.php
Normal file
15
examples/Todo.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
// 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 \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');
|
||||
$container->addComponent($containerMenu);
|
||||
$app->setRoot($container);
|
||||
$app->run();
|
||||
129
src/Core/Collection.php
Normal file
129
src/Core/Collection.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Core;
|
||||
|
||||
use ArrayIterator;
|
||||
use IteratorAggregate;
|
||||
use Traversable;
|
||||
|
||||
abstract class Collection implements IteratorAggregate
|
||||
{
|
||||
public function __construct(
|
||||
private array $elements,
|
||||
) {}
|
||||
|
||||
public static function createEmpty(): static
|
||||
{
|
||||
return new static([]);
|
||||
}
|
||||
|
||||
public static function fromMap(array $items, callable $fn): static
|
||||
{
|
||||
return new static(array_map($fn, $items));
|
||||
}
|
||||
|
||||
public function reduce(callable $fn, mixed $initial): mixed
|
||||
{
|
||||
return array_reduce($this->elements, $fn, $initial);
|
||||
}
|
||||
|
||||
public function map(callable $fn): array
|
||||
{
|
||||
return array_map($fn, $this->elements);
|
||||
}
|
||||
|
||||
public function each(callable $fn): void
|
||||
{
|
||||
array_walk($this->elements, $fn);
|
||||
}
|
||||
|
||||
public function some(callable $fn): bool
|
||||
{
|
||||
foreach ($this->elements as $index => $element) {
|
||||
if ($fn($element, $index, $this->elements)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setElements($elements): void
|
||||
{
|
||||
$this->elements = $elements;
|
||||
}
|
||||
|
||||
public function filter(callable $fn): static
|
||||
{
|
||||
return new static(array_filter($this->elements, $fn, ARRAY_FILTER_USE_BOTH));
|
||||
}
|
||||
|
||||
public function first(): mixed
|
||||
{
|
||||
return reset($this->elements);
|
||||
}
|
||||
|
||||
public function last(): mixed
|
||||
{
|
||||
return end($this->elements);
|
||||
}
|
||||
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->elements);
|
||||
}
|
||||
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return empty($this->elements);
|
||||
}
|
||||
|
||||
public function add(mixed $element): void
|
||||
{
|
||||
$this->elements[] = $element;
|
||||
}
|
||||
|
||||
public function values(): array
|
||||
{
|
||||
return array_values($this->elements);
|
||||
}
|
||||
|
||||
public function items(): array
|
||||
{
|
||||
return $this->elements;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return new ArrayIterator($this->elements);
|
||||
}
|
||||
|
||||
public function removeFirstItem(): void
|
||||
{
|
||||
if (count($this->elements) > 0) {
|
||||
array_shift($this->elements);
|
||||
}
|
||||
}
|
||||
|
||||
public function sort(callable $callback): void
|
||||
{
|
||||
usort($this->elements, $callback);
|
||||
}
|
||||
|
||||
public function removeItem($item): void
|
||||
{
|
||||
$temp = $this->filter(function ($value, $key) use ($item) {
|
||||
return $value::class != $item::class;
|
||||
});
|
||||
|
||||
$this->elements = $temp->items();
|
||||
}
|
||||
|
||||
public function clear(): void
|
||||
{
|
||||
$this->elements = [];
|
||||
}
|
||||
}
|
||||
20
src/Core/TypedCollection.php
Normal file
20
src/Core/TypedCollection.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Core;
|
||||
|
||||
abstract class TypedCollection extends Collection
|
||||
{
|
||||
public function __construct(array $elements = [])
|
||||
{
|
||||
parent::__construct($elements);
|
||||
}
|
||||
|
||||
abstract protected function type(): string;
|
||||
|
||||
public function add(mixed $element): void
|
||||
{
|
||||
parent::add($element);
|
||||
}
|
||||
}
|
||||
209
src/Framework/Application.php
Normal file
209
src/Framework/Application.php
Normal file
@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Framework;
|
||||
|
||||
use PHPNative\Ui\Component;
|
||||
use PHPNative\Ui\Viewport;
|
||||
|
||||
class Application
|
||||
{
|
||||
protected array $windows = [];
|
||||
protected int $nextWindowId = 0;
|
||||
|
||||
protected $window;
|
||||
protected $windowWidth;
|
||||
protected $windowHeight;
|
||||
protected $title;
|
||||
protected $running = true;
|
||||
protected $rootComponent = null;
|
||||
private TextRenderer $textRenderer;
|
||||
private $mouseX;
|
||||
private $mouseY;
|
||||
private Viewport $viewport;
|
||||
private bool $shouldBeReLayouted = true;
|
||||
|
||||
public function __construct(string $title, int $width = 800, int $height = 600)
|
||||
{
|
||||
$this->title = $title;
|
||||
$this->windowWidth = $width;
|
||||
$this->windowHeight = $height;
|
||||
|
||||
// Create window
|
||||
$this->window = rgfw_createWindow($title, 100, 100, $width, $height, RGFW_CENTER);
|
||||
if (!$this->window) {
|
||||
throw new \Exception('Failed to create window');
|
||||
}
|
||||
|
||||
// Initialize RSGL renderer
|
||||
if (!rsgl_init($this->window)) {
|
||||
throw new \Exception('Failed to initialize RSGL renderer');
|
||||
}
|
||||
|
||||
// Initialize text renderer
|
||||
$this->textRenderer = new TextRenderer($this->window);
|
||||
if (!$this->textRenderer->init()) {
|
||||
error_log('Warning: Failed to initialize text renderer. Text rendering will not be available.');
|
||||
}
|
||||
|
||||
// Get actual window size
|
||||
$size = rgfw_window_getSize($this->window);
|
||||
$this->windowWidth = $size[0];
|
||||
$this->windowHeight = $size[1];
|
||||
$this->viewport = new Viewport(
|
||||
windowWidth: $this->windowWidth,
|
||||
windowHeight: $this->windowHeight,
|
||||
width: $this->windowWidth,
|
||||
height: $this->windowHeight,
|
||||
);
|
||||
}
|
||||
|
||||
public function createWindow(): void
|
||||
{
|
||||
}
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
while ($this->running && !rgfw_window_shouldClose($this->window)) {
|
||||
$this->handleEvents();
|
||||
$this->update();
|
||||
$this->layout();
|
||||
$this->render();
|
||||
|
||||
// Limit frame rate to ~60 FPS
|
||||
usleep(16666);
|
||||
}
|
||||
|
||||
$this->cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle window and input events
|
||||
*/
|
||||
protected function handleEvents(): void
|
||||
{
|
||||
while ($event = rgfw_window_checkEvent($this->window)) {
|
||||
switch ($event['type']) {
|
||||
case RGFW_quit:
|
||||
$this->running = false;
|
||||
break;
|
||||
|
||||
case RGFW_keyPressed:
|
||||
$keyCode = $event['keyCode'] ?? 0;
|
||||
if ($keyCode == RGFW_Escape) {
|
||||
$this->running = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_windowResized:
|
||||
// Update window dimensions (from event data)
|
||||
$newWidth = $event[0] ?? $this->windowWidth;
|
||||
$newHeight = $event[1] ?? $this->windowHeight;
|
||||
|
||||
$this->windowWidth = $newWidth;
|
||||
$this->windowHeight = $newHeight;
|
||||
|
||||
// Update RSGL renderer size and viewport
|
||||
// This ensures the renderer is properly configured for the new window size
|
||||
rsgl_updateRendererSize($this->window);
|
||||
|
||||
// Update text renderer framebuffer
|
||||
if ($this->textRenderer && $this->textRenderer->isInitialized()) {
|
||||
$this->textRenderer->updateFramebuffer($newWidth, $newHeight);
|
||||
}
|
||||
|
||||
$this->viewport->x = 0;
|
||||
$this->viewport->y = 0;
|
||||
$this->viewport->windowWidth = $newWidth;
|
||||
$this->viewport->width = $newWidth;
|
||||
$this->viewport->height = $newHeight;
|
||||
$this->viewport->windowHeight = $newHeight;
|
||||
$this->shouldBeReLayouted = true;
|
||||
break;
|
||||
|
||||
case RGFW_mousePosChanged:
|
||||
$this->mouseX = $event[0] ?? 0;
|
||||
$this->mouseY = $event[1] ?? 0;
|
||||
|
||||
// Propagate mouse move to root component
|
||||
if ($this->rootComponent) {
|
||||
// $this->rootComponent->handleMouseMove($this->mouseX, $this->mouseY);
|
||||
}
|
||||
break;
|
||||
|
||||
case RGFW_mouseButtonPressed:
|
||||
$button = $event['button'] ?? 0;
|
||||
|
||||
// Propagate click to root component
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->handleMouseClick($this->mouseX, $this->mouseY, $button);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update application state
|
||||
*/
|
||||
protected function update(): void
|
||||
{
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the application
|
||||
*/
|
||||
protected function render(): void
|
||||
{
|
||||
rsgl_clear($this->window, 255, 255, 255, 0);
|
||||
|
||||
// Render root component tree
|
||||
if ($this->rootComponent) {
|
||||
$this->rootComponent->render($this->textRenderer);
|
||||
$this->rootComponent->renderContent($this->textRenderer);
|
||||
}
|
||||
|
||||
// Render to screen
|
||||
rsgl_render($this->window);
|
||||
rgfw_window_swapBuffers($this->window);
|
||||
}
|
||||
|
||||
protected function layout(): void
|
||||
{
|
||||
// Render root component tree
|
||||
if ($this->rootComponent && $this->shouldBeReLayouted) {
|
||||
$this->rootComponent->setViewport($this->viewport);
|
||||
$this->rootComponent->setWindow($this->window);
|
||||
$this->rootComponent->layout($this->textRenderer);
|
||||
$this->shouldBeReLayouted = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
protected function cleanup(): void
|
||||
{
|
||||
if ($this->textRenderer) {
|
||||
$this->textRenderer->free();
|
||||
}
|
||||
rsgl_close($this->window);
|
||||
rgfw_window_close($this->window);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the application
|
||||
*/
|
||||
public function quit(): void
|
||||
{
|
||||
$this->running = false;
|
||||
}
|
||||
|
||||
public function setRoot(Component $component): self
|
||||
{
|
||||
$this->rootComponent = $component;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
177
src/Framework/TextRenderer.php
Normal file
177
src/Framework/TextRenderer.php
Normal file
@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* TextRenderer - Wrapper for RFont text rendering
|
||||
*/
|
||||
|
||||
namespace PHPNative\Framework;
|
||||
|
||||
class TextRenderer
|
||||
{
|
||||
private $window;
|
||||
private bool $initialized = false;
|
||||
private string $fontPath;
|
||||
private int $fontSize;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param resource $window RGFW window resource
|
||||
*/
|
||||
public function __construct($window)
|
||||
{
|
||||
$this->window = $window;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize RFont with a font file
|
||||
*
|
||||
* @param string|null $fontPath Path to TTF font file
|
||||
* @param int $fontSize Default font size
|
||||
* @param int $atlasWidth Font atlas texture width (default 512)
|
||||
* @param int $atlasHeight Font atlas texture height (default 512)
|
||||
* @return bool Success
|
||||
*/
|
||||
public function init(
|
||||
null|string $fontPath = null,
|
||||
int $fontSize = 16,
|
||||
int $atlasWidth = 512,
|
||||
int $atlasHeight = 512,
|
||||
): bool {
|
||||
// Try to find a suitable font if none provided
|
||||
if ($fontPath === null) {
|
||||
$fontPath = $this->findSystemFont();
|
||||
}
|
||||
|
||||
if (!file_exists($fontPath)) {
|
||||
error_log("Font file not found: {$fontPath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->fontPath = $fontPath;
|
||||
$this->fontSize = $fontSize;
|
||||
|
||||
$this->initialized = rfont_init($this->window, $fontPath, $fontSize, $atlasWidth, $atlasHeight);
|
||||
|
||||
if (!$this->initialized) {
|
||||
error_log("Failed to initialize RFont with font: {$fontPath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set default color to white
|
||||
$this->setColor(1.0, 1.0, 1.0, 1.0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a system font
|
||||
*
|
||||
* @return string|null Font path or null if not found
|
||||
*/
|
||||
private function findSystemFont(): null|string
|
||||
{
|
||||
$fontPaths = [
|
||||
// Linux
|
||||
'/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
|
||||
'/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf',
|
||||
'/usr/share/fonts/TTF/DejaVuSans.ttf',
|
||||
// macOS
|
||||
'/System/Library/Fonts/Helvetica.ttc',
|
||||
'/Library/Fonts/Arial.ttf',
|
||||
// Windows
|
||||
'C:/Windows/Fonts/arial.ttf',
|
||||
'C:/Windows/Fonts/calibri.ttf',
|
||||
];
|
||||
|
||||
foreach ($fontPaths as $path) {
|
||||
if (file_exists($path)) {
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw text at position
|
||||
*
|
||||
* @param string $text Text to draw
|
||||
* @param int $x X position
|
||||
* @param int $y Y position
|
||||
* @param int|null $size Font size
|
||||
*/
|
||||
public function drawText(string $text, int $x, int $y, null|int $size = null): void
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($size === null) {
|
||||
$size = $this->fontSize;
|
||||
}
|
||||
|
||||
rfont_drawText($this->window, $text, $x, $y, $size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set text color
|
||||
*
|
||||
* @param float $r Red (0.0 - 1.0)
|
||||
* @param float $g Green (0.0 - 1.0)
|
||||
* @param float $b Blue (0.0 - 1.0)
|
||||
* @param float $a Alpha (0.0 - 1.0)
|
||||
*/
|
||||
public function setColor(float $r, float $g, float $b, float $a = 1.0): void
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
rfont_setColor($this->window, $r, $g, $b, $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update framebuffer size (call on window resize)
|
||||
*
|
||||
* @param int $width Window width
|
||||
* @param int $height Window height
|
||||
*/
|
||||
public function updateFramebuffer(int $width, int $height): void
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
rfont_setFramebuffer($this->window, $width, $height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free RFont resources
|
||||
*/
|
||||
public function free(): void
|
||||
{
|
||||
if ($this->initialized) {
|
||||
rfont_free($this->window);
|
||||
$this->initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if initialized
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInitialized(): bool
|
||||
{
|
||||
return $this->initialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->free();
|
||||
}
|
||||
}
|
||||
11
src/Renderer/Widget.php
Normal file
11
src/Renderer/Widget.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
class Widget
|
||||
{
|
||||
public static function render(ViewPort $viewPort, Component $component, int $index = 1): Viewport
|
||||
{
|
||||
if ($component instanceof Container) {
|
||||
return \PHPNative\Renderer\Widgets\Container::layout($viewPort, $view, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/Renderer/Widget/Container.php
Normal file
14
src/Renderer/Widget/Container.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Renderer\Widget;
|
||||
|
||||
use PHPNative\Ui\Viewport;
|
||||
|
||||
class Container
|
||||
{
|
||||
public static function layout(Viewport $viewport, \PHPNative\UI\Widget\Container $view, int $index = 0): Viewport
|
||||
{
|
||||
var_dump('test');
|
||||
return $viewport;
|
||||
}
|
||||
}
|
||||
10
src/Tailwind/Data/Icon.php
Normal file
10
src/Tailwind/Data/Icon.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Data;
|
||||
|
||||
enum Icon:int
|
||||
{
|
||||
case plus = 57669;
|
||||
|
||||
case save = 57697;
|
||||
}
|
||||
288
src/Tailwind/Data/colors.json
Normal file
288
src/Tailwind/Data/colors.json
Normal file
@ -0,0 +1,288 @@
|
||||
{
|
||||
"slate": {
|
||||
"50": "#f8fafc",
|
||||
"100": "#f1f5f9",
|
||||
"200": "#e2e8f0",
|
||||
"300": "#cbd5e1",
|
||||
"400": "#94a3b8",
|
||||
"500": "#64748b",
|
||||
"600": "#475569",
|
||||
"700": "#334155",
|
||||
"800": "#1e293b",
|
||||
"900": "#0f172a",
|
||||
"950": "#020617"
|
||||
},
|
||||
"gray": {
|
||||
"50": "#f9fafb",
|
||||
"100": "#f3f4f6",
|
||||
"200": "#e5e7eb",
|
||||
"300": "#d1d5db",
|
||||
"400": "#9ca3af",
|
||||
"500": "#6b7280",
|
||||
"600": "#4b5563",
|
||||
"700": "#374151",
|
||||
"800": "#1f2937",
|
||||
"900": "#111827",
|
||||
"950": "#030712"
|
||||
},
|
||||
"zinc": {
|
||||
"50": "#fafafa",
|
||||
"100": "#f4f4f5",
|
||||
"200": "#e4e4e7",
|
||||
"300": "#d4d4d8",
|
||||
"400": "#a1a1aa",
|
||||
"500": "#71717a",
|
||||
"600": "#52525b",
|
||||
"700": "#3f3f46",
|
||||
"800": "#27272a",
|
||||
"900": "#18181b",
|
||||
"950": "#09090b"
|
||||
},
|
||||
"neutral": {
|
||||
"50": "#fafafa",
|
||||
"100": "#f5f5f5",
|
||||
"200": "#e5e5e5",
|
||||
"300": "#d4d4d4",
|
||||
"400": "#a3a3a3",
|
||||
"500": "#737373",
|
||||
"600": "#525252",
|
||||
"700": "#404040",
|
||||
"800": "#262626",
|
||||
"900": "#171717",
|
||||
"950": "#0a0a0a"
|
||||
},
|
||||
"stone": {
|
||||
"50": "#fafaf9",
|
||||
"100": "#f5f5f4",
|
||||
"200": "#e7e5e4",
|
||||
"300": "#d6d3d1",
|
||||
"400": "#a8a29e",
|
||||
"500": "#78716c",
|
||||
"600": "#57534e",
|
||||
"700": "#44403c",
|
||||
"800": "#292524",
|
||||
"900": "#1c1917",
|
||||
"950": "#0c0a09"
|
||||
},
|
||||
"red": {
|
||||
"50": "#fef2f2",
|
||||
"100": "#fee2e2",
|
||||
"200": "#fecaca",
|
||||
"300": "#fca5a5",
|
||||
"400": "#f87171",
|
||||
"500": "#ef4444",
|
||||
"600": "#dc2626",
|
||||
"700": "#b91c1c",
|
||||
"800": "#991b1b",
|
||||
"900": "#7f1d1d",
|
||||
"950": "#450a0a"
|
||||
},
|
||||
"orange": {
|
||||
"50": "#fff7ed",
|
||||
"100": "#ffedd5",
|
||||
"200": "#fed7aa",
|
||||
"300": "#fdba74",
|
||||
"400": "#fb923c",
|
||||
"500": "#f97316",
|
||||
"600": "#ea580c",
|
||||
"700": "#c2410c",
|
||||
"800": "#9a3412",
|
||||
"900": "#7c2d12",
|
||||
"950": "#431407"
|
||||
},
|
||||
"amber": {
|
||||
"50": "#fffbeb",
|
||||
"100": "#fef3c7",
|
||||
"200": "#fde68a",
|
||||
"300": "#fcd34d",
|
||||
"400": "#fbbf24",
|
||||
"500": "#f59e0b",
|
||||
"600": "#d97706",
|
||||
"700": "#b45309",
|
||||
"800": "#92400e",
|
||||
"900": "#78350f",
|
||||
"950": "#451a03"
|
||||
},
|
||||
"yellow": {
|
||||
"50": "#fefce8",
|
||||
"100": "#fef9c3",
|
||||
"200": "#fef08a",
|
||||
"300": "#fde047",
|
||||
"400": "#facc15",
|
||||
"500": "#eab308",
|
||||
"600": "#ca8a04",
|
||||
"700": "#a16207",
|
||||
"800": "#854d0e",
|
||||
"900": "#713f12",
|
||||
"950": "#422006"
|
||||
},
|
||||
"lime": {
|
||||
"50": "#f7fee7",
|
||||
"100": "#ecfccb",
|
||||
"200": "#d9f99d",
|
||||
"300": "#bef264",
|
||||
"400": "#a3e635",
|
||||
"500": "#84cc16",
|
||||
"600": "#65a30d",
|
||||
"700": "#4d7c0f",
|
||||
"800": "#3f6212",
|
||||
"900": "#365314",
|
||||
"950": "#1a2e05"
|
||||
},
|
||||
"green": {
|
||||
"50": "#f0fdf4",
|
||||
"100": "#dcfce7",
|
||||
"200": "#bbf7d0",
|
||||
"300": "#86efac",
|
||||
"400": "#4ade80",
|
||||
"500": "#22c55e",
|
||||
"600": "#16a34a",
|
||||
"700": "#15803d",
|
||||
"800": "#166534",
|
||||
"900": "#14532d",
|
||||
"950": "#052e16"
|
||||
},
|
||||
"emerald": {
|
||||
"50": "#ecfdf5",
|
||||
"100": "#d1fae5",
|
||||
"200": "#a7f3d0",
|
||||
"300": "#6ee7b7",
|
||||
"400": "#34d399",
|
||||
"500": "#10b981",
|
||||
"600": "#059669",
|
||||
"700": "#047857",
|
||||
"800": "#065f46",
|
||||
"900": "#064e3b",
|
||||
"950": "#022c22"
|
||||
},
|
||||
"teal": {
|
||||
"50": "#f0fdfa",
|
||||
"100": "#ccfbf1",
|
||||
"200": "#99f6e4",
|
||||
"300": "#5eead4",
|
||||
"400": "#2dd4bf",
|
||||
"500": "#14b8a6",
|
||||
"600": "#0d9488",
|
||||
"700": "#0f766e",
|
||||
"800": "#115e59",
|
||||
"900": "#134e4a",
|
||||
"950": "#042f2e"
|
||||
},
|
||||
"cyan": {
|
||||
"50": "#ecfeff",
|
||||
"100": "#cffafe",
|
||||
"200": "#a5f3fc",
|
||||
"300": "#67e8f9",
|
||||
"400": "#22d3ee",
|
||||
"500": "#06b6d4",
|
||||
"600": "#0891b2",
|
||||
"700": "#0e7490",
|
||||
"800": "#155e75",
|
||||
"900": "#164e63",
|
||||
"950": "#083344"
|
||||
},
|
||||
"sky": {
|
||||
"50": "#f0f9ff",
|
||||
"100": "#e0f2fe",
|
||||
"200": "#bae6fd",
|
||||
"300": "#7dd3fc",
|
||||
"400": "#38bdf8",
|
||||
"500": "#0ea5e9",
|
||||
"600": "#0284c7",
|
||||
"700": "#0369a1",
|
||||
"800": "#075985",
|
||||
"900": "#0c4a6e",
|
||||
"950": "#082f49"
|
||||
},
|
||||
"blue": {
|
||||
"50": "#eff6ff",
|
||||
"100": "#dbeafe",
|
||||
"200": "#bfdbfe",
|
||||
"300": "#93c5fd",
|
||||
"400": "#60a5fa",
|
||||
"500": "#3b82f6",
|
||||
"600": "#2563eb",
|
||||
"700": "#1d4ed8",
|
||||
"800": "#1e40af",
|
||||
"900": "#1e3a8a",
|
||||
"950": "#172554"
|
||||
},
|
||||
"indigo": {
|
||||
"50": "#eef2ff",
|
||||
"100": "#e0e7ff",
|
||||
"200": "#c7d2fe",
|
||||
"300": "#a5b4fc",
|
||||
"400": "#818cf8",
|
||||
"500": "#6366f1",
|
||||
"600": "#4f46e5",
|
||||
"700": "#4338ca",
|
||||
"800": "#3730a3",
|
||||
"900": "#312e81",
|
||||
"950": "#1e1b4b"
|
||||
},
|
||||
"violet": {
|
||||
"50": "#f5f3ff",
|
||||
"100": "#ede9fe",
|
||||
"200": "#ddd6fe",
|
||||
"300": "#c4b5fd",
|
||||
"400": "#a78bfa",
|
||||
"500": "#8b5cf6",
|
||||
"600": "#7c3aed",
|
||||
"700": "#6d28d9",
|
||||
"800": "#5b21b6",
|
||||
"900": "#4c1d95",
|
||||
"950": "#2e1065"
|
||||
},
|
||||
"purple": {
|
||||
"50": "#faf5ff",
|
||||
"100": "#f3e8ff",
|
||||
"200": "#e9d5ff",
|
||||
"300": "#d8b4fe",
|
||||
"400": "#c084fc",
|
||||
"500": "#a855f7",
|
||||
"600": "#9333ea",
|
||||
"700": "#7e22ce",
|
||||
"800": "#6b21a8",
|
||||
"900": "#581c87",
|
||||
"950": "#3b0764"
|
||||
},
|
||||
"fuchsia": {
|
||||
"50": "#fdf4ff",
|
||||
"100": "#fae8ff",
|
||||
"200": "#f5d0fe",
|
||||
"300": "#f0abfc",
|
||||
"400": "#e879f9",
|
||||
"500": "#d946ef",
|
||||
"600": "#c026d3",
|
||||
"700": "#a21caf",
|
||||
"800": "#86198f",
|
||||
"900": "#701a75",
|
||||
"950": "#4a044e"
|
||||
},
|
||||
"pink": {
|
||||
"50": "#fdf2f8",
|
||||
"100": "#fce7f3",
|
||||
"200": "#fbcfe8",
|
||||
"300": "#f9a8d4",
|
||||
"400": "#f472b6",
|
||||
"500": "#ec4899",
|
||||
"600": "#db2777",
|
||||
"700": "#be185d",
|
||||
"800": "#9d174d",
|
||||
"900": "#831843",
|
||||
"950": "#500724"
|
||||
},
|
||||
"rose": {
|
||||
"50": "#fff1f2",
|
||||
"100": "#ffe4e6",
|
||||
"200": "#fecdd3",
|
||||
"300": "#fda4af",
|
||||
"400": "#fb7185",
|
||||
"500": "#f43f5e",
|
||||
"600": "#e11d48",
|
||||
"700": "#be123c",
|
||||
"800": "#9f1239",
|
||||
"900": "#881337",
|
||||
"950": "#4c0519"
|
||||
}
|
||||
}
|
||||
55
src/Tailwind/LayoutParser.php
Normal file
55
src/Tailwind/LayoutParser.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind;
|
||||
|
||||
use PHPNative\Renderer\Cache\Styles;
|
||||
use PHPNative\Renderer\Viewport;
|
||||
use PHPNative\Tailwind\Style\DirectionEnum;
|
||||
use PHPNative\Tailwind\Style\Flex;
|
||||
use PHPNative\Tailwind\Style\FlexTypeEnum;
|
||||
use PHPNative\UI\View;
|
||||
|
||||
class LayoutParser
|
||||
{
|
||||
|
||||
public static function sortByStyles(Styles $stylesCache, View $container, Viewport $viewport): View
|
||||
{
|
||||
$i = 0;
|
||||
$flexOne = 0;
|
||||
$container->getViews()->map(function (View $a) use (&$flexOne, &$i, &$stylesCache, $viewport) {
|
||||
$aStyles = $stylesCache->getStyle($a->getId(), $viewport->windowMediaQuery, $a->getState(), $a->getStyle());
|
||||
if(isset($aStyles[Flex::class]) && $aStyles[Flex::class]->type == FlexTypeEnum::one) {
|
||||
$flexOne++;
|
||||
}
|
||||
$a->setRenderSort($i);
|
||||
$i++;
|
||||
});
|
||||
|
||||
$container->getViews()->sort(function (View $a, View $b) use (&$stylesCache, $viewport) {
|
||||
$aStyles = $stylesCache->getStyle($a->getId(), $viewport->windowMediaQuery, $a->getState(), $a->getStyle());
|
||||
$bStyles = $stylesCache->getStyle($b->getId(), $viewport->windowMediaQuery, $b->getState(), $b->getStyle());
|
||||
if(isset($aStyles[Flex::class]) && $aStyles[Flex::class]->type == FlexTypeEnum::none &&
|
||||
isset($bStyles[Flex::class]) && $bStyles[Flex::class]->type == FlexTypeEnum::one) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
});
|
||||
|
||||
$container->getViews()->countOne = $flexOne;
|
||||
|
||||
return $container;
|
||||
|
||||
}
|
||||
|
||||
public static function sortByRenderSort(View $container)
|
||||
{
|
||||
$container->getViews()->sort(function (View $a, View $b) {
|
||||
if($a->getRenderSort() == $b->getRenderSort()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $a->getRenderSort() <=> $b->getRenderSort();
|
||||
});
|
||||
}
|
||||
}
|
||||
17
src/Tailwind/Model/Style.php
Normal file
17
src/Tailwind/Model/Style.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Model;
|
||||
|
||||
use PHPNative\Tailwind\Style\MediaQueryEnum;
|
||||
use PHPNative\Tailwind\Style\StateEnum;
|
||||
|
||||
class Style
|
||||
{
|
||||
public function __construct(
|
||||
public \PHPNative\Tailwind\Style\Style $style,
|
||||
public MediaQueryEnum $mediaQuery = MediaQueryEnum::normal,
|
||||
public StateEnum $state = StateEnum::normal
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
56
src/Tailwind/Model/StyleCollection.php
Normal file
56
src/Tailwind/Model/StyleCollection.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Model;
|
||||
|
||||
use PHPNative\Core\TypedCollection;
|
||||
use PHPNative\Tailwind\Style\Border;
|
||||
use PHPNative\Tailwind\Style\Flex;
|
||||
use PHPNative\Tailwind\Style\Margin;
|
||||
use PHPNative\Tailwind\Style\MediaQueryEnum;
|
||||
use PHPNative\Tailwind\Style\Padding;
|
||||
use PHPNative\Tailwind\Style\StateEnum;
|
||||
use PHPNative\Tailwind\Style\Text;
|
||||
|
||||
class StyleCollection extends TypedCollection
|
||||
{
|
||||
protected function type(): string
|
||||
{
|
||||
return Style::class;
|
||||
}
|
||||
|
||||
public function getValidStyles(MediaQueryEnum $mediaQueryEnum, StateEnum $state): array
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach($this->items() as $style) {
|
||||
if(($style->state == StateEnum::normal || $style->state === $state) && ($style->mediaQuery->value === 0 || $style->mediaQuery->value <= $mediaQueryEnum->value)) {
|
||||
$items[] = $style;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->merge($items);
|
||||
}
|
||||
|
||||
private function merge(array $styles): array
|
||||
{
|
||||
$tmp = [];
|
||||
|
||||
foreach($styles as $style) {
|
||||
if(isset($tmp[$style->style::class]) && $style->style::class === Padding::class) {
|
||||
\PHPNative\Tailwind\Parser\Padding::merge($tmp[$style->style::class], $style->style);
|
||||
}elseif(isset($tmp[$style->style::class]) && $style->style::class === Margin::class) {
|
||||
\PHPNative\Tailwind\Parser\Margin::merge($tmp[$style->style::class], $style->style);
|
||||
}elseif(isset($tmp[$style->style::class]) && $style->style::class === Border::class) {
|
||||
\PHPNative\Tailwind\Parser\Border::merge($tmp[$style->style::class], $style->style);
|
||||
}elseif(isset($tmp[$style->style::class]) && $style->style::class === Text::class) {
|
||||
\PHPNative\Tailwind\Parser\Text::merge($tmp[$style->style::class], $style->style);
|
||||
}elseif(isset($tmp[$style->style::class]) && $style->style::class === Flex::class) {
|
||||
\PHPNative\Tailwind\Parser\Flex::merge($tmp[$style->style::class], $style->style);
|
||||
}else{
|
||||
$tmp[$style->style::class] = $style->style;
|
||||
}
|
||||
}
|
||||
|
||||
return $tmp;
|
||||
}
|
||||
}
|
||||
22
src/Tailwind/Parser/Background.php
Normal file
22
src/Tailwind/Parser/Background.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
class Background implements Parser
|
||||
{
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\Background
|
||||
{
|
||||
$color = new \PHPNative\Tailwind\Style\Color();
|
||||
|
||||
preg_match_all('/bg-(.*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$colorStyle = $output_array[1][0];
|
||||
$color = Color::parse($colorStyle);
|
||||
return new \PHPNative\Tailwind\Style\Background($color);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
60
src/Tailwind/Parser/Basis.php
Normal file
60
src/Tailwind/Parser/Basis.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
use PHPNative\Tailwind\Style\Unit;
|
||||
|
||||
class Basis implements Parser
|
||||
{
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\Basis
|
||||
{
|
||||
$value = -1;
|
||||
$unit = Unit::Pixel;
|
||||
$found = false;
|
||||
preg_match_all('/basis-(\d*)\/(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$value1 = (int)$output_array[1][0];
|
||||
$value2 = (int)$output_array[2][0];
|
||||
$unit = Unit::Percent;
|
||||
$value = (int)round(100/$value2*$value1,0);
|
||||
$found = true;
|
||||
}
|
||||
|
||||
preg_match_all('/basis-(\d*)/', $style, $output_array);
|
||||
if (!$found && count($output_array[0]) > 0) {
|
||||
$value = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/basis-full/', $style, $output_array);
|
||||
if (!$found && count($output_array[0]) > 0) {
|
||||
$value = 100;
|
||||
$unit = Unit::Percent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if($value != -1) {
|
||||
return new \PHPNative\Tailwind\Style\Basis($unit, $value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function merge(\PHPNative\Tailwind\Style\Padding $class, \PHPNative\Tailwind\Style\Padding $style)
|
||||
{
|
||||
if($style->left != null) {
|
||||
$class->left = $style->left;
|
||||
}
|
||||
if($style->right != null) {
|
||||
$class->right = $style->right;
|
||||
}
|
||||
if($style->top != null) {
|
||||
$class->top = $style->top;
|
||||
}
|
||||
if($style->bottom != null) {
|
||||
$class->bottom = $style->bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
151
src/Tailwind/Parser/Border.php
Normal file
151
src/Tailwind/Parser/Border.php
Normal file
@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
class Border implements Parser
|
||||
{
|
||||
public static function parse(string $style): null|\PHPNative\Tailwind\Style\Border
|
||||
{
|
||||
$color = new \PHPNative\Tailwind\Style\Color();
|
||||
|
||||
preg_match_all('/rounded-(t|b|l|r)-(none|sm|md|lg|xl|2xl|3xl)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$size = match ((string) $output_array[2][0]) {
|
||||
'none' => 0,
|
||||
'sm' => 2,
|
||||
'md' => 6,
|
||||
'lg' => 8,
|
||||
'xl' => 12,
|
||||
'2xl' => 16,
|
||||
'3xl' => 24,
|
||||
};
|
||||
return match ((string) $output_array[1][0]) {
|
||||
't' => new \PHPNative\Tailwind\Style\Border(
|
||||
roundTopLeft: $size,
|
||||
roundTopRight: $size,
|
||||
),
|
||||
'b' => new \PHPNative\Tailwind\Style\Border(
|
||||
roundBottomLeft: $size,
|
||||
roundBottomRight: $size,
|
||||
),
|
||||
'r' => new \PHPNative\Tailwind\Style\Border(
|
||||
roundTopRight: $size,
|
||||
roundBottomRight: $size,
|
||||
),
|
||||
'l' => new \PHPNative\Tailwind\Style\Border(
|
||||
roundTopLeft: $size,
|
||||
roundBottomLeft: $size,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
preg_match_all('/rounded-(none|sm|md|lg|xl|2xl|3xl)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$size = match ((string) $output_array[1][0]) {
|
||||
'none' => 0,
|
||||
'sm' => 2,
|
||||
'md' => 6,
|
||||
'lg' => 8,
|
||||
'xl' => 12,
|
||||
'2xl' => 16,
|
||||
'3xl' => 24,
|
||||
};
|
||||
|
||||
return new \PHPNative\Tailwind\Style\Border(
|
||||
roundTopLeft: $size,
|
||||
roundTopRight: $size,
|
||||
roundBottomLeft: $size,
|
||||
roundBottomRight: $size,
|
||||
);
|
||||
}
|
||||
|
||||
preg_match_all('/rounded/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$size = 4;
|
||||
|
||||
return new \PHPNative\Tailwind\Style\Border(
|
||||
roundTopLeft: $size,
|
||||
roundTopRight: $size,
|
||||
roundBottomLeft: $size,
|
||||
roundBottomRight: $size,
|
||||
);
|
||||
}
|
||||
|
||||
preg_match_all('/border-([tblr])-(.*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
return match ((string) $output_array[1][0]) {
|
||||
't' => new \PHPNative\Tailwind\Style\Border(true, top: (int) $output_array[2][0]),
|
||||
'b' => new \PHPNative\Tailwind\Style\Border(true, bottom: (int) $output_array[2][0]),
|
||||
'r' => new \PHPNative\Tailwind\Style\Border(true, right: (int) $output_array[2][0]),
|
||||
'l' => new \PHPNative\Tailwind\Style\Border(true, left: (int) $output_array[2][0]),
|
||||
};
|
||||
}
|
||||
|
||||
preg_match_all('/border-(.*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$colorStyle = $output_array[1][0];
|
||||
$color = Color::parse($colorStyle);
|
||||
return new \PHPNative\Tailwind\Style\Border(false, $color);
|
||||
}
|
||||
|
||||
preg_match_all('/border/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
return new \PHPNative\Tailwind\Style\Border(
|
||||
enabled: true,
|
||||
left: 1,
|
||||
right: 1,
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function merge(
|
||||
\PHPNative\Tailwind\Style\Border $style1,
|
||||
\PHPNative\Tailwind\Style\Border $style2,
|
||||
): void {
|
||||
if ($style2->enabled && !$style1->enabled) {
|
||||
$style1->enabled = true;
|
||||
}
|
||||
if ($style2->color->red != null) {
|
||||
$style1->color->red = $style2->color->red;
|
||||
}
|
||||
if ($style2->color->green != null) {
|
||||
$style1->color->green = $style2->color->green;
|
||||
}
|
||||
if ($style2->color->blue != null) {
|
||||
$style1->color->blue = $style2->color->blue;
|
||||
}
|
||||
if ($style2->color->alpha != null) {
|
||||
$style1->color->alpha = $style2->color->alpha;
|
||||
}
|
||||
if ($style2->top != null) {
|
||||
$style1->top = $style2->top;
|
||||
}
|
||||
if ($style2->bottom != null) {
|
||||
$style1->bottom = $style2->bottom;
|
||||
}
|
||||
if ($style2->left != null) {
|
||||
$style1->left = $style2->left;
|
||||
}
|
||||
if ($style2->right != null) {
|
||||
$style1->right = $style2->right;
|
||||
}
|
||||
if ($style2->roundTopLeft != null) {
|
||||
$style1->roundTopLeft = $style2->roundTopLeft;
|
||||
}
|
||||
if ($style2->roundTopRight != null) {
|
||||
$style1->roundTopRight = $style2->roundTopRight;
|
||||
}
|
||||
if ($style2->roundBottomLeft != null) {
|
||||
$style1->roundBottomLeft = $style2->roundBottomLeft;
|
||||
}
|
||||
if ($style2->roundBottomRight != null) {
|
||||
$style1->roundBottomRight = $style2->roundBottomRight;
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/Tailwind/Parser/Color.php
Normal file
41
src/Tailwind/Parser/Color.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
class Color implements Parser
|
||||
{
|
||||
public static function parse(string $style): \PHPNative\Tailwind\Style\Color
|
||||
{
|
||||
$red = 0;
|
||||
$green = 0;
|
||||
$blue = 0;
|
||||
|
||||
$data = json_decode(file_get_contents(__DIR__ . '/../Data/colors.json'), true);
|
||||
|
||||
if($style == "black") {
|
||||
return new \PHPNative\Tailwind\Style\Color(0, 0, 0);
|
||||
}
|
||||
if($style == "white") {
|
||||
return new \PHPNative\Tailwind\Style\Color(255, 255, 255);
|
||||
}
|
||||
|
||||
preg_match_all('/(\w{1,8})/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$color = (string)$output_array[1][0];
|
||||
[$red, $green, $blue] = sscanf($data[$color]['500'], "#%02x%02x%02x");
|
||||
}
|
||||
|
||||
preg_match_all('/(\w{1,8})-(\d{1,3})/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$color = (string)$output_array[1][0];
|
||||
$variant = (string)$output_array[2][0];
|
||||
[$red, $green, $blue] = sscanf($data[$color][$variant], "#%02x%02x%02x");
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new \PHPNative\Tailwind\Style\Color($red, $green, $blue);
|
||||
}
|
||||
}
|
||||
48
src/Tailwind/Parser/Flex.php
Normal file
48
src/Tailwind/Parser/Flex.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
use PHPNative\Tailwind\Style\DirectionEnum;
|
||||
use PHPNative\Tailwind\Style\FlexTypeEnum;
|
||||
|
||||
class Flex implements Parser
|
||||
{
|
||||
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\Style
|
||||
{
|
||||
preg_match_all('/flex-none/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::none);
|
||||
}
|
||||
preg_match_all('/flex-1/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::one);
|
||||
}
|
||||
preg_match_all('/flex-auto/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
return new \PHPNative\Tailwind\Style\Flex(type:FlexTypeEnum::auto);
|
||||
}
|
||||
preg_match_all('/flex-col/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
return new \PHPNative\Tailwind\Style\Flex(DirectionEnum::column);
|
||||
}
|
||||
|
||||
preg_match_all('/(?!flex-col)(flex-row|flex)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
return new \PHPNative\Tailwind\Style\Flex(DirectionEnum::row);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public static function merge(\PHPNative\Tailwind\Style\Flex $class, \PHPNative\Tailwind\Style\Flex $style)
|
||||
{
|
||||
if($style->type != FlexTypeEnum::none) {
|
||||
$class->type = $style->type;
|
||||
}
|
||||
if($style->direction != DirectionEnum::row) {
|
||||
$class->direction = $style->direction;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/Tailwind/Parser/Height.php
Normal file
60
src/Tailwind/Parser/Height.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
use PHPNative\Tailwind\Style\Unit;
|
||||
|
||||
class Height implements Parser
|
||||
{
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\Height
|
||||
{
|
||||
$value = -1;
|
||||
$unit = Unit::Pixel;
|
||||
$found = false;
|
||||
preg_match_all('/h-(\d*)\/(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$value1 = (int)$output_array[1][0];
|
||||
$value2 = (int)$output_array[2][0];
|
||||
$unit = Unit::Percent;
|
||||
$value = 100/$value2*$value1;
|
||||
$found = true;
|
||||
}
|
||||
|
||||
preg_match_all('/h-(\d*)/', $style, $output_array);
|
||||
if (!$found && count($output_array[0]) > 0) {
|
||||
$value = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/(h-full|h-screen)/', $style, $output_array);
|
||||
if (!$found && count($output_array[0]) > 0) {
|
||||
$value = 100;
|
||||
$unit = Unit::Percent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if($value != -1) {
|
||||
return new \PHPNative\Tailwind\Style\Height($unit, $value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function merge(\PHPNative\Tailwind\Style\Padding $class, \PHPNative\Tailwind\Style\Padding $style)
|
||||
{
|
||||
if($style->left != null) {
|
||||
$class->left = $style->left;
|
||||
}
|
||||
if($style->right != null) {
|
||||
$class->right = $style->right;
|
||||
}
|
||||
if($style->top != null) {
|
||||
$class->top = $style->top;
|
||||
}
|
||||
if($style->bottom != null) {
|
||||
$class->bottom = $style->bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/Tailwind/Parser/Margin.php
Normal file
78
src/Tailwind/Parser/Margin.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
class Margin implements Parser
|
||||
{
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\Margin
|
||||
{
|
||||
$l = null;
|
||||
$r = null;
|
||||
$t = null;
|
||||
$b = null;
|
||||
|
||||
preg_match_all('/m-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$l = (int)$output_array[1][0];
|
||||
$r = (int)$output_array[1][0];
|
||||
$t = (int)$output_array[1][0];
|
||||
$b = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/mx-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$l = (int)$output_array[1][0];
|
||||
$r = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/my-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$t = (int)$output_array[1][0];
|
||||
$b = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/mt-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$t = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/mb-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$b = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/ml-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$l = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/mr-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$r = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
if($l != null || $r != null || $t != null || $b != null) {
|
||||
return new \PHPNative\Tailwind\Style\Margin($l, $r, $t, $b);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function merge(\PHPNative\Tailwind\Style\Margin $class, \PHPNative\Tailwind\Style\Margin $style)
|
||||
{
|
||||
if($style->left != null) {
|
||||
$class->left = $style->left;
|
||||
}
|
||||
if($style->right != null) {
|
||||
$class->right = $style->right;
|
||||
}
|
||||
if($style->top != null) {
|
||||
$class->top = $style->top;
|
||||
}
|
||||
if($style->bottom != null) {
|
||||
$class->bottom = $style->bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/Tailwind/Parser/MediaQuery.php
Normal file
18
src/Tailwind/Parser/MediaQuery.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
class MediaQuery
|
||||
{
|
||||
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\MediaQueryEnum
|
||||
{
|
||||
preg_match_all("/^(sm|md|lg|xl|2xl)\:(.*)/", $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$query = strtolower(strrev($output_array[1][0]));
|
||||
return \PHPNative\Tailwind\Style\MediaQueryEnum::{$query};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
39
src/Tailwind/Parser/Overflow.php
Normal file
39
src/Tailwind/Parser/Overflow.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
use PHPNative\Tailwind\Style\OverflowEnum;
|
||||
|
||||
class Overflow
|
||||
{
|
||||
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\Overflow
|
||||
{
|
||||
preg_match_all("/overflow-auto/", $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
return new \PHPNative\Tailwind\Style\Overflow(x: OverflowEnum::auto, y: OverflowEnum::auto);
|
||||
}
|
||||
|
||||
preg_match_all("/overflow-x-(auto|scroll|hidden|clip)/", $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$value = match($output_array[1][0]) {
|
||||
'auto' => OverflowEnum::auto,
|
||||
'scroll' => OverflowEnum::scroll,
|
||||
'hidden' => OverflowEnum::hidden,
|
||||
'clip' => OverflowEnum::clip
|
||||
};
|
||||
return new \PHPNative\Tailwind\Style\Overflow(x: $value);
|
||||
}
|
||||
preg_match_all("/overflow-y-(auto|scroll|hidden|clip)/", $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$value = match($output_array[1][0]) {
|
||||
'auto' => OverflowEnum::auto,
|
||||
'scroll' => OverflowEnum::scroll,
|
||||
'hidden' => OverflowEnum::hidden,
|
||||
'clip' => OverflowEnum::clip
|
||||
};
|
||||
return new \PHPNative\Tailwind\Style\Overflow(y: $value);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
78
src/Tailwind/Parser/Padding.php
Normal file
78
src/Tailwind/Parser/Padding.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
class Padding implements Parser
|
||||
{
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\Padding
|
||||
{
|
||||
$l = null;
|
||||
$r = null;
|
||||
$t = null;
|
||||
$b = null;
|
||||
|
||||
preg_match_all('/p-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$l = (int)$output_array[1][0];
|
||||
$r = (int)$output_array[1][0];
|
||||
$t = (int)$output_array[1][0];
|
||||
$b = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/px-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$l = (int)$output_array[1][0];
|
||||
$r = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/py-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$t = (int)$output_array[1][0];
|
||||
$b = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/pt-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$t = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/pb-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$b = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/pl-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$l = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/pr-(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$r = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
if($l != null || $r != null || $t != null || $b != null) {
|
||||
return new \PHPNative\Tailwind\Style\Padding($l, $r, $t, $b);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function merge(\PHPNative\Tailwind\Style\Padding $class, \PHPNative\Tailwind\Style\Padding $style)
|
||||
{
|
||||
if($style->left != null) {
|
||||
$class->left = $style->left;
|
||||
}
|
||||
if($style->right != null) {
|
||||
$class->right = $style->right;
|
||||
}
|
||||
if($style->top != null) {
|
||||
$class->top = $style->top;
|
||||
}
|
||||
if($style->bottom != null) {
|
||||
$class->bottom = $style->bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/Tailwind/Parser/Parser.php
Normal file
12
src/Tailwind/Parser/Parser.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
use PHPNative\Tailwind\Style\Style;
|
||||
|
||||
interface Parser
|
||||
{
|
||||
public static function parse(string $style): ?Style;
|
||||
}
|
||||
18
src/Tailwind/Parser/State.php
Normal file
18
src/Tailwind/Parser/State.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
class State
|
||||
{
|
||||
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\StateEnum
|
||||
{
|
||||
preg_match_all("/(hover|focus|active)\:(.*)/", $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$query = strtolower($output_array[1][0]);
|
||||
return \PHPNative\Tailwind\Style\StateEnum::{$query};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
75
src/Tailwind/Parser/Text.php
Normal file
75
src/Tailwind/Parser/Text.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
use PHPNative\Tailwind\Style\AlignEnum;
|
||||
|
||||
class Text implements Parser
|
||||
{
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\Text
|
||||
{
|
||||
$color = new \PHPNative\Tailwind\Style\Color();
|
||||
|
||||
preg_match_all('/text-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$size = match ((string)$output_array[1][0]) {
|
||||
'xs' => 12,
|
||||
'sm' => 14,
|
||||
'base' => 16,
|
||||
'lg' => 18,
|
||||
'xl' => 20,
|
||||
'2xl' => 24,
|
||||
'3xl' => 30,
|
||||
'4xl' => 36,
|
||||
'5xl' => 48,
|
||||
'6xl' => 60,
|
||||
'7xl' => 72,
|
||||
'8xl' => 96,
|
||||
'9xl' => 128,
|
||||
};
|
||||
return new \PHPNative\Tailwind\Style\Text(size: $size);
|
||||
}
|
||||
|
||||
preg_match_all('/text-(center|right|left)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
return match ((string)$output_array[1][0]) {
|
||||
'left' => new \PHPNative\Tailwind\Style\Text(align: AlignEnum::left),
|
||||
'right' => new \PHPNative\Tailwind\Style\Text(align: AlignEnum::right),
|
||||
'center' => new \PHPNative\Tailwind\Style\Text(align: AlignEnum::center),
|
||||
};
|
||||
}
|
||||
|
||||
preg_match_all('/text-(.*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$colorStyle = $output_array[1][0];
|
||||
$color = Color::parse($colorStyle);
|
||||
return new \PHPNative\Tailwind\Style\Text($color);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function merge(\PHPNative\Tailwind\Style\Text $style1, \PHPNative\Tailwind\Style\Text $style2): void
|
||||
{
|
||||
if($style2->color->red != -1) {
|
||||
$style1->color->red = $style2->color->red;
|
||||
}
|
||||
if($style2->color->green != -1) {
|
||||
$style1->color->green = $style2->color->green;
|
||||
}
|
||||
if($style2->color->blue != -1) {
|
||||
$style1->color->blue = $style2->color->blue;
|
||||
}
|
||||
if($style2->color->alpha != -1) {
|
||||
$style1->color->alpha = $style2->color->alpha;
|
||||
}
|
||||
if($style2->size != 16) {
|
||||
$style1->size = $style2->size;
|
||||
}
|
||||
if($style2->align != AlignEnum::left) {
|
||||
$style1->align = $style2->align;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
src/Tailwind/Parser/Width.php
Normal file
58
src/Tailwind/Parser/Width.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Parser;
|
||||
|
||||
use PHPNative\Tailwind\Style\Unit;
|
||||
|
||||
class Width implements Parser
|
||||
{
|
||||
public static function parse(string $style): ?\PHPNative\Tailwind\Style\Width
|
||||
{
|
||||
$value = -1;
|
||||
$unit = Unit::Pixel;
|
||||
$found = false;
|
||||
preg_match_all('/w-(\d*)\/(\d*)/', $style, $output_array);
|
||||
if (count($output_array[0]) > 0) {
|
||||
$value1 = (int)$output_array[1][0];
|
||||
$value2 = (int)$output_array[2][0];
|
||||
$unit = Unit::Percent;
|
||||
$value = 100/$value2*$value1;
|
||||
$found = true;
|
||||
}
|
||||
|
||||
preg_match_all('/w-(\d*)/', $style, $output_array);
|
||||
if (!$found && count($output_array[0]) > 0) {
|
||||
$value = (int)$output_array[1][0];
|
||||
}
|
||||
|
||||
preg_match_all('/(w-screen|w-full)/', $style, $output_array);
|
||||
if (!$found && count($output_array[0]) > 0) {
|
||||
$value = 100;
|
||||
$unit = Unit::Percent;
|
||||
}
|
||||
|
||||
if($value != -1) {
|
||||
return new \PHPNative\Tailwind\Style\Width($unit, $value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function merge(\PHPNative\Tailwind\Style\Padding $class, \PHPNative\Tailwind\Style\Padding $style)
|
||||
{
|
||||
if($style->left != null) {
|
||||
$class->left = $style->left;
|
||||
}
|
||||
if($style->right != null) {
|
||||
$class->right = $style->right;
|
||||
}
|
||||
if($style->top != null) {
|
||||
$class->top = $style->top;
|
||||
}
|
||||
if($style->bottom != null) {
|
||||
$class->bottom = $style->bottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/Tailwind/Style/AlignEnum.php
Normal file
10
src/Tailwind/Style/AlignEnum.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
enum AlignEnum
|
||||
{
|
||||
case left;
|
||||
case center;
|
||||
case right;
|
||||
}
|
||||
12
src/Tailwind/Style/Background.php
Normal file
12
src/Tailwind/Style/Background.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Background implements Style
|
||||
{
|
||||
public function __construct(public Color $color = new Color())
|
||||
{
|
||||
}
|
||||
}
|
||||
12
src/Tailwind/Style/Basis.php
Normal file
12
src/Tailwind/Style/Basis.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Basis implements Style
|
||||
{
|
||||
public function __construct(public Unit $unit = Unit::Pixel, public int $value = 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
21
src/Tailwind/Style/Border.php
Normal file
21
src/Tailwind/Style/Border.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Border implements Style
|
||||
{
|
||||
public function __construct(
|
||||
public bool $enabled = false,
|
||||
public Color $color = new Color(),
|
||||
public null|int $left = null,
|
||||
public null|int $right = null,
|
||||
public null|int $top = null,
|
||||
public null|int $bottom = null,
|
||||
public null|int $roundTopLeft = null,
|
||||
public null|int $roundTopRight = null,
|
||||
public null|int $roundBottomLeft = null,
|
||||
public null|int $roundBottomRight = null,
|
||||
) {}
|
||||
}
|
||||
12
src/Tailwind/Style/Color.php
Normal file
12
src/Tailwind/Style/Color.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Color implements Style
|
||||
{
|
||||
public function __construct(public int $red = -1, public int $green = -1, public int $blue = -1, public int $alpha = 255)
|
||||
{
|
||||
}
|
||||
}
|
||||
9
src/Tailwind/Style/DirectionEnum.php
Normal file
9
src/Tailwind/Style/DirectionEnum.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
enum DirectionEnum
|
||||
{
|
||||
case row;
|
||||
case column;
|
||||
}
|
||||
12
src/Tailwind/Style/Flex.php
Normal file
12
src/Tailwind/Style/Flex.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Flex implements Style
|
||||
{
|
||||
public function __construct(public DirectionEnum $direction = DirectionEnum::row, public FlexTypeEnum $type = FlexTypeEnum::none)
|
||||
{
|
||||
}
|
||||
}
|
||||
12
src/Tailwind/Style/FlexTypeEnum.php
Normal file
12
src/Tailwind/Style/FlexTypeEnum.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
enum FlexTypeEnum
|
||||
{
|
||||
case none;
|
||||
case auto;
|
||||
case initial;
|
||||
case one;
|
||||
}
|
||||
12
src/Tailwind/Style/Height.php
Normal file
12
src/Tailwind/Style/Height.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Height implements Style
|
||||
{
|
||||
public function __construct(public Unit $unit = Unit::Pixel, public int $value = 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
12
src/Tailwind/Style/Margin.php
Normal file
12
src/Tailwind/Style/Margin.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Margin implements Style
|
||||
{
|
||||
public function __construct(public int|null $left = null, public int|null $right = null, public int|null $top = null, public int|null $bottom = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
10
src/Tailwind/Style/MediaQuery.php
Normal file
10
src/Tailwind/Style/MediaQuery.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class MediaQuery
|
||||
{
|
||||
public function __construct(public MediaQueryEnum $mediaQuery, public string $restStyle)
|
||||
{
|
||||
}
|
||||
}
|
||||
33
src/Tailwind/Style/MediaQueryEnum.php
Normal file
33
src/Tailwind/Style/MediaQueryEnum.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
enum MediaQueryEnum: int
|
||||
{
|
||||
case normal = 0;
|
||||
case ms = 640;
|
||||
case dm = 768;
|
||||
case gl = 1024;
|
||||
case lx = 1280;
|
||||
case lx2 = 1536;
|
||||
|
||||
public static function getFromPixel(int $windowWidth)
|
||||
{
|
||||
if($windowWidth > self::lx2->value) {
|
||||
return self::lx2;
|
||||
}
|
||||
if($windowWidth > self::lx->value) {
|
||||
return self::lx;
|
||||
}
|
||||
if($windowWidth > self::gl->value) {
|
||||
return self::gl;
|
||||
}
|
||||
if($windowWidth > self::dm->value) {
|
||||
return self::dm;
|
||||
}
|
||||
if($windowWidth > self::ms->value) {
|
||||
return self::ms;
|
||||
}
|
||||
return self::normal;
|
||||
}
|
||||
}
|
||||
14
src/Tailwind/Style/Overflow.php
Normal file
14
src/Tailwind/Style/Overflow.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
use PHPNative\Tailwind\Style\OverflowEnum;
|
||||
|
||||
class Overflow implements Style
|
||||
{
|
||||
public function __construct(public OverflowEnum $x = OverflowEnum::hidden, public OverflowEnum $y = OverflowEnum::hidden)
|
||||
{
|
||||
}
|
||||
}
|
||||
11
src/Tailwind/Style/OverflowEnum.php
Normal file
11
src/Tailwind/Style/OverflowEnum.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
enum OverflowEnum
|
||||
{
|
||||
case hidden;
|
||||
case clip;
|
||||
case scroll;
|
||||
case auto;
|
||||
}
|
||||
12
src/Tailwind/Style/Padding.php
Normal file
12
src/Tailwind/Style/Padding.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Padding implements Style
|
||||
{
|
||||
public function __construct(public int|null $left = null, public int|null $right = null, public int|null $top = null, public int|null $bottom = null)
|
||||
{
|
||||
}
|
||||
}
|
||||
10
src/Tailwind/Style/State.php
Normal file
10
src/Tailwind/Style/State.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class State
|
||||
{
|
||||
public function __construct(public StateEnum $state, public string $restStyle)
|
||||
{
|
||||
}
|
||||
}
|
||||
12
src/Tailwind/Style/StateEnum.php
Normal file
12
src/Tailwind/Style/StateEnum.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
enum StateEnum
|
||||
{
|
||||
case normal;
|
||||
case hover;
|
||||
case hoverfocus;
|
||||
case focus;
|
||||
case active;
|
||||
}
|
||||
9
src/Tailwind/Style/Style.php
Normal file
9
src/Tailwind/Style/Style.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
interface Style
|
||||
{
|
||||
}
|
||||
12
src/Tailwind/Style/Text.php
Normal file
12
src/Tailwind/Style/Text.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Text implements Style
|
||||
{
|
||||
public function __construct(public Color $color = new Color(), public AlignEnum $align = AlignEnum::left, public int $size = 16)
|
||||
{
|
||||
}
|
||||
}
|
||||
11
src/Tailwind/Style/Unit.php
Normal file
11
src/Tailwind/Style/Unit.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
enum Unit
|
||||
{
|
||||
case Pixel;
|
||||
case Point;
|
||||
case Percent;
|
||||
}
|
||||
12
src/Tailwind/Style/Width.php
Normal file
12
src/Tailwind/Style/Width.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind\Style;
|
||||
|
||||
class Width implements Style
|
||||
{
|
||||
public function __construct(public Unit $unit = Unit::Pixel, public int $value = 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
74
src/Tailwind/StyleParser.php
Normal file
74
src/Tailwind/StyleParser.php
Normal file
@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Tailwind;
|
||||
|
||||
use PHPNative\Tailwind\Model\Size;
|
||||
use PHPNative\Tailwind\Model\StyleCollection;
|
||||
use PHPNative\Tailwind\Style\Style;
|
||||
|
||||
class StyleParser
|
||||
{
|
||||
public static function parse($style): StyleCollection
|
||||
{
|
||||
$computed = new StyleCollection();
|
||||
if($style === null || strlen(trim($style)) === 0) {
|
||||
return $computed;
|
||||
}
|
||||
$styles = explode(" ", $style);
|
||||
|
||||
foreach($styles as $styleStr) {
|
||||
$styleStr = trim($styleStr);
|
||||
|
||||
$style = self::parseSimpleStyle($styleStr);
|
||||
$s = new \PHPNative\Tailwind\Model\Style($style);
|
||||
$mq = \PHPNative\Tailwind\Parser\MediaQuery::parse($styleStr);
|
||||
if($mq) {
|
||||
$s->mediaQuery = $mq;
|
||||
}
|
||||
$state = \PHPNative\Tailwind\Parser\State::parse($styleStr);
|
||||
if($state) {
|
||||
$s->state = $state;
|
||||
}
|
||||
$computed->add($s);
|
||||
}
|
||||
return $computed;
|
||||
}
|
||||
|
||||
private static function parseSimpleStyle(string $style): ?Style
|
||||
{
|
||||
if($pd = \PHPNative\Tailwind\Parser\Padding::parse($style)) {
|
||||
return $pd;
|
||||
}
|
||||
if($m = \PHPNative\Tailwind\Parser\Margin::parse($style)) {
|
||||
return $m;
|
||||
}
|
||||
if($o = \PHPNative\Tailwind\Parser\Overflow::parse($style)) {
|
||||
return $o;
|
||||
}
|
||||
if($w = \PHPNative\Tailwind\Parser\Width::parse($style)) {
|
||||
return $w;
|
||||
}
|
||||
if($h = \PHPNative\Tailwind\Parser\Height::parse($style)) {
|
||||
return $h;
|
||||
}
|
||||
if($b = \PHPNative\Tailwind\Parser\Basis::parse($style)) {
|
||||
return $b;
|
||||
}
|
||||
if($f = \PHPNative\Tailwind\Parser\Flex::parse($style)) {
|
||||
return $f;
|
||||
}
|
||||
if($bg = \PHPNative\Tailwind\Parser\Background::parse($style)) {
|
||||
return $bg;
|
||||
}
|
||||
if($t = \PHPNative\Tailwind\Parser\Text::parse($style)) {
|
||||
return $t;
|
||||
}
|
||||
if($b = \PHPNative\Tailwind\Parser\Border::parse($style)) {
|
||||
return $b;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
134
src/Ui/Component.php
Normal file
134
src/Ui/Component.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Ui;
|
||||
|
||||
use PHPNative\Framework\TextRenderer;
|
||||
use PHPNative\Tailwind\Style\Margin;
|
||||
use PHPNative\Tailwind\Style\MediaQueryEnum;
|
||||
use PHPNative\Tailwind\Style\Padding;
|
||||
use PHPNative\Tailwind\Style\StateEnum;
|
||||
use PHPNative\Tailwind\StyleParser;
|
||||
|
||||
abstract class Component
|
||||
{
|
||||
protected $children = [];
|
||||
|
||||
protected $window;
|
||||
|
||||
protected string $styles = '';
|
||||
|
||||
protected bool $visible = true;
|
||||
|
||||
protected Viewport $viewport;
|
||||
|
||||
protected array $computedStyles = [];
|
||||
private Viewport $contentViewport;
|
||||
|
||||
public function setViewport(Viewport $viewport): void
|
||||
{
|
||||
$this->viewport = $viewport;
|
||||
}
|
||||
|
||||
public function setWindow($window): void
|
||||
{
|
||||
$this->window = $window;
|
||||
}
|
||||
|
||||
public function update(): void
|
||||
{
|
||||
foreach ($this->children as $child) {
|
||||
$child->update();
|
||||
}
|
||||
}
|
||||
|
||||
public function layout(null|TextRenderer $textRenderer = null): void
|
||||
{
|
||||
$this->computedStyles = StyleParser::parse($this->style)->getValidStyles(
|
||||
MediaQueryEnum::normal,
|
||||
StateEnum::normal,
|
||||
);
|
||||
|
||||
if (isset($this->computedStyles[Margin::class]) && ($m = $this->computedStyles[Margin::class])) {
|
||||
$this->viewport->x += $m->left;
|
||||
$this->viewport->width -= $m->right + $m->left;
|
||||
$this->viewport->y += $m->top;
|
||||
$this->viewport->height -= $m->bottom + $m->top;
|
||||
}
|
||||
|
||||
$this->contentViewport = clone $this->viewport;
|
||||
|
||||
if (isset($this->computedStyles[Padding::class]) && ($p = $this->computedStyles[Padding::class])) {
|
||||
$this->contentViewport->x += $p->left;
|
||||
$this->contentViewport->width -= $p->right + $p->left;
|
||||
$this->contentViewport->y += $p->top;
|
||||
$this->contentViewport->height -= $p->bottom + $p->top;
|
||||
}
|
||||
|
||||
foreach ($this->children as $child) {
|
||||
$child->setViewport($this->contentViewport);
|
||||
$child->layout($textRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
public function render(null|TextRenderer $textRenderer = null): void
|
||||
{
|
||||
if (!$this->visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isset($this->computedStyles[\PHPNative\Tailwind\Style\Background::class]) &&
|
||||
($bg = $this->computedStyles[\PHPNative\Tailwind\Style\Background::class])
|
||||
) {
|
||||
rsgl_setColor($this->window, $bg->color->red, $bg->color->green, $bg->color->blue, $bg->color->alpha);
|
||||
|
||||
if (
|
||||
isset($this->computedStyles[\PHPNative\Tailwind\Style\Border::class]) &&
|
||||
($border = $this->computedStyles[\PHPNative\Tailwind\Style\Border::class])
|
||||
) {
|
||||
rsgl_drawRoundRectExF(
|
||||
$this->window,
|
||||
$this->viewport->x,
|
||||
$this->viewport->y,
|
||||
$this->viewport->width,
|
||||
$this->viewport->height,
|
||||
$border->roundTopLeft ?? 0,
|
||||
$border->roundTopLeft ?? 0,
|
||||
$border->roundTopRight ?? 0,
|
||||
$border->roundTopRight ?? 0,
|
||||
$border->roundBottomLeft ?? 0,
|
||||
$border->roundBottomLeft ?? 0,
|
||||
$border->roundBottomRight ?? 0,
|
||||
$border->roundBottomRight ?? 0,
|
||||
);
|
||||
} else {
|
||||
rsgl_drawRectF(
|
||||
$this->window,
|
||||
$this->viewport->x,
|
||||
$this->viewport->y,
|
||||
$this->viewport->width,
|
||||
$this->viewport->height,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function renderContent(null|TextRenderer $textRenderer = null): void
|
||||
{
|
||||
if (!$this->visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Render children
|
||||
foreach ($this->children as $child) {
|
||||
$child->setWindow($this->window);
|
||||
$child->render($textRenderer);
|
||||
$child->renderContent($textRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
public function addComponent(Component $component): void
|
||||
{
|
||||
$this->children[] = $component;
|
||||
}
|
||||
}
|
||||
22
src/Ui/Viewport.php
Normal file
22
src/Ui/Viewport.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PHPNative\Ui;
|
||||
|
||||
use PHPNative\Tailwind\Style\MediaQueryEnum;
|
||||
|
||||
class Viewport
|
||||
{
|
||||
public function __construct(
|
||||
public int $x = 0,
|
||||
public int $y = 0,
|
||||
public $width = 0,
|
||||
public $height = 0,
|
||||
public $windowWidth = 0,
|
||||
public $windowHeight = 0,
|
||||
public $addX = 0,
|
||||
public $addY = 0,
|
||||
public MediaQueryEnum $windowMediaQuery = MediaQueryEnum::normal,
|
||||
) {}
|
||||
}
|
||||
12
src/Ui/Widget/Container.php
Normal file
12
src/Ui/Widget/Container.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Ui\Widget;
|
||||
|
||||
use PHPNative\Ui\Component;
|
||||
|
||||
class Container extends Component
|
||||
{
|
||||
public function __construct(
|
||||
public string $style = '',
|
||||
) {}
|
||||
}
|
||||
7
src/Ui/Window.php
Normal file
7
src/Ui/Window.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace PHPNative\Ui;
|
||||
|
||||
class Window
|
||||
{
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user