File: Cva.php

package info (click to toggle)
php-twig 3.20.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,940 kB
  • sloc: php: 23,320; makefile: 110; sh: 43
file content (129 lines) | stat: -rw-r--r-- 3,969 bytes parent folder | download
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'] ?? []);
    }
}