Related
I'm adding a MaterialUI Tooltip to my React app and I need to have two text lines.
One will be treated as the title main text the other one as a sublabel.
Here in the screenshot, you can see where I stuck
The goal is to have it all in one card as
The label should read: Self-assessment opened: [n]
Sub-label: Click to drill down
An example from the Figma I have which shows how the card should look
I'm new to MUI and not sure how I should do it and this is the code I have so far
import { Box, Popper, Tooltip } from '#mui/material';
import { styled } from '#mui/material/styles';
import { useTheme } from '#mui/styles';
import { useIntl } from 'react-intl';
// Use styles from src/analytics/components/TooltipCard/TooltipCard.js to make it look the same
const TooltipCardPopper = styled(Popper)(({ theme }) => ({
'& > div': {
...theme.typography.caption,
backgroundColor: 'white',
boxShadow: theme.shadows[7],
borderRadius: '5px',
paddingLeft: theme.spacing(1.2),
paddingRight: theme.spacing(1.2),
paddingTop: theme.spacing(0.5),
paddingBottom: theme.spacing(0.5),
borderWidth: 1,
borderStyle: 'solid',
borderColor: theme.palette.grey[300],
},
}));
const calculateTotals = ({ data }) =>
data?.reduce(function (accumulator, item) {
return accumulator + item.total;
}, 0);
const CenteredMetricToolTip = ({ position, data }) => {
const theme = useTheme();
const intl = useIntl();
const show = !!position;
const total = calculateTotals({ data });
return (
<Tooltip
open={show}
disableFocusListener
disableHoverListener
disableTouchListener
disableInteractive
title={intl.formatMessage({ defaultMessage: 'Click to drill down' })}
PopperComponent={TooltipCardPopper}
TransitionProps={{ timeout: 0 }} // timeout more than 0 => transition => causes re-positioning and blinking
>
<Box
sx={{
position: 'absolute',
display: show ? 'block' : 'hide',
left: `${position?.x ?? 0}px`,
top: `${position?.y ?? 0}px`,
}}
>
{intl.formatMessage(
{ defaultMessage: ' Self-assessment opened: {total}' },
{ total },
)}
</Box>
</Tooltip>
);
};
export default CenteredMetricToolTip;
try to change title property value from title={intl.formatMessage({ defaultMessage: 'Click to drill down' })} to
title={
<div>
<div>{intl.formatMessage({ defaultMessage:"Self-assessment opened: [n]" })}<div>
<div>{intl.formatMessage({ defaultMessage:"Click to drill down"})}</div>
<div>
}
on Tooltip Component
Currently trying to figure out how to render the styles(without having to do inline styles) from the styles object for the stepper component. However, it is giving me an error when I try to do something similar to what Material UI's demo gave me. I took bits and pieces from it basically and tried to implement it in my code. Here is what Material UI's demo looks like which I want to replicate as well.
And here is the code they have
const ColorlibStepIconRoot = styled('div')(({ theme, ownerState }) => ({
backgroundColor: theme.palette.mode === 'dark' ? theme.palette.grey[700] : '#ccc',
zIndex: 1,
color: '#fff',
width: 50,
height: 50,
display: 'flex',
borderRadius: '50%',
justifyContent: 'center',
alignItems: 'center',
...(ownerState.active && {
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
boxShadow: '0 4px 10px 0 rgba(0,0,0,.25)',
}),
...(ownerState.completed && {
backgroundImage:
'linear-gradient( 136deg, rgb(242,113,33) 0%, rgb(233,64,87) 50%, rgb(138,35,135) 100%)',
}),
}));
function ColorlibStepIcon(props) {
const { active, completed, className } = props;
const icons = {
1: <SettingsIcon />,
2: <GroupAddIcon />,
3: <VideoLabelIcon />,
};
return (
<ColorlibStepIconRoot ownerState={{ completed, active }} className={className}>
{icons[String(props.icon)]}
</ColorlibStepIconRoot>
);
}
I figured I could scrap the HOC component(color step icon root) and just do without root. Also, they are importing styled from
import { styled } from '#mui/material/styles';
Which when I try to do, its undefined. So I tried to use it in the styles object the way Ive been always doing with withStyles.
But am getting this error:
Here is my code:
import React from 'react';
import { Typography } from '#material-ui/core';
import { withStyles } from '#material-ui/core/styles';
import Stepper from '#material-ui/core/Stepper';
import Step from '#material-ui/core/Step';
import StepLabel from '#material-ui/core/StepLabel';
import Icon from '#material-ui/core/Icon';
import PropTypes from 'prop-types';
const styles = theme => ({
stepLabelRoot: {
fontWeight: 'bold',
color: '#fff',
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
},
checklistHeader: {
fontWeight: 'bold',
marginTop: '80px',
color: 'white'
},
connectorIcon: {
color: theme.palette.text.secondary
},
active: {
backgroundColor: 'green'
},
stepper: {
background: 'none',
fontWeight: 'bold',
height: '500px'
},
checklistImage: {
width: '42px',
height: '42px'
}
});
const steps = ['Are you there?', 'Adopt', 'Buy'];
const Checklist = ({ classes }) => {
const ColorlibStepIcon = ({
icon, completed, className, classes, theme
}) => {
const icons = {
1: <img className={classes.checkListImage} src="https://i.pinimg.com/474x/be/54/bd/be54bd5e8e9c23e3ce570ff2acae592d.jpg" alt="check" />,
2: <img className={classes.checkListImage} src="https://i.pinimg.com/474x/be/54/bd/be54bd5e8e9c23e3ce570ff2acae592d.jpg" alt="check" />,
3: <img className={classes.checkListImage} src="https://i.pinimg.com/474x/be/54/bd/be54bd5e8e9c23e3ce570ff2acae592d.jpg" />,
};
return (
<div ownerState={{ completed }} className={className}>
{icons[String(icon)]}
</div>
);
};
return (
<React.Fragment>
<Typography variant="h6" align="center" gutterBottom className={classes.checklistHeader}>Check the following</Typography>
<Stepper alternativeLabel activeStep={2} className={classes.stepper}>
{steps.map(label => (
<Step key={label}>
<StepLabel classes={{ root: classes.stepLabelRoot }} StepIconComponent={ColorlibStepIcon}>
<div className={classes.stepLabelRoot}>
{label}
</div>
<span>
Lorem Ipsum
</span>
</StepLabel>
</Step>
))}
</Stepper>
</React.Fragment>
);
};
Checklist.defaultProps = {
};
Checklist.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles, { withTheme: true })(Checklist);
You have 2 bugs in your code. In your icon component:
<img className={classes.checkListImage}
The rule name you defined above is called checklistImage not checkListImage, so change it to:
<img className={classes.checklistImage}
Secondly, the classes from the Checklist component is shadowed by the classes inside ColorlibStepIcon:
const ColorlibStepIcon = ({
icon,
completed,
className,
theme,
/*classes <-- this classes is undefined which shadows the real classes
passing from above, so remove it */
}) => {
I am using Native Base Text in 2 different components. In this one by default, the Text is shown in uppercase, unless I add uppercase={false}.
export const ActionButton: React.FunctionComponent<ActionButtonProps> = ({
style,
disabled,
buttonText,
onPress,
}) => {
return (
<Button rounded onPress={onPress} disabled={disabled} style={[styles.button, style]}>
<Text uppercase={false} style={styles.text}>{buttonText}</Text>
</Button>
);
};
const styles = StyleSheet.create({
button: {
width: moderateScale(160),
height: moderateScale(50),
backgroundColor: '#31C283',
justifyContent: 'center',
},
text: { color: 'white', fontSize: moderateScale(13, 0.7), fontWeight:'600' },
});
However, in the following component, text is lowercase, even without using uppercase=false. Why is it different in the two components? What am I doing wrong?
export const TripOptionsSelector: React.FunctionComponent = () => {
const navigation = useNavigation();
return (
<View style={styles.offerContainer}>
<Text style={styles.offerText}>Jetzt</Text>
<Text style={styles.offerText}>1 Person</Text>
<Text style={styles.offerText} onPress={()=>navigation.navigate('TripFilter')}>Filter</Text>
</View>
);
};
const styles = StyleSheet.create({
offerContainer: {
flexDirection: 'row',
},
offerText: {
color: 'white',
marginRight: moderateScale(20),
paddingHorizontal: 10,
fontSize: moderateScale(14),
borderColor: 'white',
borderWidth: 1,
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
},
});
Codesandbox: https://snack.expo.io/#nhammad/trusting-hummus
There is nothing wrong with Text,
actually Native Base Button makes it's child's text Uppercase.
here is the source code https://github.com/GeekyAnts/NativeBase/blob/master/src/basic/Button.js
const children =
Platform.OS === PLATFORM.IOS
? this.props.children
: React.Children.map(this.props.children, child =>
child && child.type === Text
? React.cloneElement(child, {
uppercase: this.props.buttonUppercaseAndroidText === false
? false : variables.buttonUppercaseAndroidText,
...child.props
})
: child
);
I am using React Navigation in React Native app and I want to change the backgroundColor for the header to be gradient and I found out there is a node module : react-native-linear-gradient to achieve gradient in react native.
I have Root StackNavigator like that :
const Router = StackNavigator({
Login: {
screen: Login,
navigationOptions: ({navigation}) => ({
headerTitle: <Text>SomeTitle</Text>
headerLeft: <SearchAndAgent />,
headerRight: <TouchableOpacity
onPress={() => { null }
</TouchableOpacity>,
headerStyle: { backgroundColor: '#005D97' },
}),
},
});
I can wrap Text or View to be gradient like that :
<LinearGradient colors={['#3076A7', '#19398A']}><Text style={styles.title}>{title}</Text></LinearGradient>,
How can I wrap the header background in the navigationOptions to use
the the LinearGradient module?
I know that I can create a custom header component and use it but when I am doing it all the native navigation animations from React Navigation not working like the Title Animation between two Routes so its not helping me.
Thanks for helping !
Just for your information, now with headerBackground props it's a way easier.
You can have a gradient header just doing this :
navigationOptions: {
headerBackground: (
<LinearGradient
colors={['#a13388', '#10356c']}
style={{ flex: 1 }}
start={{x: 0, y: 0}}
end={{x: 1, y: 0}}
/>
),
headerTitleStyle: { color: '#fff' },
}
This solution works good even with SafeArea for IosX
The solution of Mark P was right but now you need to define headerStyle and do the absolute positioning there:
navigationOptions: {
header: props => <GradientHeader {...props} />,
headerStyle: {
backgroundColor: 'transparent',
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
},
},
and the GradientHeader:
const GradientHeader = props => (
<View style={{ backgroundColor: '#eee' }}>
<LinearGradient
colors={['red', 'blue']}
style={[StyleSheet.absoluteFill, { height: Header.HEIGHT }]}
>
<Header {...props} />
</LinearGradient>
</View>
)
Similar to this issue: React Navigation; use image in header?
For a Linear Gradient you would simply do >
//imports
import { Image, StyleSheet, View } from 'react-native';
import { Header } from 'react-navigation' ;
import LinearGradient from 'react-native-linear-gradient';
//header
Create the Header component which is wrapped in the Linear Gradient.
by making the header backgroundColor: 'transparent' you will then show the Linear Gradient wrapping it.
const GradientHeader = props => (
<View style={{ backgroundColor: '#eee' }}>
<LinearGradient
colors={['#00a8c3', '#00373f']}
style={[StyleSheet.absoluteFill, styles.linearGradient]}
/>
<Header {...props} style={{ backgroundColor: 'transparent' }}/>
</View>
);
Return the screen with the header being your GradientHeader component.
const SimpleStack = StackNavigator({
Home: {
screen: MyHomeScreen,
},
}, {
navigationOptions: {
headerTitleStyle: { color: '#fff' },
header: (props) => <GradientHeader {...props} />,
}
});
Should look something like this with the above code.
Gradient Header
You can use LinearGradient component from the expo. It is a useful component and you can't install another library like react-native-linear-gradient. https://docs.expo.io/versions/latest/sdk/linear-gradient/. By the way, you can change the back button. It is easy.
You can implement it on inside screen with navigationOptions like that
static navigationOptions = ({ navigation }: any) => {
const onGoBack = () => navigation.goBack();
return {
header: (props: any) => <GradientHeader {...props} />,
headerStyle: { height: 68, backgroundColor: "transparent", color: colors.white },
headerTitle: "Sign Up",
headerTitleStyle: { color: colors.white, fontSize: 18 },
headerLeft: (
<TouchableOpacity style={{ width: 32, height: 32, paddingLeft: 8 }} onPress={onGoBack}>
<Image source={images.back} resizeMode="center" style={{ width: 32, height: 32 }} />
</TouchableOpacity>
),
};
};
In React Native, how do you change the style of a textInput when it gets focus? Say I have something like
class MyInput extends Component {
render () {
return <TextInput style={styles.textInput} />;
}
};
const stylesObj = {
textInput: {
height: 50,
fontSize: 15,
backgroundColor: 'yellow',
color: 'black',
}
};
const styles = StyleSheet.create(stylesObj);
And I want to change the background color on focus to green.
This documentation leads me to believe that the solution is something like
class MyInput extends Component {
constructor (props) {
super(props);
this.state = {hasFocus: false};
}
render () {
return (<TextInput
style={this.state.hasFocus ? styles.focusedTextInput : styles.textInput}
onFocus={this.setFocus.bind(this, true)}
onBlur={this.setFocus.bind(this, false)}
/>);
}
setFocus (hasFocus) {
this.setState({hasFocus});
}
};
const stylesObj = {
textInput: {
height: 50,
fontSize: 15,
backgroundColor: 'yellow',
color: 'black',
}
};
const styles = StyleSheet.create({
...stylesObj,
focusedTextInput: {
...stylesObj,
backgroundColor: 'green',
}
});
Ignoring potential mistakes in the styles structuring, would this be considered correct way to handle it? It seems very verbose to me.
You can achieve this by passing in the onFocus and onBlur events to set and unset styles when focused and blurred:
onFocus() {
this.setState({
backgroundColor: 'green'
})
},
onBlur() {
this.setState({
backgroundColor: '#ededed'
})
},
And then, in the TextInput do this:
<TextInput
onBlur={ () => this.onBlur() }
onFocus={ () => this.onFocus() }
style={{ height:60, backgroundColor: this.state.backgroundColor, color: this.state.color }} />
I've set up a full working project here. I hope this helps!
https://rnplay.org/apps/hYrKmQ
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
TextInput
} = React;
var SampleApp = React.createClass({
getInitialState() {
return {
backgroundColor: '#ededed',
color: 'white'
}
},
onFocus() {
this.setState({
backgroundColor: 'green'
})
},
onBlur() {
this.setState({
backgroundColor: '#ededed'
})
},
render: function() {
return (
<View style={styles.container}>
<TextInput
onBlur={ () => this.onBlur() }
onFocus={ () => this.onFocus() }
style={{ height:60, backgroundColor: this.state.backgroundColor, color: this.state.color }} />
</View>
);
}
});
var styles = StyleSheet.create({
container: {
flex: 1,
marginTop:60
}
});
AppRegistry.registerComponent('SampleApp', () => SampleApp);
The best way to control the style when the element is focused / blurred is to create your own TextInput wrapper
export const MyAppTextInput = (props) => {
return (
<TextInput
{...props}
/>
);
};
Note that {...props} will pass in any property you already set or available for TextInput.
Then extend the above component by adding state to apply styles when focus / blur
export const MyAppTextInput = (props) => {
const [isFocused, setIsFocused] = useState(false);
return (
<TextInput
{...props}
style={[props.style, isFocused && {borderWidth: 5, borderColor: 'blue'}]}
onBlur={() => setIsFocused(false)}
onFocus={() => setIsFocused(true)}
/>
);
};
And remember when you use the component to bind the value like in the example (see value={passwordText}); otherwise the value will disappear on blur as a new render commences after the state change.
<MyAppTextInput
style={styles.input}
value={passwordText}
textContentType="password"
autoCompleteType="off"
secureTextEntry
onChangeText={text => {
setPasswordText(text);
}}
/>
You can of course avoid creating a wrapper but if you have more than one input it will create a mess in your input(s) parent components as you will have to add repeating logic.
Use refs, DirectManipulation and setNativeProps for more performance: https://facebook.github.io/react-native/docs/direct-manipulation.
class MyInput extends Component {
focusedInput = () => {
this.textInput.setNativeProps({
style: { backgroundColor: 'green' }
})
}
blurredInput = () => {
this.textInput.setNativeProps({
style: { backgroundColor: 'yellow' }
})
}
render () {
return <TextInput
ref={c => { this.textInput = c}}
style={styles.textInput}
onFocus={this.focusedInput}
onBlur={this.blurredInput} />
}
}
const stylesObj = {
textInput: {
height: 50,
fontSize: 15,
backgroundColor: 'yellow',
color: 'black',
}
}
const styles = StyleSheet.create(stylesObj)
This updates the TextInput component directly without re-rendering the component hierarchy.
You can create a state to keep track of the input-state and use that state to toggle the style. Here is a simple example
const App = () => {
const [isActive, setActive] = useState(false);
return (
<TextInput style={{ color: isActive ? 'black' : 'grey' }} onFocus={() => setActive(true)} onBlur={() => setActive(false)}/>
);
}
Nader Dabit´s pointed me to do something similar -- using the state for styles -- but I think it can be done in a cleaner way if you created separate styles for the focused and unfocused style and pass only the style ID as follows:
getInitialState() {
return { style: styles.textinput_unfocused }
}
onFocus() {
this.setState({ style: styles.textinput_focused })
}
onBlur() {
this.setState({ style: styles.textinput_unfocused })
}
in render -- referencing by styleID in this.state.style, note how the different styles are passed as an Array:
<TextInput
onBlur={ () => this.onBlur() }
onFocus={ () => this.onFocus() }
style={ [styles.textinput, this.state.style] } />
+ your stylesheet à la carte:
textinput_focused: {
backgroundColor: 'red',
color: 'white'
}
textinput_unfocused: {
backgroundColor: 'green'
}
Hey guys I kinda used everyones idea :p
#Felix gave me an idea that might be perhaps even cleaner. (I would have loved to not have included state though on this static component, just to change styling... But I am to new to this to figure that out.
here was my solution:
import React, { Component } from 'react';
import { StyleSheet, TextInput } from 'react-native';
class TxtInput extends Component {
constructor(props) {
super(props);
this.state = {
style: {},
};
}
onFocus = () => {
const state = { ...this.state };
state.style = {
borderStyle: 'solid',
borderColor: '#e74712',
};
this.setState(state);
}
onBlur = () => {
console.log('on ONBLUR')
const state = { ...this.state };
state.style = {};
this.setState(state);
}
render = () => <TextInput style={[styles.input, this.state.style]} onFocus={() => this.onFocus()} onBlur={() => this.onBlur()} />;
}
const styles = StyleSheet.create({
input: {
color: '#000000',
fontFamily: 'MuseoSans 700 Italic',
fontSize: 22,
borderRadius: 34,
borderStyle: 'solid',
borderColor: 'transparent',
borderWidth: 5,
backgroundColor: '#ffffff',
textAlign: 'center',
width: '25%',
},
});
export default TxtInput;
I added the style into an array, have all the actual input styling done on the first property of the array and the second one the nit picking of the focus and blue.
Hope it helps
For that propose I design this simple logic in functional component (it works the same in class components with the pertinents changes), it can be apply to several <textinputs/>. Below I leave an example:
// state
const [isFocused, setIsFocused] = useState({
name: false,
email: false,
phone: false,
})
// handlers
const handleInputFocus = (textinput) => {
setIsFocused({
[textinput]: true
})
}
const handleInputBlur = (textinput) => {
setIsFocused({
[textinput]: false
})
}
// JSX
<View style={styles.form} >
<TextInput
style={isFocused.name ? [styles.inputs, { borderColor: 'blue' }] : styles.inputs}
placeholder='Name'
placeholderTextColor={darkColors.text}
textContentType='name'
keyboardType='default'
onFocus={() => handleInputFocus('name')}
onBlur={() => handleInputBlur('name')}
/>
<TextInput
style={isFocused.email ? [styles.inputs, { borderColor: 'blue' }] : styles.inputs}
placeholder='Email'
placeholderTextColor={darkColors.text}
textContentType='emailAddress'
keyboardType='email-address'
onFocus={() => handleInputFocus('email')}
onBlur={() => handleInputBlur('email')}
/>
<TextInput
style={isFocused.phone ? [styles.inputs, { borderColor: 'blue' }] : styles.inputs}
placeholder='Phone'
placeholderTextColor={darkColors.text}
keyboardType='phone-pad'
onFocus={() => handleInputFocus('phone')}
onBlur={() => handleInputBlur('phone')}
/>
</View>
<TextInput
style={{ backgroundColor: 'white', height: 40, width: 100, alignItems: 'center'
}}
theme={{ colors: { placeholder: 'white', text: 'white', primary: 'white',
underlineColor: 'transparent', background: '#003489' } }}
/>
set initial value in function component
const [warnColor, setWrnColor] = useState("grey");
set this in text input
style={[styles.brdColor, { borderColor: warnColor }]}
set this in stylesheet
brdColor: {
height: 40,
borderColor: "grey",
borderBottomWidth: 1,
marginTop: heightToDp("2%"),
width: "100%",
}
Change TextInput border color on focus, by using react hooks useState and useCallback.
const [isFocused, setIsFocused] = useState(false);
const handleFocus = useCallback(() => {
setIsFocused(!isFocused);
}, [isFocused]);
let bcOnFocus = {
borderColor: isFocused ? '#000000' : '#cccccc',
};
<ScrollView>
<View>
<TextInput
placeholder="Text Field"
style={bcOnFocus}
onFocus={handleFocus}
/>
</View>
</ScrollView>