swgs
Version:
swgs is a compatiblity layer between svg and react-native-svg
450 lines (406 loc) • 10.4 kB
JavaScript
import { createElement } from 'react-native-web';
import PropTypes from 'prop-types';
import rip from 'rip-out';
/**
* PropType specification where a value can be represented as number and string.
*
* @type {PropTypes}
* @private
*/
const numb = PropTypes.oneOfType([PropTypes.string, PropTypes.number]);
/**
* Helper function to copy and paste over properties to a different object if
* they exists.
*
* @param {Object} from Object to copy from.
* @param {Object} to Object to paste to.
* @param {String} props Name of the property
* @private
*/
function copypaste(from, to, ...props) {
props.forEach(prop => {
if (prop in from) to[prop] = from[prop];
});
}
/**
* The `react-native-svg` has some crazy api's that do not match with the
* properties that can be applied to SVG elements. This prepare function removes
* those properties and adds the properties back in their correct location.
*
* @param {Object} props Properties given to us.
* @returns {Object} Cleaned object.
* @private
*/
function prepare(props) {
const clean = rip(
props,
'translate',
'scale',
'rotate',
'skewX',
'skewY',
'originX',
'originY',
'fontFamily',
'fontSize',
'fontWeight',
'fontStyle',
'style',
);
const transform = [];
//
// Correctly apply the transformation properties.
// To apply originX and originY we need to translate the element on those values and
// translate them back once the element is scaled, rotated and skewed.
//
if ('originX' in props || 'originY' in props)
transform.push(`translate(${props.originX || 0}, ${props.originY || 0})`);
if ('translate' in props) transform.push(`translate(${props.translate})`);
if ('scale' in props) transform.push(`scale(${props.scale})`);
if ('rotate' in props) transform.push(`rotate(${props.rotate})`);
if ('skewX' in props) transform.push(`skewX(${props.skewX})`);
if ('skewY' in props) transform.push(`skewY(${props.skewY})`);
if ('originX' in props || 'originY' in props)
transform.push(`translate(${-props.originX || 0}, ${-props.originY || 0})`);
if (transform.length) clean.transform = transform.join(' ');
//
// Correctly set the initial style value.
//
const style = 'style' in props ? props.style : {};
//
// This is the nasty part where we depend on React internals to work as
// intended. If we add an empty object as style, it shouldn't render a `style`
// attribute. So we can safely conditionally add things to our `style` object
// and re-introduce it to our `clean` object
//
copypaste(props, style, 'fontFamily', 'fontSize', 'fontWeight', 'fontStyle');
clean.style = style;
//
// React-Native svg provides as a default of `xMidYMid` if aspectRatio is not
// specified with align information. So we need to support this behavior and
// correctly default to `xMidYMid [mode]`.
//
const preserve = clean.preserveAspectRatio;
if (preserve && preserve !== 'none' && !~preserve.indexOf(' ')) {
clean.preserveAspectRatio = 'xMidYMid ' + preserve;
}
return clean;
}
/**
* Return a circle SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Circle SVG.
* @public
*/
function Circle(props) {
return createElement('circle', prepare(props));
}
/**
* Return a clipPath SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} ClipPath SVG.
* @public
*/
function ClipPath(props) {
return createElement('clipPath', prepare(props));
}
/**
* Return a defs SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Defs SVG.
* @public
*/
function Defs(props) {
return createElement('defs', prepare(props));
}
/**
* Return a ellipse SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Ellipse SVG.
* @public
*/
function Ellipse(props) {
return createElement('ellipse', prepare(props));
}
/**
* Return a g SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} G SVG.
* @public
*/
function G(props) {
const { x, y, ...rest } = props;
if ((x || y) && !rest.translate) {
rest.translate = `${x || 0}, ${y || 0}`;
}
return createElement('g', prepare(rest));
}
/**
* PropType validation for the <G />.
*
* @type {Object}
* @private
*/
G.propTypes = {
x: numb,
y: numb
};
/**
* Return a image SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Image SVG.
* @public
*/
function Image(props) {
return createElement('image', prepare(props));
}
/**
* Return a line SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Line SVG.
* @public
*/
function Line(props) {
return createElement('line', prepare(props));
}
/**
* Return a linearGradient SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} LinearGradient SVG.
* @public
*/
function LinearGradient(props) {
return createElement('linearGradient', prepare(props));
}
/**
* Return a path SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Path SVG.
* @public
*/
function Path(props) {
return createElement('path', prepare(props));
}
/**
* Return a polygon SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Polygon SVG.
* @public
*/
function Polygon(props) {
return createElement('polygon', prepare(props));
}
/**
* Return a polyline SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Polyline SVG.
* @public
*/
function Polyline(props) {
return createElement('polyline', prepare(props));
}
/**
* Return a radialGradient SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} RadialGradient SVG.
* @public
*/
function RadialGradient(props) {
return createElement('radialGradient', prepare(props));
}
/**
* Return a rect SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Rect SVG.
* @public
*/
function Rect(props) {
return createElement('rect', prepare(props));
}
/**
* Return a stop SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Stop SVG.
* @public
*/
function Stop(props) {
return createElement('stop', prepare(props));
}
/**
* Return a SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} SVG.
* @public
*/
function Svg(props) {
const { title, ...rest } = props;
if (title) {
return createElement(
'svg',
{ 'role': 'img', 'aria-label': '[title]', ...prepare(rest) },
[createElement('title', {}, title), props.children],
);
}
return createElement('svg', prepare(rest));
}
/**
* PropType validation for the <Svg />.
*
* @type {Object}
* @private
*/
Svg.propTypes = {
title: PropTypes.string,
children: PropTypes.any
};
/**
* Return a symbol SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Symbol SVG.
* @public
*/
function Symbol(props) {
return createElement('symbol', prepare(props));
}
/**
* Return a text SVG element.
*
* @returns {React.Component} Text SVG.
* @public
* @param {Object} props The properties that are spread on the SVG element.
* @param {String} props.x x position
* @param {String} props.y y position
* @param {String} props.dx delta x
* @param {String} props.dy delta y
* @param {String} props.rotate rotation
*/
function Text(props) {
const { x, y, dx, dy, rotate, ...rest } = props;
return createElement('text', {
...prepare(rest),
...{ x, y, dx, dy, rotate }
});
}
/**
* PropType validation for the <Text />.
*
* @type {Object}
* @private
*/
Text.propTypes = {
x: numb,
y: numb,
dx: numb,
dy: numb,
rotate: numb
};
/**
* Return a tspan SVG element.
*
* @returns {React.Component} TSpan SVG.
* @public
* @param {Object} props The properties that are spread on the SVG element.
* @param {String} props.x x position
* @param {String} props.y y position
* @param {String} props.dx delta x
* @param {String} props.dy delta y
* @param {String} props.rotate rotation
*/
function TSpan(props) {
const { x, y, dx, dy, rotate, ...rest } = props;
return createElement('tspan', {
...prepare(rest),
...{ x, y, dx, dy, rotate }
});
}
/**
* PropType validation for the <TSpan />.
*
* @type {Object}
* @private
*/
TSpan.propTypes = Text.propTypes;
/**
* Return a textpath SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} TextPath SVG.
* @public
*/
function TextPath(props) {
return createElement('textPath', prepare(props));
}
/**
* Return a use SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Use SVG.
* @public
*/
function Use(props) {
return createElement('use', prepare(props));
}
/**
* Return a mask SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Use SVG.
* @public
*/
function Mask(props) {
return createElement('mask', prepare(props));
}
/**
* Return a pattern SVG element.
*
* @param {Object} props The properties that are spread on the SVG element.
* @returns {React.Component} Use SVG.
* @public
*/
function Pattern(props) {
return createElement('pattern', prepare(props));
}
//
// Expose everything in the same way as `react-native-svg` is doing.
//
export {
Circle,
ClipPath,
Defs,
Ellipse,
G,
Image,
Line,
LinearGradient,
Mask,
Path,
Pattern,
Polygon,
Polyline,
RadialGradient,
Rect,
Stop,
Svg,
Symbol,
TSpan,
Text,
TextPath,
Use
};
export default Svg;