ReactJs, Semantic UI , toggle button - javascript

Hello i wanted to make a toggle button menu
ie by clicking on the button the div would increase the height with some transition
but I can't imagine how to do this in react
function App() {
const handleClick = e => {
e.preventDefault();
};
return (
<div
className="Wrapper"
style={{
backgroundColor: "#000",
height: "30px",
width: "200px"
}}
>
<Button
style={{ padding: 0, height: "30px", width: "200px" }}
circular
icon="settings"
onClick={handleClick}
>
Button
</Button>
</div>
);
}
my codebox: https://codesandbox.io/s/funny-minsky-j9x6x

You need to add a state variable to your function. You can now use hooks for that.
import React from "react";
import ReactDOM from "react-dom";
import { Button } from "semantic-ui-react";
import "./styles.css";
import { useState } from 'react';
function App() {
const [open, setOpen] = useState(0); // declare new state variable "open" with setter
const handleClick = e => {
e.preventDefault();
setOpen(!open);
};
return (
<div
className="Wrapper"
style={{
backgroundColor: "#000",
height: (open ? "400px" : "30px"), // make the div tall when "open"
width: "200px",
transition: "height 1s" // transition for 1 second
}}
>
<Button
style={{ padding: 0, height: "30px", width: "200px" }}
circular
onClick={handleClick}
>
Button
</Button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Related

Same page section navigation using dynamic useRef hook in React

There are many packages available to navigate between sections on same page. But I don't want to integrate any package as dependency for doing a simple task. Here is a sample code for navigation which is not dynamic
import React, { useRef } from "react";
function Navigation() {
const top = useRef(null);
const scrollAnySection = (ref) => {
window.scrollTo({
top: ref.current.offsetTop - 10,
behavior: 'smooth',
});
};
return (
<>
<div className="navigation" style={{ width: "80%", margin: "3rem auto" }}>
<section className="menu--container">
<h3 onClick={() => scrollAnySection(top)}>Top</h3>
</section>
<section>
<h3 ref={top} className="section--items"
style={{ margin:"100rem 0",textAlign:"center",border:"1px solid blue" }}> Top </h3>
</section>
</div>
</>
);
}
export default Navigation;
But I am getting difficulty with dynamic useRef here where the ref of HTML element will be called from an array. Below is a sample what I am trying to do for dynamic contents. But I am not getting the expected result.
import React, { useRef } from "react";
function Navigation() {
const listSection = ["Head", "Body", "Main", "Footer", "Bottom"];
const navigateToSection = useRef([]);
const scrollAnySection = (ref) => {
window.scrollTo({
top: ref.current.offsetTop - 10,
behavior: 'smooth',
});
};
return (
<>
<div className="navigation" style={{ width: "80%", margin: "3rem auto" }}>
<section className="menu--container" style={{ display: "flex", justifyContent: "space-between"}}>
{ listSection.map((item) => (
<h3
style={{ padding: ".5rem 2rem", borderRadius: "25rem", border: "1px solid blue" }}
className="menu--items"
onClick={() => scrollAnySection ({ item }) }
>{ item }</h3>
))
}
</section>
<section>
{ listSection.map((item, index) => (
<h3
className="section--items"
style={{ margin: "100rem 0", textAlign: "center", border: "1px solid blue" }}
ref={ ref => { navigateToSection.current[index] = ref; }}
>This is { item } section</h3>
))
}
</section>
</div>
</>
);
}
export default Navigation;
I am getting Uncaught TypeError: ref.current is undefined at scrollAnySection and onClick() event.
Any suggestion or a better approach will be appreciated. Thanks.

I created a Modal using createPortal() method to render it. Then I found that modal renders twice

When the button inside the Post clicked, Popup will render with createPortal method outside from root element's tree.
With this code that popup renders twice.
I want to render it only once.
Here's the parent Post component.
import { useState } from 'react';
import PopupModal from './PopupModal/PopupModal';
import './Post.css';
const Post = (props) => {
const postData = props;
const [isOpen, setIsOpen] = useState(false);
return (
<div className="post-container">
<div className="post-img-container">
<img className="post-img" src={props.img} alt="Travels" />
</div>
<div className="post-text-container">
<h4 className="post-heading">{props.title}</h4>
<p className="post-para">{props.description}</p>
<h1 className="post-price">{props.price}</h1>
<div className="post-btn-container">
<button onClick={() => setIsOpen(true)} className="post-btn">
Check Availability
</button>
<PopupModal dataData={postData} open={isOpen} onClose={() => setIsOpen(false)}>
Button123
</PopupModal>
</div>
</div>
</div>
);
};
export default Post;
And here's the popupModal
import React from 'react';
import ReactDOM from 'react-dom';
import '../PopupModal/popupModal.css'
const MODAL_STYLES = {
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%,-50%)',
background: '#fff',
width: '40vw',
height: '90vh',
padding: '50px',
zIndex: 1000,
};
const PopupModal = ({ open, children, onClose ,dataData }) => {
if (!open) return null;
console.log('xxx');
console.log(dataData);
return ReactDOM.createPortal(
<>
<div className='modal-overlay' ></div>
<div className='modal-container'>
<button onClick={onClose}> Popup Close</button>
{children}
</div>
</>,
document.getElementById('portal')
);
};
export default PopupModal;
Here's how I figured it rendered twice.
Here's the Popup with overlay around it which covers the background.
Thanks in advance!
Try following
{
isOpen && <PopupModal dataData={postData} open={isOpen} onClose={() => setIsOpen(false)}>
Button123
</PopupModal>
}

How to make a custom menu close when I click somewhere else other than the menu

To piggyback on a previous question, I'd like my menu to close whenever I click somewhere else other than the menu.
Currently, it will open/close when I click the "Hamburger Menu Button". It will close if I click a link on the menu or the menu itself but I'd also like to close when I "blur" away from it. This would be like if you got an alert message and you clicked somewhere else, the alert would close.
Here is the current CodeSandbox
The relevant code is in Header.tsx:
import React, { useState } from "react";
import { Link } from "react-router-dom";
import {
AppBar,
Container,
createStyles,
IconButton,
makeStyles,
Theme,
Toolbar,
Typography
} from "#material-ui/core";
import MenuIcon from "#material-ui/icons/Menu";
import clsx from "clsx";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
flexGrow: 1,
"& a": {
color: "white",
textDecoration: "none"
}
},
menuButton: {
marginRight: theme.spacing(2),
zIndex: 2
},
title: {
flexGrow: 1,
zIndex: 2
},
toolBar: {
"& div": {
transition: "left .1s"
}
},
menu: {
zIndex: 1,
width: 200,
height: "100%",
position: "fixed",
top: 48,
transition: "left .1s",
marginRight: theme.spacing(2),
left: -200,
background: "#3f51b5",
"& div:first-element": {
marginTop: 100
}
},
menuOpen: {
left: 0,
transition: "left .1s"
},
menuClose: {
left: -200,
transition: "left .1s"
},
topMenu: {
display: "flex",
"& div": {
marginLeft: theme.spacing(1)
}
}
})
);
const UserMenu = () => {
const classes = useStyles();
const [menuOpen, setMenuOpen] = useState(false);
const toggleMenu = () => setMenuOpen(!menuOpen);
const handleMenuClick = () => toggleMenu();
return (
<>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="menu"
onClick={toggleMenu}
>
<MenuIcon />
</IconButton>
<div className={classes.toolBar}>
<Container
className={clsx(classes.menu, {
[classes.menuOpen]: menuOpen,
[classes.menuClose]: !menuOpen,
[classes.toolBar]: true
})}
onClick={handleMenuClick}
>
<div>
<Link to="#">My Profile</Link>
</div>
<div>
<Link to="#">Account</Link>
</div>
<div>
<Link to="#">Admin</Link>
</div>
</Container>
</div>
</>
);
};
const Header: React.FC = ({ children }) => {
const classes = useStyles();
return (
<AppBar position="static" className={classes.root}>
<Toolbar variant="dense">
<UserMenu />
<Typography variant="h6" className={classes.title}>
<Link to="#">Widgets, LLC</Link>
</Typography>
<div className={classes.topMenu}>
<div>
<Link to="#">Sign out</Link>
</div>
<div>
<Link to="#">One more</Link>
</div>
</div>
</Toolbar>
</AppBar>
);
};
export default Header;
**Edit: ** This is the current Sandbox after applying Ryan's changes and moving the UserMenu into its own file.
You can use Material-UI's ClickAwayListener for this. The only tricky part is avoiding an immediate close of your menu after clicking on the button to open the menu (because of the click event being handled by the button opening the menu and then the same click event being handled by the ClickAwayListener closing the menu). Typically you would want to avoid rendering the ClickAwayListener until the menu is open, but I think that might break the transition on the menu unless you did further changes. My example addresses this problem by calling event.stopPropagation() in the click handler for the menu button (handleOpen).
Here's a modified version of your code/sandbox demonstrating this:
import React, { useState } from "react";
import { Link } from "react-router-dom";
import {
AppBar,
Container,
ClickAwayListener,
createStyles,
IconButton,
makeStyles,
Theme,
Toolbar,
Typography
} from "#material-ui/core";
import MenuIcon from "#material-ui/icons/Menu";
import clsx from "clsx";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
flexGrow: 1,
"& a": {
color: "white",
textDecoration: "none"
}
},
menuButton: {
marginRight: theme.spacing(2),
zIndex: 2
},
title: {
flexGrow: 1,
zIndex: 2
},
toolBar: {
"& div": {
transition: "left .1s"
}
},
menu: {
zIndex: 1,
width: 200,
height: "100%",
position: "fixed",
top: 48,
transition: "left .1s",
marginRight: theme.spacing(2),
left: -200,
background: "#3f51b5",
"& div:first-element": {
marginTop: 100
}
},
menuOpen: {
left: 0,
transition: "left .1s"
},
menuClose: {
left: -200,
transition: "left .1s"
},
topMenu: {
display: "flex",
"& div": {
marginLeft: theme.spacing(1)
}
}
})
);
const UserMenu = () => {
const classes = useStyles();
const [menuOpen, setMenuOpen] = useState(false);
const handleOpen = (event: React.MouseEvent) => {
if (!menuOpen) {
event.stopPropagation();
setMenuOpen(true);
}
};
const handleClose = (event: React.MouseEvent<any, MouseEvent>) => {
if (menuOpen) {
setMenuOpen(false);
}
};
return (
<>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="menu"
onClick={handleOpen}
>
<MenuIcon />
</IconButton>
<div className={classes.toolBar}>
<ClickAwayListener onClickAway={handleClose}>
<Container
className={clsx(classes.menu, {
[classes.menuOpen]: menuOpen,
[classes.menuClose]: !menuOpen,
[classes.toolBar]: true
})}
onClick={handleClose}
>
<div>
<Link to="#">My Profile</Link>
</div>
<div>
<Link to="#">Account</Link>
</div>
<div>
<Link to="#">Admin</Link>
</div>
</Container>
</ClickAwayListener>
</div>
</>
);
};
const Header: React.FC = ({ children }) => {
const classes = useStyles();
return (
<AppBar position="static" className={classes.root}>
<Toolbar variant="dense">
<UserMenu />
<Typography variant="h6" className={classes.title}>
<Link to="#">Widgets, LLC</Link>
</Typography>
<div className={classes.topMenu}>
<div>
<Link to="#">Sign out</Link>
</div>
<div>
<Link to="#">One more</Link>
</div>
</div>
</Toolbar>
</AppBar>
);
};
export default Header;

