React: removing / adding classes from a div onClick w/ multiple buttons / classes - css

Question
I am trying to convert some of my jQuery projects over to use ReactJS. I would like to add/remove individual classes for background color, border, shape, size, etc. I want to be able to use many options (like 20 colors). If I add a background color, I want to remove the current background color without removing the current border, shape, or size classes. Is there a way to do this?
Research
I have read many posts on altering the buttons on hover, on toggling a class on/off, and changing out one class for another, but these have not pointed me in the right direction.
Image
More Details
If I click the bg_blue button, I would like the background to change without loosing the red border.
If I click the border_gray button, I would like it to change without loosing the current background color.
Start Code
import React from 'react';
var classNames = require( 'classnames' );
export class Body extends React.Component {
render() {
const red = {
backgroundColor: "red"
};
const gray = {
backgroundColor: "gray"
};
const blue = {
backgroundColor: "blue"
};
const border_red = {
borderWidth: 3,
borderColor: "red",
borderStyle: "solid"
};
const border_gray = {
borderWidth: 3,
borderColor: "gray",
borderStyle: "solid"
};
const border_blue = {
borderWidth: 3,
borderColor: "blue",
borderStyle: "solid"
};
return (
<div className="App-body">
<div className="start-shape" style= {border_red} ></div>
<button className="button" onClick="">bg_Red</button>
<button className="button">bg_Gray</button>
<button className="button">bg_Blue</button>
<button className="button">border_Red</button>
<button className="button">border_Gray</button>
<button className="button">border_Blue</button>
</div>
);
}
}

