Variant font-awesome icons do not render in editor toolbars

Xon

Well-known member
Affected version
2.3.6
Despite being marked as resolve in: https://xenforo.com/community/threa...ll-not-render-some-fa-icons-in-editor.222216/

This isn't actually fixed.


A list with the font-awesome icon of fas fa-tools renders as this via the admincp:
XML:
<i class="fa--xf fas fa-tools fa-lg fa-fw "><svg xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true"><use href="/data/local/icons/solid.svg?v=1746352377#tools"></use></svg></i>

But render as like this in the editor:
XML:
<i class="fa--xf far fa- fa-tools "><svg xmlns="[URL]http://www.w3.org/2000/svg[/URL]" role="img" aria-hidden="true">
                  
                    <use href="/data/local/icons/regular.svg?v=1746352377# fa-tools"></use>
                </svg></i>
A custom bb-code button with font-awesome icon with of chalkboard fa-flip-both as this via the admincp:
XML:
<i class="fa--xf far fa-chalkboard fa-flip-both "><svg xmlns="http://www.w3.org/2000/svg" role="img" aria-hidden="true"><use href="/data/local/icons/regular.svg?v=1746352377#chalkboard"></use></svg></i>

But render as like this in the editor:
XML:
<i class="fa--xf far fa-chalkboard fa-flip-both "><svg xmlns="[URL]http://www.w3.org/2000/svg[/URL]" role="img" aria-hidden="true">
                  
                    <use href="/data/local/icons/regular.svg?v=1746352377#chalkboard fa-flip-both"></use>
                </svg></i>

In both cases the <use href="...> link is rendered wrong. fa- fa-tools also doesn't match the admincp vs editor output
 
The button rendering incorrectly looks to be because the editor calls FroalaEditor.DefineIconTemplate/XF.Icon.getIcon to setup the icons which assumes blind substitution to the svg use statement is valid.

The regex in registerCustomCommands doesn't match the full IconRenderer::render behavior for extracting variants:
Code:
registerCustomCommands ()
{
...
faMatch = def.value.match(/^(?:(fa(?:[lrsdb]))\s)?fa-(.+)$/)`


The dropdown doesn't support font-awesome variants as it does this:
Code:
// removes the fa- prefix which we use internally
button.icon = button.icon.substr(3)
...
this.icon.create(info.icon || c)
Which converts to fas fa-tools into fa-tools

Note getButtonsFromCustomBbCodes prefixes the icon, but getButtonsFromDropdowns does not.
 
Fixing the editor dropdown doesn't require too much additional work:
JavaScript:
// removes the fa- prefix which we use internally
button.icon = button.icon.substr(3)
FroalaEditor.DefineIcon(cmd, { NAME: button.icon })

With something like this:
JavaScript:
const icon = XF.Icon.extractIconFromClass(button.icon);
FroalaEditor.DefineIconTemplate(button.icon, XF.Icon.getIcon(icon.variant, icon.name, icon.classes.join(' ')));
FroalaEditor.DefineIcon(cmd, { NAME: button.icon, template: button.icon })
Where extractIconFromClass looks like something like this
JavaScript:
XF.Icon.extractIconFromClass = function (classes) {
        const icon = {variant: "", name: "", classes: []}, possibleNames  = [];
        for (const className of classes.split(' ')) {
            if (className.match(XF.Icon.ICON_CLASS_BLOCKLIST_REGEX)) {
                icon.classes.push(className)
                continue
            }

            if (!icon.variant && ['fal', 'far', 'fas', 'fad', 'fab'].includes(className)) {
                icon.variant = XF.Icon.normalizeIconVariant(className)
                continue
            }

            if (icon.name) {
                icon.classes.push(className)
            } else {
                if (className.match(XF.Icon.ICON_CLASS_REGEX)) {
                    icon.name = XF.Icon.normalizeIconName(className)
                } else {
                    possibleNames.push(className)
                }
            }
        }
        if (!icon.name && possibleNames.length) {
            for (const className of possibleNames) {
                if (icon.name) {
                    icon.classes.push(className)
                } else {
                    icon.name = className;
                }
            }
        }
        if (!icon.variant) {
            icon.variant = 'default'
        }
        return icon
    }
 
Last edited:
Instead of:
JavaScript:
if (def.type == 'fa')
{
    faMatch = def.value.match(/^(?:(fa(?:[lrsdb]))\s)?fa-(.+)$/)
    if (faMatch)
    {
        const variant = faMatch[1] ?? 'fa'
        const name = faMatch[2]

        template = {
            FA5NAME: name,
            template: `${variant}_svg`,
        }
    }
    else
    {
        template = { NAME: def.value }
    }
}
This then fixes dropdown icons:
JavaScript:
if (def.type == 'fa')
{
    const icon = XF.Icon.extractIconFromClass(def.value);
    FroalaEditor.DefineIconTemplate(def.value, XF.Icon.getIcon(icon.variant, icon.name, icon.classes.join(' ')));
    template = { NAME: def.value, template: def.value }
}
 
extractIconFromClass ended up being much more complicated to handle case where fa- isn't part of the class list. There probably is a better way
 
Back
Top Bottom