[TWIG] Add SVG icon embed function

This commit is contained in:
Angelo D. Moura 2020-10-20 23:38:56 +01:00 committed by Hugo Sales
parent 5a7b895476
commit cacd9a574d
16 changed files with 190 additions and 51 deletions

View File

@ -1,5 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<svg class="{{ iconClass|default('') }}" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>attach</title>
<path d="M4.703 29.192c-3.652-3.768-3.584-9.844 0.087-13.599l13.11-13.41c2.77-2.833 7.272-2.833 10.042 0 2.743 2.806 2.746 7.333 0 10.142l-13.051 11.381c-2.048 1.659-4.032 1.617-5.243 0.067-1.048-1.407-1.181-3.381 0.454-5.377l8.775-9.424c1.421-1.318 3.019 0.439 1.965 1.644l-8.723 9.548c-0.833 0.91 0.286 2.144 1.163 1.34l11.8-11.976c1.226-1.254 1.226-3.295-0.001-4.55-1.199-1.227-3.122-1.227-4.322 0l-13.11 13.41c-2.303 2.326-1.592 5.802 0.391 7.61 3.605 3.643 7.555 0.332 12.211-3.69 0.386-0.395 5.076-4.978 7.183-6.899 0.901-0.618 1.824 0.244 1.201 1.554l-9.69 11.7c-3.892 4.233-10.565 4.322-14.242 0.528z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 778 B

After

Width:  |  Height:  |  Size: 814 B

View File

@ -1,5 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<svg class="{{ iconClass|default('') }}" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>avatar</title>
<path opacity="0.992" fill="#fff" d="M31.612 16.632c-0.347 8.622-7.618 15.329-16.241 14.98s-15.331-7.622-14.984-16.245c0.347-8.622 7.618-15.329 16.241-14.98s15.331 7.622 14.984 16.245z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 354 B

After

Width:  |  Height:  |  Size: 390 B

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
class="{{ iconClass|default('') }}"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
class="{{ iconClass|default('') }}"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -1,5 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<svg class="{{ iconClass|default('') }}" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>logo</title>
<path d="M14.225 29.263c1.48-0.748 2.909-2.155 3.451-3.398 0.827-1.893 1.068-1.818-5.781-1.818-6.744 0-7.349-0.115-8.593-1.628-0.61-0.742-0.633-1.086-0.633-9.456 0-8.422 0.020-8.713 0.651-9.562 1.2-1.613 1.699-1.679 12.7-1.679 11.005 0 11.456 0.062 12.71 1.748 0.566 0.761 0.6 1.3 0.6 9.493 0 8.37-0.023 8.715-0.633 9.456-0.909 1.105-2.149 1.628-3.864 1.628-1.331 0-1.517 0.078-1.681 0.703-0.249 0.95-1.56 2.395-2.912 3.212-1.363 0.823-4.873 1.889-6.166 1.871l-0.953-0.013zM19.7 20.572c1.193-0.547 2.345-2.309 2.627-4.017 0.135-0.818 0.175-1.554 0.090-1.636-0.308-0.294-5.508-0.153-5.508 0.15 0 0.165-0.186 1.211 0.050 1.752 0.31 0.711 1.031 0.333 1.608 0.408 0.496 0.065 0.767 0.336 0.352 0.875-0.364 0.472-0.79 0.578-2.292 0.57-1.599-0.009-2.145-0.173-2.784-0.735-0.708-0.623-0.728-1.11-0.826-3.188-0.133-2.808 0.201-3.318 2.359-3.602 1.79-0.236 3.638 0.109 4.14 1.164 0.299 0.628 0.194 0.74 1.31 0.74 1.54 0 1.643-0.426 1.047-1.992-0.385-1.013-0.726-0.77-0.284-1.661 0.764-1.541 0.606-2.617-0.564-3.839-1.151-1.202-1.664-1.118-1.102 0.18 0.202 0.466 0.322 1.221 0.268 1.676-0.077 0.645-0.273 0.848-0.882 0.916-0.436 0.049-0.956-0.112-1.173-0.362-0.449-0.518-1.263-0.587-1.724-0.146-0.221 0.212-0.56 0.198-1.12-0.046-0.676-0.295-0.894-0.27-1.384 0.154-0.32 0.277-0.9 0.461-1.289 0.408-0.624-0.085-0.705-0.249-0.69-1.4 0.009-0.718 0.126-1.533 0.26-1.812 0.413-0.864-0.597-0.598-1.462 0.385-0.91 1.034-1.070 2.938-0.332 3.946 0.407 0.556 0.083 0.645-0.145 1.877-1.099 5.937 0.040 8.144 3.019 9.304 1.949 0.759 4.694 0.73 6.434-0.068z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,5 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<svg class="{{ iconClass|default('') }}" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>menu</title>
<path d="M2.222 12.6h27.497c1.219 0 2.208 0.988 2.208 2.208v1.263c0 1.219-0.988 2.208-2.208 2.208h-27.497c-1.219 0-2.208-0.988-2.208-2.208v-1.263c0-1.219 0.988-2.208 2.208-2.208z"></path>
<path d="M2.222 21.787h27.497c1.219 0 2.208 0.988 2.208 2.208v1.263c0 1.219-0.988 2.208-2.208 2.208h-27.497c-1.219 0-2.208-0.988-2.208-2.208v-1.263c0-1.219 0.988-2.208 2.208-2.208z"></path>

