How to make a Semantic Ui React Sidebar With Dropdown - reactjs

Im trying to achieve `Semantic Ui React Sidebar With Dropdown
I want it like this:
I know its a lot to request but i hope you help me... Im trying to add a dropdown on my menu
This is my current sidebar code:
import React from 'react';
import cn from 'classnames';
import { Icon, Menu } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
export default function Sidebar(props) {
const classes = cn(
'ui',
'sidebar',
'push',
'left',
'inverted',
'menu',
'vertical',
'animating',
{ visible: props.toggleMenu }
);
return (
<div className={classes}>
<Menu.Item as={Link} to="/admin">
Dashboard <Icon name="home" />
</Menu.Item>
<Menu.Item as={Link} to="/admin/orders">
Orders
</Menu.Item>
</div>
);
}
and it looks like this:
Btw here is where i get the sidebar code... https://react.semantic-ui.com/modules/sidebar/
Help a newbie out? Thanks so much in advance!!!

Check this codesandbox which implements basic expand/collapse functionality but without icons.
You can adapt the logic to your code.
The main idea is to store the expanded/collapsed menus in your state
const [expandState, setExpandState] = useState({});
and depending on their state, to show hide sub menu items.
<Menu.Item as={Link} to="/admin">
<i className="fa fa-home" />
Dashboard
<span
style={{ float: "right" }}
onClick={() =>
setExpandState({
...expandState,
dashboard: !expandState.dashboard
})
}
>
Expand/Collapse
</span>
{expandState.dashboard ? (
<Menu.Item as={Link} to="/admin/orders">
Orders2
</Menu.Item>
) : null}
</Menu.Item>

Just float the icon left
<Icon name='home' style={{float : "left"}}/>
OR
you can use css
*** please check class Name ***
i {
float: left;
margin-right: 12px !important;
}
the menu item itself just becomes an a element
a {
text-align: center;
line-height: 40px;
}

Related

How can I make a dropdown menu from just an icon with ant design?

I'm trying to make a user icon with a dropdown menu using ant design libraries and reactjs. If I use Dropdown.Button, I'm getting a box around the icon. I think this is related to a CSS border-box property, but I can't seem to get rid of it. If I use the Dropdown component, then I can't add an icon and can use only text. What's the right way to achieve this result?
import { Menu, Dropdown } from 'antd';
import { UserOutlined } from '#ant-design/icons';
const Dummy = () => {
const userMenu = (
<Menu>
<Menu.Item key="1">Item 1</Menu.Item>
<Menu.Item key="2">Item 2</Menu.Item>
<Menu.Item key="3">Item 3</Menu.Item>
<Menu.Divider />
<Menu.Item key="3">Logout</Menu.Item>
</Menu>
);
return (
<div>
<Dropdown.Button
style={{ float: 'right' }}
overlay={userMenu}
icon={
<UserOutlined
style={{
fontSize: '28px',
backgroundColor: '#f0f0f0',
borderRadius: '50%',
}}
/>
}
></Dropdown.Button>
</div>
);
};
export default Dummy;
You can achieve the desired styling via CSS.
Add a className=dropdown-btn to the Dropdown.Button component
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Menu, Dropdown } from 'antd';
import { DownOutlined } from '#ant-design/icons';
import { Menu, Dropdown } from 'antd';
import { UserOutlined } from '#ant-design/icons';
const Dummy = () => {
const userMenu = (
<Menu>
<Menu.Item key="1">Item 1</Menu.Item>
<Menu.Item key="2">Item 2</Menu.Item>
<Menu.Item key="3">Item 3</Menu.Item>
<Menu.Divider />
<Menu.Item key="3">Logout</Menu.Item>
</Menu>
);
return (
<div>
<Dropdown.Button
style={{ float: 'right' }}
className="dropdown-btn"
overlay={userMenu}
icon={
<UserOutlined
style={{
fontSize: '28px',
backgroundColor: '#f0f0f0',
borderRadius: '50%',
}}
/>
}
></Dropdown.Button>
</div>
);
};
export default Dummy;
ReactDOM.render(
<Dummy/>,
document.getElementById('container'),
);
Add CSS to target the offending border and background-color:
.dropdown-btn > .ant-dropdown-trigger {
border: none;
}
.dropdown-btn > .ant-dropdown-trigger > span {
background-color: white !important;
}
If the use of !important is a problem, you can add more selectors to increase the specificity.
Working StackBlitz link:
https://ant-design-dropdown-link-button.stackblitz.io
P.S. Another quick and dirty solution is to add the type="link" attribute to the Dropdown.Button. This will get rid of the border styling, but the background color will remain and the icon will turn blue.

How to Prevent React Dropdown Menu from disappearing when I try to hover over it?

So I have this Navbar that shows the dropdown menu whenever I hover over it
<li>
<Link
to='/'
className='nav-links'
onClick={() => setClick(false)}
onMouseEnter={() => setDropdown(true)}
onMouseLeave={() => setDropdown(false)}
>
Home
</Link>
{dropdown && <Dropdown />}
</li>
My issue is when I try to hover over the actual dropdown menu it disappears since the onMouseEnter is only set on the <Link> tag
Here is the Dropdown Component Code
function Dropdown() {
return (
<>
<ul className='dropdown-menu'>
{MenuItems.map((item, index) => {
return (
<li key={index}>
<a className={item.cName} href={item.url}>
{item.title}
</a>
</li>
);
})}
</ul>
</>
);
}
export default Dropdown;
CSS that displays the Dropdown
.dropdown-menu {
background: red;
width: 200px;
position: absolute;
top: 80px;
list-style: none;
}
.dropdown-menu li {
background: yellow;
padding: 16px;
}
How would I prevent the dropdown from disappearing whenever I hover over the <Link> tag?
I think you should pass to the Dropdown component in props a function that calls setDropdown(true) and one that calls setDropdown(false). Then in the Dropdown component get the functions from props and call them on mouseEnter and mouseLeave.
If you want this to work you should assign onMouseEnter and onMouseLeave to the "li" tag.
Otherwise it won't be working because you leave the area of the Link with your mouse.

Adding color for a span inside a Link component doesn't change the color of the span?

import React, { Component } from 'react';
import { Menu, Icon } from 'antd';
import { Link } from 'react-router-dom';
const Sidebar = () => (
<div style={{ width: '164px', padding: '20px 0px' }}>
<Menu
style={{ width: '164px' }}
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
mode="inline"
>
<Menu.Item key="1">
<Icon type="home" />
<Link to='/'><span style={{ color: 'rgb(199, 143, 95)' }}>Home</span></Link>
</Menu.Item>
<Menu.Item key="2">
<Icon type="tags" />
<Link to='/tags'><span style={{ color: 'rgb(199, 143, 95)' }}>Tags</span></Link>
</Menu.Item>
<Menu.Item key="3">
<Icon type="team" />
<Link to='/users'><span style={{ color: 'rgb(199, 143, 95)', backgroundColor: 'yellow' }}>Users</span></Link>
</Menu.Item>
</Menu>
</div>
);
export default Sidebar;
I used antd as a ui framework. Since Link component hides the texts in the span elements i want to change the color of span elements but adding color prop doesn't have any effect.
I don't fully understand your question, but if you want to have the write "Tags" (which is also a link) written in red, you might try one these two ways:
<Link to='/tags'><span style={{ color: 'red !important' }}>Tags</span></Link>
This way, you are saying that your CSS rules should override already existing ones. Though, you should try to avoid using the !important keyword, unless extreme cases;
<Link to='/tags' class='myLink'><span>Tags</span></Link> This way, first you give a class to the link you want in red, then you need to write some a CSS rule:
a.myLink:link, a.myLink:visited, a.myLink:hover, a.myLink:active {
color: red;
}
Of course, whit this rule you will the red link anytime (if you click it, if you are hover it,..) You can customize that by dividing the CSS rules.

How to change Content based on Menu Item click in AntD (React UI Library)

I am following AntD Menu tutorial from here and it shows how to use this component. But I am failed to understand that how to change my Content when I click on different menu option. Tried searching a lot but did not find any useful help.
import { Layout, Menu, Icon } from 'antd';
const { Header, Sider, Content } = Layout;
class SiderDemo extends React.Component {
state = {
collapsed: false,
};
toggle = () => {
this.setState({
collapsed: !this.state.collapsed,
});
}
render() {
return (
<Layout>
<Sider
trigger={null}
collapsible
collapsed={this.state.collapsed}
>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={['1']}>
<Menu.Item key="1">
<Icon type="user" />
<span>nav 1</span>
</Menu.Item>
<Menu.Item key="2">
<Icon type="video-camera" />
<span>nav 2</span>
</Menu.Item>
<Menu.Item key="3">
<Icon type="upload" />
<span>nav 3</span>
</Menu.Item>
</Menu>
</Sider>
<Layout>
<Header style={{ background: '#fff', padding: 0 }}>
<Icon
className="trigger"
type={this.state.collapsed ? 'menu-unfold' : 'menu-fold'}
onClick={this.toggle}
/>
</Header>
<Content style={{ margin: '24px 16px', padding: 24, background: '#fff', minHeight: 280 }}>
Some Conent
</Content>
</Layout>
</Layout>
);
}
}
ReactDOM.render(<SiderDemo />, mountNode);
Can you pls help.
Just WRAP your Layout tag inside Router tag of react-router-dom.
import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import { Layout, Menu, Icon } from 'antd';
import Dashboard from './containers/Dashboard/Dashboard';
import Meseros from './containers/Meseros/Meseros';
const { Header, Content, Footer, Sider } = Layout;
const SubMenu = Menu.SubMenu;
class RouterApp extends Component {
state = {
collapsed: false,
};
onCollapse = (collapsed) => {
this.setState({ collapsed });
}
toggle = () => {
this.setState({
collapsed: !this.state.collapsed,
});
}
render() {
return (
<Router>
<Layout style={{ minHeight: '100vh' }}>
<Sider
collapsible
collapsed={this.state.collapsed}
onCollapse={this.onCollapse}>
<div className="logo" />
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline">
<Menu.Item key="1">
<Icon type="pie-chart" />
<span>Deshboard</span>
<Link to="/" />
</Menu.Item>
<Menu.Item key="2">
<Icon type="desktop" />
<span>Meseros</span>
<Link to="/meseros" />
</Menu.Item>
</Menu>
</Sider>
<Layout>
<Header style={{ background: '#fff', padding: 0, paddingLeft: 16 }}>
<Icon
className="trigger"
type={this.state.collapsed ? 'menu-unfold' : 'menu-fold'}
style={{ cursor: 'pointer' }}
onClick={this.toggle}
/>
</Header>
<Content style={{ margin: '24px 16px', padding: 24, background: '#fff', minHeight: 280 }}>
<Route exact path="/" component={Dashboard} />
<Route path="/meseros" component={Meseros} />
</Content>
</Layout>
</Layout>
</Router>
);
}
}
export default RouterApp;
If you don't want to change the route every time you choose a menu item you can use the following approach
function MyComponent(){
const [selectedMenuItem, setSelectedMenuItem]= useState('item1');
const componentsSwtich = (key) => {
switch (key) {
case 'item1':
return (<h1>item1</h1>);
case 'item2':
return (<h1>item2</h1>);
case 'item3':
return (<h3>item3/h3>);
default:
break;
}
};
return(
<div>
<Menu selectedKeys={selectedMenuItem} mode="horizontal" onClick={(e) =>
setSelectedMenuItem(e.key)}>
<Menu.Item key="item1">your first component here</Menu.Item>
<Menu.Item key="item2">your second here</Menu.Item>
<Menu.Item key="item3">your third here</Menu.Item>
</Menu>
<div>
{componentsSwitch(selectedMenuItem)}
</div>
</div>)
You can bind onClick with your MenuItem and re render the component upon clicking any menuItem. And you can make you content in your Constant.js or initialize it in your component state.
Add onClick method
<Menu.Item
key="1"
onClick={this.handleMenuClick}
>
<Icon type="user" />
<span>nav 1</span>
</Menu.Item>
make a handle for MenuItem click:
handleMenuClick = event => {
//you can get here event.target.value
//filter the content
//setState the content your component will re render and content will be updated.
}
In case you want to change the route, there is a working example here
https://codesandbox.io/embed/fervent-thunder-egyk1?fontsize=14&hidenavigation=1&theme=dark
Add Link component in Menu.Item and wrap with BrowserRouter from react-router-dom
<Menu.Item key="2">
CI/CD Pipelines
<Link to={`${match.url}/cicd`} />
</Menu.Item>
You can add the onClick prop for the Menu item as follow:
NB. I am using NextJs router to link the menus to their own corresponding page. In case you are using React, use Link from router-dom.
<pre>
<Menu
onClick={({ keyPath }) => router.push(`/${keyPath}`)}
mode="inline"
defaultSelectedKeys={["dashboard"]}
defaultOpenKeys={["company-info"]}
items={navs}
theme="dark"
/>

Semantic-UI-react fixed sidebar

Have Googled, searched within semantic ui's docs and issues page, and searched within stackoverflow. Couldn't find the answer.
Within Semantic-ui-react, how do I make a sidebar whose content is fixed to the screen? What I currently have is this:
<Sidebar.Pushable as={Segment}>
<Sidebar
id="sidebar"
as={Menu}
animation="overlay"
direction="right"
visible={this.state.visible}
vertical
inverted
>
{this.getMenuItems()}
</Sidebar>
<Sidebar.Pusher>
<Route path="/" component={Filler} />
</Sidebar.Pusher>
</Sidebar.Pushable>
There doesn't seem to be any word in it in the semantic-ui-react documentation, and making Sidebar.Pushable, Sidebar, or any of the Menu Items position:fixed; doesn't seem to work either.
I was able to achieve a sticky sidebar with the help of this answer.
Basically, it states that in order to have a fixed sidebar that sticks to the our infinite scrolling page, we must remove the transform attribute
on the parent container. The reasoning is because the transform changes the positioning context from the viewport to the
rotated element. As a result, the "fixed" child element, behaves as if it has "absolute" positioning.
I added this to the sidebar.overrides file
/* Page Context */
.pushable:not(body) {
transform: none;
}
.pushable:not(body) > .ui.sidebar,
.pushable:not(body) > .fixed,
.pushable:not(body) > .pusher:after {
position: fixed;
}
This solution is meant for the base semantic-ui library. Since semantic-ui-react requires semantic-ui, this ends up working for semantic-ui-react sidebars as well.
Give a try with below code.
<Sidebar as={Menu} animation='overlay' icon='labeled' inverted vertical visible width='wide'>
<Menu.Item as={Link} to="/admin">
<Icon name='building' />
Rubykraft
</Menu.Item>
<Menu.Item as='a'>
<Icon name='user' />
Shan
</Menu.Item>
<Menu.Item as='a'>
<Icon name='user' />
Vishnu
</Menu.Item>
</Sidebar>
I've used classes from semantic-ui's Sidebar module to create the desired fixed sidebar. If you want a more Component(ish) code, you should replace the pusher class with it's correspondent Sidebar.Pusher Component.
Here's my code:
import React, { Component } from 'react'
import { Dropdown, Icon, Input, Menu } from 'semantic-ui-react'
export default class MySidebar extends Component {
state = {}
handleItemClick = (e, { name }) => this.setState({ activeItem: name })
componentDidMount() {}
render() {
const { activeItem } = this.state
return(
<div className='pusher'>
<div className='full height'>
<div className='toc'>
<Menu className='inverted vertical left fixed'>
<Menu.Item>
Home
<Icon name='dashboard' />
<Menu.Menu>
<Menu.Item name='search' active={activeItem === 'search'} onClick={this.handleItemClick}>
Search
</Menu.Item>
<Menu.Item name='add' active={activeItem === 'add'} onClick={this.handleItemClick}>
Add
</Menu.Item>
<Menu.Item name='about' active={activeItem === 'about'} onClick={this.handleItemClick}>
Remove
</Menu.Item>
</Menu.Menu>
</Menu.Item>
<Menu.Item name='browse' active={activeItem === 'browse'} onClick={this.handleItemClick}>
<Icon name='grid layout' />
Browse
</Menu.Item>
<Menu.Item name='messages' active={activeItem === 'messages'} onClick={this.handleItemClick}>
Messages
</Menu.Item>
<Dropdown item text='More'>
<Dropdown.Menu>
<Dropdown.Item icon='edit' text='Edit Profile' />
<Dropdown.Item icon='globe' text='Choose Language' />
<Dropdown.Item icon='settings' text='Account Settings' />
</Dropdown.Menu>
</Dropdown>
</Menu>
</div>
<div className='article'>
<div>Content</div>
</div>
</div>
</div>
)
}
}
And the style:
.toc {
width: 200px;
}
.article {
margin-left: 210px;
}
Everything is easier!
<Sidebar.Pusher style={{overflow: 'scroll', height: '100%'}}>
I think you yourself will understand why this works.
You would need to manually do it with some CSS/SCSS. Basically, you need to set the height to a fixed value.
#media only screen and (max-width: 768px) {
.ui.wide.left.sidebar, .ui.wide.right.sidebar {
height: 100vh !important;
position: absolute;
}
.pusher {
margin-left: 20px;
}
}
.pushable {
min-height: 100vh;
}
.ui.wide.left.sidebar, .ui.wide.right.sidebar {
height: 100vh;
position: fixed !important;
bottom: 0px !important;
top: 0px !important;
}
Based on the Semantic React documentation, there's an obvious way to do this, but the behavior it produces is extremely wonky: the desktop sidebar menu sticks to the top of the window as expected until you scroll near the bottom, then zooms down and attaches to the BOTTOM of the screen.
Pretty awful. This appears to be a solid fix. I've only tested it in my own setup, but it should be fairly universal, or at least a good starting point!
In order to get the sticky sidebar/mobile overlay combo described above, you would expect the relevant part of your _app.jsx to look something like this. NOT THE ONLY WAY! But also not the point, adapt to your own situation. For clarity, anything below prefixed with my... is your responsibility.
// _app.jsx
<Sidebar.Pushable>
<Sticky>
<Sidebar
as={Menu}
animation="overlay"
direction="left"
inverted
onClick={myOnHideSidebar}
onHide={myOnHideSidebar}
size="huge"
vertical
visible={mySidebarVisible}
>
<MySidebarItems />
</Sidebar>
</Sticky>
<Sidebar.Pusher dimmed={mySidebarVisible}>
<Container>
<MyPageHeader />
<Grid>
<Grid.Row>
<Grid.Column computer={4} only="computer">
<Menu fluid size="huge" vertical>
<MySidebarItems />
</Menu>
</Grid.Column>
<Grid.Column mobile={16} tablet={16} computer={12}>
<Component {...pageProps} />
</Grid.Column>
</Grid.Row>
</Grid>
<MyPageFooter />
</Container>
</Sidebar.Pusher>
</Sidebar.Pushable>
In practice, this produces the weird behavior I described above.
Here's the fix. Look for the STICKYFIX comments, and note the stylesheet entry, which is necessary because of the -webkit style.
// _app.jsx
<Sidebar.Pushable style={{ transform: 'none' }}> // STICKYFIX
<Sticky>
<Sidebar
as={Menu}
animation="overlay"
direction="left"
inverted
onClick={myOnHideSidebar}
onHide={myOnHideSidebar}
size="huge"
vertical
visible={mySidebarVisible}
>
<MySidebarItems />
</Sidebar>
</Sticky>
<Sidebar.Pusher
dimmed={mySidebarVisible}
style={{ minHeight: '100vh' }} // STICKYFIX
>
<Container>
<MyPageHeader />
<Grid>
<Grid.Row>
<Grid.Column computer={4} only="computer">
<Menu className="sidebar-menu" fluid size="huge" vertical> // STICKYFIX
<SidebarItems />
</Menu>
</Grid.Column>
<Grid.Column mobile={16} tablet={16} computer={12}>
<Component {...pageProps} />
</Grid.Column>
</Grid.Row>
</Grid>
<MyPageFooter />
</Container>
</Sidebar.Pusher>
</Sidebar.Pushable>
// styles.css
.sidebar-menu {
position: sticky;
position: -webkit-sticky;
top: 20px;
}
Here's my gist detailing the fix.

Resources