Skip to content

fix: web load svg tag data #2410

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 18 commits into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from 16 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
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@
"react-test-renderer": "^17.0.1",
"reassure": "^0.4.1",
"shell-utils": "^1.0.10",
"typescript": "4.3.2"
"typescript": "4.3.2",
"postcss": "^8.4.21",
"postcss-js": "^4.0.0"
},
"peerDependencies": {
"react": ">=17.0.1",
Expand Down
1 change: 1 addition & 0 deletions src/components/button/ButtonConstants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {ButtonSize} from './ButtonTypes';


export const PADDINGS = {
XSMALL: 3,
SMALL: 4.5,
Expand Down
64 changes: 41 additions & 23 deletions src/components/svgImage/index.web.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,59 @@
import React from 'react';
import {isSvg, isSvgUri} from '../../utils/imageUtils';
// import {SvgPackage} from '../../optionalDependencies';

// const SvgXml = SvgPackage?.SvgXml;
// const SvgCssUri = SvgPackage?.SvgCssUri;
// const SvgProps = SvgPackage?.SvgProps; TODO: not sure how (or if) we can use their props
import React, {useState} from 'react';
import {isSvg, isSvgUri, isBase64ImageContent} from '../../utils/imageUtils';

const EMPTY_STYLE = '{}';
export interface SvgImageProps {
/**
* the asset tint
*/
tintColor?: string | null;
data: any; // TODO: I thought this should be string | React.ReactNode but it doesn't work properly
style?: object[];
}

function SvgImage(props: SvgImageProps) {
// tintColor crashes Android, so we're removing this until we properly support it.
// eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
// const {data, tintColor, ...others} = props;
const {data} = props;
const {
data,
style,
...other
} = props;

const styleObj = Object.assign({}, ...(style || []));

// 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;
// }

const [svgStyleCss, setsvgStyleCss] = useState<string>(EMPTY_STYLE);
const [prossJsStyleCalled, setProssJsStyleCalled] = useState(false);

const createStyleSvgCss = async (JsCssPackage: {postcss: any, cssjs:any}) => {
setProssJsStyleCalled(true);
const {postcss, cssjs} = JsCssPackage;
postcss().process(styleObj, {parser: cssjs})
.then((style: {css: any}) => setsvgStyleCss(`{${style.css}}`));
};

if (isSvgUri(data)) {
return <img src={data.uri}/>;
// return <SvgCssUri {...others} uri={data.uri}/>;
// }
// else if (typeof data === 'string') {
// return <SvgXml xml={data} {...others}/>;
return <img {...other} src={data.uri} style={styleObj}/>;
} else if (isBase64ImageContent(data)) {
return <img {...other} src={data} style={styleObj}/>;
} else if (data) {
return <img src={data}/>;
const JsCssPackage = require('../../optionalDependencies').JsCssPackage;
if (JsCssPackage) {
if (!prossJsStyleCalled) {
createStyleSvgCss(JsCssPackage);
return null;
}
const svgStyleTag = `<style> svg ${svgStyleCss} </style>`;

return (
<div
{...other}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{__html: svgStyleTag + data}}
/>
);
}

}

return null;
}

Expand Down
2 changes: 2 additions & 0 deletions src/optionalDependencies/JsCssPackage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const JsCssPackage = undefined;
export default JsCssPackage;
13 changes: 13 additions & 0 deletions src/optionalDependencies/JsCssPackage.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
let JsCssPackage: {postcss: any, cssjs: any} | undefined;
try {
const postcss = require('postcss');
const cssjs = require('postcss-js');


JsCssPackage = {
postcss,
cssjs
};
} catch (error) {}

export default JsCssPackage;
9 changes: 9 additions & 0 deletions src/optionalDependencies/index.web.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export {default as NetInfoPackage} from './NetInfoPackage';
export {default as HapticFeedbackPackage} from './HapticFeedbackPackage';
export {PickerPackage, CommunityPickerPackage} from './PickerPackage';
export {default as SvgPackage} from './SvgPackage';
export {createShimmerPlaceholder} from './ShimmerPackage';
export {default as LinearGradientPackage} from './LinearGradientPackage';
export {default as AsyncStoragePackage} from './AsyncStoragePackage';
export {default as JsCssPackage} from './JsCssPackage';

7 changes: 7 additions & 0 deletions src/utils/imageUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ export function isSvg(source: ImageProps['source']) {
return typeof source === 'string' || typeof source === 'function' || isSvgUri(source);
}

export function isBase64ImageContent(data: string) {
const base64Content = data.split(',')[1];
const base64regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/;
return base64regex.test(base64Content);
}

export function getAsset(assetName = '', assetGroup = '') {
return get(Assets, `${assetGroup}.${assetName}`);
}

6 changes: 4 additions & 2 deletions webDemo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@
"react-native-shimmer-placeholder": "^2.0.8",
"react-native-svg": "^12.1.0",
"react-native-svg-transformer": "^0.14.3",
"react-native-ui-lib": "snapshot",
"react-native-ui-lib": "6.28.2-snapshot.2397",
"react-native-web": "^0.18.6",
"typescript": "^4.4.2"
"typescript": "^4.4.2",
"postcss": "^8.4.21",
"postcss-js": "^4.0.0"
},
"devDependencies": {
"@babel/core": "^7.14.8",
Expand Down
32 changes: 32 additions & 0 deletions webDemo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,24 @@ interface ItemToRender {
title: string,
FC: React.FC
}
const svgData = `<?xml version="1.0" encoding="UTF-8"?>
<svg data-bbox="2 2 28 28" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" height="800" width="800" data-type="ugc">
<g>
<defs>
<linearGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(.893 0 0 .893 -64.139 -782.556)" y2="878.134" x2="105.452" y1="910.226" x1="73.714" id="1c6ca7ff-eba0-4dd0-82e3-63fdfa256791">
<stop stop-color="#0296d8" offset="0"/>
<stop stop-color="#8371d9" offset="1"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(.893 0 0 .893 -64.139 -782.556)" y2="875.745" x2="102.279" y1="905.226" x1="69.813" id="85cd62d4-a6c1-4ded-b1ca-e6c438f49e1b">
<stop stop-color="#cb55c0" offset="0"/>
<stop stop-color="#f28e0e" offset="1"/>
</linearGradient>
</defs>
<path d="M2 2v28h28v-.047l-6.95-7-6.95-7.007 6.95-7.012L29.938 2Z" fill="url(#1c6ca7ff-eba0-4dd0-82e3-63fdfa256791)"/>
<path d="M16.318 2 2 16.318V30h.124l14.008-14.008-.031-.031L23.05 8.95 29.938 2Z" fill="url(#85cd62d4-a6c1-4ded-b1ca-e6c438f49e1b)"/>
</g>
</svg>
`;

const itemsToRender: ItemToRender[] = [
{
Expand Down Expand Up @@ -91,6 +109,20 @@ const itemsToRender: ItemToRender[] = [
/>
)
},
{
title: 'Button with Svg as <svg> data tag',
FC: () => (
<Button
label={'Svg tag'}
size={Button.sizes.large}
iconSource={svgData}
iconStyle={{
width: 24,
height: 24
}}
/>
)
},
{
title: 'Link Button',
FC: () => (
Expand Down
4 changes: 3 additions & 1 deletion webDemo/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ const baseProjectSource = [
path.resolve(appDirectory, 'node_modules/@react-native-community/netinfo'),
path.resolve(appDirectory, 'node_modules/@react-native-community/datetimepicker'),
path.resolve(appDirectory, 'node_modules/react-native-color'),
path.resolve(appDirectory, 'node_modules/react-native-ui-lib')
path.resolve(appDirectory, 'node_modules/react-native-ui-lib'),
path.resolve(appDirectory, 'node_modules/postcss'),
path.resolve(appDirectory, 'node_modules/postcss-js')
];

const useBabelForRN = {
Expand Down