UNPKG

@cantoo/rn-svg

Version:
319 lines (307 loc) 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _react = _interopRequireDefault(require("react")); var _reactNative = require("react-native"); var _reactNativeWeb = require("react-native-web"); var _useElementLayout = _interopRequireDefault(require("react-native-web/dist/modules/useElementLayout")); var _usePlatformMethods = _interopRequireDefault(require("react-native-web/dist/modules/usePlatformMethods")); var _useResponderEvents = _interopRequireDefault(require("react-native-web/dist/modules/useResponderEvents")); var _useMergeRefs = _interopRequireDefault(require("react-native-web/dist/modules/useMergeRefs")); var forwardedProps = _interopRequireWildcard(require("react-native-web/dist/modules/forwardedProps")); var _pick = _interopRequireDefault(require("react-native-web/dist/modules/pick")); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const forwardPropsList = { ...forwardedProps.defaultProps, ...forwardedProps.accessibilityProps, ...forwardedProps.clickProps, ...forwardedProps.focusProps, ...forwardedProps.keyboardProps, ...forwardedProps.mouseProps, ...forwardedProps.touchProps, ...forwardedProps.styleProps }; const pickProps = props => (0, _pick.default)(props, forwardPropsList); // rgba values inside range 0 to 1 inclusive // rgbaArray = [r, g, b, a] // argb values inside range 0x00 to 0xff inclusive // int32ARGBColor = 0xaarrggbb /* ColumnMajorTransformMatrix [a, b, c, d, tx, ty] This matrix can be visualized as: ╔═ ═╗ ║ a c tx ║ ║ b d ty ║ ║ 0 0 1 ║ ╚═ ═╝ */ const SvgXml = /*#__PURE__*/_react.default.forwardRef(({ xml, ...props }, forwardedRef) => { const { innerSVG, svgAttributes } = _react.default.useMemo(() => { const { attributes, innerHTML } = parseSVG(xml || ''); return { innerSVG: innerHTML, svgAttributes: kebabToCamel(attributes) }; }, [xml]); const svgRef = _react.default.useRef(null); _react.default.useLayoutEffect(() => { if (!svgRef.current) { return; } svgRef.current.innerHTML = innerSVG; }, [innerSVG]); const { height, width, viewBox, preserveAspectRatio, title, opacity, fill, fillOpacity, fillRule, transform, stroke, strokeWidth, strokeOpacity, strokeDasharray, strokeDashoffset, strokeLinecap, strokeLinejoin, strokeMiterlimit, clipRule, clipPath, vectorEffect, id, markerStart, markerMid, markerEnd, mask, originX, originY, translate, scale, rotation, skewX, skewY, pointerEvents = 'none', style, // props that should be applyed to the View container ...containerProps } = props; const svgTransform = _react.default.useMemo(() => { const transformArray = []; if (originX != null || originY != null) { transformArray.push(`translate(${originX || 0}, ${originY || 0})`); } if (translate != null) { transformArray.push(`translate(${translate})`); } if (scale != null) { transformArray.push(`scale(${scale})`); } // rotation maps to rotate, not to collide with the text rotate attribute (which acts per glyph rather than block) if (rotation != null) { transformArray.push(`rotate(${rotation})`); } if (skewX != null) { transformArray.push(`skewX(${skewX})`); } if (skewY != null) { transformArray.push(`skewY(${skewY})`); } if (originX != null || originY != null) { transformArray.push(`translate(${-(originX || 0)}, ${-(originY || 0)})`); } if (transform) { transformArray.push(transform); } return transformArray.length ? transformArray.join(' ') : undefined; }, [originX, originY, rotation, scale, skewX, skewY, transform, translate]); const containerStyle = _react.default.useMemo(() => { const [,, widthBox, heightBox] = (viewBox || '').split(' '); const { width: styleWidth, height: styleHeight, ...otherStyle } = _reactNative.StyleSheet.flatten(style) || {}; return [styleSheet.view$raw, removeUndefined({ ...otherStyle, display: 'inline-flex', width: parseDimension(width ?? styleWidth ?? svgAttributes.width ?? widthBox), height: parseDimension(height ?? styleHeight ?? svgAttributes.height ?? heightBox) })]; }, [svgAttributes.height, svgAttributes.width, height, style, viewBox, width]); // these props should override the xml props const overrideProps = _react.default.useMemo(() => ({ ...removeUndefined(svgAttributes), ...removeUndefined({ style: styleSheet.svg, transform: svgTransform, viewBox, preserveAspectRatio, title, opacity, fill, fillOpacity, fillRule, stroke, strokeWidth, strokeOpacity, strokeDasharray, strokeDashoffset, strokeLinecap, strokeLinejoin, strokeMiterlimit, clipRule, clipPath, vectorEffect, pointerEvents, id, markerStart, markerMid, markerEnd, mask }), width: undefined, height: undefined }), [svgAttributes, svgTransform, viewBox, preserveAspectRatio, title, opacity, fill, fillOpacity, fillRule, stroke, strokeWidth, strokeOpacity, strokeDasharray, strokeDashoffset, strokeLinecap, strokeLinejoin, strokeMiterlimit, clipRule, clipPath, vectorEffect, pointerEvents, id, markerStart, markerMid, markerEnd, mask]); const Svg = (0, _reactNativeWeb.unstable_createElement)('svg', { ref: svgRef, ...overrideProps }); const { onLayout, onMoveShouldSetResponder, onMoveShouldSetResponderCapture, onResponderEnd, onResponderGrant, onResponderMove, onResponderReject, onResponderRelease, onResponderStart, onResponderTerminate, onResponderTerminationRequest, onStartShouldSetResponder, onStartShouldSetResponderCapture, ...finalProps } = containerProps; const finalContainerProps = pickProps({ ...finalProps, children: Svg, style: containerStyle }); const hostRef = _react.default.useRef(null); (0, _useElementLayout.default)(hostRef, onLayout); const responderConfig = _react.default.useMemo(() => ({ onMoveShouldSetResponder, onMoveShouldSetResponderCapture, onResponderEnd, onResponderGrant, onResponderMove, onResponderReject, onResponderRelease, onResponderStart, onResponderTerminate, onResponderTerminationRequest, onStartShouldSetResponder, onStartShouldSetResponderCapture }), [onMoveShouldSetResponder, onMoveShouldSetResponderCapture, onResponderEnd, onResponderGrant, onResponderMove, onResponderReject, onResponderRelease, onResponderStart, onResponderTerminate, onResponderTerminationRequest, onStartShouldSetResponder, onStartShouldSetResponderCapture]); (0, _useResponderEvents.default)(hostRef, responderConfig); const platformMethodsRef = (0, _usePlatformMethods.default)(finalContainerProps); const setRef = (0, _useMergeRefs.default)(hostRef, platformMethodsRef, forwardedRef); finalContainerProps.ref = setRef; return (0, _reactNativeWeb.unstable_createElement)('span', finalContainerProps); }); SvgXml.displayName = 'Svg'; var _default = exports.default = SvgXml; /** polyfill for Node < 12 */ function matchAll(str) { return re => { const matches = []; let groups; while (groups = re.exec(str)) { matches.push(groups); } return matches; }; } function parseSVG(svg) { const contentMatch = svg.match(/<svg(.*)<\/svg>/ims); const content = contentMatch ? contentMatch[1] : ''; const [, attrs, innerHTML] = content.match(/(.*?)>(.*)/ims) || ['', '', '']; const attributes = [...matchAll(attrs)(/([a-z0-9-]+)(=['"](.*?)['"])?/gims)].map(([, key,, value]) => ({ [key]: value })); return { attributes, innerHTML }; } const styleSheet = _reactNative.StyleSheet.create({ svg: { flexGrow: 1, flexShrink: 1 }, view$raw: { alignItems: 'stretch', backgroundColor: 'transparent', // Hack because those are web props unknown from react-native ['border']: '0 solid black', ['boxSizing']: 'border-box', display: 'inline-flex', flexBasis: 'auto', flexDirection: 'column', color: 'inherit', flexShrink: 0, margin: 0, minHeight: 0, minWidth: 0, padding: 0, position: 'relative', zIndex: 0 } }); function kebabToCamel(attrs) { const camelObj = {}; attrs.forEach(attr => { const key = Object.keys(attr)[0]; camelObj[key.replace(/-./g, x => x.toUpperCase()[1])] = attr[key]; }); return camelObj; } function removeUndefined(obj) { const finalObj = {}; Object.keys(obj).forEach(key => { const value = obj[key]; if (value !== undefined) { finalObj[key] = value; } }); return finalObj; } /** Ensure that the dimension is valid for React Native */ function parseDimension(dimension) { if (typeof dimension === 'number') { return dimension; } else if (!dimension) { return dimension; } else { return dimension.trim().match(/^\d+(\.\d+)?$/) ? Number.parseFloat(dimension) : dimension; } } //# sourceMappingURL=SvgXml.web.js.map