FontAwesomeSvg::getStyleClass()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
/*
4
 * This file is part of ocubom/twig-svg-extension
5
 *
6
 * © Oscar Cubo Medina <https://ocubom.github.io>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Ocubom\Twig\Extension\Svg\Provider\FontAwesome;
13
14
use Ocubom\Twig\Extension\Svg\Exception\ParseException;
15
use Ocubom\Twig\Extension\Svg\Processor\ClassProcessor;
16
use Ocubom\Twig\Extension\Svg\Processor\RemoveAttributesProcessor;
17
use Ocubom\Twig\Extension\Svg\Svg;
18
use Ocubom\Twig\Extension\Svg\Util\DomUtil;
19
use Symfony\Component\OptionsResolver\Options;
20
use Symfony\Component\OptionsResolver\OptionsResolver;
21
22
use function BenTools\IterableFunctions\iterable_to_array;
23
24
class FontAwesomeSvg extends Svg
25
{
26
    /**
27
     * Full path to the icon.
28
     */
29
    protected \SplFileInfo $path;
30
31
    /**
32
     * @param mixed $data The FontAwesome Icon data
33
     */
34 3
    public function __construct($data, iterable $options = null)
35
    {
36
        try {
37
            switch (true) {
38 3
                case $data instanceof FontAwesomeSvg: // "Copy" constructor
39
                    $this->path = $data->path;
40
                    break;
41
42 3
                case $data instanceof \SplFileInfo:
43 3
                    $this->path = $data;
44 3
                    break;
45
46
                default:
47
                    throw new ParseException(sprintf('Unable to create "%s" from "%s"', __CLASS__, get_debug_type($data)));
48
            }
49
50
            /** @var array $options */
51 3
            $options = iterable_to_array($options ?? []);
52
53
            // Construct from path
54 3
            parent::__construct($this->path, array_merge($options, [
55 3
                'class_default' => array_merge($options['class_default'] ?? [], [
56 3
                    FontAwesome::INLINE_CLASS, // Add inlined class
57 3
                    'fa-'.$this->getName(), // Add icon name class
58 3
                ]),
59 3
                'class_block' => array_merge($options['class_block'] ?? [], [
60 3
                    $this->getStyle(), // Block style
61 3
                    $this->getStyleClass(), // Block current classes
62 3
                    $this->getStyleClass('5.0'), // Block pre-6.0 classes
63 3
                ]),
64
                // Add Font Awesome data-*
65 3
                'data-prefix' => $this->getStyleClass('5.0'),
66 3
                'data-icon' => $this->getName(),
67 3
            ]));
68
        } catch (ParseException $exc) {
69
            throw new ParseException(sprintf('Unable to create a FontAwesome Icon from "%s"', get_debug_type($data)), 0, $exc);
70
        }
71
    }
72
73
    /**
74
     * @codeCoverageIgnore
75
     */
76
    public function getFaId(): string
77
    {
78
        return sprintf(
79
            'fa-%s-%s',
80
            $this->getStyle(),
81
            $this->getName(),
82
        );
83
    }
84
85 3
    public function getName(): string
86
    {
87 3
        return $this->path->getBasename('.svg');
88
    }
89
90 3
    public function getStyle(): string
91
    {
92 3
        $path = $this->path->getPathInfo();
93
        assert($path instanceof \SplFileInfo);
94
95 3
        return $path->getBasename();
96
    }
97
98 3
    public function getStyleClass(string $version = '6.0'): string
99
    {
100 3
        return version_compare($version, '6.0', '<')
101 3
            ? 'fa'.$this->getStyle()[0]
102 3
            : 'fa-'.$this->getStyle();
103
    }
104
105 1
    public function getHtmlTag(iterable $options = null): \DOMElement
106
    {
107
        // Create the HTML Tag node
108 1
        $node = DomUtil::createElement(FontAwesome::HTML_TAG);
109
        // Copy options as attributes
110 1
        foreach ($options ?? [] as $key => $val) {
111 1
            if (!empty($val)) {
112 1
                $val = is_iterable($val) ? implode(' ', iterable_to_array($val)) : (string) $val;
113
114 1
                $node->setAttribute($key, $val);
115
            }
116
        }
117
118
        // Process classes
119 1
        $processor = new ClassProcessor();
120 1
        $processor($node, [
121 1
            'class' => [
122 1
                $this->getStyleClass(),
123 1
                'fa-'.$this->getName(),
124 1
            ],
125 1
            'class_block' => [
126 1
                FontAwesome::INLINE_CLASS,
127 1
            ],
128 1
        ]);
129
130 1
        return $node;
131
    }