Before

Width:  |  Height:  |  Size: 725 B

After

Width:  |  Height:  |  Size: 761 B

View File

@ -1,5 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<svg class="{{ iconClass|default('') }}" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>repeat</title>
<path d="M16.93 22.627c0.914-0.547 1.931-0.247 2.325 0.762l0.544 1.697 6.041 0.087c1.566 0.034 2.565-1.676 1.765-3.022l-1.661-2.793c-0.212-0.356-0.868-1.092-0.094-1.553l2.149-1.278c0.74-0.44 1.172 0.43 1.383 0.786l1.658 2.787c2.407 4.047-0.585 9.169-5.289 9.066l-6.075 0.337-0.691 1.568c-0.455 0.764-1.106 1.047-2.245 0.51l-4.627-3.773c-0.382-0.399-0.382-1.349 0.017-1.73z"></path>
<path d="M19.998 14.054c-0.935-0.511-1.191-1.54-0.52-2.391l1.187-1.328-2.984-5.253c-0.763-1.368-2.744-1.363-3.499 0.009l-1.567 2.846c-0.2 0.363-0.502 1.302-1.291 0.867l-2.19-1.206c-0.755-0.415-0.223-1.228-0.023-1.591l1.564-2.841c2.271-4.125 8.203-4.139 10.496-0.030l3.367 5.068 1.702-0.198c0.889 0.005 1.463 0.423 1.577 1.678l-0.91 5.9c-0.151 0.531-0.969 1.012-1.501 0.862z"></path>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,5 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<svg class="{{ iconClass|default('') }}" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>reply</title>
<path d="M0.958 15.559l5.203-10.197c0.499-1.17 1.745-3.012 3.015-0.799l0.933 1.625c10.329-3.208 17.723 1.178 19.613 6.884 1.208 3.645 0.922 8.871-1.711 13.899-0.663 0.954-1.522 0.315-1.375-0.532 3.126-11.733-6.743-17.108-11.882-10.468l0.81 1.99c0.518 1.271-0.64 1.541-2.779 1.161l-10.828-1.524c-0.884-0.232-1.358-1.197-0.999-2.039z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 536 B

View File

