3 Icon Picker Shared by Jeff Richmond, The Well Community Church 2 years ago 12.0 General, Web Intermediate Description While building our website, I got tired of going back and forth between Rock and the Font Awesome website to find the icons I wanted to use, so I built a system that catalogs the icons in the Font Awesome files as well as our own custom icon font, and then adds a searchable icon picker to nearly every Icon CSS Class field in Rock. Details There are two main parts of this system. The first is the icon importer. Information about the installed icon fonts is stored in a defined type. Whenever an icon font is updated, either via a Rock update or a custom font update, there is an Import Icon Fonts workflow that should be run manually. The workflow finds the files associated with each font in the defined type and extracts the details of each individual icon into a second defined type. The second part is the icon picker. A special shortcode with all of the necessary Lava, CSS and JavaScript for adding searchable icon picker functionality to (nearly) every Icon CSS Class field is inserted on every page on the site. When the user starts typing into one of the icon fields, the JavaScript searches the icon class names to display the matching icons. Clicking on an icon in the search results automatically inserts the corresponding CSS classes into the icon class field. Disclaimers This recipe relies heavily on things like file names, folder structure and even file contents, so there's a decent chance it could break at any time if any of those things change. However, we have been using it for over 2 years without any issues. None of this has been tested with FontAwesome Pro. It may just work, but we don't have a Pro license, so I don't know for sure. If you have FontAwesome Pro and try this out, please let me know how it goes. How-To Create the Icon Font Families Defined Type Name: Icon Font Families Description: Details about currently installed icon font families Attribute: Prefix Name: Prefix Description: The CSS class prefix for the icon font Key: Prefix Field Type: Text Required: Yes Show in Grid: Yes Default Value: fa Attribute: SVG Files Name: SVG Files Description: A comma separated list of SVG font file paths (relative to the /Assets/Fonts folder) Key: SVGFiles Field Type: Text Required: Yes Show in Grid: No Add at least one value for the built in FontAwesome font, as well as values for each custom icon font that you have. Value: Font Awesome Prefix: fa SVG Files: FontAwesome\fa-regular-400.svg,FontAwesome\fa-solid-900.svg,FontAwesome\fa-brands-400.svg Create the Font Icons Defined Type Name: Font Icons Description: Individual icons from an icon font Attribute: Font Family Name: Font Family Key: FontFamily Field Type: Defined Value Required: Yes Show in Grid: Yes Defined Type: Icon Font Families Import and Edit the Import Icon Fonts Workflow Type Download and import the Import Icon Fonts workflow type. Once it's imported, edit the workflow type. Find the Import Icons > Run Import Code action and change the Icon Font Family Defined Type ID and Font Icon Defined Type ID to match the defined types you just created. Run the workflow once to import your icons! Create the Icon Picker Shortcode Create a new Lava Shortcode with the following details: Name: Icon Picker Tag Name: iconpicker Tag Type: Inline Description: Adds an icon picker dropdown to nearly any Icon CSS Class fields on the page. Enabled Lava Commands: None Documentation: <p><strong>Usage:</strong></p> <pre style="position: relative;">{[ iconpicker ]}<div class="open_grepper_editor" title="Edit & Save To Grepper"></div><div class="open_grepper_editor" title="Edit & Save To Grepper"></div></pre> <p>No parameters.</p> <p>This shortcode should only be used once per page to enable the icon search feature on all <code>label</code> or <code>input</code> elements that have the word <code>Icon</code> in their <code>id</code> or content. The output of this shortcode includes all necessary JavaScript and CSS for creating font icon dropdowns on the entire page.</p> Shortcode Markup: Be sure to edit the Font Icons defined type ID on the first line to match the defined type you created earlier. {%- assign iconDefTypeID = 102 -%} {%- stylesheet id:'icon-picker-css' cacheduration:'3600' compile:'less' -%} .icon-input-wrapper { position: relative; } .icon-dropdown { max-width: 300px; max-height: 250px; min-width: 0; overflow: auto; padding: 3px; text-align: left; li { display: inline-block; margin: 1px; } &>li>a { display: inline-block; width: 2em; height: auto; padding: 3px; border-radius: 3px; text-align: center; line-height: 1.25em; cursor: pointer; transition: color .25s ease-in-out; &:hover { background-color: #e8e8e8; } &:active { color: #777; } i { line-height: inherit; } } } {%- endstylesheet -%} {%- assign inputSelector = 'input[id*="Icon"], label:contains(Icon) ~ .control-wrapper input' -%} {%- javascript id:'icon-picker-js' -%} const DEBUGICONS = false; $('document').ready(function() { if (DEBUGICONS === true) console.log('Initialize Icon Picker'); $('body') .on('propertychange change click keyup input paste', '{{ inputSelector }}', function(e) { var $Input = $(this); var term = $Input.val().replace('fa ','').replace('fas ','').replace('fab ','').replace('fa-',''); if ($Input.data('initialized') !== true) { InitializeInput($Input, true); } else if ($Input.data('prev-term') === '' || $Input.data('prev-term') !== term) { if (DEBUGICONS === true) console.log('Input Change'); var $Dropdown = $Input.data('dropdown'); RefreshIcons($Input, $Dropdown); } }) .on('focusin', '{{ inputSelector }}', function(e) { if (DEBUGICONS === true) console.log('Focus'); var $Input = $(this); ShowIconDropdown($Input); }) .on('mouseup', '{{ inputSelector }}', function(e) { //only trigger on left click if (e.which === 1) { if (DEBUGICONS === true) console.log('MouseUp'); var $Input = $(this); ShowIconDropdown($Input); } }) .click(function() { $('.icon-dropdown').filter(':visible').stop(true, true).slideUp('fast'); $('{{ inputSelector }}').data('prev-term', '') }); function InitializeInput($Input, showDropdown = false) { if (DEBUGICONS === true) console.log('Already Initializing: ' + $Input.data('initializing')); if ($Input.data('initializing') === true) return; if (DEBUGICONS === true) console.log('START: Initialize'); $Input.data('initializing', true); var $Dropdown = $('<ul class="icon-dropdown dropdown-menu" role="menu"></ul>'); $Dropdown.hide().insertAfter($Input); $Input.data('dropdown', $Dropdown); var apiURL = '{{ 'Global' | Attribute:'InternalApplicationRoot' }}api/DefinedValues?$filter=DefinedTypeId%20eq%20{{ iconDefTypeID }}%20and%20IsActive%20eq%20true&$select=Value%2CDescription&$orderby=Order' $.getJSON(apiURL, function(data) { if (DEBUGICONS === true) console.log('START: Add Icons'); $.each(data, function(i, icon) { var cssClass = icon.Value; var name = icon.Description; $Dropdown.append('<li><a data-class="' + cssClass + '" title="' + name + '"><i class="fa-lg ' + cssClass + '"></i></a></li>'); }); if (DEBUGICONS === true) console.log('DONE: Icons Added (' + data.length + ')'); $Dropdown.on('click', 'a', function(e) { if (DEBUGICONS === true) console.log('Icon Click'); e.preventDefault(); e.stopPropagation(); $Input.val($(this).data('class')); $Dropdown.find('.active').removeClass('active'); $(this).parent().addClass('active'); HideIconDropdown($Input); return false; }); $Input.parent() .addClass('icon-input-wrapper') .click(function(e) { e.stopPropagation(); }); $Input.data('initializing', false).data('initialized', true); if (DEBUGICONS === true) console.log('DONE: Initialize'); if (showDropdown === true) ShowIconDropdown($Input); }); } function ShowIconDropdown($Input) { if ($Input.data('initialized') !== true) { InitializeInput($Input, true); } else { if (DEBUGICONS === true) console.log('START: Show Dropdown'); var $Dropdown = $Input.data('dropdown'); if (!$Dropdown.data('opening')) { if (DEBUGICONS === true) console.log(' > Show Dropdown'); $Dropdown .data('opening', true) .stop(true, true) .slideDown('fast', function() { $(this).data('opening', false) }); RefreshIcons($Input, $Dropdown); } if (DEBUGICONS === true) console.log('DONE: Show Dropdown'); } } function HideIconDropdown($Input) { if (DEBUGICONS === true) console.log('START: Hide Dropdown'); var $Dropdown = $Input.data('dropdown'); if (!$Dropdown.data('closing')) { if (DEBUGICONS === true) console.log(' > Hide Dropdown'); $Dropdown .data('closing', true) .stop(true, true) .slideUp('fast', function() { $(this).data('closing', false) }); } if (DEBUGICONS === true) console.log('START: Hide Dropdown'); } function RefreshIcons($Input, $Dropdown) { if (DEBUGICONS === true) console.log('START: Refresh Icons'); var term = $Input.val().replace('fa ','').replace('fas ','').replace('fab ','').replace('fa-',''); var count = 0; if ($Input.data('prev-term') === '' || $Input.data('prev-term') !== term) { if (DEBUGICONS === true) console.log(' > Refresh Icons'); $Input.data('prev-term', term); $Dropdown.find('.active').removeClass('active'); $Dropdown.find('a:hidden').each(function() { var $Icon = $(this); var cssClass = $Icon.data('class'); if (term === '' || cssClass.includes(term)) { $Icon .addClass('visible') .parent() .stop(true, true) .fadeIn('fast'); } }); $Dropdown.find('a').filter(':visible, .visible').each(function() { var $Icon = $(this); var cssClass = $Icon.data('class'); if (term !== '' && !cssClass.includes(term)) { $Icon .removeClass('visible') .parent() .stop(true, true) .fadeOut('fast'); } }); var count = $Dropdown.find('a.visible').length; if (DEBUGICONS === true) console.log(' > Matches: ' + count); if ($Dropdown.is(':hidden') && (term === '' || count > 0)) ShowIconDropdown($Input); else if ($Dropdown.is(':visible') && count <= 0 && term !== '') HideIconDropdown($Input); } if (DEBUGICONS === true) console.log('DONE: Refresh Icons'); } }); {%- endjavascript -%} Add the Shortcode to The Internal Site Add an HTML block to the Footer zone of your Internal Rock Website (Site, not Layout or Page), and set the content to {[ iconpicker ]}. Go to Admin Tools > CMS Configuration > Pages and find a page on the Internal site that uses the Dialog layout. There should be one at Internal Homepage > System Dialogs > ZoneBlocks. Add an HTML block to the Main zone of the Layout, and set the content to {[ iconpicker ]}. All done! You should be able to start typing into nearly any Icon CSS Class field and see a list of matching icons pop up. Follow Up Please don't hesitate to leave a comment or hit me up on Rock Chat (@JeffRichmond) if you have questions or find any issues with this recipe. Change Log 2024-09-13 - Added note about running the workflow after importing it. Removed unnecessary Lava command requirements on the shortcode. Updated section about adding shortcode to the site to be more specific. 2023-05-08 - Updated shortcode markup to use stylesheet and javascript commands to prevent duplicate styles/scripts 2022-09-15 - Initial version Download File