-
React, 컴포넌트 디자인, styled-component 적용Topic/React | Redux 2022. 1. 4. 22:17반응형
리액트 컴포넌트 디자인 실습 ✔️
리액트 모달 창 띄우기 ☑️
삼항 연산자를 이용한 조건부 렌더링을 활용한다.
alert창과 다르게 어두운 배경 태그 내부에 창 태그를 삽입해 구현한다.import { useState } from 'react'; import styled from 'styled-components'; export const ModalContainer = styled.div` // Modal을 구현하는데 전체적으로 필요한 CSS를 구현합니다. display: flex; justify-content: center; align-items: center; height: 100%; position: relative; `; export const ModalBackdrop = styled.div`ㅁ // Modal이 떴을 때의 배경을 깔아주는 CSS를 구현합니다. position: fixed; top: 0; bottom: 0; left: 0; right: 0; background-color: rgba(0,0,0,0.3); z-index: 1; `; export const ModalBtn = styled.button` background-color: #4000c7; text-decoration: none; border: none; padding: 20px; color: white; border-radius: 30px; cursor: grab; `; export const ModalView = styled.div.attrs(props => ({ // attrs 메소드를 이용해서 아래와 같이 div 엘리먼트에 속성을 추가할 수 있습니다. role: 'dialog' }))` // TODO : Modal창 CSS를 구현합니다. position: absolute; top: calc(50vh - 100px); left: calc(50vw - 200px); background-color: white; display: flex; justify-content: center; align-items: center; border-radius: 10px; width: 400px; height: 200px; >.close-btn{ position: absolute; top: 2px; right: 7px; cursor: pointer; } `; export const Modal = () => { const [isOpen, setIsOpen] = useState(false); const openModalHandler = () => { // isOpen의 상태를 변경하는 메소드를 구현합니다. setIsOpen(!isOpen); }; return ( <> <ModalContainer onClick={openModalHandler}> <ModalBtn // 클릭하면 Modal이 열린 상태(isOpen)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다. > {isOpen ? 'Opened!' : 'Open Modal'} {/*조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때는 ModalBtn의 내부 텍스트가 'Opened!' 로 Modal이 닫힌 상태(isOpen이 false인 상태)일 때는 ModalBtn 의 내부 텍스트가 'Open Modal'이 되도록 구현해야 합니다. */} </ModalBtn> {/*조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때만 모달창과 배경이 뜰 수 있게 구현해야 합니다. */} {isOpen ? <ModalBackdrop onClick={openModalHandler}> <ModalView> <div className='close-btn'>닫기 </div> <div>HELLO CODESTATES!</div> </ModalView> </ModalBackdrop> : null} </ModalContainer> </> ); };
리액트 토글 구현하기 ☑️
토글 버튼을 직접 만들어보는 과정에서 Styled-component를 사용했고,
버튼 클릭에 따른 스타일을 적용하는 방법을 익혔다.import { useState } from 'react'; import styled from 'styled-components'; 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 클래스가 활성화 되었을 경우의 CSS를 구현합니다. &.toggle--checked { background-color: green; } } > .toggle-circle { position: absolute; top: 1px; left: 1px; width: 22px; height: 22px; border-radius: 50%; background-color: #ffffff; transition: left 0.02s ease-in 0.1s; // .toggle--checked 클래스가 활성화 되었을 경우의 CSS를 구현합니다. &.toggle--checked { left: 27px; background-color: #ffffff; transition: left 0.02s ease-in 0.1s; } } `; const Desc = styled.div` // 설명 부분의 CSS를 구현합니다. text-align: center; `; export const Toggle = () => { const [isOn, setisOn] = useState(false); const toggleHandler = () => { // isOn의 상태를 변경하는 메소드를 구현합니다. setisOn(!isOn) }; return ( <> <ToggleContainer onClick={toggleHandler}> {/* 아래에 div 엘리먼트 2개가 있습니다. 각각의 클래스를 'toggle-container', 'toggle-circle' 로 지정하세요. */} {/* TIP : Toggle Switch가 ON인 상태일 경우에만 toggle--checked 클래스를 div 엘리먼트 2개에 모두 추가합니다. 조건부 스타일링을 활용하세요. */} <div className={'toggle-container' + (isOn ? ' toggle--checked' : '')}/> <div className={'toggle-circle' + (isOn ? ' toggle--checked' : '')}/> </ToggleContainer> <Desc> {isOn ? 'Toggle Switch ON' : 'Toggle Switch OFF'} </Desc> </> ); };
리액트 탭 구현하기 ☑️
텍스트가 있는 탭을 클릭하면 사용자에게 변화를 알려주는 탭을 직접 구현해봤다.
import { useState } from 'react'; import styled from 'styled-components'; // TODO: Styled-Component 라이브러리를 활용해 TabMenu 와 Desc 컴포넌트의 CSS를 구현합니다. 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; .submenu { ${'' /* 기본 Tabmenu 에 대한 CSS를 구현합니다. */} background-color: white; color: black; padding: 10px; padding-left: 40px; padding-right: 40px; } .focused { ${'' /* 선택된 Tabmenu 에만 적용되는 CSS를 구현합니다. */} background-color: royalblue; } & div.desc { text-align: center; } `; const Desc = styled.div` text-align: center; `; export const Tab = () => { // TIP: Tab Menu 중 현재 어떤 Tab이 선택되어 있는지 확인하기 위한 // currentTab 상태와 currentTab을 갱신하는 함수가 존재해야 하고, 초기값은 0 입니다. const [currentTab, setCurrentTab] = 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) => { // TIP: parameter로 현재 선택한 인덱스 값을 전달해야 하며, 이벤트 객체(event)는 쓰지 않습니다 // 해당 함수가 실행되면 현재 선택된 Tab Menu 가 갱신되도록 함수를 완성하세요. setCurrentTab(index); }; return ( <> <div> <TabMenu> {/* map을 이용한 반복으로 코드를 작성합니다.*/} {/*TIP: li 엘리먼트의 class명의 경우 선택된 tab 은 'submenu focused' 가 되며, 나머지 2개의 tab은 'submenu' 가 됩니다.*/} { menuArr.map((item, i)=> { return <li className={i === currentTab ? 'submenu focused' : 'submenu'} onClick={() => selectMenuHandler(i)}> {item.name} </li> }) } </TabMenu> <Desc> {/* 현재 선택된 메뉴 따른 content를 표시하세요*/} <p>{menuArr[currentTab].content}</p> </Desc> </div> </> ); };
리액트 태그 구현하기 ☑️
중복되는 태그는 삽입하지 못하고, x버튼을 클릭해 삽입된 태그들을 삭제하는 기능을 구현해보았다.
import { useState } from 'react'; import styled from 'styled-components'; // TODO: Styled-Component 라이브러리를 활용해 여러분만의 tag 를 자유롭게 꾸며 보세요! 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: #4000c7; > .tag-close-icon { display: block; width: 16px; height: 16px; line-height: 16px; text-align: center; font-size: 14px; margin-left: 8px; color: #4000c7; 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 #4000c7; } `; export const Tag = () => { const initialTags = ['CodeStates', 'kimcoding']; const [tags, setTags] = useState(initialTags); const removeTags = (indexToRemove) => { // TODO : 태그를 삭제하는 메소드를 완성하세요. setTags(tags.filter(item => { return item !== tags[indexToRemove] })) }; const addTags = (event) => { // TODO : tags 배열에 새로운 태그를 추가하는 메소드를 완성하세요. // 이 메소드는 태그 추가 외에도 아래 3 가지 기능을 수행할 수 있어야 합니다. // - 이미 입력되어 있는 태그인지 검사하여 이미 있는 태그라면 추가하지 말기 // - 아무것도 입력하지 않은 채 Enter 키 입력시 메소드 실행하지 말기 // - 태그가 추가되면 input 창 비우기 if (event.key === 'Enter') { if (event.target.value && !(tags.includes(event.target.value))) { setTags(tags.concat(event.target.value)) 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 {/* TODO : tag-close-icon이 tag-title 오른쪽에 x 로 표시되도록 하고, 삭제 아이콘을 click 했을 때 removeTags 메소드가 실행되어야 합니다. */} </span> </li> ))} </ul> <input className='tag-input' type='text' onKeyUp={(e)=> addTags(e)} placeholder='Press enter to add tags' /> </TagsInput> </> ); };
반응형'Topic > React | Redux' 카테고리의 다른 글
Redux, React, action, reducer, store (0) 2022.01.07 React, 컴포넌트, Autocomplete 기능, ClickToEdit 기능 구현 (0) 2022.01.05 REACT 컴포넌트 디자인, CDD, Storybook, useRef, Styled-Component (0) 2022.01.03 [정리] REACT, 데이터 흐름의 이해와 비동기 (0) 2021.12.31 REACT, Effect Hook, Side Effect (0) 2021.12.29