1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
|
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Extra\Html;
/**
* Class Variant Authority (CVA) resolver.
*
* @author Mathéo Daninos <matheo.daninos@gmail.com>
*/
final class Cva
{
/**
* @var list<string|null>
*/
private array $base;
/**
* @param string|list<string|null> $base The base classes to apply to the component
*/
public function __construct(
string|array $base = [],
/**
* The variants to apply based on recipes.
*
* Format: [variantCategory => [variantName => classes]]
*
* Example:
* 'colors' => [
* 'primary' => 'bleu-8000',
* 'danger' => 'red-800 text-bold',
* ],
* 'size' => [...],
*
* @var array<string, array<string, string|list<string>>>
*/
private array $variants = [],
/**
* The compound variants to apply based on recipes.
*
* Format: [variantsCategory => ['variantName', 'variantName'], class: classes]
*
* Example:
* [
* 'colors' => ['primary'],
* 'size' => ['small'],
* 'class' => 'text-red-500',
* ],
* [
* 'size' => ['large'],
* 'class' => 'font-weight-500',
* ]
*
* @var array<array<string, string|array<string>>>
*/
private array $compoundVariants = [],
/**
* The default variants to apply if specific recipes aren't provided.
*
* Format: [variantCategory => variantName]
*
* Example:
* 'colors' => 'primary',
*
* @var array<string, string>
*/
private array $defaultVariants = [],
) {
$this->base = (array) $base;
}
public function apply(array $recipes, ?string ...$additionalClasses): string
{
$classes = $this->base;
// Resolve recipes against variants
foreach ($recipes as $recipeName => $recipeValue) {
if (\is_bool($recipeValue)) {
$recipeValue = $recipeValue ? 'true' : 'false';
}
$recipeClasses = $this->variants[$recipeName][$recipeValue] ?? [];
$classes = [...$classes, ...(array) $recipeClasses];
}
// Resolve compound variants
foreach ($this->compoundVariants as $compound) {
$compoundClasses = $this->resolveCompoundVariant($compound, $recipes) ?? [];
$classes = [...$classes, ...$compoundClasses];
}
// Apply default variants if specific recipes aren't provided
foreach ($this->defaultVariants as $defaultVariantName => $defaultVariantValue) {
if (!isset($recipes[$defaultVariantName])) {
$variantClasses = $this->variants[$defaultVariantName][$defaultVariantValue] ?? [];
$classes = [...$classes, ...(array) $variantClasses];
}
}
$classes = [...$classes, ...array_values($additionalClasses)];
$classes = implode(' ', array_filter($classes, 'is_string'));
$classes = preg_split('#\s+#', $classes, -1, \PREG_SPLIT_NO_EMPTY) ?: [];
return implode(' ', array_unique($classes));
}
private function resolveCompoundVariant(array $compound, array $recipes): array
{
foreach ($compound as $compoundName => $compoundValues) {
if ('class' === $compoundName) {
continue;
}
if (!isset($recipes[$compoundName]) || !\in_array($recipes[$compoundName], (array) $compoundValues)) {
return [];
}
}
return (array) ($compound['class'] ?? []);
}
}
|