update a value using useRef and useEffect - reactjs

I need to get the left position of an element after elements got mounted to show a progress bar. It works on clicking links however when it got mounted the progressWidth is not getting calculated. basically, the useEffect seems running before the component is mounted!!!!
export default ({ task, selectedLocal, selectedScenarioId, taskId, selectedTaskId }: ITaskNavItem) => {
const isActiveNav = (match: any, location: object) => match;
const isBefore = taskId <= selectedTaskId;
const isActive = taskId === selectedTaskId;
const navItemWidth = 100;
const [progressWidth, setProgressWidth] = useState(0);
const ref = useRef<HTMLInputElement>(null);
useEffect(() => {
if (ref.current) {
console.log(ref.current.getBoundingClientRect().left);
setProgressWidth(ref.current.getBoundingClientRect().left);
}
});
const theme = getTheme();
const styles = StyleSheet.create({
navLink: {
display: 'flex',
fontSize: '12px',
textDecoration: 'none',
color: theme.palette.neutralPrimary
},
navLinkActive: {
color: theme.palette.neutralPrimary,
fontWeight: 'bold'
},
navTitle: {
width: `${navItemWidth}px`,
textAlign: 'center',
wordBreak: 'break-word',
wordSpacing: `${navItemWidth}px`
},
linkText: {
display: 'flex',
flexFlow: 'column',
'align-items': 'center'
},
navIndicator: {
borderRadius: '50%',
margin: '10px 0 0 0',
backgroundColor: theme.palette.white,
width: '30px',
height: '30px',
border: '2px solid',
borderColor: theme.palette.neutralPrimary,
position: 'relative',
'z-index': '3'
},
innerIndicator: {
position: 'absolute',
borderRadius: '50%',
width: '20px',
height: '20px',
backgroundColor: theme.palette.neutralPrimary,
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
},
activeNavIndicator: { borderColor: theme.palette.themePrimary },
activeInnerIndicator: { backgroundColor: theme.palette.themePrimary },
progress: {
marginTop: '59px',
'z-index': '2',
position: 'fixed',
left: '0',
width: `${progressWidth}px`,
borderBottom: '2px solid',
borderColor: theme.palette.themePrimary
}
});
return (
<div className={css(styles.navLink)}>
<NavLink
exact
isActive={isActiveNav}
className={css(isActive ? [styles.navLink, styles.navLinkActive] : styles.navLink)}
to={`/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}`}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div
ref={ref}
className={css(
isBefore ? [styles.navIndicator, styles.activeNavIndicator] : styles.navIndicator
)}
>
<div
className={css(
isBefore ? [styles.innerIndicator, styles.activeInnerIndicator] : styles.innerIndicator
)}
/>
</div>
</div>
</NavLink>
{isActive && <div className={css(styles.progress)} />}
</div>
);
};
So when component is getting loaded I get image 1, when I click on the component I get image 2. What I need to happen is when component is getting loaded it should look like image 2.

Related

How to use "React.useState()" in const classes in React

