diff --git a/index.js b/index.js index c6419021..c92486eb 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,8 @@ import React, {Component} from "react"; import {View} from 'react-native'; import PropTypes from 'prop-types' import xmldom from 'xmldom'; +import Regexp from 'regexp'; + import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; import Svg,{ @@ -18,6 +20,7 @@ import Svg,{ Text, TSpan, Defs, + Image, Stop } from 'react-native-svg'; @@ -38,6 +41,7 @@ const ACCEPTED_SVG_ELEMENTS = [ 'polygon', 'polyline', 'text', + 'image', 'tspan' ]; @@ -47,7 +51,7 @@ const G_ATTS = ['id']; const CIRCLE_ATTS = ['cx', 'cy', 'r']; const PATH_ATTS = ['d']; -const RECT_ATTS = ['width', 'height']; +const RECT_ATTS = ['width', 'height', 'rx', 'ry']; const LINE_ATTS = ['x1', 'y1', 'x2', 'y2']; const LINEARG_ATTS = LINE_ATTS.concat(['id', 'gradientUnits']); const RADIALG_ATTS = CIRCLE_ATTS.concat(['id', 'gradientUnits']); @@ -56,6 +60,8 @@ const ELLIPSE_ATTS = ['cx', 'cy', 'rx', 'ry']; const TEXT_ATTS = ['fontFamily', 'fontSize', 'fontWeight'] +const IMAGE_ATTS = ['width', 'height', 'preserveAspectRatio', 'href', 'clipPath'] + const POLYGON_ATTS = ['points']; const POLYLINE_ATTS = ['points']; @@ -83,7 +89,7 @@ class SvgUri extends Component{ constructor(props){ super(props); - this.state = {fill: props.fill, svgXmlData: props.svgXmlData}; + this.state = {fill: props.fill, stroke: props.stroke, svgXmlData: props.svgXmlData}; this.createSVGElement = this.createSVGElement.bind(this); this.obtainComponentAtts = this.obtainComponentAtts.bind(this); @@ -134,19 +140,20 @@ class SvgUri extends Component{ console.error("ERROR SVG", e); } finally { if (this.isComponentMounted) { + responseXML = this.replaceSVGProps(responseXML); this.setState({svgXmlData:responseXML}); } } return responseXML; } - - // Remove empty strings from children array + + // 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); + children.splice(children.indexOf(child), 1); } } } @@ -207,6 +214,12 @@ class SvgUri extends Component{ componentAtts.y = fixYPosition(componentAtts.y, node) } return {childs}; + case 'image': + componentAtts = this.obtainComponentAtts(node, IMAGE_ATTS); + if (componentAtts.y) { + componentAtts.y = fixYPosition(componentAtts.y, node) + } + return {childs}; case 'tspan': componentAtts = this.obtainComponentAtts(node, TEXT_ATTS); if (componentAtts.y) { @@ -228,12 +241,15 @@ class SvgUri extends Component{ })); }); - 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] = (this.state.href && nodeName === 'href') ? '{{uri: "' + this.state.href + '"}}' : nodeValue + return acc }, {}); Object.assign(componentAtts, styleAtts); @@ -269,6 +285,75 @@ class SvgUri extends Component{ return this.createSVGElement(node, arrayElements); } + replaceSVGProps (svgXmlData) { + if (this.state.fill) { + let regexp = /fill=["']#[A-F0-9]{3,6}["']/gm; + + switch (typeof this.state.fill) { + case 'string': + svgXmlData = svgXmlData.replace(regexp, `fill="${this.state.fill}"`); + break; + + // pass associative key - value object, with example {'#fff': '#000'} + case 'object': + // mappable object + let fill; + const colors = []; + let isArray = false; + Object.keys(this.state.fill).map(key => { + fill = this.state.fill[key].replace('#', ''); + if (isNaN(parseInt(key))) { + svgXmlData = svgXmlData.split('fill="#' + key + '"').join('fill="#' + fill + '"'); + } else { + isArray = true; + } + }); + if (isArray && this.state.fill.length) { + svgXmlData = svgXmlData.split(regexp).map( + (str, i) => this.state.fill[i] ? `${str}fill="#${this.state.fill[i].replace('#', '')}"` : str + ).join(''); + } + break; + default: + throw new Error('unknown color'); + } + } + // stroke + if (this.state.stroke) { + let regexp = /stroke=["']#[A-F0-9]{3,6}["']/gm; + + switch (typeof this.state.stroke) { + case 'string': + svgXmlData = svgXmlData.replace(regexp, `stroke="${this.state.stroke}"`); + break; + + // pass associative key - value object, with example {'#fff': '#000'} + case 'object': + // mappable object + let fill; + const colors = []; + let isArray = false; + Object.keys(this.state.stroke).map(key => { + stroke = this.state.stroke[key].replace('#', ''); + if (isNaN(parseInt(key))) { + svgXmlData = svgXmlData.split('stroke="#' + key + '"').join('stroke="#' + fill + '"'); + } else { + isArray = true; + } + }); + if (isArray && this.state.stroke.length) { + svgXmlData = svgXmlData.split(regexp).map( + (str, i) => this.state.stroke[i] ? `${str}fill="#${this.state.stroke[i].replace('#', '')}"` : str + ).join(''); + } + break; + default: + throw new Error('unknown color'); + } + } + return svgXmlData + } + render () { try { if (this.state.svgXmlData == null) { @@ -302,7 +387,7 @@ SvgUri.propTypes = { height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), svgXmlData: PropTypes.string, source: PropTypes.any, - fill: PropTypes.string, + fill: PropTypes.oneOfType([PropTypes.string, PropTypes.array, PropTypes.shape()]) } module.exports = SvgUri; diff --git a/package.json b/package.json index 140b53db..bd872c07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-svg-uri", - "version": "1.2.1", + "version": "1.2.2", "description": "Render an SVG Image from an URL", "main": "index.js", "scripts": { @@ -23,7 +23,7 @@ "xmldom": "^0.1.22" }, "peerDependencies": { - "react-native-svg": "^5.3.0" + "react-native-svg": "^5.5.1" }, "bugs": { "url": "https://github.com/matiascba/react-native-svg-uri/issues" @@ -34,5 +34,8 @@ "babel-preset-react-native": "^2.1.0", "chai": "^3.5.0", "mocha": "^3.2.0" + }, + "dependencies": { + "regexp": "^1.0.0" } }