1 | import React, { Component } from 'react';
|
2 | import {
|
3 | findNodeHandle,
|
4 | MeasureInWindowOnSuccessCallback,
|
5 | MeasureLayoutOnSuccessCallback,
|
6 | MeasureOnSuccessCallback,
|
7 | NativeModules,
|
8 | StyleSheet,
|
9 | ViewStyle,
|
10 | } from 'react-native';
|
11 | import {
|
12 | ClipProps,
|
13 | Color,
|
14 | extractedProps,
|
15 | FillProps,
|
16 | NumberProp,
|
17 | ResponderInstanceProps,
|
18 | ResponderProps,
|
19 | StrokeProps,
|
20 | TransformProps,
|
21 | } from '../lib/extract/types';
|
22 | import extractResponder from '../lib/extract/extractResponder';
|
23 | import extractViewBox from '../lib/extract/extractViewBox';
|
24 | import extractColor from '../lib/extract/extractColor';
|
25 | import Shape from './Shape';
|
26 | import G from './G';
|
27 | import { RNSVGSvg } from './NativeComponents';
|
28 |
|
29 | const RNSVGSvgViewManager = NativeModules.RNSVGSvgViewManager;
|
30 |
|
31 | const styles = StyleSheet.create({
|
32 | svg: {
|
33 | backgroundColor: 'transparent',
|
34 | borderWidth: 0,
|
35 | },
|
36 | });
|
37 | const defaultStyle = styles.svg;
|
38 |
|
39 | export default class Svg extends Shape<
|
40 | {
|
41 | color?: Color;
|
42 | viewBox?: string;
|
43 | opacity?: NumberProp;
|
44 | onLayout?: () => void;
|
45 | preserveAspectRatio?: string;
|
46 | style?: ViewStyle[] | ViewStyle;
|
47 | } & TransformProps &
|
48 | ResponderProps &
|
49 | StrokeProps &
|
50 | FillProps &
|
51 | ClipProps
|
52 | > {
|
53 | static displayName = 'Svg';
|
54 |
|
55 | static defaultProps = {
|
56 | preserveAspectRatio: 'xMidYMid meet',
|
57 | };
|
58 |
|
59 | measureInWindow = (callback: MeasureInWindowOnSuccessCallback) => {
|
60 | const { root } = this;
|
61 | root && root.measureInWindow(callback);
|
62 | };
|
63 |
|
64 | measure = (callback: MeasureOnSuccessCallback) => {
|
65 | const { root } = this;
|
66 | root && root.measure(callback);
|
67 | };
|
68 |
|
69 | measureLayout = (
|
70 | relativeToNativeNode: number,
|
71 | onSuccess: MeasureLayoutOnSuccessCallback,
|
72 | onFail: () => void ,
|
73 | ) => {
|
74 | const { root } = this;
|
75 | root && root.measureLayout(relativeToNativeNode, onSuccess, onFail);
|
76 | };
|
77 |
|
78 | setNativeProps = (
|
79 | props: Object & {
|
80 | width?: NumberProp;
|
81 | height?: NumberProp;
|
82 | bbWidth?: NumberProp;
|
83 | bbHeight?: NumberProp;
|
84 | },
|
85 | ) => {
|
86 | const { width, height } = props;
|
87 | if (width) {
|
88 | props.bbWidth = width;
|
89 | }
|
90 | if (height) {
|
91 | props.bbHeight = height;
|
92 | }
|
93 | const { root } = this;
|
94 | root && root.setNativeProps(props);
|
95 | };
|
96 |
|
97 | toDataURL = (callback: () => void, options?: Object) => {
|
98 | if (!callback) {
|
99 | return;
|
100 | }
|
101 | const handle = findNodeHandle(this.root as Component);
|
102 | RNSVGSvgViewManager.toDataURL(handle, options, callback);
|
103 | };
|
104 |
|
105 | render() {
|
106 | const {
|
107 | style,
|
108 | opacity,
|
109 | viewBox,
|
110 | children,
|
111 | onLayout,
|
112 | preserveAspectRatio,
|
113 | ...extracted
|
114 | } = this.props;
|
115 | const stylesAndProps = {
|
116 | ...(Array.isArray(style) ? Object.assign({}, ...style) : style),
|
117 | ...extracted,
|
118 | };
|
119 | let {
|
120 | color,
|
121 | width,
|
122 | height,
|
123 | focusable,
|
124 |
|
125 |
|
126 | font,
|
127 | transform,
|
128 | fill,
|
129 | fillOpacity,
|
130 | fillRule,
|
131 | stroke,
|
132 | strokeWidth,
|
133 | strokeOpacity,
|
134 | strokeDasharray,
|
135 | strokeDashoffset,
|
136 | strokeLinecap,
|
137 | strokeLinejoin,
|
138 | strokeMiterlimit,
|
139 | } = stylesAndProps;
|
140 | if (width === undefined && height === undefined) {
|
141 | width = height = '100%';
|
142 | }
|
143 |
|
144 | const props: extractedProps = extracted as extractedProps;
|
145 | props.focusable = Boolean(focusable) && focusable !== 'false';
|
146 | const rootStyles: (ViewStyle | ViewStyle[])[] = [defaultStyle];
|
147 |
|
148 | if (style) {
|
149 | rootStyles.push(style);
|
150 | }
|
151 |
|
152 | let override = false;
|
153 | const overrideStyles: ViewStyle = {};
|
154 | const o = opacity != null ? +opacity : NaN;
|
155 | if (!isNaN(o)) {
|
156 | override = true;
|
157 | overrideStyles.opacity = o;
|
158 | }
|
159 |
|
160 | if (width && height) {
|
161 | override = true;
|
162 | const w = parseInt(width, 10);
|
163 | const h = parseInt(height, 10);
|
164 | const doNotParseWidth = isNaN(w) || width[width.length - 1] === '%';
|
165 | const doNotParseHeight = isNaN(h) || height[height.length - 1] === '%';
|
166 | overrideStyles.width = doNotParseWidth ? width : w;
|
167 | overrideStyles.height = doNotParseHeight ? height : h;
|
168 | overrideStyles.flex = 0;
|
169 | }
|
170 |
|
171 | if (override) {
|
172 | rootStyles.push(overrideStyles);
|
173 | }
|
174 |
|
175 | props.style = rootStyles.length > 1 ? rootStyles : defaultStyle;
|
176 |
|
177 | if (width != null) {
|
178 | props.bbWidth = width;
|
179 | }
|
180 | if (height != null) {
|
181 | props.bbHeight = height;
|
182 | }
|
183 |
|
184 | extractResponder(props, props, this as ResponderInstanceProps);
|
185 |
|
186 | const tint = extractColor(color);
|
187 | if (tint != null) {
|
188 | props.color = tint;
|
189 | props.tintColor = tint;
|
190 | }
|
191 |
|
192 | if (onLayout != null) {
|
193 | props.onLayout = onLayout;
|
194 | }
|
195 |
|
196 | return (
|
197 | <RNSVGSvg
|
198 | {...props}
|
199 | ref={this.refMethod}
|
200 | {...extractViewBox({ viewBox, preserveAspectRatio })}
|
201 | >
|
202 | <G
|
203 | {...{
|
204 | children,
|
205 | style,
|
206 | font,
|
207 | transform,
|
208 | fill,
|
209 | fillOpacity,
|
210 | fillRule,
|
211 | stroke,
|
212 | strokeWidth,
|
213 | strokeOpacity,
|
214 | strokeDasharray,
|
215 | strokeDashoffset,
|
216 | strokeLinecap,
|
217 | strokeLinejoin,
|
218 | strokeMiterlimit,
|
219 | }}
|
220 | />
|
221 | </RNSVGSvg>
|
222 | );
|
223 | }
|
224 | }
|