In this code, I am not able to use const [ isAlertVisible, setIsAlertVisible ] inside/outside of const FoodRow.js file.
How to fix this code? This is entire code which I am using.
Along with that, I am also placing .css file.
Alert is not getting displayed. Also on button click, Entire view goes away where, I am using FoodRow.js. Screen becomes blank.
import React from 'react'
import {View, Text,StyleSheet } from 'react-native';
import './FoodRow.css';
const FoodRow = (props) => {
const [ isAlertVisible, setIsAlertVisible ] = React.useState(false);
const addToCartClick = () => {
console.log("addToCartClick");
setIsAlertVisible(true);
setTimeout(() => {
setIsAlertVisible(false);
}, 3000);
console.log("addToCartClick , isAlertVisible = "+isAlertVisible);
}
return (
<View style={{flex:1, flexDirection: 'row'}}>
<Text style = {styles.restaurantName}>{props.title }</Text>
<Text style = {styles.restaurantNameDesc}>{props.price }</Text>
<button style={{backgroundColor: "blue" , width: "100px", height: "20px"}} onClick={addToCartClick}>
Add To Cart
</button>
{isAlertVisible && <div className='alert-container'>
<div className='alert-inner'>Alert! Alert!</div>
</div> }
</View>
)
}
export default FoodRow
const styles = StyleSheet.create ({
container: {
alignItems: 'center',
marginTop: 100,
padding: 20
},
restaurantName: {
color: 'red',
fontSize: 30,
fontStyle: 'italic',
paddingLeft: 100,
marginTop: 50
},
restaurantNameDesc: {
color: 'black',
fontSize: 20,
fontStyle: 'italic',
paddingLeft: 100
},
capitalLetter: {
color: 'red',
fontSize: 20
},
wordBold: {
fontWeight: 'bold',
color: 'black'
},
italicText: {
color: '#37859b',
fontStyle: 'italic'
},
textShadow: {
textShadowColor: 'red',
textShadowOffset: { width: 2, height: 2 },
textShadowRadius : 5
}
})
FoodRow.css:
.alert-container {
position: absolute;
top: 2rem;
left: 0;
right: 0;
}
.alert-inner {
display: inline-block;
padding: 8px 16px;
z-index: 1;
background-color: #ffffff;
box-shadow: 1px 2px 10px -3px rgb(0 0 0 / 70%);
-webkit-box-shadow: 1px 2px 10px -3px rgb(0 0 0 / 70%);
-moz-box-shadow: 1px 2px 10px -3px rgb(0 0 0 / 70%);
}
Move function addToCartClick to component, like this:
import React from 'react'
import {View, Text,StyleSheet, Button } from 'react-native';
const FoodRow = (props) => {
const [ isAlertVisible, setIsAlertVisible ] = React.useState(false);
const addToCartClick = () => {
console.log("addToCartClick");
setIsAlertVisible(true);
setTimeout(() => {
setIsAlertVisible(false);
}, 3000);
}
return (
<View style={{flex:1, flexDirection: 'row'}}>
<Text style = {styles.restaurantName}>{props.title }</Text>
<Text style = {styles.restaurantNameDesc}>{props.price }</Text>
<Button style={{backgroundColor: "blue" , width: "100px", height: "20px"}} onPress={addToCartClick}>
Add To Cart
</Button >
{isAlertVisible && <View className='alert-container'>
<Text className='alert-inner'>Alert! Alert!</Text >
</View >
}
</View>
)
}
export default FoodRow
Another thing is that you are using react native but alert is displayed as a "div", you should change it to View and Text, and import button from react-native. However if you want to style your button a bit more I suggest using TouchableOpacity

How to replace string for multiple lines in react js

Screenshot1 Here i want to replace Special characters"E" with
"bold tag" but it is changing only in one line i want to replace for
all the lines which contains special character E & F please verify the
screenshot for the reference
import React from 'react'; import '../screens/MainScreen.css'; import
{ FaArrowRight } from 'react-icons/fa';
export default function MainScreen() {
// let formData = newFormData();
const onFileChange = async (e) => {
e.preventDefault()
const exampleFileReader = new FileReader()
exampleFileReader.onload = async (e) => {
var text = (e.target.result)
var result = text.replace("\u001bE","<b>").replace('\u001bF','</b>').replace('\u001b-1','<u>').replace('\u001b-0','</u>');
console.log(result,"result")
};
exampleFileReader.readAsText(e.target.files[0]) };
return (
<div>
<center>
<img src={Pic} style={{ width: '120px', height: '100px', marginTop: '40px' }} />
<h1 style={{
color: 'red', fontSize: 50,
textAlign: 'center', border: '1px solid green', width: '40%'
}}>Text to PDF Converter</h1>
</center>
<div className="container">
<input type="file" multiple={true} style={{ color: 'white', backgroundColor: "green", padding: 10, fontSize: 20, width: '20%',
paddingTop: 20, paddingBottom: 20 }}
onChange={(e)=>onFileChange(e)} />
<FaArrowRight size={50} color='red' />
<h1 style={{ color: 'white', backgroundColor: "red", padding: 10, fontSize: 20, width: '20%', paddingTop: 20, paddingBottom: 20 }}
>Convert</h1>
<FaArrowRight size={50} color='green' />
<h1 style={{ color: 'white', backgroundColor: "green", padding: 10, fontSize: 20, width: '20%', paddingTop: 20,
paddingBottom: 20 }}>PDF Download</h1>
</div>
</div> ) };
e.target.result is probably not correct, please console.log it and post the general e.target in order to help you better (try this)
const onFileChange = async (e) => {
e.preventDefault()
const exampleFileReader = new FileReader()
exampleFileReader.onload = async (e) => {
var text = (e.target.value)
var result = text.replace("\u001bE","<b>").replace('\u001bF','</b>').replace('\u001b-1','<u>').replace('\u001b-0','</u>');
console.log(result,"result")
};
exampleFileReader.readAsText(e.target.files[0]) };

