diff --git a/.gitignore b/.gitignore index eb79dd5f..6ef54e0c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules .idea +.yarnrc +.npmrc \ No newline at end of file diff --git a/index.d.ts b/index.d.ts index 3fd7685f..c3d21ccb 100644 --- a/index.d.ts +++ b/index.d.ts @@ -4,7 +4,7 @@ // TypeScript Version: 2.2.2 import React, { Component } from 'react' -import { ImageURISource } from 'react-native' +import { ImageURISource, StyleProp } from 'react-native' interface SvgUriProps { /** @@ -35,6 +35,11 @@ interface SvgUriProps { */ fill?: string + /** + * Stroke color for the svg object + */ + stroke?: string + /** * Invoked when load completes successfully. */ @@ -44,6 +49,8 @@ interface SvgUriProps { * Fill the entire svg element with same color */ fillAll?: boolean + + style: StyleProp } export default class SvgUri extends Component { } diff --git a/index.js b/index.js index ee69ec70..568813eb 100644 --- a/index.js +++ b/index.js @@ -1,24 +1,24 @@ -import React, {Component} from "react"; -import {View} from 'react-native'; -import PropTypes from 'prop-types' +import React, { Component } from 'react'; +import { View } from 'react-native'; +import PropTypes from 'prop-types'; import xmldom from 'xmldom'; import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; -import Svg,{ - Circle, - Ellipse, - G , - LinearGradient, - RadialGradient, - Line, - Path, - Polygon, - Polyline, - Rect, - Text, - TSpan, - Defs, - Stop +import Svg, { + Circle, + Ellipse, + G, + LinearGradient, + RadialGradient, + Line, + Path, + Polygon, + Polyline, + Rect, + Text, + TSpan, + Defs, + Stop } from 'react-native-svg'; import * as utils from './utils'; @@ -47,25 +47,45 @@ 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']); -const STOP_ATTS = ['offset']; +const STOP_ATTS = ['offset', 'stopColor']; const ELLIPSE_ATTS = ['cx', 'cy', 'rx', 'ry']; -const TEXT_ATTS = ['fontFamily', 'fontSize', 'fontWeight'] +const TEXT_ATTS = ['fontFamily', 'fontSize', 'fontWeight', 'textAnchor']; + +const IMAGE_ATTS = ['width', 'height', 'preserveAspectRatio', 'href', 'clipPath']; const POLYGON_ATTS = ['points']; const POLYLINE_ATTS = ['points']; -const COMMON_ATTS = ['fill', 'fillOpacity', 'stroke', 'strokeWidth', 'strokeOpacity', 'opacity', - 'strokeLinecap', 'strokeLinejoin', - 'strokeDasharray', 'strokeDashoffset', 'x', 'y', 'rotate', 'scale', 'origin', 'originX', 'originY']; +const COMMON_ATTS = [ + 'fill', + 'fillOpacity', + 'stroke', + 'strokeWidth', + 'strokeOpacity', + 'opacity', + 'strokeLinecap', + 'strokeLinejoin', + 'strokeDasharray', + 'strokeDashoffset', + 'x', + 'y', + 'rotate', + 'scale', + 'origin', + 'originX', + 'originY', + 'transform', + 'clipPath' +]; let ind = 0; -function fixYPosition (y, node) { +function fixYPosition(y, node) { if (node.attributes) { const fontSizeAttr = Object.keys(node.attributes).find(a => node.attributes[a].name === 'font-size'); if (fontSizeAttr) { @@ -75,22 +95,22 @@ function fixYPosition (y, node) { if (!node.parentNode) { return y; } - return fixYPosition(y, node.parentNode) + return fixYPosition(y, node.parentNode); } -class SvgUri extends Component{ - - constructor(props){ +class SvgUri extends Component { + constructor(props) { super(props); - this.state = {fill: props.fill, svgXmlData: props.svgXmlData}; + this.state = { fill: props.fill, svgXmlData: props.svgXmlData, stroke: props.stroke }; + // 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.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; + this.isComponentMounted = false; // Gets the image data from an URL or a static file if (props.source) { @@ -103,11 +123,11 @@ class SvgUri extends Component{ this.isComponentMounted = true; } - componentWillReceiveProps (nextProps){ + componentWillReceiveProps(nextProps) { if (nextProps.source) { const source = resolveAssetSource(nextProps.source) || {}; const oldSource = resolveAssetSource(this.props.source) || {}; - if(source.uri !== oldSource.uri){ + if (source.uri !== oldSource.uri) { this.fetchSVGData(source.uri); } } @@ -119,20 +139,25 @@ class SvgUri extends Component{ if (nextProps.fill !== this.props.fill) { this.setState({ fill: nextProps.fill }); } + + if (nextProps.stroke !== this.props.stroke) { + this.setState({ stroke: nextProps.stroke }); + } } componentWillUnmount() { - this.isComponentMounted = false + this.isComponentMounted = false; } async fetchSVGData(uri) { - let responseXML = null, error = null; + let responseXML = null, + error = null; try { const response = await fetch(uri); responseXML = await response.text(); - } catch(e) { + } catch (e) { error = e; - console.error("ERROR SVG", e); + console.error('ERROR SVG', e); } finally { if (this.isComponentMounted) { this.setState({ svgXmlData: responseXML }, () => { @@ -148,114 +173,190 @@ class SvgUri extends Component{ } // Remove empty strings from children array - trimElementChilden(children) { - for (child of children) { + trimElementChildren(children) { + for (let child of children) { if (typeof child === 'string') { - if (child.trim.length === 0) - children.splice(children.indexOf(child), 1); + if (child.trim().length === 0) children.splice(children.indexOf(child), 1); } } } - createSVGElement(node, childs){ - this.trimElementChilden(childs); + createSVGElement(node, childs) { + this.trimElementChildren(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 = this.obtainComponentAtts(node, SVG_ATTS); + if (this.props.width) { + componentAtts.width = this.props.width; + } + if (this.props.height) { + componentAtts.height = this.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); - if (componentAtts.y) { - componentAtts.y = fixYPosition(componentAtts.y, node) - } - 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 = 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); + if (componentAtts.y) { + componentAtts.y = fixYPosition(componentAtts.y, node); + } + return ( + + {childs} + + ); + case 'tspan': + componentAtts = this.obtainComponentAtts(node, TEXT_ATTS); + if (componentAtts.y) { + 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} + + ); + default: + return null; } } - obtainComponentAtts({attributes}, enabledAttributes) { + obtainComponentAtts({ attributes }, enabledAttributes) { const styleAtts = {}; if (this.state.fill && this.props.fillAll) { styleAtts.fill = this.state.fill; } - Array.from(attributes).forEach(({nodeName, nodeValue}) => { - Object.assign(styleAtts, utils.transformStyle({ - nodeName, - nodeValue, - fillProp: this.state.fill - })); + Array.from(attributes).forEach(({ nodeName, nodeValue }) => { + Object.assign( + styleAtts, + utils.transformStyle({ + nodeName, + nodeValue, + fillProp: this.state.fill, + strokeProp: this.state.stroke + }) + ); }); - 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 - return acc + .reduce((acc, { nodeName, nodeValue }) => { + acc[nodeName] = + this.state.fill && nodeName === 'fill' && nodeValue !== 'none' + ? this.state.fill + : this.state.stroke && nodeName === 'stroke' && nodeValue !== 'none' + ? this.state.stroke + : nodeValue; + // acc[nodeName] = + // this.state.fill && nodeName === 'fill' && nodeValue !== 'none' ? this.state.fill : nodeValue; + return acc; }, {}); Object.assign(componentAtts, styleAtts); return componentAtts; } - inspectNode(node){ + inspectNode(node) { // Only process accepted elements if (!ACCEPTED_SVG_ELEMENTS.includes(node.nodeName)) { - return null; + return ; } // Process the xml node @@ -263,45 +364,40 @@ 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); - } + 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); } } + } } return this.createSVGElement(node, arrayElements); } - render () { + render() { try { if (this.state.svgXmlData == null) { return null; } - const inputSVG = this.state.svgXmlData.substring( - this.state.svgXmlData.indexOf("") + 6) - ); + const inputSVG = this.state.svgXmlData + .substring(this.state.svgXmlData.indexOf('') + 6) + .replace(//g, ''); const doc = new xmldom.DOMParser().parseFromString(inputSVG); const rootSVG = this.inspectNode(doc.childNodes[0]); - return( - - {rootSVG} - - ); - } catch(e){ - console.error("ERROR SVG", e); + return {rootSVG}; + } catch (e) { + console.error('ERROR SVG', e); return null; } } @@ -313,9 +409,10 @@ SvgUri.propTypes = { height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), svgXmlData: PropTypes.string, source: PropTypes.any, + strokeProp: this.state.stroke, fill: PropTypes.string, onLoad: PropTypes.func, fillAll: PropTypes.bool -} +}; module.exports = SvgUri; diff --git a/package.json b/package.json index 76f6db58..64fd8f12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "react-native-svg-uri", - "version": "1.2.3", + "name": "react-native-svg-uri-sh", + "version": "0.0.6", "description": "Render an SVG Image from an URL", "main": "index.js", "scripts": { @@ -8,7 +8,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/matiascba/react-native-svg-uri.git" + "url": "git+https://github.com/Youzhigang/react-native-svg-uri.git" }, "keywords": [ "react-native", diff --git a/utils.js b/utils.js index 38c3dd9d..c129400e 100644 --- a/utils.js +++ b/utils.js @@ -10,9 +10,10 @@ export const transformStyle = ({nodeName, nodeValue, fillProp}) => { .reduce((acc, attribute) => { const [property, value] = attribute.split(':'); if (property == "") - return acc; + return acc; else - return {...acc, [camelCase(property)]: fillProp && property === 'fill' ? fillProp : value}; + return { ...acc, [camelCase(property)]: fillProp && property === 'fill' ? fillProp : (strokeProp && property === 'stroke' ? strokeProp : value) }; + // return {...acc, [camelCase(property)]: fillProp && property === 'fill' ? fillProp : value}; }, {}); } return null;