diff --git a/README.md b/README.md
index 2795268d..d9fb209f 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
# react-native-svg-uri
+This package will finished as a pull request to the package ault-development/react-native-svg-uri but while not feel free to make pull requests
Render SVG images in React Native from an URL or a static file
This was tested with RN 0.33 and react-native-svg 4.3.1 (depends on this library)
@@ -27,6 +28,7 @@ react-native link react-native-svg # not react-native-svg-uri !!!
| `source` | `ImageSource` | | Same kind of `source` prop that `` component has
| `svgXmlData` | `String` | | You can pass the SVG as String directly
| `fill` | `Color` | | Overrides all fill attributes of the svg file
+| `classes` | `Object` | | Overrides attributes of classes (only overrides those attributes present in the given object)
## Known Bugs
@@ -64,4 +66,4 @@ This will render:
## Testing
1. Make sure you have installed dependencies with `npm i`
-2. Run tests with `npm test`
+2. Run tests with `npm test`
\ No newline at end of file
diff --git a/index.js b/index.js
index c6419021..f4d819b6 100644
--- a/index.js
+++ b/index.js
@@ -1,96 +1,37 @@
-import React, {Component} from "react";
-import {View} from 'react-native';
+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,
+ ClipPath,
+ G,
+ LinearGradient,
+ RadialGradient,
+ Line,
+ Path,
+ Polygon,
+ Polyline,
+ Rect,
+ Text,
+ TSpan,
+ Defs,
+ Stop
} from 'react-native-svg';
import * as utils from './utils';
-const ACCEPTED_SVG_ELEMENTS = [
- 'svg',
- 'g',
- 'circle',
- 'path',
- 'rect',
- 'defs',
- 'line',
- 'linearGradient',
- 'radialGradient',
- 'stop',
- 'ellipse',
- 'polygon',
- 'polyline',
- 'text',
- 'tspan'
-];
-
-// Attributes from SVG elements that are mapped directly.
-const SVG_ATTS = ['viewBox', 'width', 'height'];
-const G_ATTS = ['id'];
-
-const CIRCLE_ATTS = ['cx', 'cy', 'r'];
-const PATH_ATTS = ['d'];
-const RECT_ATTS = ['width', 'height'];
-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 ELLIPSE_ATTS = ['cx', 'cy', 'rx', 'ry'];
-
-const TEXT_ATTS = ['fontFamily', 'fontSize', 'fontWeight']
-
-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'];
-
-let ind = 0;
-
-function fixYPosition (y, node) {
- if (node.attributes) {
- const fontSizeAttr = Object.keys(node.attributes).find(a => node.attributes[a].name === 'font-size');
- if (fontSizeAttr) {
- return '' + (parseFloat(y) - parseFloat(node.attributes[fontSizeAttr].value));
- }
- }
- if (!node.parentNode) {
- return y;
- }
- return fixYPosition(y, node.parentNode)
-}
+class SvgUri extends Component {
-class SvgUri extends Component{
-
- constructor(props){
+ constructor(props) {
super(props);
- 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.state = { fill: props.fill, svgXmlData: props.svgXmlData };
- this.isComponentMounted = false;
+ this.isComponentMounted = false;
// Gets the image data from an URL or a static file
if (props.source) {
@@ -99,15 +40,50 @@ class SvgUri extends Component{
}
}
+ tagHandlers = {
+ 'defs': (index, node, childs) =>
+ {childs},
+ 'g': (index, node, childs, styleClasses) =>
+ {childs},
+ 'clipPath': (index, node, childs, styleClasses) =>
+ {childs},
+ 'path': (index, node, childs, styleClasses) =>
+ {childs},
+ 'circle': (index, node, childs, styleClasses) =>
+ {childs},
+ 'rect': (index, node, childs, styleClasses) =>
+ {childs},
+ 'line': (index, node, childs, styleClasses) =>
+ {childs},
+ 'linearGradient': (index, node, childs, styleClasses) =>
+ {childs},
+ 'radialGradient': (index, node, childs, styleClasses) =>
+ {childs},
+ 'stop': (index, node, childs, styleClasses) =>
+ {childs},
+ 'ellipse': (index, node, childs, styleClasses) =>
+ {childs},
+ 'polygon': (index, node, childs, styleClasses) =>
+ {childs},
+ 'polyline': (index, node, childs, styleClasses) =>
+ {childs},
+ 'text': (index, node, childs, styleClasses) =>
+ {childs},
+ 'tspan': (index, node, childs, styleClasses) =>
+ {childs},
+ 'svg': (index, node, childs, styleClasses) =>
+
+ }
+
componentWillMount() {
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);
}
}
@@ -125,171 +101,129 @@ class SvgUri extends Component{
this.isComponentMounted = false
}
- async fetchSVGData(uri){
+ async fetchSVGData(uri) {
let responseXML = null;
try {
const response = await fetch(uri);
responseXML = await response.text();
- } catch(e) {
+ } catch (e) {
console.error("ERROR SVG", e);
} finally {
if (this.isComponentMounted) {
- this.setState({svgXmlData:responseXML});
+ this.setState({ svgXmlData: responseXML });
}
}
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);
- }
+
+ overrideRootElementAttributes(attributes) {
+ if (!attributes.viewBox) {
+ attributes.viewBox = `0 0 ${attributes.width} ${attributes.height}`;
+ }
+ if (this.props.width) {
+ attributes.width = this.props.width;
}
+ if (this.props.height) {
+ attributes.height = this.props.height;
+ }
+ return attributes;
}
- createSVGElement(node, childs){
- this.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;
- }
+ overrideFillAttribute(attributes) {
+ if (this.state.fill && (!attributes.fill || attributes.fill !== 'none')) {
+ attributes.fill = this.state.fill
+ }
+ return attributes;
+ }
- return ;
- 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;
+ getStyleAttsForClass(attributes, styleClasses) {
+ const classObj = Array.from(attributes).find(attr => attr.name === 'class');
+ if (!classObj || !styleClasses) {
+ return {};
}
+ const regex = utils.getRegExpForClassName(classObj.nodeValue)
+ return Object.keys(styleClasses).reduce((aggr, key) => {
+ if(regex.test(key)){
+ Object.assign(aggr, styleClasses[key])
+ }
+ return aggr
+ }, {})
}
- obtainComponentAtts({attributes}, enabledAttributes) {
- const styleAtts = {};
- Array.from(attributes).forEach(({nodeName, nodeValue}) => {
- Object.assign(styleAtts, utils.transformStyle({
- nodeName,
- nodeValue,
- fillProp: this.state.fill
- }));
+ obtainComponentAtts({ attributes }, styleClasses) {
+ const styleAtts = this.getStyleAttsForClass(attributes, styleClasses)
+
+ Array.from(attributes).forEach(({ nodeName, nodeValue }) => {
+ Object.assign(styleAtts, utils.transformStyle({ nodeName, nodeValue }, this.state.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
+ .reduce((acc, { nodeName, nodeValue }) => {
+ acc[nodeName] = nodeValue
return acc
}, {});
+
Object.assign(componentAtts, styleAtts);
- return componentAtts;
+ return this.overrideFillAttribute(componentAtts);
}
- inspectNode(node){
+ // Remove empty strings from children array
+ trimElementChilden = (childrens) => childrens.filter((children) => typeof children !== 'string' || children.trim.length !== 0)
+
+ processNode(node, styleClasses) {
+ // check if is text value
+ if (node.nodeValue) {
+ return node.nodeValue
+ }
+
// Only process accepted elements
- if (!ACCEPTED_SVG_ELEMENTS.includes(node.nodeName)) {
+ if (!this.tagHandlers[node.nodeName]) {
return null;
}
- // Process the xml node
- const arrayElements = [];
-
// 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);
- }
- }
+ let childrens = [];
+ if (node.childNodes) {
+ childrens = Array.from(node.childNodes).reduce((aggr, childNode) => {
+ const node = this.processNode(childNode, styleClasses);
+ if (node) {
+ childrens.push(node);
}
+ return childrens
+ }, childrens)
}
- return this.createSVGElement(node, arrayElements);
+ return this.tagHandlers[node.nodeName](index++, node, this.trimElementChilden(childrens), styleClasses);
}
- render () {
+ index;
+
+ render() {
try {
if (this.state.svgXmlData == null) {
return null;
}
- const inputSVG = this.state.svgXmlData.substring(
- this.state.svgXmlData.indexOf("