<x-hero::icon name="o:users" size=32></x-hero::icon>
This is how this Heroicons blade component used on a blade template, it uses namespace hero
because I made a laravel package on this alternate implementation - however, we can use this without having to install a laravel package.
Anonymous Component
The component is defined in a single blade file without class association, it has a few props which control the size and the color of the icon.
@props([
'name' => 'o:user',
'size' => 24,
'fill' => 'none' ,
'stroke' => 'currentColor' ])
<svg
id="heroIcon-{{$name}}"
data-group="heroicons"
data-name="{{$name}}"
width="{{$size}}"
height="{{$size}}"
xmlns="http://www.w3.org/2000/svg"
{{$attributes->merge(['class' => 'tj-heroicons'])}}
fill="{{$fill}}"
viewBox="0 0 24 24"
stroke="{{$stroke}}"
aria-hidden="true">
<!-- no path -->
</svg>
The svg tag used as a template for applying props
, without any path. Paths will be populated later by javascript through window.fetch
. This is done n a DOMContentLoaded
event, the fetched svg file content then parsed and apply paths element inside svg in the blade component.
Fetching the svg file
The source of the svg icon should be placed inside the public directory so it is available for us to fetch. In this case, the icons is inside heroicons/icons
url path.
heroicons/
├── icons/
│ ├── outline/
| ├── solid/
Displayed icon on the specific page gathered throuh querying into the data-group attribute of the svg element inside the blade component.
document.addEventListener('DOMContentLoaded', () => {
document
.querySelectorAll('svg[data-group="heroicons"]')
.forEach(heroIcon => {
let iconPropName = heroIcon.dataset.name
let iconPropNameSplitted = iconPropName.split(":")
let iconType = iconPropNameSplitted[0] == 'o' ? 'outline' : 'solid'
let iconName = iconPropNameSplitted[1]
let iconPath = `/heroicons/icons/${iconType}/${iconName}.svg`
window.fetch(request(iconPath))
.then(res => {
if (res.ok) {
return res.text()
}
console.error('Failed to fetch the svg icon.')
})
.then(data => insertPath(data, heroIcon))
.catch(err => console.error(err.message))
})
})
The request argument supplied to window.fetch
is a Request
object, initiated through a function. While the insertPath function is the part where we get the paths out of the text file and inject it into heroIcon
component from the array loop attached as a function argument.
const request = (url) => {
return new Request(url, {
method: 'GET',
redirect: 'follow',
headers: {
'Accept': 'image/svg+xml',
},
})
}
The headers section can be skipped the most important part is the url
and the method
. It shouldn't have problem with CORS - since it is from your own front end.
const insertPath = (svgText, el) => {
let newEl = document.createElement('div')
newEl.innerHTML = svgText
// Standard style
el.style.display = 'inline'
el.style.marginBottom = '.2rem'
newEl.querySelectorAll('path').forEach(p => {
el.appendChild(p)
})
}
I notice that some of the svg icons have more than one path, hence the forEach on the end of the function.
Top comments (0)