Skip to content

Commit 511114b

Browse files
committed
ux icon attributes
1 parent 781f66c commit 511114b

File tree

9 files changed

+308
-53
lines changed

9 files changed

+308
-53
lines changed

src/Icons/config/services.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\UX\Icons\Command\WarmCacheCommand;
1515
use Symfony\UX\Icons\IconCacheWarmer;
1616
use Symfony\UX\Icons\IconRenderer;
17+
use Symfony\UX\Icons\IconRendererInterface;
1718
use Symfony\UX\Icons\Registry\CacheIconRegistry;
1819
use Symfony\UX\Icons\Registry\ChainIconRegistry;
1920
use Symfony\UX\Icons\Registry\LocalSvgIconRegistry;
@@ -60,7 +61,7 @@
6061
abstract_arg('icon_aliases'),
6162
])
6263

63-
->alias('Symfony\UX\Icons\IconRendererInterface', '.ux_icons.icon_renderer')
64+
->alias(IconRendererInterface::class, '.ux_icons.icon_renderer')
6465

6566
->set('.ux_icons.icon_finder', IconFinder::class)
6667
->args([

src/Icons/src/DependencyInjection/UXIconsExtension.php

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,30 @@ public function getConfigTreeBuilder(): TreeBuilder
4343
->info('Default attributes to add to all icons.')
4444
->defaultValue(['fill' => 'currentColor'])
4545
->end()
46+
->arrayNode('icon_sets')
47+
->normalizeKeys(false)
48+
->useAttributeAsKey('prefix')
49+
->arrayPrototype()
50+
->children()
51+
->scalarNode('path')
52+
->info('The local icon set directory path (e.g. \'%kernel.project_dir%/assets/svg/acme\').')
53+
->end()
54+
->scalarNode('alias')
55+
->info('The original icon set identifier on iconify (e.g. "simple-icons").')
56+
->end()
57+
->arrayNode('icon_attributes')
58+
->info('Attributes to add to icons in this set.')
59+
->normalizeKeys(false)
60+
->variablePrototype()
61+
->end()
62+
->end()
63+
->end()
64+
->end()
65+
->validate()
66+
->ifTrue(fn (array $v) => isset($v['path']) && isset($v['alias']))
67+
->thenInvalid('You cannot define both "path" and "alias" for an icon set.')
68+
->end()
69+
->end()
4670
->arrayNode('aliases')
4771
->info('Icon aliases (alias => icon name).')
4872
->example(['dots' => 'clarity:ellipsis-horizontal-line'])
@@ -94,9 +118,25 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
94118
$loader->load('asset_mapper.php');
95119
}
96120

121+
$iconSetAliases = [];
122+
$iconSetAttributes = [];
123+
$iconSetPaths = [];
124+
foreach ($mergedConfig['icon_sets'] as $prefix => $config) {
125+
if (isset($config['icon_attributes'])) {
126+
$iconSetAttributes[$prefix] = $config['icon_attributes'];
127+
}
128+
if (isset($config['alias'])) {
129+
$iconSetAliases[$prefix] = $config['alias'];
130+
}
131+
if (isset($config['path'])) {
132+
$iconSetPaths[$prefix] = $config['path'];
133+
}
134+
}
135+
97136
$container->getDefinition('.ux_icons.local_svg_icon_registry')
98137
->setArguments([
99138
$mergedConfig['icon_dir'],
139+
$iconSetPaths,
100140
])
101141
;
102142

@@ -107,6 +147,7 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
107147
$container->getDefinition('.ux_icons.icon_renderer')
108148
->setArgument(1, $mergedConfig['default_icon_attributes'])
109149
->setArgument(2, $mergedConfig['aliases'])
150+
->setArgument(3, $iconSetAttributes)
110151
;
111152

112153
$container->getDefinition('.ux_icons.twig_icon_runtime')
@@ -117,8 +158,10 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
117158
$loader->load('iconify.php');
118159

119160
$container->getDefinition('.ux_icons.iconify')
120-
->setArgument(1, $mergedConfig['iconify']['endpoint'])
121-
;
161+
->setArgument(1, $mergedConfig['iconify']['endpoint']);
162+
163+
$container->getDefinition('.ux_icons.iconify_on_demand_registry')
164+
->setArgument(1, $iconSetAliases);
122165

123166
if (!$mergedConfig['iconify']['on_demand']) {
124167
$container->removeDefinition('.ux_icons.iconify_on_demand_registry');

src/Icons/src/IconRenderer.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,16 @@
1818
*/
1919
final class IconRenderer implements IconRendererInterface
2020
{
21+
/**
22+
* @param array<string, mixed> $defaultIconAttributes
23+
* @param array<string, string> $iconAliases
24+
* @param array<string, array<string, mixed>> $iconSetsAttributes
25+
*/
2126
public function __construct(
2227
private readonly IconRegistryInterface $registry,
2328
private readonly array $defaultIconAttributes = [],
24-
private readonly ?array $iconAliases = [],
29+
private readonly array $iconAliases = [],
30+
private readonly array $iconSetsAttributes = [],
2531
) {
2632
}
2733

@@ -36,11 +42,16 @@ public function __construct(
3642
*/
3743
public function renderIcon(string $name, array $attributes = []): string
3844
{
39-
$name = $this->iconAliases[$name] ?? $name;
45+
$iconName = $this->iconAliases[$name] ?? $name;
4046

41-
$icon = $this->registry->get($name)
42-
->withAttributes($this->defaultIconAttributes)
43-
->withAttributes($attributes);
47+
$icon = $this->registry->get($iconName);
48+
49+
if (0 < (int) $pos = strpos($name, ':')) {
50+
$setAttributes = $this->iconSetsAttributes[substr($name, 0, $pos)] ?? [];
51+
} elseif ($iconName !== $name && 0 < (int) $pos = strpos($iconName, ':')) {
52+
$setAttributes = $this->iconSetsAttributes[substr($iconName, 0, $pos)] ?? [];
53+
}
54+
$icon = $icon->withAttributes([...$this->defaultIconAttributes, ...($setAttributes ?? []), ...$attributes]);
4455

4556
foreach ($this->getPreRenderers() as $preRenderer) {
4657
$icon = $preRenderer($icon);

src/Icons/src/Registry/IconifyOnDemandRegistry.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@
2323
*/
2424
final class IconifyOnDemandRegistry implements IconRegistryInterface
2525
{
26-
public function __construct(private Iconify $iconify)
27-
{
26+
public function __construct(
27+
private Iconify $iconify,
28+
private ?array $prefixAliases = [],
29+
) {
2830
}
2931

3032
public function get(string $name): Icon
3133
{
3234
if (2 !== \count($parts = explode(':', $name))) {
3335
throw new IconNotFoundException(\sprintf('The icon name "%s" is not valid.', $name));
3436
}
37+
[$prefix, $icon] = $parts;
3538

36-
return $this->iconify->fetchIcon(...$parts);
39+
return $this->iconify->fetchIcon($this->prefixAliases[$prefix] ?? $prefix, $icon);
3740
}
3841
}

src/Icons/src/Registry/LocalSvgIconRegistry.php

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,38 @@
2323
*/
2424
final class LocalSvgIconRegistry implements IconRegistryInterface
2525
{
26-
public function __construct(private string $iconDir)
27-
{
26+
/**
27+
* @param array<string, string> $iconSetPaths
28+
*/
29+
public function __construct(
30+
private readonly string $iconDir,
31+
private readonly array $iconSetPaths = [],
32+
) {
2833
}
2934

3035
public function get(string $name): Icon
3136
{
32-
if (!file_exists($filename = \sprintf('%s/%s.svg', $this->iconDir, str_replace(':', '/', $name)))) {
33-
throw new IconNotFoundException(\sprintf('The icon "%s" (%s) does not exist.', $name, $filename));
37+
if (str_contains($name, ':')) {
38+
[$prefix, $icon] = explode(':', $name, 2) + ['', ''];
39+
if ('' === $prefix || '' === $icon) {
40+
throw new IconNotFoundException(\sprintf('The icon name "%s" is not valid.', $name));
41+
}
42+
43+
if ($prefixPath = $this->iconSetPaths[$prefix] ?? null) {
44+
if (!file_exists($filename = $prefixPath.'/'.str_replace(':', '/', $icon).'.svg')) {
45+
throw new IconNotFoundException(\sprintf('The icon "%s" (%s) does not exist.', $name, $filename));
46+
}
47+
48+
return Icon::fromFile($filename);
49+
}
50+
}
51+
52+
$filepath = str_replace(':', '/', $name).'.svg';
53+
if (file_exists($filename = $this->iconDir.'/'.$filepath)) {
54+
return Icon::fromFile($filename);
3455
}
3556

36-
return Icon::fromFile($filename);
57+
throw new IconNotFoundException(\sprintf('The icon "%s" (%s) does not exist.', $name, $filename));
3758
}
3859

3960
public function has(string $name): bool

src/Icons/tests/Fixtures/TestKernel.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ protected function configureContainer(ContainerConfigurator $container): void
5050
]);
5151

5252
$container->extension('twig', [
53-
'default_path' => __DIR__ . '/templates',
53+
'default_path' => __DIR__.'/templates',
5454
]);
5555

5656
$container->extension('twig_component', [
@@ -60,6 +60,14 @@ protected function configureContainer(ContainerConfigurator $container): void
6060

6161
$container->extension('ux_icons', [
6262
'icon_dir' => '%kernel.project_dir%/tests/Fixtures/icons',
63+
'icon_sets' => [
64+
'fla' => [
65+
'path' => '%kernel.project_dir%/tests/Fixtures/images/flags',
66+
],
67+
'lu' => [
68+
'alias' => 'lucide',
69+
],
70+
],
6371
]);
6472

6573
$container->services()->set('logger', NullLogger::class);
Lines changed: 5 additions & 0 deletions
Loading
Lines changed: 5 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)