211 lines
6.0 KiB
PHP
211 lines
6.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Termwind\Html;
|
|
|
|
use Termwind\Components\Element;
|
|
use Termwind\Termwind;
|
|
use Termwind\ValueObjects\Styles;
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
final class InheritStyles
|
|
{
|
|
/**
|
|
* Applies styles from parent element to child elements.
|
|
*
|
|
* @param array<int, Element|string> $elements
|
|
* @return array<int, Element|string>
|
|
*/
|
|
public function __invoke(array $elements, Styles $styles): array
|
|
{
|
|
$elements = array_values($elements);
|
|
|
|
foreach ($elements as &$element) {
|
|
if (is_string($element)) {
|
|
$element = Termwind::raw($element);
|
|
}
|
|
|
|
$element->inheritFromStyles($styles);
|
|
}
|
|
|
|
/** @var Element[] $elements */
|
|
if (($styles->getProperties()['styles']['display'] ?? 'inline') === 'flex') {
|
|
$elements = $this->applyFlex($elements);
|
|
}
|
|
|
|
return match ($styles->getProperties()['styles']['justifyContent'] ?? false) {
|
|
'between' => $this->applyJustifyBetween($elements),
|
|
'evenly' => $this->applyJustifyEvenly($elements),
|
|
'around' => $this->applyJustifyAround($elements),
|
|
'center' => $this->applyJustifyCenter($elements),
|
|
default => $elements,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Applies flex-1 to child elements with the class.
|
|
*
|
|
* @param array<int, Element> $elements
|
|
* @return array<int, Element>
|
|
*/
|
|
private function applyFlex(array $elements): array
|
|
{
|
|
[$totalWidth, $parentWidth] = $this->getWidthFromElements($elements);
|
|
|
|
$width = max(0, array_reduce($elements, function ($carry, $element) {
|
|
return $carry += $element->hasStyle('flex-1') ? $element->getInnerWidth() : 0;
|
|
}, $parentWidth - $totalWidth));
|
|
|
|
$flexed = array_values(array_filter(
|
|
$elements, fn ($element) => $element->hasStyle('flex-1')
|
|
));
|
|
|
|
foreach ($flexed as $index => &$element) {
|
|
if ($width === 0 && ! ($element->getProperties()['styles']['contentRepeat'] ?? false)) {
|
|
continue;
|
|
}
|
|
|
|
$float = $width / count($flexed);
|
|
$elementWidth = floor($float);
|
|
|
|
if ($index === count($flexed) - 1) {
|
|
$elementWidth += ($float - floor($float)) * count($flexed);
|
|
}
|
|
|
|
$element->addStyle("w-{$elementWidth}");
|
|
}
|
|
|
|
return $elements;
|
|
}
|
|
|
|
/**
|
|
* Applies the space between the elements.
|
|
*
|
|
* @param array<int, Element> $elements
|
|
* @return array<int, Element|string>
|
|
*/
|
|
private function applyJustifyBetween(array $elements): array
|
|
{
|
|
[$totalWidth, $parentWidth] = $this->getWidthFromElements($elements);
|
|
$space = ($parentWidth - $totalWidth) / (count($elements) - 1);
|
|
|
|
if ($space < 1) {
|
|
return $elements;
|
|
}
|
|
|
|
$arr = [];
|
|
|
|
foreach ($elements as $index => &$element) {
|
|
if ($index !== 0) {
|
|
// Since there is no float pixel, on the last one it should round up...
|
|
$length = $index === count($elements) - 1 ? ceil($space) : floor($space);
|
|
$arr[] = str_repeat(' ', (int) $length);
|
|
}
|
|
|
|
$arr[] = $element;
|
|
}
|
|
|
|
return $arr;
|
|
}
|
|
|
|
/**
|
|
* Applies the space between and around the elements.
|
|
*
|
|
* @param array<int, Element> $elements
|
|
* @return array<int, Element|string>
|
|
*/
|
|
private function applyJustifyEvenly(array $elements): array
|
|
{
|
|
[$totalWidth, $parentWidth] = $this->getWidthFromElements($elements);
|
|
$space = ($parentWidth - $totalWidth) / (count($elements) + 1);
|
|
|
|
if ($space < 1) {
|
|
return $elements;
|
|
}
|
|
|
|
$arr = [];
|
|
foreach ($elements as &$element) {
|
|
$arr[] = str_repeat(' ', (int) floor($space));
|
|
$arr[] = $element;
|
|
}
|
|
|
|
$decimals = ceil(($space - floor($space)) * (count($elements) + 1));
|
|
$arr[] = str_repeat(' ', (int) (floor($space) + $decimals));
|
|
|
|
return $arr;
|
|
}
|
|
|
|
/**
|
|
* Applies the space around the elements.
|
|
*
|
|
* @param array<int, Element> $elements
|
|
* @return array<int, Element|string>
|
|
*/
|
|
private function applyJustifyAround(array $elements): array
|
|
{
|
|
[$totalWidth, $parentWidth] = $this->getWidthFromElements($elements);
|
|
$space = ($parentWidth - $totalWidth) / count($elements);
|
|
|
|
if ($space < 1) {
|
|
return $elements;
|
|
}
|
|
|
|
$contentSize = $totalWidth;
|
|
$arr = [];
|
|
|
|
foreach ($elements as $index => &$element) {
|
|
if ($index !== 0) {
|
|
$arr[] = str_repeat(' ', (int) ceil($space));
|
|
$contentSize += ceil($space);
|
|
}
|
|
|
|
$arr[] = $element;
|
|
}
|
|
|
|
return [
|
|
str_repeat(' ', (int) floor(($parentWidth - $contentSize) / 2)),
|
|
...$arr,
|
|
str_repeat(' ', (int) ceil(($parentWidth - $contentSize) / 2)),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Applies the space on before first element and after last element.
|
|
*
|
|
* @param array<int, Element> $elements
|
|
* @return array<int, Element|string>
|
|
*/
|
|
private function applyJustifyCenter(array $elements): array
|
|
{
|
|
[$totalWidth, $parentWidth] = $this->getWidthFromElements($elements);
|
|
$space = $parentWidth - $totalWidth;
|
|
|
|
if ($space < 1) {
|
|
return $elements;
|
|
}
|
|
|
|
return [
|
|
str_repeat(' ', (int) floor($space / 2)),
|
|
...$elements,
|
|
str_repeat(' ', (int) ceil($space / 2)),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Gets the total width for the elements and their parent width.
|
|
*
|
|
* @param array<int, Element> $elements
|
|
* @return int[]
|
|
*/
|
|
private function getWidthFromElements(array $elements)
|
|
{
|
|
$totalWidth = (int) array_reduce($elements, fn ($carry, $element) => $carry += $element->getLength(), 0);
|
|
$parentWidth = Styles::getParentWidth($elements[0]->getProperties()['parentStyles'] ?? []);
|
|
|
|
return [$totalWidth, $parentWidth];
|
|
}
|
|
}
|