Skip to content

Commit aa918ef

Browse files
committed
Support SVG image
1 parent dbdb01f commit aa918ef

File tree

13 files changed

+157
-12
lines changed

13 files changed

+157
-12
lines changed

demo/src/screens/MenuStructure.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ export const navigationData = {
152152
screens: [
153153
{title: 'Native TouchableOpacity', tags: 'touchable native', screen: 'unicorn.incubator.TouchableOpacityScreen'},
154154
{title: '(New) TextField', tags: 'text field input', screen: 'unicorn.components.IncubatorTextFieldScreen'},
155+
{title: 'SVG Image (Incubator)', tags: 'svg image experimental', screen: 'unicorn.incubator.SvgImageScreen'},
155156
{title: 'WheelPicker (Incubator)', tags: 'wheel picker spinner experimental', screen: 'unicorn.incubator.WheelPickerScreen'}
156157
]
157158
},
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React, {Component} from 'react';
2+
import {Image, View, Text} from 'react-native-ui-lib';
3+
// @ts-ignore
4+
import {renderBooleanOption, renderRadioGroup} from '../ExampleScreenPresenter';
5+
const file = require('../../../../uilib-docs/src/images/newDesign/headerLogo.svg').default;
6+
const xml = `
7+
<svg width="32" height="32" viewBox="0 0 32 32">
8+
<path
9+
fill-rule="evenodd"
10+
clip-rule="evenodd"
11+
fill="url(#gradient)"
12+
d="M4 0C1.79086 0 0 1.79086 0 4V28C0 30.2091 1.79086 32 4 32H28C30.2091 32 32 30.2091 32 28V4C32 1.79086 30.2091 0 28 0H4ZM17 6C17 5.44772 17.4477 5 18 5H20C20.5523 5 21 5.44772 21 6V25C21 25.5523 20.5523 26 20 26H18C17.4477 26 17 25.5523 17 25V6ZM12 11C11.4477 11 11 11.4477 11 12V25C11 25.5523 11.4477 26 12 26H14C14.5523 26 15 25.5523 15 25V12C15 11.4477 14.5523 11 14 11H12ZM6 18C5.44772 18 5 18.4477 5 19V25C5 25.5523 5.44772 26 6 26H8C8.55228 26 9 25.5523 9 25V19C9 18.4477 8.55228 18 8 18H6ZM24 14C23.4477 14 23 14.4477 23 15V25C23 25.5523 23.4477 26 24 26H26C26.5523 26 27 25.5523 27 25V15C27 14.4477 26.5523 14 26 14H24Z"
13+
/>
14+
<defs>
15+
<linearGradient
16+
id="gradient"
17+
x1="0"
18+
y1="0"
19+
x2="8.46631"
20+
y2="37.3364"
21+
gradient-units="userSpaceOnUse">
22+
<stop offset="0" stop-color="#FEA267" />
23+
<stop offset="1" stop-color="#E75A4C" />
24+
</linearGradient>
25+
</defs>
26+
</svg>
27+
`;
28+
29+
enum SizeType {
30+
None = '',
31+
Fixed = '50',
32+
Percentage = '50%'
33+
}
34+
35+
export default class SvgImageScreen extends Component {
36+
state = {
37+
isFile: false,
38+
sizeType: SizeType.None
39+
};
40+
41+
render() {
42+
const {isFile, sizeType} = this.state;
43+
const size: any = Number(sizeType) || sizeType;
44+
return (
45+
<View flex padding-page>
46+
<Text h1 marginB-s4>
47+
SVG Image
48+
</Text>
49+
{renderBooleanOption.call(this, isFile ? 'Load from file' : 'Use xml const', 'isFile')}
50+
{renderRadioGroup.call(this, 'Size Type', 'sizeType', SizeType, {isRow: true})}
51+
{size ? (
52+
<Image source={isFile ? file : xml} width={size} height={size}/>
53+
) : (
54+
<Image source={isFile ? file : xml}/>
55+
)}
56+
</View>
57+
);
58+
}
59+
}

demo/src/screens/incubatorScreens/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ export function registerScreens(registrar) {
55
gestureHandlerRootHOC(require('./TouchableOpacityScreen').default));
66
registrar('unicorn.components.IncubatorTextFieldScreen', () => require('./IncubatorTextFieldScreen').default);
77
registrar('unicorn.incubator.WheelPickerScreen', () => gestureHandlerRootHOC(require('./WheelPickerScreen').default));
8+
registrar('unicorn.incubator.SvgImageScreen', () => require('./SvgImageScreen').default);
89
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference types="react" />
2+
export interface SvgImageProps {
3+
data: any;
4+
}
5+
declare function SvgImage(props: SvgImageProps): JSX.Element | null;
6+
declare namespace SvgImage {
7+
var displayName: string;
8+
}
9+
export default SvgImage;

generatedTypes/components/image/index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ declare class Image extends PureComponent<Props, State> {
8181
getVerifiedSource(source?: ImageSourcePropType): any;
8282
getImageSource(): any;
8383
onError: (event: NativeSyntheticEvent<ImageErrorEventData>) => void;
84+
renderSvg: () => JSX.Element;
85+
renderRegularImage(): JSX.Element;
8486
render(): JSX.Element;
8587
}
8688
export { Image };
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
declare let SvgPackage: any;
2+
export default SvgPackage;

