react-svg-inline
Version:
A React component to inline your SVGs.
146 lines (129 loc) • 3.85 kB
JavaScript
import React, { Component } from "react";
import PropTypes from "prop-types";
import cx from "classnames";
// import styles from "./styles"
const cleanups = {
// some useless stuff for us
// that svgo doesn't remove
title: /<title>.*<\/title>/gi,
desc: /<desc>.*<\/desc>/gi,
comment: /<!--.*-->/gi,
defs: /<defs>.*<\/defs>/gi,
// remove hardcoded dimensions
width: / +width="\d+(\.\d+)?(px)?"/gi,
height: / +height="\d+(\.\d+)?(px)?"/gi,
// remove fill
fill: / +fill="(none|#[0-9a-f]+)"/gi,
// Sketch.app shit
sketchMSShapeGroup: / +sketch:type="MSShapeGroup"/gi,
sketchMSPage: / +sketch:type="MSPage"/gi,
sketchMSLayerGroup: / +sketch:type="MSLayerGroup"/gi
};
// @styled(styles)
class SVGInline extends Component {
render() {
const {
className,
component,
svg,
fill,
width,
accessibilityLabel,
accessibilityDesc,
classSuffix,
cleanupExceptions,
...componentProps
} = this.props;
let { cleanup, height } = this.props;
if (
// simple way to enable entire cleanup
cleanup === true ||
// passing cleanupExceptions enable cleanup as well
(cleanup.length === 0 && cleanupExceptions.length > 0)
) {
cleanup = Object.keys(cleanups);
}
cleanup = cleanup.filter(key => {
return !(cleanupExceptions.indexOf(key) > -1);
});
if (width && height === undefined) {
height = width;
}
// remove useless props for wrapper
delete componentProps.cleanup;
delete componentProps.height;
const classes = cx({
SVGInline: true,
"SVGInline--cleaned": cleanup.length,
[className]: className
});
const svgClasses = classes.split(" ").join(classSuffix + " ") + classSuffix;
let svgStr = SVGInline.cleanupSvg(svg, cleanup).replace(
/<svg/,
`<svg class="${svgClasses}"` +
(fill ? ` fill="${fill}"` : "") +
(width || height
? ' style="' +
(width ? `width: ${width};` : "") +
(height ? `height: ${height};` : "") +
'"'
: "")
);
let match;
if (accessibilityDesc) {
match = /<svg(.|\n|\r\n)*?>/.exec(svgStr);
const pos = match.index + match[0].length;
svgStr =
svgStr.substr(0, pos) +
`<desc>${accessibilityDesc}</desc>` +
svgStr.substr(pos);
}
if (accessibilityLabel) {
match = match || /<svg(.|\n|\r\n)*?>/.exec(svgStr);
const pos = match.index + match[0].length - 1;
const id = `SVGInline-${SVGInline.idCount++}-title`;
svgStr =
svgStr.substr(0, pos) +
` role="img" aria-labelledby="${id}"` +
svgStr.substr(pos, 1) +
`<title id="${id}">${accessibilityLabel}</title>` +
svgStr.substr(pos + 1);
}
return React.createElement(component, {
...componentProps, // take most props
className: classes,
dangerouslySetInnerHTML: {
__html: svgStr
}
});
}
}
SVGInline.propTypes = {
className: PropTypes.string,
classSuffix: PropTypes.string,
component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
svg: PropTypes.string.isRequired,
fill: PropTypes.string,
cleanup: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]),
cleanupExceptions: PropTypes.array,
width: PropTypes.string,
height: PropTypes.string,
accessibilityLabel: PropTypes.string,
accessibilityDesc: PropTypes.string
};
SVGInline.defaultProps = {
component: "span",
classSuffix: "-svg",
cleanup: [],
cleanupExceptions: []
};
SVGInline.idCount = 0;
SVGInline.cleanupSvg = (svg, cleanup = []) => {
return Object.keys(cleanups)
.filter(key => cleanup.indexOf(key) > -1)
.reduce((acc, key) => {
return acc.replace(cleanups[key], "");
}, svg)
.trim();
};
export default SVGInline;