I have a problem with styled. I will write my problem with images. I have a image button and if i click on image button, the MenuItemContainer must be visibility.
const MenuItemContainer = styled.div`
visibility: hidden;
display: inline-block;
box-sizing: border-box;
width: 200px;
padding-left: 16px;
padding-right: 16px;
border: 1px solid ${({ theme }) => theme.palette.lightBlueGrey};
border-radius: 5px;
box-shadow: 0 12px 24px 0px ${({ theme }) => theme.palette.dark15};
`;
const ProfileNameWrapper = styled.div`
display: flex;
width: 36px;
height: 36px;
border-radius: 50%;
margin-right: 25px;
background-color: ${({ theme }) => theme.palette.darkGreyBlue};
cursor: pointer;
justify-content: center;
align-items: center;
&:hover ${TooltipText} {
visibility: visible;
}
&:????? ${MenuItemContainer} {
visibility: visible;
}
`;
can i use onClick here?
&:????? ${MenuItemContainer} {
visibility: visible;
}
Even though, I find it wrong to attach click events on divs, you can utilize the aria attributes to hack your way around your problem.
const ProfileNameWrapper = styled.div`
...
&:[aria-expanded='true'] ${MenuItemContainer} {
visibility: visible;
}
`
const Component: React.FC<Props> = (props) => {
const [expanded, setExpanded] = React.useState(false)
return (
<ProfileNameWrapper onClick={() => setExpanded(!expanded)} aria-expanded={expanded} />
)
}
Related
You may not understand the title, but I'll explain what the question is.
I'm trying to make some buttons.
the button is changed when it's hovered.
The button originally selected does not change when other button is hovered, but changes when other button is selected.
I think you can understand what I mean immediately if you look at the code or codesandbox link below.
Question:
As you can see, I used 'onMouseOver' and 'onMouseLeave' function to implement hover.
But, when the component is first rendered, if one button is clicked, other button should change, but it doesn't change.
I'm sure this is because 'onMouseLeave' function is executed when mouse is entered to button at first.
So, when I click other button, the first one doesn't change because I didn't enter it.
I want it to work properly even if I click another button without passing the first button.
How can I modify it??
code
import { useState } from "react";
import styled from "styled-components";
export default function App() {
// declare hover state
const [choonsik, setChoonsik] = useState(true);
const [ryan, setRyan] = useState(false);
const [apeech, setApeech] = useState(false);
// declare click state
const [isChoonsik, setIsChoonsik] = useState(true);
const [isRyan, setIsRyan] = useState(false);
const [isApeech, setIsApeech] = useState(false);
// make click state true
const clickChoonsik = () => {
setIsChoonsik(true);
setIsRyan(false);
setIsApeech(false);
};
const clickRyan = () => {
setIsChoonsik(false);
setIsRyan(true);
setIsApeech(false);
};
const clickApeech = () => {
setIsChoonsik(false);
setIsRyan(false);
setIsApeech(true);
};
// make hover state true
const trueChoonsik = () => {
setChoonsik(true);
};
const trueRyan = () => {
setRyan(true);
};
const trueApeech = () => {
setApeech(true);
};
// make hover state false
const falseChoonsik = () => {
setChoonsik(false);
};
const falseRyan = () => {
setRyan(false);
};
const falseApeech = () => {
setApeech(false);
};
return (
<Wrap
choonsik={choonsik}
ryan={ryan}
apeach={apeech}
isChoonsik={isChoonsik}
isRyan={isRyan}
isApeech={isApeech}
>
<div className="characterWrap">
<ul className="characterWrap__list">
<li className="characterWrap__card">
<div
onMouseEnter={() => {
trueChoonsik();
}}
onMouseLeave={() => {
falseChoonsik();
}}
onClick={() => {
clickChoonsik();
}}
className={`characterWrap__choonsik ${
choonsik || isChoonsik ? "active" : ""
}`}
>
<div
className={`choonsik ${choonsik || isChoonsik ? "active" : ""}`}
/>
춘식이
</div>
</li>
<li className="characterWrap__card">
<div
onMouseEnter={() => {
trueRyan();
}}
onMouseLeave={() => {
falseRyan();
}}
onClick={() => {
clickRyan();
}}
className={`characterWrap__ryan ${
ryan || isRyan ? "active" : ""
}`}
>
<div className={`ryan ${ryan || isRyan ? "active" : ""}`} />
라이언
</div>
</li>
<li className="characterWrap__card">
<div
onMouseEnter={() => {
trueApeech();
}}
onMouseLeave={() => {
falseApeech();
}}
onClick={() => {
clickApeech();
}}
className={`characterWrap__apeach ${
apeech || isApeech ? "active" : ""
}`}
>
<div className={`apeach ${apeech || isApeech ? "active" : ""}`} />
어피치
</div>
</li>
</ul>
</div>
</Wrap>
);
}
const Wrap = styled.div`
position: relative;
width: 1000px;
top: calc(50vh - 100px);
.characterWrap {
padding-bottom: 20px;
margin: 0 auto;
width: 100%;
text-align: center;
}
.characterWrap__list {
display: inline-flex;
list-style: none;
}
.characterWrap__card {
margin: 0 9px;
text-align: center;
float: left;
}
.characterWrap__choonsik {
color: #999;
font-size: 13px;
position: relative;
display: inline-block;
cursor: pointer;
}
.characterWrap__choonsik.active {
color: #333;
font-size: 13px;
position: relative;
display: inline-block;
cursor: pointer;
}
.characterWrap__ryan {
color: #999;
font-size: 13px;
position: relative;
display: inline-block;
cursor: pointer;
}
.characterWrap__ryan.active {
color: #333;
font-size: 13px;
position: relative;
display: inline-block;
cursor: pointer;
}
.characterWrap__apeach {
color: #999;
font-size: 13px;
position: relative;
display: inline-block;
cursor: pointer;
}
.characterWrap__apeach.active {
color: #333;
font-size: 13px;
position: relative;
display: inline-block;
cursor: pointer;
}
.choonsik {
background-position: 0 17.647059%;
background-size: 100%;
display: block;
transition: hover 0.2s;
margin-bottom: 5px;
background-image: url(https://www.kakaofriendsgolf.com/img/v3_img_sprites_friends#3x.png);
background-repeat: no-repeat;
width: 70px;
height: 70px;
}
.choonsik.active {
background-position: 0 11.764706%;
background-size: 100%;
display: block;
transition: hover 0.2s;
margin-bottom: 5px;
background-image: url(https://www.kakaofriendsgolf.com/img/v3_img_sprites_friends#3x.png);
background-repeat: no-repeat;
width: 70px;
height: 70px;
}
.ryan {
background-position: 0 88.235295%;
background-size: 100%;
display: block;
transition: hover 0.2s;
margin-bottom: 5px;
background-image: url(https://www.kakaofriendsgolf.com/img/v3_img_sprites_friends#3x.png);
background-repeat: no-repeat;
width: 70px;
height: 70px;
}
.ryan.active {
background-position: 0 82.352941%;
background-size: 100%;
display: block;
transition: hover 0.2s;
margin-bottom: 5px;
background-image: url(https://www.kakaofriendsgolf.com/img/v3_img_sprites_friends#3x.png);
background-repeat: no-repeat;
width: 70px;
height: 70px;
}
.apeach {
background-position: 0 5.882353%;
background-size: 100%;
display: block;
transition: hover 0.2s;
margin-bottom: 5px;
background-image: url(https://www.kakaofriendsgolf.com/img/v3_img_sprites_friends#3x.png);
background-repeat: no-repeat;
width: 70px;
height: 70px;
}
.apeach.active {
background-position: 0 0%;
background-size: 100%;
display: block;
transition: hover 0.2s;
margin-bottom: 5px;
background-image: url(https://www.kakaofriendsgolf.com/img/v3_img_sprites_friends#3x.png);
background-repeat: no-repeat;
width: 70px;
height: 70px;
}
`;
codesandbox
https://codesandbox.io/s/characterselect-4o6he0?file=/src/App.js
Looked at the CodeSendbox. Looks nice!
If I understood correctly, your problem is that when you click any other icon without hovering on the first one, the first one doesn't get grayed out.
change following line
const [choonsik, setChoonsik] = useState(true);
to
const [choonsik, setChoonsik] = useState(false);
I have made some pure banner CSS js with react. Right now it's static not autoplay. How to make my banner autoplay properly and responsive for mobile. Autoplay and smooth transition when going to another banner.
This is the code for working static banner without autoplay things :
import { ArrowLeftOutlined, ArrowRightOutlined } from "#material-ui/icons"
import { useState } from 'react'
import styled from "styled-components"
import {sliderItems} from '../data'
const Container = styled.div`
width: 100%;
height: 80vh;
display: flex;
position: relative;
overflow: hidden;
`
const Arrow = styled.div`
width: 50px;
height: 50px;
background-color: #fff7f7;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
bottom: 0;
left: ${props=> props.direction === "left" && "10px"};
right: ${props=> props.direction === "right" && "10px"};
margin: auto;
cursor: pointer;
opacity: 0.5;
z-index: 2;
`
const Wrapper = styled.div`
height: 100%;
display: flex;
transition: all 1.5s ease;
transform: translateX(${props=>props.slideIndex * -100}vw);
`
const Slide = styled.div`
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
background: ${props => `url(${props.bg}) no-repeat top center`};
`
const ImgContainer = styled.div`
height: 80%;
flex: 1;
`
const Image = styled.img`
height: 80%;
`
const Image2 = styled.img`
height: 40%;
`
const InfoContainer = styled.div`
flex: 1;
padding: 50px;
`
const Title = styled.h1`
color: white;
font-size: 70px;
`
const Desc = styled.p`
color: white;
margin: 50px 0px;
font-size: 20px;
font-weight: bold;
letter-spacing: 3px;
`
const Button = styled.button`
border: 1px solid white;
padding: 10px;
font-size: 20px;
color: #0070ba;
background-color: white;
border-radius: 25px;
cursor: pointer;
font-weight: 800;
&:hover{
color: white;
background-color: #0070ba;
}
`
const Banner = () => {
const [slideIndex, setSlideIndex] = useState(0)
const handleClick = (direction) => {
if(direction==="left"){
setSlideIndex(slideIndex > 0 ? slideIndex-1 : 6)
} else {
setSlideIndex(slideIndex < 6 ? slideIndex +1 : 0)
}
}
return (
<Container>
<Arrow direction="left" onClick={()=>handleClick("left")}>
<ArrowLeftOutlined />
</Arrow>
<Wrapper slideIndex={slideIndex}>
{sliderItems.map(item=>(
<Slide bg={item.bg}>
<Image2 src={item.img2}/>
<InfoContainer>
<Title>{item.title}</Title>
<Desc>{item.desc}</Desc>
<Button>COBA SEKARANG</Button>
</InfoContainer>
<ImgContainer>
<Image src={item.img}/>
</ImgContainer>
</Slide>
))}
</Wrapper>
<Arrow direction="right" onClick={()=>handleClick("right")}>
<ArrowRightOutlined />
</Arrow>
</Container>
)
}
export default Banner
Thank you guys for helping!
I am doing an accordion in JavaScipt, in my HTML I have two separate divs for questions title and separate div for "+" sign. My goal is when I click on div with class accordionTitle I want "+" sign change to "-" and at the same time show the answer. I am pretty new to development, what the best approach here? Can you help me?
HTML:
<section class="qnaSection">
<h1 class="qnaTitle">FAQ</h1>
<div id="qnaContainer" class="qnaContainer"></div>
</section>
CSS:
.qnaSection {
background-color: rgb(35, 57, 94);
color: var(--brown);
}
.qnaTitle {
text-align: center;
margin: 10px 0;
font-size: 28px;
}
.qnaContainer {
width: 90%;
max-width: 1000px;
margin: 20px auto;
}
.accordionItem {
background-color: rgb(222, 222, 222);
color: black;
margin: 10px 0;
border-radius: 10px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.25);
}
.accordionItemTitle {
padding: 1rem;
min-height: 3.5rem;
width: 100%;
line-height: 1.25rem;
font-weight: bold;
display: flex;
justify-content: space-between;
position: relative;
cursor: pointer;
box-sizing: border-box;
}
.accordionItemPush {
/* content: '\002B'; */
font-size: 2rem;
/* position: absolute;
right: 1rem; */
transition: transform 0.2s ease-in-out;
min-width: 70px;
display: flex;
justify-content: space-around;
align-items: center;
}
.accordionItemPush.active::after {
/* content: '\2212'; */
transform: rotate(45deg);
}
.accordionContent {
max-height: 0;
overflow: hidden;
}
.accordionParagraph {
padding: 1rem;
line-height: 1.5rem;
border-top: 1px solid;
border-image: linear-gradient(to right, transparent, rgb(101, 101, 136), transparent) 1 ;
transition: max-heigth 0.2s ease-out;
}
.accordionItem.active + .accordionContent {
max-height: auto;
display: block;
}
JS:
const questions = [] // array with questions
const qnaContainer = document.getElementById("qnaContainer")
const generateTitleHtml = title => {
return `
<h3>${title}</h3>
`
}
const generateQuestionHtml = item => {
return `
<div class="accordionItem">
<div class="accordionItemTitle">
<p>${item.question}</p>
<div class="accordionItemPush">
+
</div>
</div>
<div class="accordionContent">
<p class="accordionParagraph">${item.answer}</p>
</div>
</div>
`
}
const listQ = questions => {
qnaContainer.insertAdjacentHTML('beforeend', generateTitleHtml(questions.title))
questions.items.forEach( question => {
qnaContainer.insertAdjacentHTML('beforeend', generateQuestionHtml(question))
})
}
listQ(questions.regularQuestions)
listQ(questions.membershipBilling)
const accordionItemHeaders = document.querySelectorAll(".accordionItemTitle");
const toggleBar = () => {
accordionItemHeaders.forEach(item => {
item.addEventListener("click", () => {
checkCurrentlyActiveItem(item)
toggleActive(item)
})
});
}
const checkCurrentlyActiveItem = (accordionItemHeader) => {
const currentlyActiveItem = document.querySelector(".accordionItemTitle.active");
if(currentlyActiveItem && currentlyActiveItem!==accordionItemHeader) {
currentlyActiveItem.classList.toggle("active");
currentlyActiveItem.nextElementSibling.style.maxHeight = 0;
}
}
const toggleActive = (accordionItemHeader, itemBtn) => {
accordionItemHeader.classList.toggle("active");
const accordionItemBody = accordionItemHeader.nextElementSibling;
if(accordionItemHeader.classList.contains("active")) {
accordionItemBody.style.maxHeight = accordionItemBody.scrollHeight + "px";
}
else {
accordionItemBody.style.maxHeight = 0;
}
}
toggleBar()
What you could do is: instead of putting the HTML code in the javascript, create elements with document.createElement("elementName") and then set its class name or id. Then you can set an onclick-event on the element and change its innerText or innerHTML to a "-". I think this link can be useful. If you have any questions, ask them :)
The following code gives this result:
Is there a way I can get the dropdown to go over on top of the 'HELLO's so it doesn't show the h1 and p tag? I tried using z-index but to no success. I tried making parent element of overall component to be relative and dropdown (child) to be absolute, but has not worked. I specifically want the dropdown to be on top of ANYTHING, and don't want to make z-index of the h1 or p to be negative for example. Would there be a solution that makes the dropdown be on top of every element?
Again, I would really appreciate a solution that makes the DROPDOWN go on top of every element, not the h1 or p tag. Thanks!
I abstracted most of the methods away for the sake of simplicity, but here is the fully working version:
https://codesandbox.io/s/quirky-shadow-zsr21?file=/src/styled-dropdown-selector.js
App.js
const App = () => {
return (
<>
<DropdownSelector
lineColour="#000000"
arrowColour="#000000"
width="100%"
onSelect={(value, options) => console.log(value, options)}
options={[
{ label: '3777 Kingsway, Burnaby, BC V5H 3Z7', id: 1 },
{ label: '3888 Kingston, Ontario, ON V5H 3Z7', id: 2 },
{ label: '2999 Address, Vancouver, BC V5H 3Z7', id: 3 },
{ label: '4777 George, Richmond, BC V5H 3Z7', id: 4 },
{ label: '4222 Topaz, Coquitlam, BC V5H 3Z7', id: 5 },
{ label: '4333 Walnut, Langley, BC V5H 3Z7', id: 6 },
]}
/>
<p>HELLO HELLO HELLO</p>
<h1>HELLO HELLO HELLO</h1>
<h1>HELLO HELLO HELLO</h1>
</>
)
};
DropdownSelector.js
import React, { useState, useRef } from 'react';
import { SelectBox, SuggestionBox, SuggestionLabel, InvisibleButton } from './styled-dropdown-selector';
const DropdownSelector = ({ options, onSelect }) => {
const initialValue = options ? options[0].label : '';
const [val, setVal] = useState(initialValue);
const [suggestions, setSuggestions] = useState([]);
const [toggleEnabled, setToggleEnabled] = useState(false);
const suggestionValue = () => {
return suggestions.map(option => {
return (
<SuggestionLabel onMouseDown={() => setVal(option.label)}>
{option.label}
</SuggestionLabel>
);
});
};
return (
<div>
<SelectBox value={val} />
<InvisibleButton >
<img src={Arrow} alt="arrow" />
</InvisibleButton>
<div>
{toggleEnabled && (
<SuggestionBox>
{suggestionValue()}
</SuggestionBox>
)}
</div>
</div>
);
};
export default DropdownSelector;
styled-drpodown-selector.js
import styled from 'styled-components';
export const SelectBox = styled.input`
outline: none;
border: none;
background: none;
width: 100%;
height: auto;
margin: auto;
display: inline;
padding-bottom: 5px;
margin-bottom: 5px;
margin-top: 5px;
font-style: normal;
font-weight: normal;
font-size: 14px;
line-height: 19px;
`;
export const SuggestionBox = styled.div`
position: absolute;
width: ${props => props.width};
margin-left: 0px;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.05);
padding-top: 5px;
padding-bottom: 5px;
`;
export const SuggestionLabel = styled.button`
outline: none;
background: none;
border: none;
width: 100%;
font-style: normal;
font-weight: normal;
font-size: 14px;
line-height: 19px;
text-align: left;
margin-left: 5px;
margin-right: 5px;
padding: 5px 0px 5px 0px;
transition: all 0.3s ease;
&:hover {
background: #e8e8e8;
}
`;
export const InvisibleButton = styled.button`
position: relative;
top: 5px;
float: right;
margin-right: 3px;
margin-top: -32px;
cursor: pointer;
background: none;
outline: none;
border: none;
width: auto;
height: auto;
display: inline;
`
Not sure if this is a lo-fi solve for what you need
export const SuggestionBox = styled.div`
position: absolute;
width: ${(props) => props.width};
margin-left: 0px;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.05);
padding-top: 5px;
padding-bottom: 5px;
background-color: white;
height: 100%;
`;
Added the background-color and height to get you there.
https://codesandbox.io/s/great-mendeleev-2dl7n?file=/src/styled-dropdown-selector.js:353-609
tldr; How do I make my component hide the overflow and make it "toggle" views with a button. ex) user can initially see tab 1,2,3 and 4,5,6 is hidden, a button click will hide the 1,2,3 and show 4,5,6 now (without knowing that the screen will cut after 1,2,3)
I'm trying to make a navbar that can be reused in any screen size. My goal is to allow developers to dynamically add tabs (which is a child element) to the navbar without it overflowing. In the case where the navbar overflows, meaning it makes a new row because it reached its max width, a clickable arrow key goes to the next set of tabs. This is my code so far. How can I achieve this behavior?
Navbar.js
import React, { useState } from 'react';
import Tab from './Tab';
import { StyledTabs } from '../styledComponents/StyledNavbar';
import { NavbarOutline } from '../styledComponents/StyledNavbar';
const Navbar = ({ children }) => {
const [activeTab, setActiveTab] = useState(children[0].props.label);
const onClickTabItem = tab => {
setActiveTab(tab);
}
return (
<>
<NavbarOutline>
<ol>
{children.map(child => {
const { label } = child.props;
return <Tab activeTab={activeTab} key={label} label={label} handleClick={onClickTabItem} />;
})}
</ol>
</NavbarOutline>
<div>
{children.map(child => {
if (child.props.label !== activeTab) return undefined;
return <StyledTabs className="content">{child.props.children}</StyledTabs>
})}
</div>
</>
);
}
export default Navbar;
Tab.js
import React from 'react';
import { StyledTabs } from '../styledComponents/StyledNavbar';
const Tab = props => {
const { activeTab, label, handleClick } = props;
let className = 'not-active';
const onClick = () => {
handleClick(label);
};
if (activeTab === label) {
className = 'active';
}
return (
<StyledTabs className={className} onClick={onClick}>
{label}
</StyledTabs>
);
};
export default Tab;
EDIT: here is my css as well; I used styled components for styling
StyledNavbar.js
import styled from 'styled-components';
export const NavbarOutline = styled.div`
border-bottom: 2px solid #e3e3e3;
padding-left: 0;
display: inline-block;
margin-left: 35px;
margin-right: 35px;
`;
export const StyledTabs = styled.button.attrs(props => ({
className: props.className,
}))`
&.not-active {
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 20px;
display: inline-block;
list-style: none;
padding: 16px 31px 16px 31px;
background: none;
border: none;
}
&.active {
font-style: normal;
font-weight: normal;
font-size: 16px;
line-height: 20px;
display: inline-block;
list-style: none;
margin-bottom: -2px;
padding: 16px 31px 16px 31px;
background: none;
border: none;
color: #2b8000;
border-bottom: 2px solid #2b8000;
}
&.content {
display: inline-block;
list-style: none;
background: none;
border: none;
margin-left: 35px;
margin-right: 35px;
`;
This seems like a CSS issue, it would be great if you could've attached the css as well. However, you can solve this using flexbox. Add the following css to the parent container to ensure the tabs create a new row instead of overflowing,
.flex-item {
padding: 5px;
width: 100px;
height: 100px;
margin: 10px;
line-height: 100px;
color: white;
font-weight: bold;
font-size: 2em;
text-align: center;
}
.wrap {
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
}
.wrap li {
background: gold;
}
.flex-container {
padding: 0;
margin: 0;
list-style: none;
border: 1px solid silver;
-ms-box-orient: horizontal;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -moz-flex;
display: -webkit-flex;
display: flex;
}
<ol class="flex-container wrap">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
<li class="flex-item">6</li>
<li class="flex-item">7</li>
<li class="flex-item">8</li>
<li class="flex-item">9</li>
<li class="flex-item">10</li>
</ol>