2022.07.01(Fri)
[TIL] Day48
[SEB FE] Day47
์ค๋์ ์ค๋๋ง์ React ์ฝ๋ ๋์ ๋์ โ๏ธ
๋ช ์ผ Express, UI/UX ํ๋ค๊ณ ๋ฒ์จ ๊ฐ๋ฌผ๊ฐ๋ฌผํ๋ค ๐ซ
โ๏ธย ์ค๋์ Bare minimum Requirement๋ React, Styled Components, Storybook์ ํ์ฉํด์React-custom-component
(Modal
,Toggle
,Tab
,Tag
)๋ฅผ ๊ตฌํํด๋ณด๊ธฐ ๐
Modal
import { useState } from "react";
import styled from "styled-components";
// ๋ชจ๋ฌ์ฐฝ์ ๋์์ฃผ๋ ๊ณต๊ฐ
export const ModalContainer = styled.div`
position: relative; // ๋ถ๋ชจ ์์์ธ ModalContainer๊ฐ ์์ ์์์ธ ModalBackdrop์ ๊ธฐ์ค์ ์ด ๋จ
height: 100%;
`;
// ๋ชจ๋ฌ์ฐฝ์ด ์ด๋ ธ์ ๋์ ๋ฐฐ๊ฒฝ
export const ModalBackdrop = styled.div`
display: flex;
justify-content: center;
align-items: center;
position: absolute; // ๋ถ๋ชจ ์์์ธ ModalContainer๋ฅผ ๊ธฐ์ค์ผ๋ก ์์น ๊ฒฐ์
top: 0; // ModalContainer์ ๊ธฐ์ค์ผ๋ก top์ ๋ฑ ๋ถ์ด๋ ๊ฐ๋
left: 0; // ์์ ๊ฐ์ด ์ผ์ชฝ, ์ค๋ฅธ์ชฝ, ์๋ ๋ชจ๋ ModalContainer์ ๋ฑ ๋ถ๋๋ก!
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5); // ๋ง์ง๋ง ์๋ฆฌ๋ ๋ฐฐ๊ฒฝ์ ํฌ๋ช
๋ ์ค์
`;
// ํด๋ฆญํด์ ๋ชจ๋ฌ์ฐฝ์ ์ฌ๋ ๋ฒํผ
export const ModalBtn = styled.button`
// ... CSS ์๋ต
// ์๋๋ ๋ฒํผ์ ๊ฐ์ด๋ฐ ์์นํ๋๋ก ํ๋ ์ฝ๋
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
`;
// ๋ชจ๋ฌ์ฐฝ
export const ModalView = styled.div.attrs((props) => ({
// attrs() ๋ฉ์๋๋ฅผ ์ด์ฉํด์ div element์ ์์ฑ ์ถ๊ฐ ๊ฐ๋ฅ
role: "dialog", // dialog -> modal ๊ฐ๋
?
}))`
// ... CSS ์๋ต
// ๊ฒ์ํ๋ค๊ฐ ๋ง์ ๋ ํ
๋๋ฆฌ ๊ทธ๋ฆผ์ css ์ฑ์น
box-shadow: rgba(50, 50, 93, 0.25) 0px 50px 100px -20px,
rgba(0, 0, 0, 0.3) 0px 30px 60px -30px,
rgba(10, 37, 64, 0.35) 0px -2px 6px 0px inset;
// ๋ชจ๋ฌ์ฐฝ ์์ ์๋ ๋ซ๊ธฐ x ์์ด์ฝ
> .fa-times-circle {
margin-bottom: 1rem;
}
`;
export const Modal = () => {
const [isOpen, setIsOpen] = useState(false); // ๋ชจ๋ฌ์ฐฝ open, close ์ํ
const openModalHandler = () => {
setIsOpen(!isOpen); // ๋ฒํผ ํด๋ฆญ์ ๋ชจ๋ฌ์ฐฝ์ ์ํ ๋ฐ๊ฟ์ฃผ๊ธฐ
};
return (
<>
<ModalContainer>
<ModalBtn onClick={openModalHandler}>
{isOpen ? "Opened!" : "Open Modal"}
</ModalBtn>
{isOpen ? (
<ModalBackdrop onClick={openModalHandler}>
<ModalView>
<i className="fas fa-times-circle" onClick={openModalHandler} />
Opened Modal!
</ModalView>
</ModalBackdrop>
) : (
""
)}
</ModalContainer>
</>
);
};
Toggle
import { useState } from "react";
import styled from "styled-components";
const ToggleContainer = styled.div`
position: relative;
margin-top: 8rem;
left: 47%;
cursor: pointer;
width: 50px;
> .toggle-container {
width: 45px;
height: 24px;
border-radius: 30px;
background-color: #dad9ff;
transition: all 1s ease-out; // ์ ๋๋ฉ์ด์
ํจ๊ณผ๋ก ์ค๋ฌด์คํ๊ฒ ๋ฐฐ๊ฒฝ์ ๋ณ๊ฒฝ
&.toggle--checked {
background-color: #4641d9;
}
}
> .toggle-circle {
position: absolute;
// ... css ์๋ต
transition: all 0.5s ease-out; // ์ ๋๋ฉ์ด์
ํจ๊ณผ๋ก ์ค๋ฌด์คํ๊ฒ ํ ๊ธ ๋ฒํผ์ด ์์ง์
&.toggle--checked {
left: 24px;
}
}
`;
const Desc = styled.div`
text-align: center;
margin-top: 1rem;
`;
export const Toggle = () => {
const [isOn, setisOn] = useState(false);
const toggleHandler = () => {
setisOn(!isOn);
};
return (
<>
<ToggleContainer
onClick={toggleHandler}
{/* ์กฐ๊ฑด๋ถ ์คํ์ผ๋ง ์ ์ฉ */}
<div className={`toggle-container ${isOn ? "toggle--checked" : ""}`} />
<div className={`toggle-circle ${isOn ? "toggle--checked" : ""}`} />
</ToggleContainer>
<Desc>{isOn ? "Toggle Switch ON" : "Toggle Switch OFF"}</Desc>
</>
);
};
Tab
import { useState } from "react";
import styled from "styled-components";
// Tab์ ๋ณด์ฌ์ฃผ๋ ์๋ฆฌ
const TabMenu = styled.ul`
// ... css ์๋ต
// Tab1, Tab2, Tab3 ๊ฐ๊ฐ์ Tab
.submenu {
display: flex;
flex: 1 1 0; // Tab๋ผ๋ฆฌ ๋ค๋ฅ๋ค๋ฅ ๋ถ์ด์๋ ๋ฌธ์ ํด๊ฒฐ
height: 100%;
justify-content: center;
align-items: center;
}
// ์ ํํ Tabmenu CSS
.focused {
background-color: #4374D9;
color: white;
flex: 1 1 0;
}
& div.desc {
text-align: center;
}
`;
const Desc = styled.div`
text-align: center;
`;
export const Tab = () => {
const [isTab, setIsTab] = 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) => {
setIsTab(index);
};
return (
<>
<div>
<TabMenu>
{menuArr.map((item, idx) => (
<li
className={`submenu${isTab === idx ? " focused" : ""}`}
key={idx} // map ์ฌ์ฉ์์ ๋ฌด์กฐ๊ฑด key ์ค์ !
onClick={() => selectMenuHandler(idx)}
>
{item.name}
</li>
))}
</TabMenu>
<Desc>
<p>{menuArr[isTab].content}</p>
</Desc>
</div>
</>
);
};
Tag
import { useState } from "react";
import styled from "styled-components";
export const TagsInput = styled.div`
// ... CSS ์๋ต
> ul {
// ... CSS ์๋ต
> .tag {
// ... CSS ์๋ต
// ํ๊ทธ ๋ด์ ์๋ X ํ๊ทธ ์ทจ์ ์์ด์ฝ css
> .tag-close-icon {
// ... CSS ์๋ต
&:hover { // x ์์ด์ฝ ์์ ๋ง์ฐ์ค๋ฅผ ์ฌ๋ ธ์ ๋ css
background-color: #8c8c8c;
color: white;
}
}
}
}
// ํ๊ทธ ์ถ๊ฐ input CSS
> input {
// ... CSS ์๋ต
:focus {
outline: transparent;
}
}
&:focus-within {
border: 1px solid #4000c7;
}
`;
export const Tag = () => {
const initialTags = ["Coding", "kimcoding"];
const [text, setText] = useState("");
const [tags, setTags] = useState(initialTags);
// Tag ์ญ์
const removeTags = (indexToRemove) => {
//
const removes = tags.filter((item, index) => index !== indexToRemove);
setTags(removes);
};
const addTags = (event) => {
// ์ด๋ฏธ ํ๊ทธ๋ก ์ถ๊ฐ๋์๊ฑฐ๋ ์๋ฌด๊ฒ๋ ์
๋ ฅ๋์ด ์์ง ์์์ ๋ ํ๊ทธ ์ถ๊ฐ XXX
if (!tags.includes(event) && event.length !== 0) {
setTags([...tags, event]); // ๊ธฐ์กด ํ๊ทธ์ ์๋ก์ด ํ๊ทธ ์ถ๊ฐ
setText(""); // ํ๊ทธ ์ถ๊ฐ ํ, input ๋น์ฐ๊ธฐ
}
};
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"
value={text}
onChange={(e) => setText(e.target.value)} // ์
๋ ฅํ๋ ํ๊ทธ ์ํ ๋ณ๊ฒฝ ํจ์
onKeyUp={(e) => {
if (e.key === "Enter") { // Enter ๋ฒํผ์ ๋๋ ์ ๋
addTags(e.target.value); // addTags() ์คํ
}
}}
placeholder="Press enter to add tags"
/>
</TagsInput>
</>
);
};
โฐย Bare minimum Requirement
19/19 Test Pass โ๏ธ
โ ย ๋ด์ฃผ ์์์ ์๋์์ฑ ๊ธฐ๋ฅ๊ณผ input ํด๋ฆญ์ ๋ด์ฉ ์์ ๊ธฐ๋ฅ ๊ตฌํํด๋ณด๊ธฐ ๐ซ
๋ฒ์จ 7์์ด๋ผ๋.. 2022๋ ์ด ๋ฒ์จ ์ ๋ฐ์ด ํ์ฉ ์ง๋๊ฐ๋ค.๐ฅฒย ๋ฐฑ์ ์ํ 4๊ฐ์ing ใฐ๏ธ
์์ฆ ๋ ์จ๊ฐ ๋์์ ๊ทธ๋ฐ์ง ์๋ ์ ์ข์๋ ์ง์ค๋ ฅ์ด ๋๋๋ ์ ์ข์์ง ๋๋์ด๋ค,, ๋ ์ด์ฌํ ๋บ์ค๋บ์ค ๐ช
[๐ซถ Todo List]
โ๏ธย ํ๋ก๊ทธ๋๋จธ์ค Lv.1_์ ์ ๋ด๋ฆผ์ฐจ์์ผ๋ก ๋ฐฐ์นํ๊ธฐ
โ๏ธย Coplit ๊ฐ์ฒด ๋ฌธ์ ๋ณต์ต
โ๏ธย Udemy ์๊ณ ๋ฆฌ์ฆ&์๋ฃ๊ตฌ์กฐ ๊ฐ์ (๋น ์คํ๊ธฐ๋ฒ) ๋ฃ๊ธฐ
โ๏ธ Udemy React ๊ฐ์ (Section2) ๋ฃ๊ธฐ
โ๏ธ ์ธํ๋ฐ ํ์ ๋ฆฌ์กํธ - JS์์ฉ ๊ฐ์ ๋ฃ๊ธฐ