Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
289 changes: 128 additions & 161 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {Component} from "react";
import React, {Component, useState, useEffect} from "react";
import {View} from 'react-native';
import PropTypes from 'prop-types'
import xmldom from 'xmldom';
Expand Down Expand Up @@ -78,178 +78,106 @@ function fixYPosition (y, node) {
return fixYPosition(y, node.parentNode)
}

class SvgUri extends Component{
const SvgUri = (props) => {

constructor(props){
super(props);
const [fill, setFill] = useState(props.fill)
const [source, setSource] = useState(props.source)
const [svgXmlData, setSvgXmlData] = useState(props.svgXmlData)
const [isComponentMounted, setIsComponentMounted] = useState(false)

this.state = {fill: props.fill, svgXmlData: props.svgXmlData};

this.createSVGElement = this.createSVGElement.bind(this);
this.obtainComponentAtts = this.obtainComponentAtts.bind(this);
this.inspectNode = this.inspectNode.bind(this);
this.fetchSVGData = this.fetchSVGData.bind(this);

this.isComponentMounted = false;

// Gets the image data from an URL or a static file
if (props.source) {
const source = resolveAssetSource(props.source) || {};
this.fetchSVGData(source.uri);
}
}

componentWillMount() {
this.isComponentMounted = true;
}

componentWillReceiveProps (nextProps){
if (nextProps.source) {
const source = resolveAssetSource(nextProps.source) || {};
const oldSource = resolveAssetSource(this.props.source) || {};
if(source.uri !== oldSource.uri){
this.fetchSVGData(source.uri);
}
}

if (nextProps.svgXmlData !== this.props.svgXmlData) {
this.setState({ svgXmlData: nextProps.svgXmlData });
}

if (nextProps.fill !== this.props.fill) {
this.setState({ fill: nextProps.fill });
}
}

componentWillUnmount() {
this.isComponentMounted = false
}

async fetchSVGData(uri) {
let responseXML = null, error = null;
try {
const response = await fetch(uri);
responseXML = await response.text();
} catch(e) {
error = e;
console.error("ERROR SVG", e);
} finally {
if (this.isComponentMounted) {
this.setState({ svgXmlData: responseXML }, () => {
const { onLoad } = this.props;
if (onLoad && !error) {
onLoad();
}
});
}
}

return responseXML;
}

// Remove empty strings from children array
trimElementChilden(children) {
for (child of children) {
if (typeof child === 'string') {
if (child.trim().length === 0)
children.splice(children.indexOf(child), 1);
}
}
}

createSVGElement(node, childs){
this.trimElementChilden(childs);
const createSVGElement = (node, childs) => {
const children = trimElementChilden(childs);
let componentAtts = {};
const i = ind++;
switch (node.nodeName) {
case 'svg':
componentAtts = this.obtainComponentAtts(node, SVG_ATTS);
if (this.props.width) {
componentAtts.width = this.props.width;
}
if (this.props.height) {
componentAtts.height = this.props.height;
}
case 'svg':
componentAtts = obtainComponentAtts(node, SVG_ATTS);
if (props.width) {
componentAtts.width = props.width;
}
if (props.height) {
componentAtts.height = props.height;
}

return <Svg key={i} {...componentAtts}>{childs}</Svg>;
case 'g':
componentAtts = this.obtainComponentAtts(node, G_ATTS);
return <G key={i} {...componentAtts}>{childs}</G>;
case 'path':
componentAtts = this.obtainComponentAtts(node, PATH_ATTS);
return <Path key={i} {...componentAtts}>{childs}</Path>;
case 'circle':
componentAtts = this.obtainComponentAtts(node, CIRCLE_ATTS);
return <Circle key={i} {...componentAtts}>{childs}</Circle>;
case 'rect':
componentAtts = this.obtainComponentAtts(node, RECT_ATTS);
return <Rect key={i} {...componentAtts}>{childs}</Rect>;
case 'line':
componentAtts = this.obtainComponentAtts(node, LINE_ATTS);
return <Line key={i} {...componentAtts}>{childs}</Line>;
case 'defs':
return <Defs key={i}>{childs}</Defs>;
case 'linearGradient':
componentAtts = this.obtainComponentAtts(node, LINEARG_ATTS);
return <LinearGradient key={i} {...componentAtts}>{childs}</LinearGradient>;
case 'radialGradient':
componentAtts = this.obtainComponentAtts(node, RADIALG_ATTS);
return <RadialGradient key={i} {...componentAtts}>{childs}</RadialGradient>;
case 'stop':
componentAtts = this.obtainComponentAtts(node, STOP_ATTS);
return <Stop key={i} {...componentAtts}>{childs}</Stop>;
case 'ellipse':
componentAtts = this.obtainComponentAtts(node, ELLIPSE_ATTS);
return <Ellipse key={i} {...componentAtts}>{childs}</Ellipse>;
case 'polygon':
componentAtts = this.obtainComponentAtts(node, POLYGON_ATTS);
return <Polygon key={i} {...componentAtts}>{childs}</Polygon>;
case 'polyline':
componentAtts = this.obtainComponentAtts(node, POLYLINE_ATTS);
return <Polyline key={i} {...componentAtts}>{childs}</Polyline>;
case 'text':
componentAtts = this.obtainComponentAtts(node, TEXT_ATTS);
return <Text key={i} {...componentAtts}>{childs}</Text>;
case 'tspan':
componentAtts = this.obtainComponentAtts(node, TEXT_ATTS);
if (componentAtts.y) {
componentAtts.y = fixYPosition(componentAtts.y, node)
}
return <TSpan key={i} {...componentAtts}>{childs}</TSpan>;
default:
return null;
return <Svg key={i} {...componentAtts}>{childs}</Svg>;
case 'g':
componentAtts = obtainComponentAtts(node, G_ATTS);
return <G key={i} {...componentAtts}>{childs}</G>;
case 'path':
componentAtts = obtainComponentAtts(node, PATH_ATTS);
return <Path key={i} {...componentAtts}>{childs}</Path>;
case 'circle':
componentAtts = obtainComponentAtts(node, CIRCLE_ATTS);
return <Circle key={i} {...componentAtts}>{childs}</Circle>;
case 'rect':
componentAtts = obtainComponentAtts(node, RECT_ATTS);
return <Rect key={i} {...componentAtts}>{childs}</Rect>;
case 'line':
componentAtts = obtainComponentAtts(node, LINE_ATTS);
return <Line key={i} {...componentAtts}>{childs}</Line>;
case 'defs':
return <Defs key={i}>{childs}</Defs>;
case 'linearGradient':
componentAtts = obtainComponentAtts(node, LINEARG_ATTS);
return <LinearGradient key={i} {...componentAtts}>{childs}</LinearGradient>;
case 'radialGradient':
componentAtts = obtainComponentAtts(node, RADIALG_ATTS);
return <RadialGradient key={i} {...componentAtts}>{childs}</RadialGradient>;
case 'stop':
componentAtts = obtainComponentAtts(node, STOP_ATTS);
return <Stop key={i} {...componentAtts}>{childs}</Stop>;
case 'ellipse':
componentAtts = obtainComponentAtts(node, ELLIPSE_ATTS);
return <Ellipse key={i} {...componentAtts}>{childs}</Ellipse>;
case 'polygon':
componentAtts = obtainComponentAtts(node, POLYGON_ATTS);
return <Polygon key={i} {...componentAtts}>{childs}</Polygon>;
case 'polyline':
componentAtts = obtainComponentAtts(node, POLYLINE_ATTS);
return <Polyline key={i} {...componentAtts}>{childs}</Polyline>;
case 'text':
componentAtts = obtainComponentAtts(node, TEXT_ATTS);
return <Text key={i} {...componentAtts}>{childs}</Text>;
case 'tspan':
componentAtts = obtainComponentAtts(node, TEXT_ATTS);
if (componentAtts.y) {
componentAtts.y = fixYPosition(componentAtts.y, node)
}
return <TSpan key={i} {...componentAtts}>{childs}</TSpan>;
default:
return null;
}
}

obtainComponentAtts({attributes}, enabledAttributes) {
const obtainComponentAtts = ({attributes}, enabledAttributes) => {
const styleAtts = {};

if (this.state.fill && this.props.fillAll) {
styleAtts.fill = this.state.fill;
if (fill && props.fillAll) {
styleAtts.fill = fill;
}

Array.from(attributes).forEach(({nodeName, nodeValue}) => {
Object.assign(styleAtts, utils.transformStyle({
nodeName,
nodeValue,
fillProp: this.state.fill
fillProp: fill
}));
});

const componentAtts = Array.from(attributes)
const componentAtts = Array.from(attributes)
.map(utils.camelCaseNodeName)
.map(utils.removePixelsFromNodeValue)
.filter(utils.getEnabledAttributes(enabledAttributes.concat(COMMON_ATTS)))
.reduce((acc, {nodeName, nodeValue}) => {
acc[nodeName] = (this.state.fill && nodeName === 'fill' && nodeValue !== 'none') ? this.state.fill : nodeValue
acc[nodeName] = (fill && nodeName === 'fill' && nodeValue !== 'none') ? fill : nodeValue
return acc
}, {});
Object.assign(componentAtts, styleAtts);

return componentAtts;
}

inspectNode(node){
const inspectNode = (node) => {
// Only process accepted elements
if (!ACCEPTED_SVG_ELEMENTS.includes(node.nodeName)) {
return (<View />);
Expand All @@ -261,47 +189,86 @@ class SvgUri extends Component{
// if have children process them.
// Recursive function.
if (node.childNodes && node.childNodes.length > 0){
for (let i = 0; i < node.childNodes.length; i++){
const isTextValue = node.childNodes[i].nodeValue
if (isTextValue) {
arrayElements.push(node.childNodes[i].nodeValue)
} else {
const nodo = this.inspectNode(node.childNodes[i]);
if (nodo != null) {
arrayElements.push(nodo);
}
for (let i = 0; i < node.childNodes.length; i++){
const isTextValue = node.childNodes[i].nodeValue
if (isTextValue) {
arrayElements.push(node.childNodes[i].nodeValue)
} else {
const nodo = inspectNode(node.childNodes[i]);
if (nodo != null) {
arrayElements.push(nodo);
}
}
}
}

return createSVGElement(node, arrayElements);
}

const fetchSVGData = async(uri) => {
let responseXML = null, error = null;
try {
const response = await fetch(uri);
responseXML = await response.text();
} catch(e) {
error = e;
console.error("ERROR SVG", e);
} finally {
setSvgXmlData(responseXML)
}
}

return this.createSVGElement(node, arrayElements);
const trimElementChilden = (children) => {
for (child of children) {
if (typeof child === 'string') {
if (child.trim().length === 0)
children.splice(children.indexOf(child), 1);
}
}
return children
}

render () {
useEffect(() => {
if (props.source) {
const src = resolveAssetSource(props.source) || {};
if(src.uri){
fetchSVGData(src.uri)
}
}

if (props.fill) {
setFill(props.fill)
}

}, [props.source])

const renderSvgElement = () => {
try {
if (this.state.svgXmlData == null) {
if (svgXmlData == null) {
return null;
}

const inputSVG = this.state.svgXmlData.substring(
this.state.svgXmlData.indexOf("<svg "),
(this.state.svgXmlData.indexOf("</svg>") + 6)
const inputSVG = svgXmlData.substring(
svgXmlData.indexOf("<svg "),
(svgXmlData.indexOf("</svg>") + 6)
).replace(/<!-(.*?)->/g, '');

const doc = new xmldom.DOMParser().parseFromString(inputSVG);

const rootSVG = this.inspectNode(doc.childNodes[0]);
const rootSVG = inspectNode(doc.childNodes[0]);

return(
<View style={this.props.style}>
{rootSVG}
</View>
<View style={props.style}>
{rootSVG}
</View>
);
} catch(e){
console.error("ERROR SVG", e);
return null;
}
}

return <>{renderSvgElement()}</>
}

SvgUri.propTypes = {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"xmldom": "^0.1.22"
},
"peerDependencies": {
"react-native-svg": "^5.3.0"
"react-native-svg": "^12.1.0"
},
"bugs": {
"url": "https://github.com/matiascba/react-native-svg-uri/issues"
Expand Down