generatedTypes/optionalDependencies/index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export { default as DateTimePickerPackage } from './DateTimePickerPackage';
22
export { default as BlurViewPackage } from './BlurViewPackage';
33
export { default as NetInfoPackage } from './NetInfoPackage';
44
export { PickerPackage, CommunityPickerPackage } from './PickerPackage';
5+
export { default as SvgPackage } from './SvgPackage';

metro.config.js

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,28 @@
1+
const {getDefaultConfig} = require('metro-config');
2+
13
/**
24
* Metro configuration for React Native
35
* https://github.com/facebook/react-native
46
*
57
* @format
68
*/
7-
8-
module.exports = {
9-
transformer: {
10-
getTransformOptions: async () => ({
11-
transform: {
12-
experimentalImportSupport: false,
13-
inlineRequires: false,
14-
},
15-
}),
16-
},
17-
};
9+
module.exports = (async () => {
10+
const {
11+
resolver: {sourceExts, assetExts}
12+
} = await getDefaultConfig();
13+
return {
14+
transformer: {
15+
getTransformOptions: async () => ({
16+
transform: {
17+
experimentalImportSupport: false,
18+
inlineRequires: false
19+
}
20+
}),
21+
babelTransformerPath: require.resolve('react-native-svg-transformer')
22+
},
23+
resolver: {
24+
assetExts: assetExts.filter(ext => ext !== 'svg'),
25+
sourceExts: [...sourceExts, 'svg']
26+
}
27+
};
28+
})();

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@
100100
"react-native-keyboard-tracking-view": "^5.6.1",
101101
"react-native-navigation": "7.6.0",
102102
"react-native-reanimated": "1.13.2",
103+
"react-native-svg": "^12.1.0",
104+
"react-native-svg-transformer": "^0.14.3",
103105
"react-test-renderer": "^16.13.1",
104106
"shell-utils": "^1.0.10",
105107
"typescript": "3.9.7"
@@ -109,6 +111,8 @@
109111
"react-native": ">=0.63.4",
110112
"react-native-gesture-handler": ">=1.9.0",
111113
"react-native-reanimated": ">=1.13.2",
114+
"react-native-svg": "^12.1.0",
115+
"react-native-svg-transformer": "^0.14.3",
112116
"@react-native-community/blur": ">=3.4.1",
113117
"@react-native-community/datetimepicker": "^2.1.0",
114118
"@react-native-community/netinfo": "^5.6.2",

src/components/image/SvgImage.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react';
2+
import {SvgPackage} from '../../optionalDependencies';
3+
const SvgXml = SvgPackage?.SvgXml;
4+
// const SvgProps = SvgPackage?.SvgProps; TODO: not sure how (or if) we can use their props
5+
6+
export interface SvgImageProps {
7+
data: any; // TODO: I thought this should be string | React.ReactNode but it doesn't work properly
8+
}
9+
10+
function SvgImage(props: SvgImageProps) {
11+
const {data, ...others} = props;
12+
13+
if (!SvgXml) {
14+
// eslint-disable-next-line max-len
15+
console.error(`RNUILib Image "svg" prop requires installing "react-native-svg" and "react-native-svg-transformer" dependencies`);
16+
return null;
17+
}
18+
19+
if (typeof data === 'string') {
20+
return <SvgXml xml={data} {...others}/>;
21+
} else if (data) {
22+
const File = data; // Must be with capital letter
23+
return <File {...others}/>;
24+
} else {
25+
return null;
26+
}
27+
}
28+
29+
SvgImage.displayName = 'IGNORE';
30+
31+
export default SvgImage;

src/components/image/index.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {asBaseComponent, ForwardRefInjectedProps, BaseComponentInjectedProps, Ma
1616
// @ts-ignore
1717
import Assets from '../../assets';
1818
import Overlay, {OverlayTypeType} from '../overlay';
19+
import SvgImage from './SvgImage';
1920

2021

2122
export type ImageProps = RNImageProps & MarginModifiers & {
@@ -153,7 +154,12 @@ class Image extends PureComponent<Props, State> {
153154
}
154155
}
155156

156-
render() {
157+
renderSvg = () => {
158+
const {source, ...others} = this.props;
159+
return <SvgImage data={source} {...others}/>;
160+
}
161+
162+
renderRegularImage() {
157163
const {error} = this.state;
158164
const source = error ? this.getVerifiedSource(this.props.errorSource) : this.getImageSource();
159165
const {
@@ -196,6 +202,16 @@ class Image extends PureComponent<Props, State> {
196202
</ImageView>
197203
);
198204
}
205+
206+
render() {
207+
const {source} = this.props;
208+
const isSvg = typeof source === 'string' || typeof source === 'function';
209+
if (isSvg) {
210+
return this.renderSvg();
211+
} else {
212+
return this.renderRegularImage();
213+
}
214+
}
199215
}
200216

201217
const styles = StyleSheet.create({
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
let SvgPackage: any;
2+
try {
3+
SvgPackage = require('react-native-svg');
4+
// Sadly we cannot verify with require('react-native-svg-transformer');
5+
} catch (error) {}
6+
7+
export default SvgPackage;

src/optionalDependencies/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export {default as DateTimePickerPackage} from './DateTimePickerPackage';
22
export {default as BlurViewPackage} from './BlurViewPackage';
33
export {default as NetInfoPackage} from './NetInfoPackage';
44
export {PickerPackage, CommunityPickerPackage} from './PickerPackage';
5+
export {default as SvgPackage} from './SvgPackage';

0 commit comments

Comments
 (0)