132
133 3
    protected static function getProcessors(): array
134
    {
135 1
        return array_merge(parent::getProcessors(), [
136
            // Remove non-attributes options
137 1
            [new RemoveAttributesProcessor(
138
                // Options
139 1
                'class_default',
140 1
                'class_block',
141 1
                'fill',
142 1
                'opacity',
143 1
                'primary_fill',
144 1
                'primary_opacity',
145 1
                'secondary_fill',
146 1
                'secondary_opacity',
147
                // Remove special attributes
148 1
                'data-fa-title-id',
149 1
            ), 1000],
150
151
            // Global changes
152 1
            function (\DOMElement $svg, array $options = []): \DOMElement {
153
                // Add FontAwesome fill and opacity values to each path
154
                /** @var \DOMElement $path */
155 3
                foreach ($svg->getElementsByTagName('path') as $path) {
156 3
                    $class = array_intersect(
157 3
                        ['fa-primary', 'fa-secondary'],
158 3
                        preg_split('@\s+@Uis', $path->getAttribute('class'))
159 3
                    );
160 3
                    $class = count($class) > 0
161
                        ? substr($class[0], 3)
162 3
                        : '';
163
164 3
                    foreach (['fill', 'opacity'] as $name) {
165 3
                        $key = $class.'_'.$name;
166 3
                        if (!empty($options[$key])) {
167
                            $path->setAttribute($name, $options[$key]);
168 3
                        } elseif (!empty($options[$name])) {
169 3
                            $path->setAttribute($name, $options[$name]);
170
                        }
171
                    }
172
                }
173
174 3
                return $svg;
175 1
            },
176 1
        ]);
177
    }
178
179 3
    public static function configureOptions(OptionsResolver $resolver = null): OptionsResolver
180
    {
181 3
        $resolver = parent::configureOptions($resolver);
182
183
        /** @psalm-suppress MissingClosureParamType */
184 3
        $normalizeFloat = function (Options $options, $value): ?float {
185 3
            return is_numeric((string) $value) ? floatval((string) $value) : null;
186 3
        };
187
188 3
        $resolver->define('class_default')
189 3
            ->default([])
190 3
            ->allowedTypes('string[]')
191 3
            ->info('Default classes to add unless null');
192
193 3
        $resolver->define('fill')
194 3
            ->default('currentColor')
195 3
            ->allowedTypes('null', 'string')
196 3
            ->info('Default fill color for paths');
197
198 3
        $resolver->define('opacity')
199 3
            ->default(null)
200 3
            ->allowedTypes('null', 'string', 'float')
201 3
            ->normalize($normalizeFloat)
202 3
            ->info('Default opacity color for paths');
203
204 3
        $resolver->define('primary_fill')
205 3
            ->default(null)
206 3
            ->allowedTypes('null', 'string')
207 3
            ->info('Default fill color for primary paths (duotone)');
208
209 3
        $resolver->define('primary_opacity')
210 3
            ->default(null)
211 3
            ->allowedTypes('null', 'string', 'float')
212 3
            ->normalize($normalizeFloat)
213 3
            ->info('Default opacity color for primary paths (duotone)');
214
215 3
        $resolver->define('secondary_fill')
216 3
            ->default(null)
217 3
            ->allowedTypes('null', 'string')
218 3
            ->info('Default fill color for secondary paths (duotone)');
219
220 3
        $resolver->define('secondary_opacity')
221 3
            ->default(null)
222 3
            ->allowedTypes('null', 'string', 'float')
223 3
            ->normalize($normalizeFloat)
224 3
            ->info('Default opacity color for secondary paths (duotone)');
225
226 3
        $resolver->define('data-fa-title-id')
227 3
            ->default(null)
228 3
            ->allowedTypes('null', 'string')
229 3
            ->info('Set the icon title id instead of generate a new one');
230
231
        // Uses data-fa-title-id as aria-labelledby if not defined
232 3
        $resolver->addNormalizer(
233 3
            'aria-labelledby',
234 3
            function (Options $options, ?string $value): ?string {
235 3
                if (empty($value) && !empty($options['data-fa-title-id'])) {
236 1
                    return $options['data-fa-title-id'];
237
                }
238
239 3
                return $value;
240 3
            },
241 3
            true
242 3
        );
243
244 3
        return $resolver;
245
    }
246
}
247