Skip to content
3 changes: 2 additions & 1 deletion build/build-modules-js/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,8 @@
},
"filesExtra": {
"scss": "scss",
"webfonts": "webfonts"
"webfonts": "webfonts",
"svgs": "images"
},
"provideAssets": [
{
Expand Down
88 changes: 88 additions & 0 deletions libraries/src/Document/HtmlDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Joomla\CMS\Factory as CmsFactory;
use Joomla\CMS\Filter\InputFilter;
use Joomla\CMS\Helper\ModuleHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\CMS\Uri\Uri;
Expand Down Expand Up @@ -116,6 +117,14 @@ class HtmlDocument extends Document
*/
private $html5 = true;

/**
* Array of icons
*
* @var array
* @since 4.0.0
*/
private $icons = array();

/**
* Class constructor
*
Expand All @@ -132,6 +141,11 @@ public function __construct($options = array())

// Set default mime type and document metadata (metadata syncs with mime type by default)
$this->setMimeEncoding('text/html');

if (array_key_exists('icons', $options))
{
$this->setIcons($options['icons']);
}
}

/**
Expand All @@ -154,6 +168,7 @@ public function getHeadData()
$data['scripts'] = $this->_scripts;
$data['script'] = $this->_script;
$data['custom'] = $this->_custom;
$data['icons'] = $this->icons;

// @deprecated 5.0 This property is for backwards compatibility. Pass text through script options in the future
$data['scriptText'] = Text::getScriptStrings();
Expand All @@ -163,6 +178,73 @@ public function getHeadData()
return $data;
}

/**
* Returns the icon names
*
* @return array
*
* @since 4.0.0
*/
public function getIcons()
{
return $this->icons;
}

/**
* Setter for all icons
*
* @param array $icons
* @return string
*
* @since 4.0.0
*/
public function setIcons($icons = [])
{
$this->icons = $icons;
}

/**
* Setter for an icon
*
* @param array $icon
* @return string
*
* @since 4.0.0
*/
public function setIcon($icon = [])
{
if (empty($icon['icon']))
{
return '';
}

$attribs = '';
$provider = $icon['provider'] ? $icon['provider'] : 'fontawesome-free';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the option to state a provider? Is this really needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we want 3pd to roll their own icon sets. Hardcoding this to Font Awesome (by the way it's the fallback, meaning for the core you could ignore this or the group option below, if the icon is in the group solid) is rather limiting

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something but most custom SVGs would not require any of the following...

'provider'   => 'fontawesome-free',
'group'      => 'solid',
'icon'       => 'user',

Even with FA these are mostly covered by the class option.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These options actually resolve the path to the icon... The icons are not stored in the same directory

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that mean that these are required?

$group = $icon['group'] ? $icon['group'] : 'solid';
$classes = $icon['classes'] ? $icon['classes'] : '';

// Setup options object
if (!in_array($provider . '.' . $group . '.' . $icon['icon'], $this->getIcons()))
{
$this->icons[$provider . '.' . $group . '.' . $icon['icon']] = [
'provider' => $provider,
'group' => $group,
'icon' => $icon['icon'],
'text' => $icon['text'] ? $icon['text'] : ''
];
}

if (isset($icon['attributes']) && count($icon['attributes']))
{
foreach ($icon['attributes'] as $key => $val)
{
$attribs .= $key . '="' . $val . '" ';
}
}

return '<svg class="' . $classes . '" ' . $attribs . '><use href="#' . $provider . '-' . $group . '-' . $icon['icon'] . '"></use></svg>';
}

/**
* Reset the HTML document head data
*
Expand All @@ -186,6 +268,7 @@ public function resetHeadData($types = null)
$this->_scripts = array();
$this->_script = array();
$this->_custom = array();
$this->icons = array();
$this->scriptOptions = array();
}

Expand Down Expand Up @@ -235,6 +318,10 @@ private function resetHeadDatum($type)
$this->{$realType} = array();
break;

case 'icons':
$this->icons = array();
break;

case 'scriptOptions':
$this->{$type} = array();
break;
Expand Down Expand Up @@ -267,6 +354,7 @@ public function setHeadData($data)
$this->_scripts = $data['scripts'] ?? $this->_scripts;
$this->_script = $data['script'] ?? $this->_script;
$this->_custom = $data['custom'] ?? $this->_custom;
$this->icons = $data['icons'] ?? $this->icons;
$this->scriptOptions = (isset($data['scriptOptions']) && !empty($data['scriptOptions'])) ? $data['scriptOptions'] : $this->scriptOptions;

return $this;
Expand Down
69 changes: 69 additions & 0 deletions libraries/src/Document/Renderer/Html/IconsRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
/**
* Joomla! Content Management System
*
* @copyright Copyright (C) 2005 - 2019 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

namespace Joomla\CMS\Document\Renderer\Html;

\defined('JPATH_PLATFORM') or die;

use Joomla\CMS\Document\Document;
use Joomla\CMS\Document\DocumentRenderer;
use Joomla\CMS\HTML\HTMLHelper;

/**
* HTML document renderer for the inline svg icons output
*
* @since 4.0.0
*/
class IconsRenderer extends DocumentRenderer
{
/**
* Renders the inline svg icons and returns the results as a string
*
* @param string $name Not used.
* @param array $params Associative array of values
* @param string $content Not used.
*
* @return string The output of the script
*
* @since __DEPLOY_VERSION__
*/
public function render($name, $params = array(), $content = null)
{
$files = [];

// Generate the file and load the stylesheet link
foreach ($this->_doc->getIcons() as $key => $icon)
{
$file = HTMLHelper::image('vendor/' . $icon['provider'] . '/' . $icon['group'] . '/' . $icon['icon'] . '.svg', '', null, true, 1);

if ($file)
{
$content = @file_get_contents(JPATH_ROOT . substr($file, strpos($file, '/media')));
$content = str_replace(
'<svg',
'<svg id="' . $icon['provider'] . '-' . $icon['group'] . '-' . $icon['icon'] . '"'
. ' title="' . $icon['text'] . '"'
. ' role="img"',
$content
);

$files[] = $content;
}
}

if (!empty($files))
{
// Reset the icons registry
$this->_doc->setIcons([]);

return '<div style="display:none">' . implode('', $files) . '</div>';
}

return '';
}
}
31 changes: 28 additions & 3 deletions modules/mod_login/tmpl/default.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
defined('_JEXEC') or die;

use Joomla\CMS\Component\ComponentHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
Expand All @@ -21,6 +22,19 @@

Text::script('JSHOWPASSWORD');
Text::script('JHIDEPASSWORD');

Factory::getDocument()->addStyleDeclaration(
<<<CSS
.demo-class {
height: 1.2rem;
width: 1.2rem;
-webkit-text-stroke-color: currentColor;
stroke: currentColor;
-webkit-text-stroke-width: 16px;
stroke-width: 16px;
}
CSS
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ciar4n I've added this css for demo purposes I guess you will come up with something better in the template level

Copy link
Contributor

@ciar4n ciar4n Mar 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The majority of SVG icons would be displayed inline so might be an idea to apply the styling for that by default...

svg {
  display: inline-block;
  font-size: inherit;
  max-height: 1em;
}

?>
<form id="login-form-<?php echo $module->id; ?>" class="mod-login" action="<?php echo Route::_('index.php', true); ?>" method="post">

Expand All @@ -37,9 +51,20 @@
<input id="modlgn-username-<?php echo $module->id; ?>" type="text" name="username" class="form-control" autocomplete="username" placeholder="<?php echo Text::_('MOD_LOGIN_VALUE_USERNAME'); ?>">
<span class="input-group-append">
<label for="modlgn-username-<?php echo $module->id; ?>" class="sr-only"><?php echo Text::_('MOD_LOGIN_VALUE_USERNAME'); ?></label>
<span class="input-group-text" title="<?php echo Text::_('MOD_LOGIN_VALUE_USERNAME'); ?>">
<span class="fas fa-user" aria-hidden="true"></span>
</span>
<span class="input-group-text"><?php echo Factory::getDocument()->setIcon(
[
'provider' => 'fontawesome-free',
'group' => 'solid',
'icon' => 'user',
'classes' => 'demo-class',
'text' => Text::_('MOD_LOGIN_VALUE_USERNAME'),
'attributes' => [
'data-foo' => 'bar',
'id' => 'foobar'
]
]
);
?></span>
</span>
</div>
<?php else : ?>
Expand Down
1 change: 1 addition & 0 deletions templates/cassiopeia/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@
<?php endif; ?>

<jdoc:include type="modules" name="debug" style="none" />
<jdoc:include type="icons" />

</body>
</html>