Material-UI DataGrid Linear Progress Bar in Column Cells

I would like to add a column like Filled Quantity
But I cannot figure out how to do it from the docs, I feel like I have to use renderCell when setting up the column but I can't see how to accomplish it.
https://mui.com/components/data-grid/demo/
https://mui.com/components/data-grid/columns/
You can copy the bar renderer from the demo here:
const defaultTheme = createTheme();
const useStyles = makeStyles(
(theme) =>
createStyles({
root: {
border: `1px solid ${theme.palette.divider}`,
position: "relative",
overflow: "hidden",
width: "100%",
height: 26,
borderRadius: 2
},
value: {
position: "absolute",
lineHeight: "24px",
width: "100%",
display: "flex",
justifyContent: "center"
},
bar: {
height: "100%",
"&.low": {
backgroundColor: "#f44336"
},
"&.medium": {
backgroundColor: "#efbb5aa3"
},
"&.high": {
backgroundColor: "#088208a3"
}
}
}),
{ defaultTheme }
);
const ProgressBar = React.memo(function ProgressBar(props) {
const { value } = props;
const valueInPercent = value * 100;
const classes = useStyles();
return (
<div className={classes.root}>
<div
className={classes.value}
>{`${valueInPercent.toLocaleString()} %`}</div>
<div
className={clsx(classes.bar, {
low: valueInPercent < 30,
medium: valueInPercent >= 30 && valueInPercent <= 70,
high: valueInPercent > 70
})}
style={{ maxWidth: `${valueInPercent}%` }}
/>
</div>
);
});
export function renderProgress(params) {
return <ProgressBar value={Number(params.value)} />;
}
Usage
{
field: "filledQuantity",
headerName: "Filled Quantity",
renderCell: renderProgress,
type: "number",
width: 120
}
Live Demo

ToggleButtonGroup react material ui

