Skip to content

Support SVG image #1226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 5, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions demo/src/screens/MenuStructure.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export const navigationData = {
screens: [
{title: 'Native TouchableOpacity', tags: 'touchable native', screen: 'unicorn.incubator.TouchableOpacityScreen'},
{title: '(New) TextField', tags: 'text field input', screen: 'unicorn.components.IncubatorTextFieldScreen'},
{title: 'SVG Image (Incubator)', tags: 'svg image experimental', screen: 'unicorn.incubator.SvgImageScreen'},
{title: 'WheelPicker (Incubator)', tags: 'wheel picker spinner experimental', screen: 'unicorn.incubator.WheelPickerScreen'}
]
},
Expand Down
59 changes: 59 additions & 0 deletions demo/src/screens/incubatorScreens/SvgImageScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, {Component} from 'react';
import {Image, View, Text} from 'react-native-ui-lib';
// @ts-ignore
import {renderBooleanOption, renderRadioGroup} from '../ExampleScreenPresenter';
const file = require('../../../../uilib-docs/src/images/newDesign/headerLogo.svg').default;
const xml = `
<svg width="32" height="32" viewBox="0 0 32 32">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fill="url(#gradient)"
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"
/>
<defs>
<linearGradient
id="gradient"
x1="0"
y1="0"
x2="8.46631"
y2="37.3364"
gradient-units="userSpaceOnUse">
<stop offset="0" stop-color="#FEA267" />
<stop offset="1" stop-color="#E75A4C" />
</linearGradient>
</defs>
</svg>
`;

enum SizeType {
None = '',
Fixed = '50',
Percentage = '50%'
}

export default class SvgImageScreen extends Component {
state = {
isFile: false,
sizeType: SizeType.None
};

render() {
const {isFile, sizeType} = this.state;
const size: any = Number(sizeType) || sizeType;
return (
<View flex padding-page>
<Text h1 marginB-s4>
SVG Image
</Text>
{renderBooleanOption.call(this, isFile ? 'Load from file' : 'Use xml const', 'isFile')}
{renderRadioGroup.call(this, 'Size Type', 'sizeType', SizeType, {isRow: true})}
{size ? (
<Image source={isFile ? file : xml} width={size} height={size}/>
) : (
<Image source={isFile ? file : xml}/>
)}
</View>
);
}
}
1 change: 1 addition & 0 deletions demo/src/screens/incubatorScreens/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export function registerScreens(registrar) {
gestureHandlerRootHOC(require('./TouchableOpacityScreen').default));
registrar('unicorn.components.IncubatorTextFieldScreen', () => require('./IncubatorTextFieldScreen').default);
registrar('unicorn.incubator.WheelPickerScreen', () => gestureHandlerRootHOC(require('./WheelPickerScreen').default));
registrar('unicorn.incubator.SvgImageScreen', () => require('./SvgImageScreen').default);
}
9 changes: 9 additions & 0 deletions generatedTypes/components/image/SvgImage.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/// <reference types="react" />
export interface SvgImageProps {
data: any;
}
declare function SvgImage(props: SvgImageProps): JSX.Element | null;
declare namespace SvgImage {
var displayName: string;
}
export default SvgImage;
2 changes: 2 additions & 0 deletions generatedTypes/components/image/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ declare class Image extends PureComponent<Props, State> {
getVerifiedSource(source?: ImageSourcePropType): any;
getImageSource(): any;
onError: (event: NativeSyntheticEvent<ImageErrorEventData>) => void;
renderSvg: () => JSX.Element;
renderRegularImage(): JSX.Element;
render(): JSX.Element;
}
export { Image };
Expand Down
2 changes: 2 additions & 0 deletions generatedTypes/optionalDependencies/SvgPackage.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare let SvgPackage: any;
export default SvgPackage;
1 change: 1 addition & 0 deletions generatedTypes/optionalDependencies/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as DateTimePickerPackage } from './DateTimePickerPackage';
export { default as BlurViewPackage } from './BlurViewPackage';
export { default as NetInfoPackage } from './NetInfoPackage';
export { PickerPackage, CommunityPickerPackage } from './PickerPackage';
export { default as SvgPackage } from './SvgPackage';
33 changes: 22 additions & 11 deletions metro.config.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
const {getDefaultConfig} = require('metro-config');

