Is it possible to display the popup of a vertical submenu "the other way around" ? I have one SubMenu item in a fixed Sidebar at the bottom of my page. It contains links for the users profile and the logout link. But since it is at the bottom of the page the submenu will open and part of it is outside the of the page.
Here is a screenshot of the current situation.
I searched for options the documentation but I couldn't find a suitable solution for the problem. So basically what I want to achieve is growing the popup from bottom to top and not top to bottom.
Here is the source for the Sidebar component. It is quite a early stage so there are still other improvements to the code to do.
import React from 'react';
import { connect } from 'react-redux';
import { Layout, Menu, Icon } from 'antd';
import { withRouter } from 'react-router-dom';
import styled from 'styled-components';
import { toggleSidebar } from '../../actions/ui';
import { logoutUser } from '../../actions/user';
const { Sider } = Layout;
const SubMenu = Menu.SubMenu;
const Logo = styled.div`
height: 32px;
background: rgba(255, 255, 255, 0.2);
margin: 16px;
`;
const UserMenu = styled(SubMenu)`
position: fixed;
text-align: center;
bottom: 0;
cursor: pointer;
height: 48px;
line-height: 48px;
color: #fff;
background: #002140;
z-index: 1;
transition: all 0.2s;
`;
const mapStateToProps = state => ({
ui: state.ui
});
const mapDispatchToProps = dispatch => {
return {
toggleSidebar: () => dispatch(toggleSidebar()),
logoutUser: () => dispatch(logoutUser())
};
};
class Sidebar extends React.Component {
componentDidMount() {}
render() {
const { ui, logoutUser } = this.props;
return (
<Sider
collapsed={ui.sidebarCollapsed}
//onCollapse={toggleSidebar} // toggle is disabled
style={{
overflow: 'auto',
height: '100vh',
position: 'fixed',
left: 0
}}
>
<Logo />
<Menu theme="dark" defaultSelectedKeys={['1']} mode="inline">
<UserMenu
key="sub1"
placement="topLeft"
title={
<span>
<Icon type="user" />
<span>User</span>
</span>
}
>
<Menu.Item onClick={logoutUser} key="3">
Logout
</Menu.Item>
<Menu.Item key="4">Profile</Menu.Item>
</UserMenu>
</Menu>
</Sider>
);
}
}
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps
)(Sidebar)
);
Is there a way to achieve this ?
Yes it should be possible. <Menu>uses the rc-menu package, and supports all the properties from this package, even if they are not documented in the and.design doc page.
Menu position is guided by the builtinPlacements property, which in turn uses https://github.com/yiminghe/dom-align, which gives you lot of flexibility in how to position the elements.
Related
I have a component like this:
// MyComponent.tsx
export function MyComponent(): React.ReactElement {
return <Wrapper>
<Text>
hello there
</Text>
<AnotherText>
bye bye
</AnotherText>
</Wrapper>
}
export const Wrapper = styled.div`
color: #FEB240;
background: #f5f5f5;
padding-bottom: 5rem;
padding-left: 7rem;
padding-right: 7rem;
gap: 2rem;
`;
export const Text = styled.span`
width: 50%;
cursor: pointer;
color: rgba(28, 33, 120, 1);
`;
export const AnotherText = styled.span`
color: red;
`;
I want to be able to style the wrapper. I tried to like this (from this answer Styling Nested Components in Styled-Components), but I don't see any change:
// AnotherPlace.tsx
const NewlyStyledMyComponent = styled(MyComponent)`
${Wrapper} {
color: brown;
background: magenta;
}
`;
It seems that MyComponent also need to take (generated) className as props and assign it to the root wrapping element to make the nested styles to work as expected.
Simplified live demo: stackblitz
A basic example in MyComponent:
import styled from 'styled-components';
interface Props {
className?: string;
}
export const Wrapper = styled.div`
background-color: hotpink;
`;
export const Text = styled.span`
color: #fff;
`;
function MyComponent({ className }: Props) {
return (
<div className={className}>
<Wrapper>
<Text>Hello</Text>
</Wrapper>
</div>
);
}
export default MyComponent;
And at where it is imported and used:
import styled from 'styled-components';
import MyComponent, { Wrapper, Text } from './MyComponent';
const NewlyStyledMyComponent = styled(MyComponent)`
margin-bottom: 7px;
${Wrapper} {
background-color: indigo;
}
${Text} {
color: gold;
}
`;
function App() {
return (
<div>
<NewlyStyledMyComponent />
<MyComponent />
</div>
);
}
export default App;
There are indeed 2 issues:
To style a custom React component (even just so that its nested components can be styled), you always need to take a className prop and to apply it on one of your rendered elements, as explained in styled-components docs:
The styled method works perfectly on all of your own or any third-party component, as long as they attach the passed className prop to a DOM element.
To style nested components, the className of the parent element must be applied on a parent DOM element as well; that is why JohnLi's answer has to add an extra <div className={className}> around the <Wrapper>.
But in your case, you could just style MyComponent and apply the className on the <Wrapper>:
export function MyComponent({
className
}: {
className?: string;
}): React.ReactElement {
return (
// Apply className directly on the Wrapper
<Wrapper className={className}>
This text can be re-colored
<Text>hello there can be re-colored if styling nested Text</Text>
<AnotherText>bye bye</AnotherText>
</Wrapper>
);
}
const NewlyStyledMyComponent = styled(MyComponent)`
/* Directly style MyComponent */
color: brown;
background: magenta;
/* Styling of nested components */
${Text} {
color: white;
}
`;
Demo: https://codesandbox.io/s/vibrant-worker-05xmil?file=/src/App.tsx
I am trying to create an element in React, but I cannot get it to be visible. It shows up in the Elements tab of the Chrome developer console, but when I hover over it there it doesn't even show a location. I have other custom elements that work fine, it is this in particular that is not rendering. Here is the code defining the element:
import React from "react";
import ReactDom from "react-dom";
const Modal = (props) => {
const [domReady, setDomReady] = React.useState(false)
React.useEffect(() => {
setDomReady(true)
})
return domReady?ReactDom.createPortal(
<>
<div className="modal">
TEST
</div>
<div onClick={() => {}} className="backdrop">
</div>
</>,
document.getElementById('modal-root')
):null
}
export default Modal;
Here is the CSS that affects it, though it is worth noting that even without this CSS I see nothing:
.modal {
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
border-radius: 6px;
background-color: white;
padding: 1rem;
text-align: center;
width: 30rem;
z-index: 10;
position: fixed;
top: 0;
left: 0;
}
.backdrop {
position: fixed;
z-index: 1;
background-color: rgba(0, 0, 0, 0.75);
width: 100%;
height: 100vh;
top: 0;
left: 0;
}
Edit: Here is the file which uses the component:
import { useState } from 'react';
//import Loading from '../Loading/Loading';
import './Income.css';
import { useNavigate } from 'react-router-dom';
import IncomeRow from '../../components/IncomeRow/IncomeRow';
import Button from '../../components/Button/Button';
import Modal from '../../components/Modal/Modal';
const Income = () => {
//Initializing
const navigate = useNavigate();
// modal visibility states
const [editModalVisible, setEditModalVisibility] = useState(false);
const [deleteModalVisible, setDeleteModalVisibility] = useState(false);
const [addModalVisible, setAddModalVisibility] = useState(false);
// show modal to edit income
const onEditClick = () => {
setEditModalVisibility(true);
}
const onDismissEditModal = () => {
setEditModalVisibility(false);
}
//returning JSX
return (
<>
<div className='Incomes'>
{/* TODO: make these autogenerate from database */}
<IncomeRow name="income 1" source="Company 1" date="1/1/2022" amount={123.45} id="1" editFunction={onEditClick}/>
<IncomeRow name="income 2" source="Company 2" date="7/31/2022" amount={420.69} id="2"editFunction={onEditClick}/>
</div>
<Button text="Add Income"/>
<Modal dismissModal={onDismissEditModal}/>
</>
);
}
export default Income
Here is App.js, which holds the base of the layout:
import React, { useState} from 'react';
import './App.css';
import Navbar from './components/Navbar/Navbar';
import 'bootstrap/dist/css/bootstrap.min.css';
//import { ThemeContext, themes } from './context/themeContext';
import { Outlet, useNavigate} from 'react-router-dom';
function App() {
return (
<div className="App">
<Navbar/>
<div className='body'>
<div id='modal-root'>
<Outlet/>
</div>
</div>
</div>
);
}
export default App;
Here:
Open the app in desktop width, notice the div has green background
Reduce browser width to mobile, the div background should change to gray
Again, increase browser width to desktop, notice the gray background remains, instead of green
What should have happened
The background in last step should be green as in the first step, isn't it?
Logging value of isMobile does seem to show it is being updated.
Here is also code:
import React from 'react';
import styled from 'styled-components';
import { useMediaQuery } from 'react-responsive';
let MenuItem = styled.div`
height: 100px;
width: 100px;
border:1px solid red;
background-color: green;
// Select those items which are children of .submenu
.submenu & {
background-color: ${({ isMobile }) => {
return isMobile && 'lightgray';
}};
}
`;
function App() {
const isMobile = useMediaQuery({ query: '(max-width: 524px)' });
return (
<div>
<div className="submenu">
<MenuItem isMobile={isMobile}>test</MenuItem>
</div>
</div>
);
}
export default App;
import React, {useEffect, useState} from 'react';
import styled from 'styled-components';
import {useMediaQuery} from 'react-responsive';
let MenuItem = styled.div`
height: 100px;
width: 100px;
border:1px solid red;
background-color: green;
`;
function App() {
const isMobile = useMediaQuery({query: '(max-width: 524px)'});
const [color, setColor] = useState('green');
useEffect(() => {
if (isMobile) setColor('silver');
else setColor('green');
}, [isMobile])
return (
<div>
<div className="submenu">
<MenuItem style={{background: color}} isMobile={isMobile}>test</MenuItem>
</div>
</div>
);
}
export default App;
You could re-write this as:
const MenuItem = styled.div`
height: 100px;
width: 100px;
border:1px solid red;
background-color: green;
`;
const SubMenu = styled.div`
${MenuItem} {
background-color: ${({ isMobile }) => (isMobile ? `red` : 'lightgray')};
}
`;
function App() {
const isMobile = useMediaQuery({ query: '(max-width: 524px)' });
return (
<>
<SubMenu isMobile={isMobile}>
<MenuItem>MenuItem in SubMenu</MenuItem>
</SubMenu>
<MenuItem>MenuItem</MenuItem>
</>
);
}
Stackblitz
It is the correct answer:
https://stackblitz.com/edit/react-t7gqwx?file=src%2FApp.js,src%2Findex.js
You shouldn't use .submenu &.
Currently trying (and failing) to learn React for a project, and not understanding why the header links appear after the image, if they're in the same wrapper. I made different components for different parts of the navbar, and made a different file for the Logo and NavLinks (each in its own section). Here's the code.
App.js
// Importing NavBar
import NavBar from './components/navbar/NavBar';
// Actual App function, has our code
function App() {
return (
<div className="App">
{/* Navbar Declaration, with statement of what links to add */}
<NavBar />
</div>
);
}
export default App;
NavBar.js
// Importing react
import React from "react";
// Importing styled to be able to style the page
import styled from "styled-components";
// Importing the logo
import Logo from "../logo/Logo";
// Importing the links
import NavLinks from "./NavLinks.js";
// ---------------------------- Stylizing the navbar using styled-components
// Main Wrapper for Navbar
const Wrapper = styled.div`
width: 100%;
height: 10rem;
align-items: center;
padding: 0 1.5 rem;
transition: background-color .5s ease;
z-index: 9999;
border-bottom: 2px solid rgba(255,255,255,.05);
margin-left: 50px;
margin-right: 50px;
`;
// NavBar is separated into left, center and right
// Left side of Navbar
const LeftSide = styled.div`
display: flex;
`;
// Center of Navbar
// Flex is a way to define how much each portion is gonna take of the size given to it
const Center = styled.div`
display: flex;
`;
// Right side of Navbar
const RightSide = styled.div`
display: flex;
`;
// Declaration of navbar links
const navbarLinks = [
"Home Page",
"Illustrator Gallery",
"Art Gallery",
"Challenges"
];
/*const navbarLinks = [
{ title: `Home Page`, path: `/` },
{ title: `Illustrator Gallery`, path: `/illustrator-gallery` },
{ title: `Art Gallery`, path: `/art-gallery` },
{ title: `Challenges`, path: `/challenges` }
];*/
// ---------------------------- Creating the NavBar function/component
function NavBar(props) {
// Setting the return value, or the component
return(
<Wrapper>
{/* For the left side, we want to import the Logo component */}
<LeftSide>
<Logo />
</LeftSide>
<Center>
{/* For the middle, we want to add the different links */}
<NavLinks links={navbarLinks} />
</Center>
<RightSide></RightSide>
</Wrapper>
);
}
export default NavBar;
Logo.js
// Importing react
import React from 'react';
// Importing styled components
import styled from "styled-components";
// Importing the image
import USLogo from "../../assets/images/bunny.png"
// Styling the wrapper for the logo
const LogoWrapper = styled.div`
display: flex;
align-items: center;
`;
// Styling the actual logo, as well as its container
const LogoImg = styled.div`
width: 50px;
height: 50px;
img {
width: 100%;
height: 100%;
}
`;
// Styling the information next to the logo
const LogoText = styled.h2`
text-transform: uppercase;
font-size: 3rem;
font-weight: bold;
margin-left: 4px;
padding-top: 8px;
color: black;
`;
// Creating logo
function Logo(props) {
return(
// First we make the wrapper
<LogoWrapper>
{/* Inside the wrapper we'll have the image, then the text */}
<LogoImg><img src={USLogo} alt="US. logo"/></LogoImg>
<LogoText>US.</LogoText>
</LogoWrapper>
);
}
export default Logo;
NavLinks.js
// Importing react
import React from 'react';
// Importing styling
import styled from 'styled-components';
// Styling the container for the links
const LinksContainer = styled.div`
display: flex;
align-items: center;
`;
// Styling the ul components, or the menu
const LinksMenu = styled.ul`
display: inline-block;
text-transform: uppercase;
letter-spacing: 3px;
`;
// Styling each li
const LinksItem = styled.li`
display: inline-block;
vertical-align: top;
align-items: center;
justify-content: space-between;
margin-top: 15px;
padding: 0 1.1rem;
`;
// Styling each link
const Link = styled.a`
text-decoration: none;
color: black;
font-size: 1.6rem;
margin: 0 2rem;
position: relative;
&:hover{
color: rgb(24, 23, 23);
}
&::after {
content: '';
width: 100%;
height: 2px;
background-color: black;
left: 0;
bottom: -3px;
transform: scaleX(0);
transform-origin: left;
transition transform .5s ease;
}
&:hover::after{
transform: scaleX(1);
}
`;
// Creating the navigation links component
function NavLinks(props) {
return(
// First we do the container
<LinksContainer>
{/* Inside the container, we have the menu of links, then the li, and finally links */}
<LinksMenu>
{/* Each link Menu has a bunch of items, with each item having a link */}
{
props.links.map(
(label) => {
return(
<LinksItem><Link>{label}</Link></LinksItem>
)
}
)
}
</LinksMenu>
</LinksContainer>
);
}
export default NavLinks;
The result shows as follows:
Additionally, if I hover over the text, it doesn't change anything. In fact, it doesn't even add the little blue color indicating it's a link anymore. I'm not sure if this is a syntactical problem, since the same code works fine when using css.
I've checked other answers on here suggesting float: left and the like for the wrapper, but for some reason they don't work. Either I'm putting them in the wrong object, or they should be somewhere else.
Any help is appreciated!
I'm starting to work with react and have to build a clock component.
I want my parent div to have the same height as a child component. Why doesn't child height affect it's parent height?
HTML:
import React, { useState, useEffect } from "react";
import "./clock.scss";
export const Clock = () => {
let [date, setDate] = useState(new Date());
useEffect(() => {
let timer = setInterval(() => setDate(new Date()), 1000);
return function cleanup() {
clearInterval(timer);
};
});
return (
<div className="clock-wrapper">
<span className="test">
{date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}
</span>
</div>
);
};
export default Clock;
SCSS (other components don't have css yet). Class "test" - green container. Class "clock-wrapper" - red container.
.dashboard {
display: flex;
justify-content: space-between;
padding: 25px 40px 25px 40px;
}
.clock-wrapper {
$_size-100: 100px;
}
.test {
color: $color-pickled-bluewood;
font-size: $_size-100;
font-weight: bold;
}
Image with current result. Red border is parent size. Green border with background color is children component.
Thanks for your help.