1515use Tempest \View \Elements \ViewComponentElement ;
1616use Tempest \View \IconConfig ;
1717use Tempest \View \ViewComponent ;
18+ use function Tempest \get ;
1819
1920final readonly class Icon implements ViewComponent
2021{
21- public function __construct (
22- private AppConfig $ appConfig ,
23- private IconCache $ iconCache ,
24- private IconConfig $ iconConfig ,
25- private HttpClient $ http ,
26- ) {}
27-
2822 public static function getName (): string
2923 {
3024 return 'x-icon ' ;
@@ -35,69 +29,88 @@ public function compile(ViewComponentElement $element): string
3529 $ name = $ element ->getAttribute ('name ' );
3630 $ class = $ element ->getAttribute ('class ' );
3731
38- $ svg = $ this ->render ($ name );
32+ return sprintf (
33+ '<?= %s::render(%s, \'%s \') ?> ' ,
34+ self ::class,
35+ // Having to replace `<?=` is a bit of a hack and should be improved
36+ str_replace (['<?= ' , '?> ' ], '' , $ name ),
37+ $ class ,
38+ );
39+ }
40+
41+ /**
42+ * Renders an icon
43+ *
44+ * This method is responsible for rendering the icon. If the icon is not
45+ * in the cache, it will download it on the fly and cache it for future
46+ * use. If the icon is already in the cache, it will be served from there.
47+ */
48+ public static function render (string $ name , ?string $ class ): ?string
49+ {
50+ $ svg = self ::svg ($ name );
51+ // We can't use injection because we don't have an instance of this view component at runtime. Might be worth refactoring, though.
52+ $ appConfig = get (AppConfig::class);
3953
4054 if (! $ svg ) {
41- return $ this -> appConfig ->environment ->isLocal ()
55+ return $ appConfig ->environment ->isLocal ()
4256 ? ('<!-- unknown-icon: ' . $ name . ' --> ' )
4357 : '' ;
4458 }
4559
46- return match ($ class ) {
47- null => $ svg ,
48- default => $ this ->injectClass ($ svg , $ class ),
49- };
60+ if ($ class !== null ) {
61+ $ svg = self ::injectClass ($ svg , $ class );
62+ }
63+
64+ return $ svg ;
5065 }
5166
52- /**
53- * Downloads the icon's SVG file from the Iconify API
54- */
55- private function download (string $ prefix , string $ name ): ?string
67+ private static function svg (string $ name ): ?string
5668 {
57- try {
58- $ url = new ImmutableString ($ this ->iconConfig ->iconifyApiUrl )
59- ->finish ('/ ' )
60- ->append ("{$ prefix }/ {$ name }.svg " )
61- ->toString ();
69+ $ iconCache = get (IconCache::class);
6270
63- $ response = $ this ->http ->get ($ url );
71+ try {
72+ $ parts = explode (': ' , $ name , 2 );
6473
65- if ($ response -> status !== Status:: OK ) {
74+ if (count ( $ parts ) !== 2 ) {
6675 return null ;
6776 }
6877
69- return $ response ->body ;
78+ [$ prefix , $ name ] = $ parts ;
79+
80+ return $ iconCache ->resolve (
81+ key: "iconify- {$ prefix }- {$ name }" ,
82+ cache: fn () => self ::download ($ prefix , $ name ),
83+ expiresAt: $ iconCache ->cacheDuration
84+ ? new DateTimeImmutable ()
85+ ->add (DateInterval::createFromDateString ("{$ iconCache ->cacheDuration } seconds " ))
86+ : null ,
87+ );
7088 } catch (Exception ) {
7189 return null ;
7290 }
7391 }
7492
7593 /**
76- * Renders an icon
77- *
78- * This method is responsible for rendering the icon. If the icon is not
79- * in the cache, it will download it on the fly and cache it for future
80- * use. If the icon is already in the cache, it will be served from there.
94+ * Downloads the icon's SVG file from the Iconify API
8195 */
82- private function render ( string $ name ): ?string
96+ private static function download ( string $ prefix , string $ name ): ?string
8397 {
98+ $ iconConfig = get (IconConfig::class);
99+ $ http = get (HttpClient::class);
100+
84101 try {
85- $ parts = explode (': ' , $ name , 2 );
102+ $ url = new ImmutableString ($ iconConfig ->iconifyApiUrl )
103+ ->finish ('/ ' )
104+ ->append ("{$ prefix }/ {$ name }.svg " )
105+ ->toString ();
86106
87- if (count ($ parts ) !== 2 ) {
107+ $ response = $ http ->get ($ url );
108+
109+ if ($ response ->status !== Status::OK ) {
88110 return null ;
89111 }
90112
91- [$ prefix , $ name ] = $ parts ;
92-
93- return $ this ->iconCache ->resolve (
94- key: "iconify- {$ prefix }- {$ name }" ,
95- cache: fn () => $ this ->download ($ prefix , $ name ),
96- expiresAt: $ this ->iconConfig ->cacheDuration
97- ? new DateTimeImmutable ()
98- ->add (DateInterval::createFromDateString ("{$ this ->iconConfig ->cacheDuration } seconds " ))
99- : null ,
100- );
113+ return $ response ->body ;
101114 } catch (Exception ) {
102115 return null ;
103116 }
@@ -106,12 +119,12 @@ private function render(string $name): ?string
106119 /**
107120 * Forwards the user-provided class attribute to the SVG element
108121 */
109- private function injectClass (string $ svg , string $ class ): string
122+ private static function injectClass (string $ svg , string $ class ): string
110123 {
111124 return new ImmutableString ($ svg )
112125 ->replace (
113- search: '<svg ' ,
114- replace: "<svg class= \"{$ class }\" " ,
126+ search: '<svg ' ,
127+ replace: "<svg class= \"{$ class }\"" ,
115128 )
116129 ->toString ();
117130 }
0 commit comments