I am currently working on a project using Ant Design and on one page I have a dropdown Menu. When you click on the menu it opens the dropdown but if you scroll down the menu still keeps open. I wish to implement that when the user is scrolling down the menu closes.
I tried to implement a handleScroll() function in order to use is with the provided prop onVisibleChange. However I am not sure what I should add in the function to get it work.
handleScroll = (e) => {
window.addEventListener('scroll', () => {
console.log('scrolling');
})
}
<Dropdown onVisibleChange={visible => this.handleScroll(
console.log(visible)) } trigger={['click']} overlay={
<Menu>
<Menu.Item key="1"
onClick={() => this.scrollTo(this.whyRef)}>
<Icon icon={u1F427} /> <strong>WHY</strong>
</Menu.Item>
</Menu>
}>
<Dropdown>
You need to add the event listener in the constructor and handle the visibility state of the dropdown yourself. Try something like this:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
visible: false
};
window.addEventListener("scroll", this.closeMenu);
}
toggleMenu = () => {
this.setState({ visible: !this.state.visible });
};
closeMenu = () => {
this.setState({ visible: false });
};
render() {
return (
<div>
<Dropdown
overlay={menu}
onVisibleChange={this.toggleMenu}
visible={this.state.visible}
trigger={["click"]}
>
<a className="ant-dropdown-link" href="#">
Click me <Icon type="down" />
</a>
</Dropdown>
</div>
);
}
}
Working example: https://codesandbox.io/s/2ovjzwqz9y
Related
I want to make the Mantine burger auto close after a nav choice is selected in the modal. It seems as if it is not possible for the Mantine burger to automatically close (because the x icon stays). I am using NextJS with Mantine & react-icons.
export default function HeaderMiddle({ links }: HeaderMiddleProps) {
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const dark = colorScheme === 'dark';
const [opened, { toggle, open, close }] = useDisclosure(false, {
onOpen: () => burgerClick(),
onClose: () => closeAllModals(),
});
const [active, setActive] = useState(links[0].link);
const { classes, cx } = useStyles();
const items = links.map((link) => (
<a
key={link.label}
href={link.link}
className={cx(classes.link, {
[classes.linkActive]: active === link.link,
})}
onClick={(event) => {
event.preventDefault();
setActive(link.link);
}}
>
{link.label}
</a>
));
const burgerItems = links.map((link) => (
<a
key={link.label}
href={link.link}
className={cx(classes.link, {
[classes.linkActive]: active === link.link,
})}
onClick={(event) => {
event.preventDefault();
setActive(link.link);
closeAllModals();
}}
>
{link.label}
</a>
));
const burgerClick = () => {
openModal({
title: 'Subscribe to newsletter',
children: <>{burgerItems}</>,
});
};
return (
<Header height={56} mb={120}>
<Container className={classes.inner}>
<Burger
opened={opened}
onClick={toggle}
size="sm"
className={classes.burger}
/>
<Group className={classes.links} spacing={5}>
{items}
</Group>
{/* <MantineLogo size={28} /> */}
<Group spacing={0} className={classes.social} position="right" noWrap>
...
</Group>
</Container>
</Header>
);
}
Any help with this would be appreciated.
next: 12.1.5
#mantine/core: 4.2.12
After clicking the link call close method of useDisclosure, it will make opened to false. Burger state is depend on opened so, it wil close the burger on false.
const burgerItems = links.map((link) => (
<a
key={link.label}
href={link.link}
className={cx(classes.link, {
[classes.linkActive]: active === link.link,
})}
onClick={(event) => {
event.preventDefault();
setActive(link.link);
closeAllModals();
// It will set opened to false (so it will close Burger)
close()
}}
>
{link.label}
</a>
));
I found something that might help you
On the Mantine Doc, one header called 'Responsive header' is working fine after you clicked on any of the burger items.
Simply follow the codes and should be fine!
Im trying to implement an Antd Dropdown Menu with Search functionality, and wanted to close the dropdown when clicking outside. But in my code, along with clicking outside even if I click on the Search box, it toggles opening and closing of the dropdown(not desired). I want my dropdown menu to be open only if there is some character input in the search box and close if there is no character in the search box or if clicked outside, and it should not open and close if I go on just clicking on the search box itself. Is there any prop Im missing out on?
Here is the sample code:
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Menu, Dropdown, Input } from "antd";
class OverlayVisible extends React.Component {
state = {
visible: false
};
handleMenuClick = (e) => {
if (e.key === "3") {
this.setState({ visible: false });
}
};
handleVisibleChange = (flag) => {
this.setState({ visible: flag });
};
render() {
const menu = (
<Menu onClick={this.handleMenuClick}>
<Menu.Item key="1">Clicking me will not close the menu.</Menu.Item>
</Menu>
);
return (
<Dropdown
onClick={() => {
this.setState({ visible: true });
}}
overlay={menu}
onVisibleChange={this.handleVisibleChange}
visible={this.state.visible}
trigger={["click"]}
>
<Input.Search
onInput={() => {
this.setState({ visible: true });
}}
></Input.Search>
{/* <a className="ant-dropdown-link" onClick={e => e.preventDefault()}>
Hover me <Icon type="down" />
</a> */}
</Dropdown>
);
}
}
ReactDOM.render(<OverlayVisible />, document.getElementById("container"));
CodeSandbox Link: https://codesandbox.io/s/aman-521r2?file=/index.js:0-1236
Ran into the same issue and solved by adding onClick={(e) => e?.stopPropagation()} to the container where the click should not close the overlay
I think you can do this by changing Input Search m
<Input.Search
onChange={(e) => {
if (e.target.value.length) {
this.setState({ visible: true });
return false;
}
this.setState({ visible: false });
}}
></Input.Search>
and remove setstate from handleVisibleChange
handleVisibleChange = (flag) => {
};
Add one more state for storing input values.
state = {
visible: false,
value: ''
};
Now set the value state when user type the value in the input
<Input.Search
onInput={() => {
this.setState({ visible: true });
}}
onChange={e => this.setState({value: e.target.value})}
/>
Now control the visibility of dropdown with the help of the length of the value.
handleVisibleChange = (flag) => {
if(this.state.value.length <= 0)
this.setState({ visible: false });
else
this.setState({ visible: flag });
};
Full code:
import React from "react";
import ReactDOM from "react-dom";
import "antd/dist/antd.css";
import "./index.css";
import { Menu, Dropdown, Input } from "antd";
class OverlayVisible extends React.Component {
state = {
visible: false,
value: ''
};
handleMenuClick = (e) => {
if (e.key === "3") {
this.setState({ visible: false });
}
};
handleVisibleChange = (flag) => {
if(this.state.value.length<=0)
this.setState({ visible: false });
else
this.setState({ visible: flag });
};
render() {
const menu = (
<Menu onClick={this.handleMenuClick}>
<Menu.Item key="1">Clicking me will not close the menu.</Menu.Item>
</Menu>
);
return (
<Dropdown
onClick={() => {
this.setState({ visible: true });
}}
overlay={menu}
onVisibleChange={this.handleVisibleChange}
visible={this.state.visible}
trigger={["click"]}
>
<Input.Search
onInput={() => {
this.setState({ visible: true });
}}
onChange={e => this.setState({value: e.target.value})}
></Input.Search>
{/* <a className="ant-dropdown-link" onClick={e => e.preventDefault()}>
Hover me <Icon type="down" />
</a> */}
</Dropdown>
);
}
}
ReactDOM.render(<OverlayVisible />, document.getElementById("container"));
Just want to add my experience to #raqibrasheed 786's answer.
I faced the same problem to control the open status of dropdown menu. I was trying to build the multiselect with items with checkbox.
type MultiSelectProps = {
options: Array<{ value: string, label: string }>;
} & Partial<DropdownProps>;
export const MultiSelect: React.FC<MultiSelectProps> = ({
options,
}) => {
const handleOnClickOption = (e: any, val: string) => {
// !!!this is the key point to stop propagation, so that the menu won't be closed on menu item click.
e.stopPropagation();
// describe your action on clicking each menu item.
};
const ignoreHandler = (e: any) => e.preventDefault();
const menu = (<Menu
items={options?.map(option => ({
key: option.value,
label:
<div
role="button"
onClick={(e) => handleOnClickOption(e, option.value)}
>
{/* You component to render the menu item, but inogre all click event from the children */}
<Checkbox
checked={!!Math.floor(Math.random() + 1)}
onClick={ignoreHandler}
/>
</div>
}))}
/>);
return (
<Dropdown
className=""
overlay={menu}
trigger={["click"]}
>
{/* render soemthing for current status */}
</Dropdown>
);
};
The key points were
to prevent the children items
to stop propagation in the menu item wrapper.
So we can prevent the menu from closing a single click event.
I have two component: 1:StudentList 2: Major in react and antd.
StudentList Component rendered a list of students.
Major Component made a list of majors that you can pick them. After selecting major, the selected major title display on the top of the students list. and the list will be filtered according to the selected major.
This is StudentList component contain Major component:
class StudentList extends Component {
render(){
return(
<>
<Major/>
<h5>20 student found in <a>selected major</a></h5>
<List>
//this is the list of students and is not related to this question
</List>
</>);
}
}
This is Major Component with a filter button to open the popover:
class Major extends Component {
render() {
return (
<Popover
trigger="click"
content={content} //list of majors
>
<Button>
<FilterOutlined /> Select major to filter
</Button>
</Popover>
);
}
}
When I click on the Select major to filter button, the popover open to select majors. I want to change the code in order to open this popover from two place:
1- click on Select major to filter button in the Major component
2- click on selected major in the title in StudentList component.
Notice: I want to open the same popover in the same place (similar to when I click on Select major to filter button)
Maybe it could handle with state and handleVisibleChange function. but I don't know how to handle it from 2 components. I glad to hearing your solutions.
You can use the visible and onVisibleChange property from Antd's tooltip because they are used by the PopOver as well. You can find an easy example from Andt how to control a PopOver by visible in the docs.
To get the button click you can use onClick from antd's Button Api.
The desired example using React Components:
class Major extends Component {
componentDidUpdate(prevProps) {
// Typical usage (don't forget to compare props):
if (this.props.value !== prevProps.value) {
this.setState({ visible: this.props.value });
}
}
state = {
visible: false
};
hide = () => {
this.setState({
visible: false
});
};
handleVisibleChange = visible => {
this.setState({ visible });
// this.props.onChange(visible); // add me to open popover on every click on studenlist
};
render() {
return (
<Popover
trigger="click"
content={<a onClick={this.hide}>Close</a>}
visible={this.state.visible}
onVisibleChange={this.handleVisibleChange}
>
<Button>Select major to filter</Button>
</Popover>
);
}
}
class StudentList extends Component {
state = {
visible: false
};
onClick = () => {
this.setState({ visible: !this.state.visible });
};
render() {
return (
<>
{/* <Major value={this.state.visible} onChange={setVisible} /> */}
<Major value={this.state.visible} />
<h5>
20 student found in <a>selected major</a>
</h5>
<Button onClick={this.onClick}>Select major from Studenlist</Button>
</>
);
}
}
Component example as a CodeSandBox.
Here is a simple example for your request using react hooks and simple buttons to open the PopOver:
function Major({ value, onChange }) {
const [visible, setVisible] = useState(value);
useEffect(() => {
value && setVisible(value);
}, [value]);
const hide = () => setVisible(false);
const handleVisibleChange = visible => {
setVisible(visible);
onChange(visible);
};
return (
<Popover
trigger="click"
content={<a onClick={hide}>Close</a>}
visible={visible}
onVisibleChange={handleVisibleChange}
>
<Button>Select major to filter</Button>
</Popover>
);
}
function StudentList() {
const [visible, setVisible] = useState(false);
const onClick = () => {
setVisible(true);
};
return (
<>
<Major value={visible} onChange={setVisible} />
<h5>
20 student found in <a>selected major</a>
</h5>
<Button onClick={onClick}>Select major from Studenlist</Button>
</>
);
}
The depended working CodeSandBox.
Edit1: Added React Component example.
I'm trying to build a custom modal off this react carousel library
Basically, I'm at the point where the modal is implemented correctly, but I can't seem to find out how to wire the featured image on the modal to always be the same like the one which is currently also being featured in the carousel.
const Modal = ({ modal, items, handleModalFalse, currentIndex, featured }) => {
return (
<div className={modal ? "modal" : "hide"} onClick={handleModalFalse}>
{/* this only features the first image */}
{/* <img src={featured} /> */}
{/* shows every image on modal*/}
{items.map((item, i) => (
<div key={i} className="wrapper">
<img src={item} />
</div>
))}
</div>
);
};
These are all the handlers that power the Carousel.
I'm struggling to wire the featured image correctly (perhaps inside componentDidMount() ?)
class Gallery2 extends React.Component {
state = {
currentIndex: 0,
items: [
"https://homepages.cae.wisc.edu/~ece533/images/airplane.png",
"https://homepages.cae.wisc.edu/~ece533/images/baboon.png",
"https://homepages.cae.wisc.edu/~ece533/images/cat.png",
"https://homepages.cae.wisc.edu/~ece533/images/boat.png"
],
modal: false,
featured: undefined
};
// what should go here instead??
componentDidMount() {
this.setState({ featured: this.state.items[0] });
}
slideTo = i => console.log("clicked") || this.setState({ currentIndex: i });
onSlideChanged = e => this.setState({ currentIndex: e.item });
slideNext = () =>
this.setState({ currentIndex: this.state.currentIndex + 1 });
slidePrev = () =>
this.setState({ currentIndex: this.state.currentIndex - 1 });
handleEnlargeClick = () => {
this.setState({ modal: true });
};
handleModalFalse = () => {
this.setState({ modal: false });
};
render() {
return (
<>
<h3>React Alice Carousel</h3>
{/* MODAL */}
<Modal
modal={this.state.modal}
items={this.state.items}
handleModalFalse={this.handleModalFalse}
currentIndex={this.state.currentIndex}
featured={this.state.featured}
/>
<RenderGallery
items={this.state.items}
responsive={this.responsive}
onSlideChanged={this.onSlideChanged}
currentIndex={this.state.currentIndex}
/>
<button onClick={() => this.handleEnlargeClick()}>
click to enlarge
</button>
<RenderThumbs
items={this.state.items}
slideNext={this.slideNext}
slidePrev={this.slidePrev}
slideTo={this.slideTo}
responsive={this.responsive}
/>
</>
);
}
}
*******
You can see the full Carousel implementation and bug in action here on this code sandbox. Please feel free to fork it.
Right now my Modal only either shows the first image or all of them...
I understand why I currently only get either the first or all of the images, I just can't figure a solution for my issue.
How can I make the featured image on the Modal to be the same as the one currently on the carousel?
The issue in your code is that your modal is looping over all of the items, and displaying them when you click the "enlarge" button. To fix this, you just need to use the selectedIndex with your items variable to get the currently selected item.
Sandbox example: https://codesandbox.io/s/21p19wmpyy
Modal Code:
const Modal = ({ modal, items, handleModalFalse, currentIndex, featured }) => {
return (
<div className={modal ? "modal" : "hide"} onClick={handleModalFalse}>
{/* this only features the first image */}
{/* <img src={featured} /> */}
{/* shows every image on modal*/}
<div className="wrapper">
<img src={items[currentIndex]} />
</div>
</div>
);
};
Ok the question is simple. I need to make the dropdown open when mouse hover rather than on click.
Currently my code is as follow.
<Nav>
<NavDropdown
onMouseEnter = {()=> isOpen=true}
open={isOpen}
noCaret
id="language-switcher-container"
>
<MenuItem>Only one Item</MenuItem>
</NavDropdown>
</Nav>
as you can see, I tried onMouseEnter but no effect. Can someone solve this issue? What attribute should I pass in to achive this.
The API page is here react-bootstrap API page
export class Nav extends React.Component {
constructor(props) {
super(props)
this.state = { isOpen: false }
}
handleOpen = () => {
this.setState({ isOpen: true })
}
handleClose = () => {
this.setState({ isOpen: false })
}
render() {
return (
<Nav>
<NavDropdown
onMouseEnter = { this.handleOpen }
onMouseLeave = { this.handleClose }
open={ this.state.isOpen }
noCaret
id="language-switcher-container"
>
<MenuItem>Only one Item</MenuItem>
</NavDropdown>
</Nav>
)
}
}
Hope this solves your issue.
A much cleaner pure CSS solution here:
.dropdown:hover .dropdown-menu {
display: block;
}
Having just run into this issue myself, I found out you need both the bootstrap CSS and a parameter for react-bootstrap. Add the following CSS:
.dropdown:hover>.dropdown-menu {
display: block;
}
Then you need to add the parameter renderMenuOnMount={true} for the child elements to render on load:
<NavDropdown renderMenuOnMount={true}>
<NavDropdown.Item href="#">Item #1</NavDropdown.Item>
</NavDropdown>
It's as simple as that :)
<NavDropdown onMouseEnter={(e) => document.getElementById("idofthiselement").click()} onMouseLeave={(e) => document.getElementById("idofthiselement").click()} title="Others" id="idofthiselement">
This seems like a hacky solution, but it was the easiest way to keep my app running after updating.
With the latest update the dropdown-menu isn't rendered until the button is clicked.
I've been using this in my css:
.dropdown:hover .dropdown-menu {
display: block;
}
and added this to my component
componentDidMount() {
// TODO: Fix so menu renders automatically w/out click
const hiddenNavToggle = document.getElementById('navbar__hamburger_wrapper-hidden_click')
hiddenNavToggle.click()
hiddenNavToggle.click()
}
I added a wrapping div with the given id just to click for this.
I'd love to hear another solution.
base on the answer of #Rei Dien
onMouseEnter = { this.handleOpen.bind(this) }
onMouseLeave = { this.handleClose.bind(this) }
Inspired by Rei Dien's answer, here's how you could do this using React Hooks.
export const Menu = () => {
const [menuOpen, toggleMenuOpen] = useState(false);
return (
<Dropdown
onMouseEnter={() => {
toggleMenuOpen(true);
}}
onMouseLeave={() => {
toggleMenuOpen(false);
}}
show={menuOpen}
>
<Dropdown.Toggle>
Menu
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item href="#">Menu!!!</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
);
};
Another way
const HoverDropDown = ({ children, ...rest }) => {
const [show, setShow] = useState(false);
return (
<NavDropdown {...rest} show={show} onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>
{children}
</NavDropdown>
);
}
<Nav>
<HoverDropDown id="language-switcher-container" title="Menu">
<Dropdown.Item>Item 1</Dropdown.Item>
</HoverDropDown>
</Nav>
You could replicate this https://codepen.io/bsngr/pen/frDqh which uses the following JS
$('ul.nav li.dropdown').hover(function() {
$(this).find('.dropdown-menu').stop(true, true).delay(200).fadeIn(500);
}, function() {
$(this).find('.dropdown-menu').stop(true, true).delay(200).fadeOut(500);
});
Or alternatively you could install a plugin like this https://github.com/CWSpear/bootstrap-hover-dropdown
EDIT: Perfectly fine solutions to bootstrap but I noticed now you said its react and I havent used that before but I dont see why the js above wouldnt work even if it requires tweaking