@ -1,5 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<svg class="{{ iconClass|default('') }}" version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>search</title>
<path d="M11.421 0.625c-5.74 0-10.394 4.653-10.394 10.394v0c0 5.74 4.653 10.394 10.394 10.394v0c2.434-0 4.673-0.837 6.445-2.239l-0.022 0.017 0.716 0.73c-0.311 0.579-0.224 1.321 0.256 1.808l9.021 9.14c0.576 0.584 1.492 0.573 2.055-0.023l0.651-0.69c0.562-0.596 0.552-1.546-0.024-2.13l-9.021-9.14c-0.471-0.477-1.17-0.557-1.716-0.244l-0.645-0.658c1.661-1.835 2.678-4.281 2.678-6.964v0c0-5.74-4.653-10.394-10.394-10.394v0zM11.421 3.932c0 0 0 0 0 0 3.914 0 7.086 3.173 7.086 7.086v0c-0 3.914-3.173 7.086-7.086 7.086-0 0-0 0-0 0v0c-3.914-0-7.086-3.173-7.086-7.086v0c0-3.914 3.173-7.086 7.086-7.086v0z"></path>
</svg>

Before

Width:  |  Height:  |  Size: 763 B

After

Width:  |  Height:  |  Size: 799 B

View File

@ -31,7 +31,6 @@
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
class Extension extends AbstractExtension
@ -51,6 +50,7 @@ class Extension extends AbstractExtension
new TwigFunction('is_route', [Runtime::class, 'isCurrentRoute']),
new TwigFunction('get_note_actions', [Runtime::class, 'getNoteActions']),
new TwigFunction('config', [Runtime::class, 'getConfig']),
new TwigFunction('icon', [Runtime::class, 'embedSvgIcon'], ['needs_environment' => true]),
];
}
}

View File

@ -0,0 +1,78 @@
<?php
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* GNU social Twig extensions
*
* @package GNUsocial
* @category Twig
*
* @author Ângelo D. Moura <up201303828@fe.up.pt>
* @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace App\Twig;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class IconsExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('icon',
[$this, 'embedSvgIcon'],
['needs_environment' => true]
),
];
}
/**
* Renders the Svg Icon template and returns it.
*
* @param Environment $twig
* @param string $icon_name
* @param string $icon_css_class
*
* @return string
*
* @author Ângelo D. Moura <up201303828@fe.up.pt>
*/
public function embedSvgIcon(Environment $twig, string $icon_name = '', string $icon_css_class = '')
{
try {
return $twig->render('@public_path/assets/icons/' . $icon_name . '.svg.twig', ['iconClass' => $icon_css_class]);
} catch (LoaderError $e) {
//return an empty string (a missing icon is not that important of an error)
return '';
} catch (RuntimeError $e) {
//return an empty string (a missing icon is not that important of an error)
return '';
} catch (SyntaxError $e) {
//return an empty string (a missing icon is not that important of an error)
return '';
}
}
}

View File

@ -39,6 +39,10 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Extension\RuntimeExtensionInterface;
class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface
@ -84,4 +88,31 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface
{
return [KernelEvents::REQUEST => 'onKernelRequest'];
}
/**
* Renders the Svg Icon template and returns it.
*
* @param Environment $twig
* @param string $icon_name
* @param string $icon_css_class
*
* @return string
*
* @author Ângelo D. Moura <up201303828@fe.up.pt>
*/
public function embedSvgIcon(Environment $twig, string $icon_name = '', string $icon_css_class = '')
{
try {
return $twig->render('@public_path/assets/icons/' . $icon_name . '.svg.twig', ['iconClass' => $icon_css_class]);
} catch (LoaderError $e) {
//return an empty string (a missing icon is not that important of an error)
return '';
} catch (RuntimeError $e) {
//return an empty string (a missing icon is not that important of an error)
return '';
} catch (SyntaxError $e) {
//return an empty string (a missing icon is not that important of an error)
return '';
}
}
}

View File

