Open ryanb opened 3 years ago
👋 thanks for opening this issue, @ryanb. ✨
If I have a list of a hundred items which all reference the same
inline_svg_tag
image, the content is duplicated for each item.
Do you have a (simplified) example? I would like to understand how the list of items is constructed, the structure of the SVG and the surrounding HTML document if possible. 🤔
Is this something that fits in the scope of
inline_svg
? I understand if not, but it seems like a nice feature.
I'm not 100% sure if this fits the scope of inline_svg
but I'm hoping some example code will help to clarify that a little.
@jamesmartin Sure, something along these lines:
<ul>
<% 100.times do %>
<li><%= inline_svg_tag "handle.svg" %> Item</li>
<% end %>
</ul>
Here the content of the handle.svg
is inlined for every list item. If it's a complex SVG this could result in significantly larger HTML.
It's possible to reference part of another SVG using an id like this:
<svg style="display: none">
<symbol id="handle-icon">...</symbol>
</svg>
<ul>
<% 100.times do %>
<li><svg><use href="#handle-icon" /></svg> Item</li>
<% end %>
</ul>
This way the SVG content is outside of the repeating content.
This is similar to the first attempt taken by GitHub mentioned in this blog post. Looks like they had cross-domain issue preventing them from doing this, but many don't have that issue.
Perhaps it's not worth the hassle of referencing external SVG icons since most of them are small. Just an idea. :)
@ryanb thanks for the examples, that does seem like a useful feature, especially if there is no cross-domain problem in your use-case.
Based on your second example, which is no doubt simplified for clarity, how do you imagine the inline_svg
API would work? The simplest thing I can of is to pass the collection to inline_svg
, which would render the SVG document first, setting the display:none
style (presumably), and then yield each item of the collection to the block, like this:
<ul>
<%= (inline_svg_tag "handle.svg", with_use_tag: 100.times) do |item| %>
<li><svg><use href="#handle-icon" /></svg> <%= item %></li>
<% end %>
</ul>
This assumes the caller would assemble their own slim SVGs in the block, with the relevant <use>
tag. I'm not sure if this actually buys you much though. I feel like I'm still missing something here. 🤔
how do you imagine the inline_svg API would work?
@jamesmartin good question. In my example I included the svg on the same page but I think it would be better done through a separate URL which could join multiple svgs together, perhaps join everything within a directory similar to how a sprite library works.
# app/assets/images/icons/handle.svg
# app/assets/images/icons/login.svg
# app/assets/images/icons/profile.svg
# app/assets/images/icons/settings.svg
Behind the scenes this would generate an app/assets/images/icons.svg
file (maybe through a configuration) which would combine all of the files in the directory under one svg and auto-generate a unique id for each one based on the filename.
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="handle" viewBox="0 0 14 14">...</symbol>
<symbol id="login" viewBox="0 0 14 14">...</symbol>
<symbol id="profile" viewBox="0 0 14 14">...</symbol>
<symbol id="settings" viewBox="0 0 14 14">...</symbol>
</svg>
Then you could do <%= inline_svg_use_tag "icons/handle.svg" %>
which could output something like this:
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"><use href="assets/icons.svg#handle" /></svg>
The icons.svg
file could be cached by the browser similar to images so we have all icons under one request and the generated HTML is very slim.
I understand if this is outside the scope of inline_svg
since it involves some asset pipeline magic. Would be cool to have though.
Update: Looks like there are a number of solutions already out there to help with this.
@ryanb thanks for the extra detail, I understand what you're getting at now.
I understand if this is outside the scope of
inline_svg
since it involves some asset pipeline magic. Would be cool to have though.
inline_svg
does have at least a superficial dependency on the asset pipeline (to find assets) and I'm not against this as a feature. Unfortunately I don't think I'm going to have the bandwidth to work on something like this any time soon. If you, or anybody else, wants to spike this out I'd be happy to help design the API and get PRs merged etc.
I'm interested in this also, although I see the API as an "inline" solution (not assets pipeline). Here's what I'm thinking.
use
tag.)module Helpers
def inline_svg_tag(filename, transform_params={})
with_asset_finder(InlineSvg.configuration.asset_finder) do
- render_inline_svg(filename, transform_params)
+ cache_inline_svg(filename, transform_params)
end
end
+ def cache_inline_svg(*args)
+ @svg_files ||= {}
+ @svg_files[args] ||= render_inline_svg(*args)
+ end
...
Look for use
flag or use_id
flag. SVG use requires a unique id per svg sprite, so it would be a great developer experience to auto-create a unique id if they did not specify one. However, use their id if they did specify one.
inline_svg_tag
with use
param returns:
<svg xmlns="http://www.w3.org/2000/svg">
<use href="#inline-svg-aircraft-a4zde5d" x="20" fill="white" stroke="red"/>
<svg>
Finally, a helper method which combines the unique @svg_files
into a single svg sprite.
<%= inline_svg_sprites_tag, style: 'display: none;' %>
or
<% content_for :svg_sprites do %>
<%= inline_svg_sprites_tag %>
<% end %>
which renders something like
<svg style="display:none;">
<symbol id="inline-svg-aircraft-a4zde5d" viewBox="0 0 62 51">
<path fill="#000000" d="M38.9872..."></path>
</symbol>
<symbol id="inline-svg-attachment-de5da4z" viewBox="0 0 60 64">
<path fill="#000000" d="M15.9264..."></path>
</symbol>
<symbol id="inline-svg-brush-hasd98w" viewBox="0 0 62 62">
<path fill="#000000" d="M7.8416..."></path>
</symbol>
<symbol id="inline-svg-camer-8hx9rf" viewBox="0 0 64 52">
<path fill="#000000" d="M32,19.2 ..."</path>
</symbol>
</svg>
This would certainly speed up pages with lots of SVG files.
FYI, others are interested in this also.
see: https://twitter.com/_swanson/status/1336783159460028421
If I have a list of a hundred items which all reference the same
inline_svg_tag
image, the content is duplicated for each item. It's possible to reduce the generated HTML by inlining the SVG once and referencing it in each item using the \<use> tag.Is this something that fits in the scope of
inline_svg
? I understand if not, but it seems like a nice feature.