티스토리 뷰
오늘 추가할것.
요놈들이다.
+Add another Card 를 클릭하면 입력 폼이 나와서 카드를 추가 할 수 있고
오른쪽엔 Add another list 로 리스트보드를 생성할 수 있게 해준다.
하지만 Add another card 와 Add another list 는 다른 컴포넌트가 아니라
하나의 컴포넌트에서 들어오는 props에 따라서 텍스트와 스타일만 바뀌게 해줄 것이다.
우선 components 폴더에 trelloActionButton.js 파일을 생성해 준다.
이놈이 저 카드를 추가해주고 리스트를 추가해주고 하는 컴포넌트가 될 것이다.
위에서 보면 알수 있지만 텍스트 이외에도 + 모양의 아이콘이 하나 있다.
이놈은 material-ui 에서 Icon 으로 가져올것이다.
import React from 'react'
import Icon from '@material-ui/core/Icon'
const TrelloActionButton = ({ list }) => {
return(
<div>
<Icon>add</Icon>
<p>Hello</p>
</div>
)
}
export default TrelloActionButton;
일단 요렇게만 하고 TrelloList 파일에서 불러와서 카드 밑에 컴포넌트를 넣었을때,
리스트 안에 이런식으로 출력되면 성공이다.
근데 나는 안됐다. 계속 안돼서 한시간을 씨름하다가 스텍오버플로우에서
나와같이 절고있는사람을 발견했는데 index.html에 링크를 추가해줘야 하더라.
** 역시 뭔가 동작을 안할때는 구글에 @@@@ not working 치면 다나온다.
강의에서는 안알려줬는데 ㅠㅠㅠㅠㅠㅠ 이것때문에 시간을 얼마나 허비한건지...
해서, 밑의 코드를 index.html의 head 안에 추가해 준다.
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
나와 같이 절고있을 사람이 있을것같아서 검색어를 추가해야겠다.
(material-ui icon 적용 안됨, material-ui icon 글씨만 나옴)
으으..끔찍해..
추가해 주고나서 안쪽의 텍스트(지금까지는 Hello)가
리스트를 추가하는 것이냐 카드를 추가하는것이냐에 따라
텍스트의 내용이 바뀌어야 하므로 삼항연산자를 이용해 조건을 만들어준다.
const buttonText = list ? 'Add another list' : 'Add another Card';
이 컴포넌트가 쓰일때 props로 list가 있냐 없냐에 따라 안쪽의 텍스트가 상황에 맞춰 변해줄꺼다. 왜변하냐?
아래와 같은 실험을 해보았을때 a가 list가 들어오지 않았을때의 값이고
b는 뭔가가 props로 들어왔을때의 값이라고 가정하고 보면
const a = undefined
const b = "Hi"
function trueOrFalse(para){
para ? console.log("true") : console.log("false");
}
trueOrFalse(a) // false
trueOrFalse(b) // true
이런 결과가 나오기 때문에 들어오는 props 가 있느냐 없느냐에 따라 문구가 달라지게 만들 수 있는것이다.
이제 너무 못생겼으므로 스타일을 줄것인데
스타일중 몇가지가 리스트의 위치에 따라 달라져야 하기때문에 아래처럼 조건을 좀 준다.
const TrelloActionButtoncopy = ({ list }) => {
const buttonText = list ? "Add another list" : "Add another Card"
const buttonTextOpacity = list ? 1 : 0.5
const buttonTextColor = list ? "white" : "inherit"
const buttonTextBackgroud = list ? "rgba(0,0,0,.15)" : "inherit"
return (
<div style={{
...styles.openFormButtonGroup,
opacity:buttonTextOpacity,
color:buttonTextColor,
backgroundColor:buttonTextBackgroud}}>
<Icon>add</Icon>
<p>{buttonText}</p>
</div>
);
};
const styles = {
openFormButtonGroup:{
display:"flex",
alignItems:"center",
cursor:"pointer",
borderRadius:3,
height:36,
width:272,
paddingLeft:10
}
}
** 그리고 div 의 style 중 . . . styles 의 저 . . . 은 객체의 껍데기를 한겹 벗겨주는 역할을 한다고 한다.
이제 생각해 보아야 할것이 지금은 그냥 저 버튼 하나만 그려지고 있는데 사실 저 버튼이 눌리면
TrelloActionButton의 폼이 완전히 바뀌게 되어야 한다. 실제로의 Trello 처럼
왼쪽의 버튼을 누르면 오른쪽처럼 입력창이 열려야 하는 것.
그렇기 때문에 지금까지 만들어놓은것을
renderAddbutton 이라는 이름의 함수로 한번 묶어줄꺼고
오른쪽의 입력폼은
renderForm 이라는 이름의 함수안에 만들어 줄 것이다.
그리고 TrelloActionButton의 기본 구조는
default State가 false 고 이때는 renderAddbutton 함수가 return 되어 위 사진에서 왼쪽의 버튼이 그려지고
이 버튼이 눌리면 State가 true로 바뀌면서 오른쪽의 폼이 나타나게 되는 것 이다.
해서, 상태를 가져와야하니까 useState 를 사용할 것이고 상태의 업데이트를 위하여
renderAddbutton 에 onClick 를 넣어 눌릴때 setState 를 바뀌게 해줄 것이다.
그럼 아래처럼 된다
import React, { useState } from 'react';
import Icon from '@material-ui/core/Icon';
const TrelloActionButtoncopy = ({ list }) => {
const [state, setState] = useState(false)
const renderForm = () => {
return(
<div>hello</div>
)
}
const renderAddbutton = () => {
const buttonText = list ? "Add another list" : "Add another Card"
const buttonTextOpacity = list ? 1 : 0.5
const buttonTextColor = list ? "white" : "inherit"
const buttonTextBackgroud = list ? "rgba(0,0,0,.15)" : "inherit"
return (
<div onClick={() => {setState(true)}} style={{
...styles.openFormButtonGroup,
opacity:buttonTextOpacity,
color:buttonTextColor,
backgroundColor:buttonTextBackgroud}}>
<Icon>add</Icon>
<p>{buttonText}</p>
</div>
);
}
return(
state ? renderFrom() : renderAddbutton()
)
};
이렇게하고 버튼을 눌렀을때 Hello 로 텍스트가 바뀌면 성공이다.
성공했다면 이제 renderForm 의 실제구조를 잡아줘야하는데
일단텍스트를 쓸 수 있는 Textarea가 하나 필요하고 작성후 완료버튼이 필요하다. 아래처럼
완료하는 버튼은 리스트보드 안에있을때랑 밖에있을때 문구가 달라야 하고(Add Card, Add List),
인풋 안에 Placeholder 도 마찬가지로 달라져야 한다.
이놈들은 아까 Button Text에 썼던방법 그대로 똑같이 써줄꺼다.
** 디자인은 material ui에서 가져와서 쓸꺼다.(X버튼도 마찬가지)
const Placeholder = list ? "Enter list title..." : "Enter a title for this card..."
const buttonTitle = list ? "Add List" : "Add Card"
우선 이렇게 list의 위치에 따라 변할 문구를 정해놓는다.
그리고 JSX 구조는
const renderFrom = () => {
return(
<div>
<Card>
<Textarea/>
</Card>
<div>
<Button></Button>
<Icon>close</Icon>
</div>
</div>
)
}
이렇게 될꺼다.
일반 카드들과 똑같은 디자인템플릿을 쓰지만 안쪽에 Textarea를 사용해서 글을 입력할 수 있게 해준다.
그리고 아래쪽에 Button을 둘꺼다.
( Textarea라는 놈은 여러글자의 글을쓰면 영역에 맞게 쭉 입력되면서 자동 줄바꿈되고 사이즈도 변하는
react-textarea-autosize 를 사용해야한다 아래의
npm i react-textarea-autosize |
를 설치해주고 임폴트 해준다. )
import Textarea from 'react-textarea-autosize'
설치가 다 되었다면 이제 안쪽에 만들어놓은 placeholder와 buttonTitle을 넣어주고 버튼은 material-ui 양식에 맞게끔 속성을 주자.
return(
<div>
<Card>
<Textarea placeholder={Placeholder}/>
</Card>
<div>
<Button variant="contained">{buttonTitle}</Button>
<Icon>close</Icon>
</div>
</div>
)
}
이렇게까지 하고 Add another card 를 눌러보면 아주못생긴
이런놈이 나올꺼다. 그러니 스타일 속성을 추가해서 이쁘게 꾸며준다.
styles 오브젝트에 openFormButtonGroup 객체 뒤에 아래 요놈도 추가해준다
(ADD CARD랑 X랑 중앙에 정렬시키고 Textarea랑 약간 떨어지게 하기위한 스타일 객체)
formButtonGroup:{
marginTop:8,
display:"flex",
alignItems:"center"
}
이후 스타일은 아래와같이 잡아주었다.
<div>
<Card style={{overflow:"visible", minHeight:80, minWidth:272, padding:"6px 8px 2px"}}>
<Textarea placeholder={Placeholder} autoFocus onBlur={()=>{setState(false)}} style={{
resize:"none", overflow:"hidden", outline:"none", border:"none", width:"100%"
}}
/>
</Card>
<div style={styles.formButtonGroup}>
<Button variant="contained" style={{color:"white", backgroundColor:"#5aac44"}}>
{buttonTitle}
</Button>
<Icon style={{marginLeft:8, cursor:"pointer"}}>close</Icon>
</div>
</div>
autoFocus 같은 경우는 처음에 추가하는 버튼을 눌러서
renderFrom이 떴을때 자동으로 포커스를 Textarea에 잡아줘서바로 입력이 가능하게 만들어주며,
onBlur같은 경우에는 Textarea 이외의 공간을 눌렀을때 다시 renderFrom이 닫히게
state를 변경해 준다. 이외의 스타일은 취향에 맞게...
그리고 아래쪽 Add card 버튼과 X 아이콘을 감싸고 있는 div에 아까 만들어 놓은
formButtonGroup스타일을 준다.
이렇게까지하고 보면
이렇게 보기좋게 바뀐다.
이게 끝이 아니라 이제 Textarea에 작성된 데이터를 어디엔가 쓸꺼니까 그걸 관리할 state를 만들어줘야한다.
이는 스터디하고있는 Todo list만들기를 참고해서 써주었다.
**react에서 state가 여러개 필요할땐 그냥 여러개 써주면 되더라. 주의할점은 useState를 쓸때는 함수안에 쓰면안된다.
즉 위의 예시를 들어보면 TrelloActionButton 이라는 컴포넌트 안에는 써도 되는데 그안에 renderForm 같은 함수안에 쓰면 에러가 난다는 것이다.
state를 하나 만들고 이름은 text와 setText 로 했다. 그리고 기본값은 ' ' 빈 문자열이다.
const [text, setText] = useState('')
이렇게 한뒤, Textarea의 속성중 value에 text를 추가해준다. ( value={text} )
그러면 글이 안써지니까 handleInputChange 라는 함수를 하나 만들고 이놈으로
키가 눌릴때마다 setText 를 통해 text state가 업데이트되게 해주면 키가 다시 눌리며, 그 안의 값이
text에 들어가게 될 것이다. 여기까지 하고 renderForm의 코드를 보면
const renderForm = () => {
const Placeholder = list ? "Enter list title..." : "Enter a title for this card..."
const buttonTitle = list ? "Add List" : "Add Card"
const handleInputChange = ({target}) => {
setText(target.value)
}
return (
<div>
<Card style={{overflow:"visible", minHeight:80, minWidth:272, padding:"6px 8px 2px"}}>
<Textarea placeholder={Placeholder} autoFocus value={text}
onChange={handleInputChange} onBlur={()=>{setState(false)}} style={{
resize:"none", overflow:"hidden", outline:"none", border:"none", width:"100%"
}}
/>
</Card>
<div style={styles.formButtonGroup}>
<Button variant="contained" style={{color:"white", backgroundColor:"#5aac44"}}>
{buttonTitle}
</Button>
<Icon style={{marginLeft:8, cursor:"pointer"}}>close</Icon>
</div>
</div>
)
}
이렇게 된다. 휴...
이제 App.js와 trelloList.js 에 만들어놓은 컴포넌트를 넣어준다.
** App에 넣을때는 리스트보드 옆에 나오는거니까 문구가 Add another list 로 나와야 하기때문에 컴포넌트에 list를 추가해주자.
그러면~~~~
오늘까지의 결과물이다!
근데 아직 그냥 그려놓은것일뿐 추가도 안되고 아무것도 안된다 ^^
'REACT STUDY' 카테고리의 다른 글
[Trello 만들기 4일차 2] Drag & Drop, react-beautiful-dnd (2) | 2020.01.03 |
---|---|
[Trello 만들기 4일차 1]카드 추가기능 (0) | 2020.01.03 |
[Trello 만들기 3일차]List 의 추가 (action, dispatch(Hook에서 사용)) (0) | 2020.01.03 |
[Trello 만들기]1일차 - 보드와 카드 만들기 (1) | 2019.12.31 |