I cant seem to get the ToggleButton selected property from material ui to work on ToggleButton.
I have made a StyledToggleButton as the documentation from Material Ui.
const StyledToggleButton = withStyles({
root: {
fontFamily: 'Arial',
fontSize: '14px',
lineHeight: '20px',
letterSpacing: '0.25',
color: 'rgba(0, 0, 0, 0.87)',
padding: '15px 15px',
textTransform: 'none',
width: '100%',
'&$selected': { //<--this is my attempt as per documentation
color: 'red !important',
backgroundColor: 'red !important',
},
selected: {},
},
})(ToggleButton);
I am using the ToggleButtonGroup and passing ToggleButton as a child.
I have looked at using MuiTheme but i did not understand how to make that work in this example.
here is the rest for reference:
const StyledToggleButton = withStyles({
root: {
fontFamily: 'Arial',
fontSize: '14px',
lineHeight: '20px',
letterSpacing: '0.25',
color: 'rgba(0, 0, 0, 0.87)',
padding: '15px 15px',
textTransform: 'none',
width: '100%',
'&$selected': {
color: 'red !important',
backgroundColor: 'red !important',
},
selected: {},
},
})(ToggleButton);
const StyledGroupButton = withStyles({
root: {
padding: '15px 15px',
width: '100%',
},
})(ToggleButtonGroup);
export default function ObjectViewCard(props) {
const classes = useStyles();
const [alignment, setAlignment] = React.useState('none');
const handleChange = (
event: React.MouseEvent<HTMLElement>,
newAlignment: string,
) => {
setAlignment(newAlignment);
};
const children = [
<StyledToggleButton key={1} value="left">
{props.leftButtonContent}
</StyledToggleButton>,
<StyledToggleButton key={2} value="right">
{props.rightButtonContent}
</StyledToggleButton>,
];
return (
<Card>
<hr className={classes.divider}/>
<div className={`row ${classes.rowContainer}`}>
<div className="col-6">
<span className={classes.header}>Velg visning</span>
</div>
<div className="col-6">
<span className={classes.info}>
<InfoOutlinedIcon className={classes.icon}/> Vis tegnforklaring
</span>
</div>
</div>
<StyledGroupButton value={alignment} exclusive onChange={handleChange}>
{children}
</StyledGroupButton>
</Card>
);
}
```
call it like i did, but selected: {} need to be on same tree-level as root, solution here:
const StyledToggleButton = withStyles({
root: {
fontFamily: 'Arial',
fontSize: '14px',
lineHeight: '20px',
letterSpacing: '0.25px',
color: 'rgba(0, 0, 0, 0.87)',
padding: '15px 15px',
textTransform: 'none',
width: '100%',
'&$selected': {
backgroundColor: 'rgba(33, 137, 214, 0.14)',
color: 'rgb(26, 88, 159)',
'&:hover': {
backgroundColor: 'rgba(33, 137, 214, 0.14)',
color: 'rgb(26, 88, 159)',
},
},
},
selected: {},
})(ToggleButton);

How to style the active link childs using CSS in JS and React Router?

I am using React ROuter and CSS in JS for style.
I'd like to change the background color and border color for the active links child divs (navIndicator and innerIndicator)
import { css, StyleSheet } from 'aphrodite/no-important';
export default ({ task, selectedLocal, selectedScenarioId, taskId }: ITaskNavItem) => {
const isActiveNav = (match: any, location: object) => match;
const theme = getTheme();
const styles = StyleSheet.create({
navLink: {
display: 'flex',
fontSize: '12px',
textDecoration: 'none',
color: theme.palette.neutralPrimary
},
navLinkActive: {
color: theme.palette.neutralPrimary,
fontWeight: 'bold',
'.navIndicator': {
borderColor: theme.palette.themePrimary
},
'.innerIndicator': {
backgroundColor: theme.palette.themePrimary
}
},
navTitle: {
width: '100px',
textAlign: 'center',
wordBreak: 'break-word',
wordSpacing: '100px'
},
linkText: {
display: 'flex',
flexFlow: 'column',
'align-items': 'center'
},
navIndicator: {
borderRadius: '50%',
margin: '10px 0 0 0',
backgroundColor: theme.palette.white,
width: '30px',
height: '30px',
border: '2px solid',
borderColor: theme.palette.neutralPrimary,
position: 'relative'
},
innerIndicator: {
position: 'absolute',
borderRadius: '50%',
width: '20px',
height: '20px',
backgroundColor: theme.palette.neutralPrimary,
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
}
});
return (
<div className={css(styles.navLink)}>
<NavLink
exact
isActive={isActiveNav}
className={css(styles.navLink)}
activeClassName={css(styles.navLinkActive)}
to={`/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}`}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div className={css(styles.navIndicator)}>
<div className={css(styles.innerIndicator)} />
</div>
</div>
</NavLink>
</div>
);
};
However, the navIndicator and innerIndicator colors doesn't change when nav link is active.
Wondering how to get the style working for active link?
NavLink element does not indicate to its children if it active. So I may suggest to get currecnt route from BrowserRouter component (your component should be child of BrowserRouter so NavLink works), compare path and set local isActive variable to indicate if specific route is active.
For example (not tested, just sample):
const StyledLinks: React.FunctionComponent<RouteComponentProps & ITaskNavItem> = ({ task, selectedLocal, selectedScenarioId, taskId, location }) => {
const to = '/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}';
const isActive = to === location.pathname;
const styles = StyleSheet.create({
// ...
navIndicatorActive: {
borderColor: theme.palette.themePrimary
},
// ...
return (
<div className={css(styles.navLink)}>
<NavLink
exact
className={css(styles.navLink)}
activeClassName={css(styles.navLinkActive)}
to={to}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div className={isActive ? css([styles.navIndicator, styles.navIndicatorActive]) : css(styles.navIndicator)}>
<div className={css(styles.innerIndicator)} />
</div>
</div>
</NavLink>
</div>
);
}
// Wrap your component withRouter to get location prop
export default withRouter(StyledLinks);

Resources