Here you go :
//intial style
state = {
borderWidth: 3,
borderColor: "red",
borderStyle: "solid"
};
// then just update/overwrite with new one
setStyle(new_style) {
this.setState(state => ({ ...state, ...new_style }));
}
// by click and passing the values
onClick={() => this.setStyle(gray)}
Here you check the code by running the code snippet :
class App extends React.Component {
state = {
borderWidth: 3,
borderColor: "red",
borderStyle: "solid"
};
setStyle(new_style) {
this.setState(state => ({ ...state, ...new_style }));
}
render() {
const red = {
backgroundColor: "red"
};
const gray = {
backgroundColor: "gray"
};
const blue = {
backgroundColor: "blue"
};
const border_red = {
borderWidth: 3,
borderColor: "red",
borderStyle: "solid"
};
const border_gray = {
borderWidth: 3,
borderColor: "gray",
borderStyle: "solid"
};
const border_blue = {
borderWidth: 3,
borderColor: "blue",
borderStyle: "solid"
};
return (
<div className="App-body">
<div className="start-shape" style={this.state} />
<button className="button" onClick={() => this.setStyle(red)}>
bg_Red
</button>
<button className="button" onClick={() => this.setStyle(gray)}>
bg_Gray
</button>
<button className="button" onClick={() => this.setStyle(blue)}>
bg_Blue
</button>
<button className="button" onClick={() => this.setStyle(border_red)}>
border_Red
</button>
<button className="button" onClick={() => this.setStyle(border_gray)}>
border_Gray
</button>
<button className="button" onClick={() => this.setStyle(border_blue)}>
border_Blue
</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('react-root'));
.start-shape {
padding : 50px;
width: 50px;
}
.button {
display: inline-block;
margin: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react-root"></div>

so you need to understand that onClick event should change the state of the component.
to add state add a constructor with initialState to class:
constructor(props){
super(props);
this.state = {
style: {}
}
}
then you need to add a function which will handle click event which will set the next state:
handleOnClick = (style) => {
// style = { <css properties>}
this.setState({style})
}
now that your click handler and state is in place, you just need to call the handler on click event with the next state you want.
<button className="button" onClick={()=>handleOnClick(bg_Red)}>bg_Red</button>
lastly, bind that latest state with the div as:
<div className="start-shape" style={this.state.style} ></div>

Related

How to style the border of the ListItemButton when selected in Material UI?

I have the functionality where if the ListItemButton is selected, it will highlight it with a given background color. But now I want to style the ListItemButton even more by adding an outline or border color around it when selected. I used the attribute borderColor but currently it is not working.
Source code:
/* eslint-disable react/react-in-jsx-scope -- Unaware of jsxImportSource */
/** #jsxImportSource #emotion/react */
import { useState } from "react";
import ListItemButton from "#mui/material/ListItemButton";
import { css } from "#emotion/react";
export default function TestSample() {
const [selected2, setSelected2] = useState(false);
return (
<div className="App">
<ListItemButton
sx={{
"&.Mui-selected": {
backgroundColor: "#02A7DD",
borderColor: "red",
},
"&.Mui-focusVisible": {
backgroundColor: "#02A7DD",
borderColor: "red",
},
":hover": {
backgroundColor: "#02A7DD",
borderColor: "red",
},
}}
selected={selected2}
onClick={() => setSelected2((prev) => !prev)}
>
ListItemButton
</ListItemButton>
</div>
);
}
Hoping for your responses as this would help me a lot especially since I am exploring MUI at the moment an trying to understand its use and implementation. Thank you very much!
You're missing the borderWidth and borderStyle properties. Right now you have a red border that is invisible and 0 pixels thick.
As a shorthand you can specify just the border property instead, for example:
sx={{
border: '1px solid red'
}}
which is the same as
sx={{
borderStyle: 'solid',
borderWidth: '1px',
borderColor: 'red'
}}

React- Conditionally applying css in div but it does not work

Have looked at other examples and trying to do the same thing but not sure why my code is not working. I have code which loops through some keys and renders a div. I want to conditionally apply some styles based on whether the key is even or odd. Example:
<div className={parseInt(key) % 2 === 0 ? 'label1' : 'label2' }>
<span style={{ marginLeft: "10px" }}>{key}:00</span>
</div>
The styles are accessible in the same file and look something like:
# Material UI
const useStyles = makeStyles((theme) => ({
label1: {
width: "50px",
height: "16px",
top: "458px",
background: "yellow",
fontSize: "12px",
},
label2: {
width: "50px",
height: "16px",
top: "458px",
background: "red",
fontSize: "12px",
},
}));
What am I doing wrong? Currently no style is getting applied to the div
You need to use the classes from the material ui useStyles hook.
const classes = useStyles()
....
<div className={parseInt(key) % 2 === 0 ? classes.label1 : classes.label2 }>
<span style={{ marginLeft: "10px" }}>{key}:00</span>
</div>
Check the useStyles hook api: https://material-ui.com/styles/basics/
If you have a class component and you can use hooks then you can do it with the withStyles higher order component, like this example:
import { withStyles } from "#material-ui/core/styles"
const styles = theme => ({
label1: {
backgroundColor: "red",
},
label2: {
backgroundColor: "red",
},
})
class ClassComponent extends Component {
state = {
searchNodes: "",
}
render() {
const { classes } = this.props
return (
<div className={parseInt(key) % 2 === 0 ? classes.label1 : classes.label2}>
<span style={{ marginLeft: "10px" }}>{key}:00</span>
</div>
)
}
}
export default withStyles(styles, { withTheme: true })(ClassComponent)

fluent ui tooltip in react js

Hello I am new to using fluent ui and applying it to my react.
I am trying to align my tooltip with my textfield and trying to change background color to black and font to white. Also increasing the width and height of tooltipbox.
const inlineBlockStyle = (styleProps) => {
const chkStyles = {
root: [{
display: 'inline-block',
color: 'white',
backgroundColor: black,
height: '100px',
width: '500px,'
}],
};
return chkStyles
};
<TooltipHost
content="If your school is not on our list, please go to the Support Page
and provide your schools details.
id={tooltip2Id}
calloutProps={calloutProps}
styles={inlineBlockStyle}
>
<Label style={{ color: "white", fontSize: "20px" }}>
Please select your county and your school
<i className="ms-Icon ms-Icon--Info" </i>
</Label>
</TooltipHost>
const styleProps= {
rootHovered: {
backgroundColor: "black"
}
};
//
<FluentPrimaryButton text = {label} styles = {styleProps} />;

How to set selected and hover color of ListItem in MUI?

Can't get 'selected' or 'hover colors to work for ListItem. For selected tried setting its classes like:
<ListItem selected button key="home" classes={{ selected: classes.listItemSelected }}>
<ListItemText primary="Hi"/>
</ListItem>
and then setting the style like:
const useStyles = makeStyles((theme) => ({
listItemSelected:{
backgroundColor: "#ff0000",
},
}));
But it doesn't do anything, the 'selected' is described in the ListItem component API here.
How do you get to set the color of both the selected and hover for ListItem?
Below is the portion of the default ListItem styles that deals with the background color:
export const styles = (theme) => ({
/* Styles applied to the (normally root) `component` element. May be wrapped by a `container`. */
root: {
'&$focusVisible': {
backgroundColor: theme.palette.action.selected,
},
'&$selected, &$selected:hover': {
backgroundColor: theme.palette.action.selected,
},
'&$disabled': {
opacity: 0.5,
},
},
/* Pseudo-class applied to the `component`'s `focusVisibleClassName` prop if `button={true}`. */
focusVisible: {},
/* Styles applied to the inner `component` element if `button={true}`. */
button: {
transition: theme.transitions.create('background-color', {
duration: theme.transitions.duration.shortest,
}),
'&:hover': {
textDecoration: 'none',
backgroundColor: theme.palette.action.hover,
// Reset on touch devices, it doesn't add specificity
'#media (hover: none)': {
backgroundColor: 'transparent',
},
},
},
/* Pseudo-class applied to the root element if `selected={true}`. */
selected: {},
});
The important thing to notice is that the selected styling is done via a combination of two classes (root and selected), so if you try to override it using a single class you will not have sufficient specificity.
Below is an example showing one way to override the selected and hover states:
import React from "react";
import { makeStyles, withStyles } from "#material-ui/core/styles";
import List from "#material-ui/core/List";
import MuiListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import ListItemText from "#material-ui/core/ListItemText";
import Divider from "#material-ui/core/Divider";
import InboxIcon from "#material-ui/icons/Inbox";
import DraftsIcon from "#material-ui/icons/Drafts";
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
maxWidth: 360,
backgroundColor: theme.palette.background.paper
}
}));
const ListItem = withStyles({
root: {
"&$selected": {
backgroundColor: "red",
color: "white",
"& .MuiListItemIcon-root": {
color: "white"
}
},
"&$selected:hover": {
backgroundColor: "purple",
color: "white",
"& .MuiListItemIcon-root": {
color: "white"
}
},
"&:hover": {
backgroundColor: "blue",
color: "white",
"& .MuiListItemIcon-root": {
color: "white"
}
}
},
selected: {}
})(MuiListItem);
export default function SelectedListItem() {
const classes = useStyles();
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleListItemClick = (event, index) => {
setSelectedIndex(index);
};
return (
<div className={classes.root}>
<List component="nav" aria-label="main mailbox folders">
<ListItem
button
selected={selectedIndex === 0}
onClick={(event) => handleListItemClick(event, 0)}
>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItem>
<ListItem
button
selected={selectedIndex === 1}
onClick={(event) => handleListItemClick(event, 1)}
>
<ListItemIcon>
<DraftsIcon />
</ListItemIcon>
<ListItemText primary="Drafts" />
</ListItem>
</List>
<Divider />
<List component="nav" aria-label="secondary mailbox folder">
<ListItem
button
selected={selectedIndex === 2}
onClick={(event) => handleListItemClick(event, 2)}
>
<ListItemText primary="Trash" />
</ListItem>
<ListItem
button
selected={selectedIndex === 3}
onClick={(event) => handleListItemClick(event, 3)}
>
<ListItemText primary="Spam" />
</ListItem>
</List>
</div>
);
}
Related answers:
How to overried the selected classes in menuItem in material ui REACTjs?
How to change the styles of ListItem element with the "onclick" event?
You can use the sx prop in MUI v5:
<List
sx={{
// selected and (selected + hover) states
'&& .Mui-selected, && .Mui-selected:hover': {
bgcolor: 'red',
'&, & .MuiListItemIcon-root': {
color: 'pink',
},
},
// hover states
'& .MuiListItemButton-root:hover': {
bgcolor: 'orange',
'&, & .MuiListItemIcon-root': {
color: 'yellow',
},
},
}}
>
Or styled to create a styled component that can be reused multiple times:
import MuiList from '#mui/material/List';
const List = styled(MuiList)({
// selected and (selected + hover) states
'&& .Mui-selected, && .Mui-selected:hover': {
backgroundColor: 'red',
'&, & .MuiListItemIcon-root': {
color: 'pink',
},
},
// hover states
'& .MuiListItemButton-root:hover': {
backgroundColor: 'orange',
'&, & .MuiListItemIcon-root': {
color: 'yellow',
},
},
});
Live Demo