@ -17,42 +17,6 @@
{% endblock %}
</head>
<body>
{% block icons %}
<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-attach" viewbox="0 0 32 32">
{{ source('@public_path'~asset('assets/icons/attach.svg')) }}
</symbol>
<symbol id="icon-heart" viewbox="0 0 32 32">
{{ source('@public_path/assets/icons/heart.svg') }}
</symbol>
<symbol id="icon-logo" viewbox="0 0 32 32">
{{ source('@public_path/assets/icons/logo.svg') }}
</symbol>
<symbol id="icon-drop" viewbox="0 0 32 32">
{{ source('@public_path/assets/icons/drop.svg') }}
</symbol>
<symbol id="icon-reply" viewbox="0 0 32 32">
{{ source('@public_path/assets/icons/reply.svg') }}
</symbol>
<symbol id="icon-menu" viewbox="0 0 32 32">
{{ source('@public_path/assets/icons/menu.svg') }}
</symbol>
<symbol id="icon-repeat" viewbox="0 0 32 32">
{{ source('@public_path/assets/icons/repeat.svg') }}
</symbol>
<symbol id="icon-search" viewbox="0 0 32 32">
{{ source('@public_path/assets/icons/search.svg') }}
</symbol>
<symbol id="icon-avatar" viewbox="0 0 32 32">
{{ source('@public_path/assets/icons/avatar.svg') }}
</symbol>
<symbol id="icon-checkmark" viewbox="0 0 32 32">
{{ source('@public_path/assets/icons/checkmark.svg') }}
</symbol>
</defs>
</svg>
{% endblock %}
<div class="container">
{% block header %}
<div id='header'>
@ -61,9 +25,7 @@
{% endblock left %}
<nav id='instance'>
<a href="{{ path('main_public') }}">
<svg class="icon icon-logo">
<use xlink:href="#icon-logo"></use>
</svg>
{{ icon('logo', 'icon icon-logo') | raw }}
<b> {{ config('site', 'name') }} </b>
</a>
</nav>

View File

@ -50,9 +50,7 @@
<div class="attachments">
{{ form_widget(post_form.attachments) }}
<label for="{{ post_form.attachments.vars.id }}">
<svg class="icon icon-attach">
<use xlink:href="#icon-attach"></use>
</svg>
{{ icon('attach', 'icon icon-attach') | raw }}
</label>
</div>
<div class="post">

View File

@ -0,0 +1,68 @@
<?php
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}} License
/**
* This file test the Macro that Embeds SVG icons.
*
* @package Tests
*
* @author Ângelo D. Moura <up201303828@fe.up.pt>
* @copyright 2020 Free Software Foundation, Inc http://www.fsf.org
* @author Hugo Sales <hugo@hsal.es>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace App\Tests\Templates\Icons;
use App\Twig\Extension;
use App\Twig\Runtime;
use DirectoryIterator;
use PHPUnit\Framework\TestCase;
class IconsExtensionTest extends TestCase
{
public function testIconsExtension()
{
// Get all Icon files names from "public/assets/icons"
$icon_file_names = [];
foreach (new DirectoryIterator('public/assets/icons/') as $file) {
if ($file->isDot()) {
continue;
}
$icon_file_names[] = $file->getFilename();
}
$twig = self::$kernel->getContainer()->get('twig');
// Check if every icon file as a ".svg.twig" extension
foreach ($icon_file_names as $icon_file_name) {
static::assertRegExp('#.+\.svg\.twig$#', $icon_file_name);
$icon_name = explode('.', basename($icon_file_name))[0];
$icon_template_render = $twig->render($icon_file_name, ['iconClass' => 'icon icon-' . $icon_name]);
$icons_extension = new Runtime();
$icon_extension_render = $icons_extension->embedSvgIcon($twig, $icon_name, 'icon icon-' . $icon_name);
//Check if the function gives a valid HTML with a class attribute equal to the one passed
self::assertEquals($icon_template_render, $iconsExtension_render);
}
}
}