Mouse pointer cannot detect element to trigger onClick()

From the snippet below, you can see that there are 2 Box components that are "underneath" the BigBox component, hence the mouse cannot detect them to trigger onClick(). Is there a way for the mouse to detect those 2 components.
I understand that you can just render the BigBox component first then Box like what I did with the <Box id="yes onClick"> component
However, for the project I'm working on, I specifically need to render a <Box>-like component first then a <BigBox>-like component.
const boxStyle = {
position: "absolute",
border: "1px #999 solid",
borderRadius: "10px",
textAlign: "center",
}
const Box = (props) => {
return (
<div style = {{ ...boxStyle, left: props.left, top: props.top, height: 50, width: 50 }}
onClick = {() => console.log("clicked")} > { props.id } </div>
);
};
const BigBox = (props) => {
return (
<div style = {{ ...boxStyle, left: props.left, top: props.top, height: 200, width: 200 }}> Big Box </div>
);
}
const App = () => {
return (
<div >
<Box id="no onClick 1" left={40} top={50}></Box>
<Box id="no onClick 2" left={80} top={100}></Box>
<BigBox left={10} top={10}></BigBox>
<Box id="yes onClick" left={150} top={150}></Box>
</div>
);
};
ReactDOM.render( <App/> ,
document.getElementById('root')
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Option 1 - pointer-events
You can set the CSS Property pointer-events of the BigBox style to none.
const boxStyle = {
position: "absolute",
border: "1px #999 solid",
borderRadius: "10px",
textAlign: "center",
}
const Box = (props) => {
return (
<div style = {{ ...boxStyle, left: props.left, top: props.top, height: 50, width: 50 }}
onClick = {() => console.log("clicked")} > { props.id } </div>
);
};
const BigBox = (props) => {
return (
<div style = {{ ...boxStyle, pointerEvents: 'none', left: props.left, top: props.top, height: 200, width: 200 }}> Big Box </div>
);
}
const App = () => {
return (
<div >
<Box id="no onClick 1" left={40} top={50}></Box>
<Box id="no onClick 2" left={80} top={100}></Box>
<BigBox left={10} top={10}></BigBox>
<Box id="yes onClick" left={150} top={150}></Box>
</div>
);
};
ReactDOM.render( <App/> ,
document.getElementById('root')
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Option 2 - z-index
Another solution would be to set the z-index of you Boxes to something bigger than the z-index of the BigBox component. That way the BigBox can still receive pointer events.
Option 3 - Use the layout hierarchy
Probably the most idiomatic way would be, to put your boxes inside BigBox.

Why isn't the <div> with classname="comments" not rerendering even though the it's style property is changed on button click?

I am a newbie in react.js and js , please consider it while answering the question.
I'm trying to toggle the visibility of the comment box that I have created when a button is clicked. I am using 'usestate' hook to set the initial state and toggle the visibility state when button is clicked. Even though the state is getting updated when the button is clicked, comment box isn't rerendered with the updated state.
APP.js
import React,{useState} from "react";
import "./App.css";
import "./main.css";
import { connect } from "react-redux";
function mapStateToProps(state) {
return {
item: state.item
};
}
function App(props) {
var [visibility,switchVisibility]=useState("visible");
switchVisibility = () => {
console.log(visibility,"outside")
if(visibility==="hidden"){
visibility="visible"
console.log(visibility,"visible")
}
else{
visibility="hidden"
console.log(visibility,"hidden")
}
};
return (
<div className="App">
<header className="Mainheader">
<p className="header-cont">Pro1</p>
</header>
<div className="Mainitem">
<div className="Itemwrapper">
<div className="Tabledetails">
<p className="Table">table</p>
<p className="Tnumber">12</p>
<p className="Orderedat">order given at</p>
<p className="Time">1:10 PM</p>
</div>
<div className="Button" onClick={switchVisibility}>
<p style={{ marginTop: "0px", marginLeft: "4px" }}>CLOSE</p>
</div>
</div>
<div className="divider" />
<div className="Maindetails">
{props.item.map((value,index)=>{console.log(value)
return(
<div key={index} className="plate-item">
<div className="item-name">{value.itemName}</div>
<div className="item-number">
<p
style={{
border: "3px solid #5FD0E2",
marginTop: "0px",
height: "19.74px",
width: "32.63px",
marginBottom: "8px"
}}
>
{value.itemNumber}
</p>
</div>
</div>
)})}
<div className="comments" style={{ visibility: visibility }}>
<p className="commentText">No comment</p>
</div>
</div>
</div>
</div>
);
}
export default connect(mapStateToProps)(App);
INDEX.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import { createStore } from "redux";
import * as serviceWorker from "./serviceWorker";
function reducer(state = initialState, action) {
console.log("reducer", state, action);
switch (action.type) {
case "CLOSE":
if (state.visibility === "visible") {
return {
visibility: "hidden"
};
} else {
return {
visibility: "visible"
};
}
default:
return state;
}
}
const initialState = {
item: [{
itemName : "Fried Rice",
itemNumber: 2,
comments: "No Comments",
visibility: "Visible"
},
{
itemName : "Chicken 65",
itemNumber: 4,
comments: "No Comments",
visibility: "Invisible"
},
{
itemName : "Mackan 65",
itemNumber: 6,
comments: "No Comments",
visibility: "Invisible"
}]
};
const store = createStore(reducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
serviceWorker.register();
Your switchVisibility function must receive the new state as first argument and you must not (never!) modify the state variable (visibility) by yourself.
You are also reassigning switchVisibility to a new function.
I suggest you to read carefully the setState hooks documentation, seems you are missing some basical knowledge of hooks.
So: it should be something like this:
<div className="Button" onClick={() => {
if(visibility==="hidden"){
switchVisibility("visible")
}
else{
switchVisibility("hidden")
}
}}>
<p style={{ marginTop: "0px", marginLeft: "4px" }}>CLOSE</p>
</div>

Categories

Resources