/**
* Metro configuration for React Native
* https://github.com/facebook/react-native
*
* @format
*/

module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
};
module.exports = (async () => {
const {
resolver: {sourceExts, assetExts}
} = await getDefaultConfig();
return {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false
}
}),
babelTransformerPath: require.resolve('react-native-svg-transformer')
},
resolver: {
assetExts: assetExts.filter(ext => ext !== 'svg'),
sourceExts: [...sourceExts, 'svg']
}
};
})();
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@
"react-native-keyboard-tracking-view": "^5.6.1",
"react-native-navigation": "7.6.0",
"react-native-reanimated": "1.13.2",
"react-native-svg": "^12.1.0",
"react-native-svg-transformer": "^0.14.3",
"react-test-renderer": "^16.13.1",
"shell-utils": "^1.0.10",
"typescript": "3.9.7"
Expand All @@ -109,6 +111,8 @@
"react-native": ">=0.63.4",
"react-native-gesture-handler": ">=1.9.0",
"react-native-reanimated": ">=1.13.2",
"react-native-svg": "^12.1.0",
"react-native-svg-transformer": "^0.14.3",
"@react-native-community/blur": ">=3.4.1",
"@react-native-community/datetimepicker": "^2.1.0",
"@react-native-community/netinfo": "^5.6.2",
Expand Down
31 changes: 31 additions & 0 deletions src/components/image/SvgImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import {SvgPackage} from '../../optionalDependencies';
const SvgXml = SvgPackage?.SvgXml;
// const SvgProps = SvgPackage?.SvgProps; TODO: not sure how (or if) we can use their props

export interface SvgImageProps {
data: any; // TODO: I thought this should be string | React.ReactNode but it doesn't work properly
}

function SvgImage(props: SvgImageProps) {
const {data, ...others} = props;

if (!SvgXml) {
// eslint-disable-next-line max-len
console.error(`RNUILib Image "svg" prop requires installing "react-native-svg" and "react-native-svg-transformer" dependencies`);
return null;
}

if (typeof data === 'string') {
return <SvgXml xml={data} {...others}/>;
} else if (data) {
const File = data; // Must be with capital letter
return <File {...others}/>;
} else {
return null;
}
}

SvgImage.displayName = 'IGNORE';

export default SvgImage;
18 changes: 17 additions & 1 deletion src/components/image/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {asBaseComponent, ForwardRefInjectedProps, BaseComponentInjectedProps, Ma
// @ts-ignore
import Assets from '../../assets';
import Overlay, {OverlayTypeType} from '../overlay';
import SvgImage from './SvgImage';


export type ImageProps = RNImageProps & MarginModifiers & {
Expand Down Expand Up @@ -153,7 +154,12 @@ class Image extends PureComponent<Props, State> {
}
}

render() {
renderSvg = () => {
const {source, ...others} = this.props;
return <SvgImage data={source} {...others}/>;
}

renderRegularImage() {
const {error} = this.state;
const source = error ? this.getVerifiedSource(this.props.errorSource) : this.getImageSource();
const {
Expand Down Expand Up @@ -196,6 +202,16 @@ class Image extends PureComponent<Props, State> {
</ImageView>
);
}

render() {
const {source} = this.props;
const isSvg = typeof source === 'string' || typeof source === 'function';
if (isSvg) {
return this.renderSvg();
} else {
return this.renderRegularImage();
}
}
}

const styles = StyleSheet.create({
Expand Down
7 changes: 7 additions & 0 deletions src/optionalDependencies/SvgPackage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
let SvgPackage: any;
try {
SvgPackage = require('react-native-svg');
// Sadly we cannot verify with require('react-native-svg-transformer');
} catch (error) {}

export default SvgPackage;
1 change: 1 addition & 0 deletions src/optionalDependencies/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export {default as DateTimePickerPackage} from './DateTimePickerPackage';
export {default as BlurViewPackage} from './BlurViewPackage';
export {default as NetInfoPackage} from './NetInfoPackage';
export {PickerPackage, CommunityPickerPackage} from './PickerPackage';
export {default as SvgPackage} from './SvgPackage';