Modifying Code Output by a React Component - reactjs

I am using a React library which outputs an svg-icon. I would like to style that icon using linear-gradients. To do so, I need to modify the code that is output. Here is a simplified schema showing what I would like to change
Current Output
<svg>
<path></path>
</svg>
Desired Output
<svg>
<defs>
<linearGradient>
<stop />
<stop />
<linearGradient>
</defs>
<path></path>
</svg>
In other words, I need to some code inject into the svg tags that get output.
Is it possible to do this with React? If so, how?
Thanks.

Related

React rendering SVG overwrites other SVGs on the page

Using babel-plugin-inline-react-svg from within my next.js app, I'm importing some SVGs into my React v16.0.0 component like so.
import React from 'react';
import Close from './close.svg';
import Chevron from './right.svg';
import EmptyCart from './empty.svg';
const Component = props => (
<div>
<Close />
<EmptyCart />
<Chevron />
</div>
);
When I run that code, the page is rendered with the 3 SVGs all being the same, like this:
Whichever of the SVGs I render first seems to take over all of the other ones. If I put <EmptyCart /> first, they'll all be cart icons. But here's the real kicker: When I inspect the DOM, the SVGs seem to all be correct (they're all completely different from each other).
Anyone seen this before? How is this even possible for the DOM to say one thing but the browser to render another thing?
It would be helpful to see the other SVGs as well, but if they are similar and the id's match, then this is your problem.
<path id="4eeded6c-befb-41ba-a055-83a9e4ddc009" d="M3.632 3.182H1.091A1.09 1.09 0 0 1 1.09 1h3.322c.467 0 .883.297 1.033.74l4.096 12.046.036.134c.083.406.53.777.928.78l8.87.056c.39.002.831-.361.925-.816l1.552-6.017a1.09 1.09 0 1 1 2.112.545l-1.539 5.96c-.285 1.417-1.625 2.518-3.064 2.51l-8.869-.057c-1.408-.008-2.718-1.073-3.036-2.451L3.632 3.182zM9.272 23a2.191 2.191 0 0 1-2.181-2.201c0-1.216.977-2.2 2.182-2.2s2.181.984 2.181 2.2A2.191 2.191 0 0 1 9.273 23zm10.91 0A2.191 2.191 0 0 1 18 20.799c0-1.216.977-2.2 2.182-2.2s2.181.984 2.181 2.2A2.191 2.191 0 0 1 20.182 23z"/>
You can see that this id get's targetted and reused within the SVG itself here:
<use xlink:href="#4eeded6c-befb-41ba-a055-83a9e4ddc009"/>
This is a common problem, especially when exporting from apps like photoshop etc. To avoid conflicts when i use svg's I manually change all id's to ensure uniqueness.
If it helps, I've created a code-pen which goes into more examples of how to re-use svg's : https://codepen.io/peter-mouland/pen/JErvZY
You should to assign different id to each svg icon in your config file. Like this:
// SVG are imported as react components
{
test: /\.svg$/,
use: [
{
loader: 'babel-loader',
},
{
loader: 'react-svg-loader',
options: {
svgo: {
plugins: [
{
removeTitle: true,
},
{cleanupIDs: {
prefix: {
toString() {
this.counter = this.counter || 0;
return `id-${this.counter++}`;
}
}
}},
],
floatPrecision: 3,
},
},
},
],
include: paths.svg,
},
The issue can be related to the non-unique ids in SVGs.
It is common that svg generators can return content with the same Ids like <mask id="mask0" /> which then is referenced by <g mask="url(#mask0)"/>.
In case you have two different SVGs with the same mask id you will likely to have an issue with rendering two different icons.
The simplest solution is to specify a unique id for each <mask /> and then don't forget to update the reference in <g />.
I also had a similar issue because i exported images from Figma and i was using them on a project.
So each time i include the other SVG as a component it will override one of the SVG and show the first one.
After a careful check, i noticed that they were actually having the same id and the same image name
having the fill attribute point to the pattern45550 which is my new svg name in my case
<rect width="48" height="52" fill="url(#pattern45550)"/>
renaming the id to pattern45550
<pattern id="pattern45550" patternContentUnits="objectBoundingBox" width="1" height="1">
renaming the image name image10000000 also in the below tag
<image id="image10000000" width="2887" height="3162" xlink:href="data:image/png;base64...
then lastly point the URL to image10000000 image
<use xlink:href="#image10000000" transform="translate(0 -0.00550212) scale(0.00034638)"/>
and it all worked well in my case.
In my case, the conflict between both SVG was because internally they have the same .className
Two solutions:
Change the intern class name in one of them
If it is possible (for example if you are using CRA) load one of the svg using <img src'file.svg'>
In some Case we define styles of our SVG like this
<svg>
<defs>
<style>.a{fill:none;}</style>
</defs>
</svg>
here we define a class name style as .a, in my project all of svgs use the same class name and if I use more than one SVG in DOM then my svgs styles are overwrite and break the design
solution: you should change classname to avoid duplicated class name
I encountered this issue when I try to use multiple svgs downloaded from a figma file on a screen, the other svg override the other. The issue was the class name in each svg was similar. So I edited the class name to prevent them from clashing
The problem is most likely that the IDs are not unique between the SVGs, as has been mentioned above. There are some loaders which can handle this problem for you automatically, so that you don't have to manually change all of the IDs and references to them. Check this out: https://github.com/SilverFox70/svg-react-loader

How could I use SVG icons with the <svg> and <use> tags

I have an Angular 1.5 application that communicate with Salesforce using REST API.
Salesforce require us to use SVG icons with the following syntax:
<svg aria-hidden="true" class="slds-icon slds-icon--large slds-icon-standard-user">
<use xlink:href="{!URLFOR($Asset.SLDS, 'assets/icons/standard-sprite/svg/symbols.svg#user')}"></use>
</svg>
In salesforce does work well.
I'm trying to use locally in order to develop but does not work. I tried the following code:
<svg aria-hidden="true" class="slds-icon slds-icon--large slds-icon-standard-user">
<use xlink:href="/path/to/the/icon.svg"></use>
</svg>
According to this documentation we need to add within the HTML element the following code:
<html xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
But no luck! :(
Any idea to use SVG with those tags and refer to a local file?
Use <img> and set your src reference to the local path of your .svg file.
<img class="papa" src="http://s.cdpn.io/3/kiwi.svg">
More here: https://css-tricks.com/using-svg/

Using Angular Bootstrap directive "uib-popover" with dynamically generated SVG content

I have a case where I am dynamically generating SVG. This is in various forms but, for example, I do things like this;
<rect uib-popover=\"I will show data!\" popover-append-to-body=\"true\" popover-placement=\"bottom\" popover-trigger=\"mouseenter\" id=\"component-#{SecureRandom.uuid}\" x=\"#{pos_x + (component_width / 2)}\" y=\"#{MID_LINE - (component_height / 2)}\" width=\"#{component_width}\" height=\"#{component_height}\" style=\"stroke-width:0.5; stroke:rgb(0,0,0)\" />
The SVG document is sent back to the browser and dynamically injected into a DIV (via an Angular controller).
The problem is that the popover will not appear. It seems as if the page is not "re-scanned" when the content is injected (and therefore the uib-popover is never seen).
I have done some tests to confirm this. For example, a direct placement of SVG in the page works;
<svg>
<circle cx="30" cy="30" r="10" uib-popover="Hello world" popover-title="Title!" popover-append-to-body="true" popover-placement="right" popover-trigger="mouseenter"/>
</svg>
And the popover is displayed.
Doing it this way though, does not work;
<div ng-bind-html="svg"></div>
Where the svg variable is within the controller and is set by;
$scope.svg = $sce.trustAsHtml('<svg><circle cx="30" cy="30" r="10" uib-popover="Hello world" popover-title="Title!" popover-append-to-body="true" popover-placement="right" popover-trigger="mouseenter"/></svg>')
In this case the popover does not appear.
So what am I missing? Is there any method by which I can inject SVG dynamically into a page and have the Angular/Bootstrap directives work? Perhaps some way to tell the directives to re-bind?
Cheers,
Ben

How to put bootstrap tooltip over SVG elements in angularjs

I want try to put bootstrap tooltip over svg element in angularjs, I am not using d3.js, I simply using angularjs. If you give explanation with example that would be preferable.
The trick is just to append it to the body instead of the parent element.
I created here an example with UI Bootstrap angular directives.
<svg>
<circle cx="60" cy="60" r="50" tooltip="Hello world"
tooltip-append-to-body="true" tooltip-placement="right"/>
</svg>
As AngularJS is deprecated, this is how is done in Angular using ng-bootstrap:
<svg>
<circle cx="60" cy="60" r="50" tooltip="Hello world" container="body" placement="right"/>
</svg>
Reference

svg issue : xlink:href not working on ios with cordova and angularjs

I got a strange issue with svg and ios. I'm using angularjs and cordova.
I include an external svg in an angular app, and display it in my view this way :
<svg viewBox="0 0 640 550">
<use xlink:href="#mysvg"></use>
</svg>
In my external svg, I got several g elements with links inside :
<a xlink:href="/path/to/page"></a>
This works fine on android but not on ios when I export the webview with cordova. It works on my iphone simulator...
Can anyone help me with that?
Try adding the xlink namespace to your SVG.
<svg viewBox="0 0 640 550" xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#mysvg"></use>
</svg>
Ok I just succeed with adding the xlink and namespace and put the links inside the svg with the use tag, not in the external svg. I had an other issue with the markup, apparently you have to close every tags otherwise each other tag will be wrapped in the first one, which breaks your layout ordering. For instance <path /> doesn't work and <path></path> works fine.
Finally, I got something like this :
<svg viewBox="0 0 640 550" xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#mysvg"></use>
<a xlink:href="/path/to/page">
<path></path>
<rect></rect>
<text></text>
</a>
</svg>

Resources