Skip to content

fix: [web] svg class selector #2720

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

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all 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
8 changes: 7 additions & 1 deletion src/components/button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,13 @@ class Button extends PureComponent<Props, ButtonState> {
} else {
if (Constants.isWeb) {
return (
<Icon style={iconStyle} tintColor={Colors.$iconDefault} source={iconSource} testID={`${testID}.icon`}/>
<Icon
id={`button_icon_${this.props.id}`}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where the id prop is coming from?
have you declared in Button props? or in Icon component's props?

style={iconStyle}
tintColor={Colors.$iconDefault}
source={iconSource}
testID={`${testID}.icon`}
/>
);
}
return <Image source={iconSource} supportRTL={supportRTL} style={iconStyle} testID={`${testID}.icon`}/>;
Expand Down
68 changes: 34 additions & 34 deletions src/components/svgImage/index.web.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useState} from 'react';
import React, {useState, useEffect} from 'react';
import {StyleSheet} from 'react-native';
import Image from '../image';
import {isSvg, isSvgUri, isBase64ImageContent} from '../../utils/imageUtils';
Expand All @@ -8,27 +8,37 @@ export interface SvgImageProps {
/**
* the asset tint
*/
id?: string;
tintColor?: string | null;
data: any; // TODO: I thought this should be string | React.ReactNode but it doesn't work properly
style?: object[];
}

type CreateStyleSvgCssProps = {
PostCssPackage: {postcss: any; cssjs: any};
styleObj?: Record<string, any>;
className: string
}

function SvgImage(props: SvgImageProps) {
const {data, style = [], tintColor, ...others} = props;
const [svgStyleCss, setSvgStyleCss] = useState<string | undefined>(undefined);
const [postCssStyleCalled, setPostCssStyleCalled] = useState(false);

const createStyleSvgCss = async (PostCssPackage: {postcss: any; cssjs: any}, styleObj?: Record<string, any>) => {
setPostCssStyleCalled(true);
const {postcss, cssjs} = PostCssPackage;
postcss()
.process(styleObj, {parser: cssjs})
.then((style: {css: any}) => {
const svgPathCss = (styleObj?.tintColor) ? `svg path {fill: ${styleObj?.tintColor}}` : '';
setSvgStyleCss(`svg {${style.css}} ${svgPathCss}}`);
});
};

const className = `${props?.id ?? 'svg-icon'} `;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe generate an uniq random id so each classname will have a different scope

useEffect(() => {
const PostCssPackage = require('../../optionalDependencies').PostCssPackage;

const createStyleSvgCss = async ({PostCssPackage, className, styleObj}: CreateStyleSvgCssProps) => {
const {postcss, cssjs} = PostCssPackage;
postcss()
.process(styleObj, {parser: cssjs})
.then((style: {css: any}) => {
const svgPathCss = styleObj?.tintColor ? `.${className} > svg path {fill: ${styleObj?.tintColor}}` : '';
setSvgStyleCss(`.${className} > svg {${style.css}} ${svgPathCss}}`);
});
};
createStyleSvgCss({PostCssPackage, styleObj: StyleSheet.flatten(style), className});
}, [className, style]);

if (isSvgUri(data)) {
return <img {...others} src={data.uri} style={StyleSheet.flatten(style)}/>;
} else if (isBase64ImageContent(data)) {
Expand All @@ -44,26 +54,16 @@ function SvgImage(props: SvgImageProps) {
);
}
return <img {...others} src={data} style={StyleSheet.flatten(style)}/>;
} else if (data) {

const PostCssPackage = require('../../optionalDependencies').PostCssPackage;
if (PostCssPackage) {
if (!postCssStyleCalled) {
createStyleSvgCss(PostCssPackage, StyleSheet.flatten(style));
return null;
}

if (svgStyleCss) {
const svgStyleTag = `<style> ${svgStyleCss} </style>`;
return (
<div
{...others}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{__html: svgStyleTag + data}}
/>
);
}
}
} else if (data && svgStyleCss) {
const svgStyleTag = `<style> ${svgStyleCss} </style>`;
return (
<div
{...others}
className={className}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{__html: svgStyleTag + data}}
/>
);
}
return null;
}
Expand Down
4 changes: 3 additions & 1 deletion webDemo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@react-native-community/netinfo": "^9.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-native": "npm:react-native-web@0.19.4",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's that?
Why do we need this?

"react-native-gesture-handler": "2.9.0",
"react-native-haptic-feedback": "^1.14.0",
"react-native-linear-gradient": "^2.6.2",
Expand All @@ -27,12 +28,13 @@
"react-native-svg": "^12.1.0",
"react-native-svg-transformer": "^0.14.3",
"react-native-ui-lib": "snapshot",
"react-native-web": "^0.18.6",
"react-native-web": "^0.19.4",
"typescript": "^4.4.2",
"postcss": "^8.4.21",
"postcss-js": "^4.0.0"
},
"devDependencies": {
"@babel/preset-env": "^7.19.4",
"@babel/core": "^7.14.8",
"@babel/plugin-proposal-export-namespace-from": "^7.18.9",
"@babel/preset-react": "^7.16.7",
Expand Down
50 changes: 37 additions & 13 deletions webDemo/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useState, useRef} from 'react';
import React, {useState, useRef, useCallback} from 'react';
import {StyleSheet, ScrollView, ActivityIndicator, Animated} from 'react-native';

// import TextField from 'react-native-ui-lib/TextField';
Expand Down Expand Up @@ -94,18 +94,42 @@ 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,
tintColor: 'red'
}}
/>
)
FC: () => {

const [firstBtnIconColor, setFirstBtnIconColor] = useState('red');
const [secondBtnIconColor, setSecondBtnIconColor] = useState('blue');

return (
<>
<Button
id={'first'}
label={'Svg tag - First'}
size={Button.sizes.large}
iconSource={svgData}
iconStyle={{
width: 100,
height: 100,
tintColor: firstBtnIconColor
}}
onPress={() => setFirstBtnIconColor(firstBtnIconColor === 'red' ? 'blue' : 'red')}

/>
<Button
id={'second'}
label={'Svg tag - Second'}
size={Button.sizes.large}
iconSource={svgData}
iconStyle={{
width: 24,
height: 24,
tintColor: secondBtnIconColor
}}
onPress={() => setSecondBtnIconColor(secondBtnIconColor === 'blue' ? 'red' : 'blue')}

/>
</>
);
}
},
{
title: 'Link Button',
Expand Down