Durchsuchbare Icon-Übersicht
In der CSS-Datei style.css
, die vom Addon ‘be_style’ bereitgestellt wird, sind Icons aus mehreren
Quellen enthalten.
- FontAwesome (
.fa
) - Glyhicons aus Bootstrap (
.glyphicon
) - REDAXO-Icons (
.rex-icon
) auf Basis von FontAwesome
Aber welche gibt es denn nun? Und wie sehen sie aus? Dazu kann man z.B. auf die Webseiten von FontAwesome oder Bootstrap gehen und suchen - aber bitte die richtige Version auswählen, die auch in REDAXO im Einsatz ist. Und wo findet sich die Übersicht der rex-icon?
Eine Hilfsseite im REDAXO-Backend soll schnelle Übersicht über die tatsächlich vorhandenen
Icons der style.css
schaffen und auch die rex-icon ausweisen.
style.css
ist per SCSS erzeugt, daher sehr klar aufgebaut und leicht durchsuchbar. Die Einträge
für Icons sehen z.B. so aus:
.fa-user:before,.rex-icon-user:before,.rex-icon-userrole:before{content:"\f007"}
Sie lassen sich leicht herausfiltern und aufbereitet als Page ausgeben. Zusätzlich wird eine Such- und Selektier-Möglichkeit geschaffen:
- Per Checkbock selektieren, welche Gruppe angezeigt wird (
.fa
,.glyphicon
,.rex-icon
) - Volltextsuche in den Icon-Bezeichnern (Im Beispiel: “user,userrole”).
Zudem kann mit Doppelklick auf den Namen der zugehörige HTML-Tag generiert und ins Clipboard übertragen werden.
Beispiel: Doppelklick auf das Wort “times” im rechten Kästchen erzeugt <i class=fa fa-times"></i>
.
Die style.css
wird nur einmal durchsucht und der HTML-Code zur Anzeige aufbereitet im Cache-Verzeichnis
abgelegt. Die Icons sind nach Unicode aufsteigend sortiert.
Das Script ist konzipiert als Seite in einem Service-Addon - z.B. im “Projekt-Addon”.
- Script speichern als
«domain»/redaxo/src/addons/project/pages/icons.php
(falls der Ordnerpages
nicht vorhanden ist, diesen vorher erstellen) -
Script
icons.php
jetzt in derindex.php
despages
-Orders einbinden (falls die Dateiindex.php
nicht vorhanden ist, diese neu erstellen)<?php echo rex_view::title('AddOn: project'); rex_be_controller::includeCurrentPageSubPath(); ?>
-
Seite über
package.yml
einbindenpage: title: 'Projekt-Addon' perm: project[] subpages: icons: title: 'Icons' icon: fa fa-table
Und hier das Script inklusive CSS und JS:
<?php
$types = ['FontAwesome'=>'fa','REX'=>'rex-icon','Glyphicons'=>'glyphicon'];
$filename = $this->getCachePath('icons.html');
//------ Typen einlesen oder neu generieren
$html = @file_get_contents($filename);
if( !$html ) {
// styles.css einlesen, Kommentare rauswerfen
$styles = preg_replace('~\/\*[^*]*\*+([^/*][^*]*\*+)*\/~m','',file_get_contents( rex_path::addonAssets('be_style','css/styles.css') ));
// in Definitionen aufsplitten (schließendes '}' fällt dabei raus, is egal.)
$styles = explode('}',$styles);
// alle Zeilen rauswerfen, die nicht mit ".rex-icon-", ".glyphicon-" oder ".fa-" beginnen
$referenz = [];
foreach( $types as $v ) $referenz[".$v-"] = strlen($v) + 2;
$styles = array_filter($styles,function($v) use($referenz){foreach($referenz as $t=>$l){ if( substr($v,0,$l)==$t ) return true;} return false;} );
// Restzeilen, die den korrekten Grundaufbau haben, in ein Icon-Array überführen (je Unicode ein Eintrag)
// .fa-name:before,.rex-icon-name{content:"\A234"
$patternA = '/^(\.[\w\-]+)\:before(\,(\.[\w\-]+)\:before)*\{content\:"\\\(?<unicode>[a-z\d]{4})"$/';
$patternB = '/\.(?<klasse>(?<typ>'.implode('|',$types).')\-(?<name>[\w\-]+))\:before/';
$icons = [];
foreach( $styles as $k=>$v ) {
if( preg_match($patternA,$v,$match) ) {
$unicode = $match['unicode'];
if( preg_match_all($patternB,substr($v,0,-16),$match) && count($match)) {
foreach( $match['typ'] as $k=>$typ ) {
if( !isset($icons[$unicode]['html']) ) $icons[$unicode]['html'] = "<i class=\"$typ $typ-{$match['name'][$k]}\"></i>";
$icons[$unicode]['name'][$match['name'][$k]] = $match['name'][$k];
$icons[$unicode]['class'][$match['klasse'][$k]] = $match['klasse'][$k];
$icons[$unicode]['type'][$typ] = "x-$typ";
}
}
}
}
unset($styles);
ksort($icons);
//------ Section zusammenbauen
// Auswahl des Anzeigeumfangs ($types)
$umfang = new rex_fragment();
$elements = [];
foreach( $types as $k=>$v ) {
$elements[] = [
'label' => '<label class="control-label">' . $k . '</label>',
'field' => '<input onchange="f_select(this);" type="checkbox" id="scope-' . $v . '" value="x-' . $v . '" checked />',
];
}
$umfang->setVar('elements', $elements, false);
$umfang->setVar('grouped', true);
$umfang->setVar('inline', true);
// Suchfeld für textbasierte Selektion
$search = new rex_fragment();
$elements = [
'field' => '<input onchange="f_search(this.value)" type="text" class="form-control" placeholder="Search for..." id="search">',
'right' => '<button onclick="f_reset()"class="btn btn-default" type="button"><i class="fa fa-close"></i></button>',
];
$search->setVar('elements', [$elements], false);
// Heading-Grid, Zeile 1: $umfang und $search
$row1 = new rex_fragment();
$row1->setVar('content', [$umfang->parse('core/form/checkbox.php'),$search->parse('core/form/input_group.php')], false);
unset($umfang,$search,$elements);
// Heading-Grid, Zeile 2: Muster-Code für Icons
$row2 = new rex_fragment();
$row2->setVar('content', array_map(function($v){return "<i class=\"$v $v-«name»\"></i>";},$types), false);
// Section insgesamt mit allen Icons ausgeben.
$section = new rex_fragment();
$content = '<div ondblclick="f_copy(event)" class="'.implode(' ',array_map(function($v){return "x-$v";},$types)).'" id="iconlist">';
foreach( $icons as $k=>$v ) {
$type = implode(' ',$v['type']);
$name = implode(',',$v['name']);
$class = implode('</span><br><span>',$v['class']);
$content .= "<div class=\"panel panel-default $type\" data-search=\"$name\"><div class=\"panel-heading\"><small>$k</small>{$v['html']}</div><div class=\"panel-body\"><span>$class</span></div></div>";
}
$content .= '</div';
$section->setVar('heading', $row1->parse('core/page/grid.php').$row2->parse('core/page/grid.php'), false);
$section->setVar('body',$content,false);
$html = $section->parse('core/page/section.php');
unset($row,$row2,$content,$section);
// generiertes HTML abspeichern
rex_dir::create($this->getCachePath());
file_put_contents($filename, $html);
}
echo $html;
//------ Style und Script
?>
<style>
#iconlist { display: flex; flex-wrap: wrap; justify-content: center;}
#iconlist .panel { display: none; margin: 1em; width: 14em; font-size: 1em; }
#iconlist .panel-heading { font-size: 2rem; text-align: center; position: relative; }
#iconlist .panel-body { padding: 0.1rem 0.2rem; }
#iconlist small { padding: 0.1rem; position: absolute; top:0; left:0; font-size: 1rem; }
<?= implode(',',array_map(function($v){return "#iconlist.x-$v .panel.x-$v";},$types)) ?> { display: block; }
</style>
<script>
var iconlist = document.getElementById('iconlist');
var search = document.getElementById('search');
var typeList = ['<?= implode('\',\'',$types) ?>'];
function f_select( node ) {
iconlist.classList.toggle(node.value,node.checked);
}
function f_reset() {
search.value = '';
f_search( search.value );
}
function f_search( pattern ) {
if( pattern ) {
var hidden;
for( var node of iconlist.children ) {
hidden = !node.dataset.search.includes(pattern.toLowerCase());
if( node.classList.contains('hidden') != hidden ) node.classList.toggle('hidden',hidden);
}
return;
}
for( var node of iconlist.children ) {
if( node.classList.contains('hidden') ) node.classList.remove('hidden');
}
}
function f_copy( event ) {
var target = event.target;
if( target.tagName.toLowerCase() != 'span' ) return;
for( var type of typeList ) {
if( target.innerText.startsWith(type) ) {
var node = document.createElement('textarea');
node.value = '<i class="'+type+' '+target.innerText+'"></i>';
node.style = {position: 'absolute', left: '-9999px'};
document.body.appendChild(node);
node.select();
document.execCommand('copy');
alert('HTML für "'+target.innerText+'" in das Clipboard kopiert ("'+node.value+'")');
document.body.removeChild(node);
event.preventDefault();
return;
}
}
}
</script>