diff --git a/index.js b/index.js index 20107ac6..18962cf8 100644 --- a/index.js +++ b/index.js @@ -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'; @@ -78,170 +78,98 @@ 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 {childs}; - case 'g': - componentAtts = this.obtainComponentAtts(node, G_ATTS); - return {childs}; - case 'path': - componentAtts = this.obtainComponentAtts(node, PATH_ATTS); - return {childs}; - case 'circle': - componentAtts = this.obtainComponentAtts(node, CIRCLE_ATTS); - return {childs}; - case 'rect': - componentAtts = this.obtainComponentAtts(node, RECT_ATTS); - return {childs}; - case 'line': - componentAtts = this.obtainComponentAtts(node, LINE_ATTS); - return {childs}; - case 'defs': - return {childs}; - case 'linearGradient': - componentAtts = this.obtainComponentAtts(node, LINEARG_ATTS); - return {childs}; - case 'radialGradient': - componentAtts = this.obtainComponentAtts(node, RADIALG_ATTS); - return {childs}; - case 'stop': - componentAtts = this.obtainComponentAtts(node, STOP_ATTS); - return {childs}; - case 'ellipse': - componentAtts = this.obtainComponentAtts(node, ELLIPSE_ATTS); - return {childs}; - case 'polygon': - componentAtts = this.obtainComponentAtts(node, POLYGON_ATTS); - return {childs}; - case 'polyline': - componentAtts = this.obtainComponentAtts(node, POLYLINE_ATTS); - return {childs}; - case 'text': - componentAtts = this.obtainComponentAtts(node, TEXT_ATTS); - return {childs}; - case 'tspan': - componentAtts = this.obtainComponentAtts(node, TEXT_ATTS); - if (componentAtts.y) { - componentAtts.y = fixYPosition(componentAtts.y, node) - } - return {childs}; - default: - return null; + return {childs}; + case 'g': + componentAtts = obtainComponentAtts(node, G_ATTS); + return {childs}; + case 'path': + componentAtts = obtainComponentAtts(node, PATH_ATTS); + return {childs}; + case 'circle': + componentAtts = obtainComponentAtts(node, CIRCLE_ATTS); + return {childs}; + case 'rect': + componentAtts = obtainComponentAtts(node, RECT_ATTS); + return {childs}; + case 'line': + componentAtts = obtainComponentAtts(node, LINE_ATTS); + return {childs}; + case 'defs': + return {childs}; + case 'linearGradient': + componentAtts = obtainComponentAtts(node, LINEARG_ATTS); + return {childs}; + case 'radialGradient': + componentAtts = obtainComponentAtts(node, RADIALG_ATTS); + return {childs}; + case 'stop': + componentAtts = obtainComponentAtts(node, STOP_ATTS); + return {childs}; + case 'ellipse': + componentAtts = obtainComponentAtts(node, ELLIPSE_ATTS); + return {childs}; + case 'polygon': + componentAtts = obtainComponentAtts(node, POLYGON_ATTS); + return {childs}; + case 'polyline': + componentAtts = obtainComponentAtts(node, POLYLINE_ATTS); + return {childs}; + case 'text': + componentAtts = obtainComponentAtts(node, TEXT_ATTS); + return {childs}; + case 'tspan': + componentAtts = obtainComponentAtts(node, TEXT_ATTS); + if (componentAtts.y) { + componentAtts.y = fixYPosition(componentAtts.y, node) + } + return {childs}; + 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); @@ -249,7 +177,7 @@ class SvgUri extends Component{ return componentAtts; } - inspectNode(node){ + const inspectNode = (node) => { // Only process accepted elements if (!ACCEPTED_SVG_ELEMENTS.includes(node.nodeName)) { return (); @@ -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("") + 6) + const inputSVG = svgXmlData.substring( + svgXmlData.indexOf("") + 6) ).replace(//g, ''); const doc = new xmldom.DOMParser().parseFromString(inputSVG); - const rootSVG = this.inspectNode(doc.childNodes[0]); + const rootSVG = inspectNode(doc.childNodes[0]); return( - - {rootSVG} - + + {rootSVG} + ); } catch(e){ console.error("ERROR SVG", e); return null; } } + + return <>{renderSvgElement()} } SvgUri.propTypes = { diff --git a/package.json b/package.json index 76f6db58..ade57d8e 100644 --- a/package.json +++ b/package.json @@ -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"