알고 가야할 점!🧐
- state로 무슨 상태를 설정해야할까?
버튼이 눌렸을때 ( on ) / 버튼이 눌리지 않았을때 ( off )
- 버튼이 on 상태일 때 추가할 css를 toggle-checked class를 만들고 조건으로 붙여준다.
- toggle은 class를 조건에 따라 붙여주는 것이 핵심이다!
<div className={`toggle-container ${isOn ? "toggle--checked" : null}`}/> <div className={`toggle-circle ${isOn ? "toggle--checked" : null}`} />
위의 코드를 살펴보고 class의 중복사용에 대해 알고가자!
export const Toggle = () => {
const [isOn, setisOn] = useState(false);
const toggleHandler = () => {
setisOn(!isOn);
};
return (
<>
<ToggleContainer
onClick={toggleHandler}
>
<div
className={`toggle-container ${isOn ? "toggle--checked" : null}`}
/>
<div className={`toggle-circle ${isOn ? "toggle--checked" : null}`} />
</ToggleContainer>
{isOn === false ? (
<Desc>
<div className="switch--Off"> 'Toggle Switch OFF'</div>
</Desc>
) : (
<Desc>
<div className="switch--On">'Toggle Switch ON'</div>
</Desc>
)}
</>
);
};
const ToggleContainer = styled.div`
position: relative;
margin-top: 8rem;
left: 47%;
cursor: pointer;
> .toggle-container {
width: 50px;
height: 24px;
border-radius: 30px;
background-color: #8b8b8b;
}
> .toggle--checked {
background-color: pink;
}
> .toggle-circle {
position: absolute;
top: 1px;
left: 1px;
width: 22px;
height: 22px;
border-radius: 50%;
background-color: #ffffff;
transition: 0.2s;
}
> .toggle--checked {
left: 28px;
transition: 0.4s;
}
`;
const Desc = styled.div`
display: flex;
align-items: center;
justify-content: center;
> .switch--On {
color: #64cd3c;
}
> .switch--Off {
color: #853c3c;
}
`;
알고 가야할 점!🧐
- Tab을 관리할때 index로 관리해보자🐹
- 인덱스를 받아 탭의상태를 변경하는 함수를 만든다.
const selectMenuHandler = (index) => { setTab(index); };
* map으로 탭을 관리하는 배열을 가져온 후 tab, index를 인자로 받는다. className은 현재 Tab이 index와 같을 때와 다를 때 각각 설정해주고, 이벤트로 탭의상태를 변경해주는 함수를 넣는다👍
export const <Tab = () => {
const [Tab, setTab] = useState(0);
const menuArr = [
{ name: "Tab1", content: "Tab menu ONE" },
{ name: "Tab2", content: "Tab menu TWO" },
{ name: "Tab3", content: "Tab menu THREE" },
];
const selectMenuHandler = (index) => {
setTab(index);
};
return (
<>
<div>
<TabMenu>
{menuArr.map((tab, index) => {
return (
<li
key={index}
className={Tab === index ? "submenu focused" : "submenu"}
onClick={() => selectMenuHandler(index)}
>
{tab.name}
</li>
);
})}
</TabMenu>
<Desc>
<p>{menuArr[Tab].content}</p>
</Desc>
</div>
</>
);
};
const TabMenu = styled.ul`
background-color: #dcdcdc;
color: rgba(73, 73, 73, 0.5);
font-weight: bold;
display: flex;
flex-direction: row;
justify-items: center;
align-items: center;
list-style: none;
margin-bottom: 7rem;
cursor: pointer;
.submenu {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 50px;
padding: 20px;
}
.focused {
${"" /* 선택된 Tabmenu 에만 적용되는 CSS를 구현합니다. */}
background-color: gray;
color: white;
}
& div.desc {
text-align: center;
}
`;
const Desc = styled.div`
text-align: center;
`;
알고 가야할 점!🧐
- removeTag를 관리할때 index로 관리해보자🐹
- tags가 삭제,추가 되니까 관리하는 state를 만들자!
- 태그를 추가할때의 input의 조건은
"enter"가 들어올 때
,인풋에 작성한 내용이 태그에 없을 때
,인풋이 비어있지 않을때
다!
조건을 잘 살펴보자🐝
- 위의 tab에서 인덱스로 상태를 관리한 것처럼, removeTag에 그대로 활용해볼것이다!
export const Tag = () => {
const initialTags = ["CodeStates", "kimcoding"];
const [tags, setTags] = useState(initialTags);
const removeTags = (indexToRemove) => {
setTags(
tags.filter((tag) => {
return tag !== tags[indexToRemove];
})
);
};
const addTags = (event) => {
//인풋value(내가 새로 치는 부분)을 변수로 설정하고 조건을 정리해보면 변수는 Mango
//event.key === "Enter"
//Mango가 비어있지 않을때
//태그의 내용이 Mango일때 > tags.includes(Mango)
let input = event.target.value;
if (event.key === "Enter" && !tags.includes(input) && input !== "") {
setTags([...tags, input]);
event.target.value = ""; //여기 input ="";라고 썼을때 안됐음. 변수가 event.target.value의 고유기능까지 대신 못하나봄..?
}
};
return (
<>
<TagsInput>
<ul id="tags">
{tags.map((tag, index) => (
<li key={index} className="tag">
<span className="tag-title">{tag}</span>
<span
className="tag-close-icon"
onClick={() => removeTags(index)}
>
x
</span>
</li>
))}
</ul>
<input
className="tag-input"
type="text"
onKeyUp={(event) => {
addTags(event);
}}
placeholder="Press enter to add tags"
/>
</TagsInput>
</>
);
};
tag - css
export const TagsInput = styled.div`
margin: 8rem auto;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
min-height: 48px;
width: 480px;
padding: 0 8px;
border: 1px solid rgb(214, 216, 218);
border-radius: 6px;
> ul {
display: flex;
flex-wrap: wrap;
padding: 0;
margin: 8px 0 0 0;
> .tag {
width: auto;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
padding: 0 8px;
font-size: 14px;
list-style: none;
border-radius: 6px;
margin: 0 8px 8px 0;
background: var(--coz-purple-600);
> .tag-close-icon {
display: block;
width: 16px;
height: 16px;
line-height: 16px;
text-align: center;
font-size: 14px;
margin-left: 8px;
color: var(--coz-purple-600);
border-radius: 50%;
background: #fff;
cursor: pointer;
}
}
}
> input {
flex: 1;
border: none;
height: 46px;
font-size: 14px;
padding: 4px 0 0 0;
:focus {
outline: transparent;
}
}
&:focus-within {
border: 1px solid var(--coz-purple-600);
}
`;