Can I make dynamic styles in React Native?

Say I have a component with a render like this:
<View style={jewelStyle}></View>
Where jewelStyle =
{
borderRadius: 10,
backgroundColor: '#FFEFCC',
width: 20,
height: 20,
},
How could I make the background colour dynamic and randomly assigned? I've tried
{
borderRadius: 10,
backgroundColor: getRandomColor(),
width: 20,
height: 20,
},
But this makes all instances of View have the same colour, I want each one to be unique.
Any tips?
I usually do something along the lines of:
<View style={this.jewelStyle()} />
...
jewelStyle = function(options) {
return {
borderRadius: 12,
background: randomColor(),
}
}
Every time View is rendered, a new style object will be instantiated with a random color associated with it. Of course, this means that the colors will change every time the component is re-rendered, which is perhaps not what you want. Instead, you could do something like this:
var myColor = randomColor()
<View style={jewelStyle(myColor)} />
...
jewelStyle = function(myColor) {
return {
borderRadius: 10,
background: myColor,
}
}
Yes you can and actually, you should use StyleSheet.create to create your styles.
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View
} from 'react-native';
class Header extends Component {
constructor(props){
super(props);
}
render() {
const { title, style } = this.props;
const { header, text } = defaultStyle;
const combineStyles = StyleSheet.flatten([header, style]);
return (
<View style={ combineStyles }>
<Text style={ text }>
{ title }
</Text>
</View>
);
}
}
const defaultStyle = StyleSheet.create({
header: {
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#fff',
height: 60,
paddingTop: 15,
shadowColor: '#000',
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.4,
elevation: 2,
position: 'relative'
},
text: {
color: '#0d4220',
fontSize: 16
}
});
export default Header;
And then:
<Header title="HOME" style={ {backgroundColor: '#10f1f0'} } />
If you still want to take advantage of StyleSheet.create and also have dynamic styles, try this out:
const Circle = ({initial}) => {
const initial = user.pending ? user.email[0] : user.firstName[0];
const colorStyles = {
backgroundColor: randomColor()
};
return (
<View style={[styles.circle, colorStyles]}>
<Text style={styles.text}>{initial.toUpperCase()}</Text>
</View>
);
};
const styles = StyleSheet.create({
circle: {
height: 40,
width: 40,
borderRadius: 30,
overflow: 'hidden'
},
text: {
fontSize: 12,
lineHeight: 40,
color: '#fff',
textAlign: 'center'
}
});
Notice how the style property of the View is set as an array that combines your stylesheet with your dynamic styles.
The easiest is mine:
<TextInput
style={[
styles.default,
this.props.singleSourceOfTruth ?
{ backgroundColor: 'black' }
: { backgroundColor: 'white' }
]}/>
Had some issue syntactically.
This worked for me
<Text style={[styles.textStyle,{color: 'red'}]}> Hello </Text>
const styles = StyleSheet.create({
textStyle :{
textAlign: 'center',
fontFamily: 'Arial',
fontSize: 16
}
});
You'll want something like this:
var RandomBgApp = React.createClass({
render: function() {
var getRandomColor = function() {
var letters = '0123456789ABCDEF'.split('');
var color = '#';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
var rows = [
{ name: 'row 1'},
{ name: 'row 2'},
{ name: 'row 3'}
];
var rowNodes = rows.map(function(row) {
return <Text style={{backgroundColor:getRandomColor()}}>{row.name}</Text>
});
return (
<View>
{rowNodes}
</View>
);
}
});
In this example I take the rows array, containing the data for the rows in the component, and map it into an array of Text components. I use inline styles to call the getRandomColor function every time I create a new Text component.
The issue with your code is that you define the style once and therefore getRandomColor only gets called once - when you define the style.
I know this is extremely late, but for anyone still wondering here's an easy solution.
You could just make an array for the styles :
this.state ={
color: "#fff"
}
style={[
styles.jewelstyle, {
backgroundColor: this.state.BGcolor
}
The second will override any original background color as stated in the stylesheet. Then have a function that changes the color:
generateNewColor(){
var randomColor = '#'+Math.floor(Math.random()*16777215).toString(16);
this.setState({BGcolor: randomColor})
}
This will generate a random hex color. Then just call that function whenever and bam, new background color.
Actually, you can write your StyleSheet.create object as a key with function value, it works properly but it has a type issue in TypeScript:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
const SomeComponent = ({ bgColor }) => (
<View style={styles.wrapper(bgColor)}>
<Text style={styles.text}>3333</Text>
</View>
);
const styles = StyleSheet.create({
wrapper: color => ({
flex: 1,
backgroundColor: color,
}),
text: {
color: 'red',
},
});
import React, { useContext, useMemo } from 'react';
import { Text, StyleSheet, View } from 'react-native';
import colors from '../utils/colors';
import ThemeContext from './../contexts/ThemeContext';
export default (props) => {
const { theme } = useContext(ThemeContext);
// Constructing styles for current theme
const styles = useMemo(() => createStyles(theme), [theme]);
return (
<View style={styles.container}>
<Text style={styles.label}>{label}</Text>
</View>
);
};
const createStyles = (theme: AppTheme) =>
StyleSheet.create({
container: { width: '100%', position: 'relative', backgroundColor: colors[theme].background },
label: {
fontSize: 13,
fontWeight: 'bold',
},
});
colors.ts
export type AppTheme = 'dark' | 'light';
const light: Colors = {
background: '#FFFFFF',
onBackground: '#333333',
gray: '#999999',
grayLight: '#DDDDDD',
red: 'red',
};
const dark: Colors = {
background: '#333333',
onBackground: '#EEEEEE',
gray: '#999999',
grayLight: '#DDDDDD',
red: 'red',
};
const colors = {
dark,
light,
primary: '#2E9767',
secondary: '#F6D130',
};
export default colors;
Using object spread operator "..." worked for me:
<View style={{...jewelStyle, ...{'backgroundColor': getRandomColor()}}}></View>
Yes, you can make dynamic styles. You can pass values from Components.
First create StyleSheetFactory.js
import { StyleSheet } from "react-native";
export default class StyleSheetFactory {
static getSheet(backColor) {
return StyleSheet.create({
jewelStyle: {
borderRadius: 10,
backgroundColor: backColor,
width: 20,
height: 20,
}
})
}
}
then use it in your component following way
import React from "react";
import { View } from "react-native";
import StyleSheetFactory from './StyleSheetFactory'
class Main extends React.Component {
getRandomColor = () => {
var letters = "0123456789ABCDEF";
var color = "#";
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
};
render() {
return (
<View>
<View
style={StyleSheetFactory.getSheet(this.getRandomColor()).jewelStyle}
/>
<View
style={StyleSheetFactory.getSheet(this.getRandomColor()).jewelStyle}
/>
<View
style={StyleSheetFactory.getSheet(this.getRandomColor()).jewelStyle}
/>
</View>
);
}
}
<View
style={[styles.categoryItem,{marginTop: index <= numOfColumns-1 ? 10 : 0 }]}
>
I know there are several answers, but i think the best and most simple is using a state "To change" is the state purpose.
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
style: {
backgroundColor: "white"
}
};
}
onPress = function() {
this.setState({style: {backgroundColor: "red"}});
}
render() {
return (
...
<View style={this.state.style}></View>
...
)
}
}
You can bind state value directly to style object. Here is an example:
class Timer extends Component{
constructor(props){
super(props);
this.state = {timer: 0, color: '#FF0000'};
setInterval(() => {
this.setState({timer: this.state.timer + 1, color: this.state.timer % 2 == 0 ? '#FF0000' : '#0000FF'});
}, 1000);
}
render(){
return (
<View>
<Text>Timer:</Text>
<Text style={{backgroundColor: this.state.color}}>{this.state.timer}</Text>
</View>
);
}
}
If you are using a screen with filters for example, and you want to set the background of the filter regarding if it was selected or not, you can do:
<TouchableOpacity style={this.props.venueFilters.includes('Bar')?styles.filterBtnActive:styles.filterBtn} onPress={()=>this.setFilter('Bar')}>
<Text numberOfLines={1}>
Bar
</Text>
</TouchableOpacity>
On which set filter is:
setVenueFilter(filter){
var filters = this.props.venueFilters;
filters.push(filter);
console.log(filters.includes('Bar'), "Inclui Bar");
this.setState(previousState => {
return { updateFilter: !previousState.updateFilter };
});
this.props.setVenueFilter(filters);
}
PS: the function this.props.setVenueFilter(filters) is a redux action, and this.props.venueFilters is a redux state.
You can do something like this.
In your component:
const getRandomColor = () => {
// you can use your component props here.
}
<View style={[styles.jewelStyle, {backgroundColor: getRandomColor()}]} />
Create your style using stylesheet:
const styles = StyleSheet.create({
jewelStyle: {
backgroundColor: 'red',
},
});
If you are following the functional approach of React-Native, you can use a package called dynamic-styles that tries to solve exactly your problem.
// -- theme.js ------------------------------------------------------
// Initialization of a StyleSheet instance called 'styleSheet'
export const styleSheet = createStyleSheet({
theme: /* optional theme */
});
// -- MyComponent.js -----------------------------------------------
// Create dynamic stylesheet that has access
// to the previously specified theme and parameters
const useStyles = styleSheet.create(({theme, params}) => ({
root: /* Dynamic Styles */,
button: /* Dynamic Styles */,
text: /* Dynamic Styles */,
}));
const MyComponent = (props) => {
// Access dynamic styles using the created 'useStyles()' hook
// and specify the corresponding parameters
const { styles } = useStyles({ color: props.color, fontSize: 10 });
return (
<div className={styles.root}>
{/* */}
</div>
);
}
It basically allows you to create dynamic stylesheets
and link them to functional Components using the React hook pattern.
-> Codesandbox
In case someone needs to apply conditions
selectedMenuUI = function(value) {
if(value==this.state.selectedMenu){
return {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 20,
paddingVertical: 10,
backgroundColor: 'rgba(255,255,255,0.3)',
borderRadius: 5
}
}
return {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 20,
paddingVertical: 10
}
}
Here is what worked for me:
render() {
const { styleValue } = this.props;
const dynamicStyleUpdatedFromProps = {
height: styleValue,
width: styleValue,
borderRadius: styleValue,
}
return (
<View style={{ ...styles.staticStyleCreatedFromStyleSheet, ...dynamicStyleUpdatedFromProps }} />
);
}
For some reason, this was the only way that mine would update properly.
you can use styled-components for react native it will provide you dynamic styling just like emotion or styled-components for web.
For something relatively simple, you can use this approach:
StyleSheet.create({
item: props.selectedId === item.id ? {
backgroundColor: 'red',
}: null
});

Resources