본문 바로가기
개인 공부/게시판

6. 모달 창 구현

by 갱생angel 2024. 2. 10.

모달 창 추가

※모달 창 열기/닫기

※모달 창 외부 버튼 클릭 허용/방지

   -stopPropagation(): 이벤트 버블링을 방지

   -이벤트 버블링: 이벤트가 동작될 시 루트까지 해당되는 부모 요소의 이벤트도 동작

※모달 창 외부 클릭 시 모달 창 닫기

 

component

  -Input.JSX

  -Button.JSX

  -Textarea.JSX

  -DiaryWrite.JSX

  -DiaryBlock.JSX

  -Category.JSX

  -CategoryButton.JSX

module

  -Diary.JSX

css

  -Diary.css

  -Category.css

 

component - < DiaryBlock.JSX> 

import React, {useState} from 'react'
import '../css/Diary.css'
import Button from './Button'
import Textarea from './Textarea'

function DiaryBlock({
  title,
  content,
  id,
  name,
  date,
  deleteDiary,
  editDiary,
  modalOutEventNone //모달 창 유무 확인
}) {
  const [editBool, setEditBool] = useState(true)
  const [editValue, setEditValue] = useState(content)

  const changeEditValue = event => {
    setEditValue(event.target.value)
  }

  const handleDelete = () => {
    deleteDiary(id)
  }

  const handleEdit = () => {
    if (editBool === true) {
      setEditBool(false)
    } else {
      setEditBool(true)
      editDiary(id, editValue)
    }
  }

  return (
    <div className="blockDiv" key={id} name={name}>
      <div className="blockTitleDiv">
        <h3 className="blockTitle">{title}</h3>
        <Button
          className={'deleteButton'}
          onClick={modalOutEventNone ? handleDelete : null}
          name={'삭제'}
        />
        <Button
          className={'editButton'}
          onClick={modalOutEventNone ? handleEdit : null}
          name={editBool ? '수정' : '적용'}
        />
      </div>
      <div className="blockDateDiv">
        <p>{date}</p>
      </div>
      <p>
        ---------------------------------------------------------------------------------------------
      </p>
      {editBool ? (
        <p>{content}</p>
      ) : (
        <Textarea
          className={'writeContentTextarea'}
          value={editValue}
          onChange={changeEditValue}
        />
      )}
    </div>
  )
}

export default DiaryBlock

 

module - <Diary.JSX> : 모달 창 열기/닫기, 모달 창 외부 버튼 클릭 허용/방지(stopPropagation())

import React, {useEffect, useRef, useState} from 'react'
import '../css/Diary.css'
import DiaryWrite from '../component/DiaryWrite'
import DiaryBlock from '../component/DiaryBlock'
import CategoryButton from '../component/CatogoryButton'
import Button from '../component/Button'

function Diary() {
  const [diary, setDiary] = useState(JSON.parse(localStorage.getItem('Diary')) || [])
  const [categoryDiary, setCategoryDiary] = useState(diary)
  const [categoryValue, setCategoryValue] = useState('none')
  const [modalBool, setModalBool] = useState(false) //모달 창 오픈 유무
  const [modalOutEventNone, setModalOutEventNone] = useState(true) //모달 창 외부 버튼 클릭 방지 유무

  const diaryKey = useRef(parseInt(localStorage.getItem('DiaryKey')) || 0)

  const changeCategoryValue = event => {
    setCategoryValue(event.target.value)
  }

  const addDiary = (title, content) => {
    if (categoryValue === 'none') {
      alert('카테고리를 지정해주세요.')
    } else {
      const newDiary = {
        title,
        content,
        name: categoryValue,
        id: diaryKey.current,
        date: new Date().toLocaleString()
      }
      diaryKey.current += 1
      setDiary([newDiary, ...diary])
      setCategoryValue('none')
      setModalBool(false) //모달 창 닫기
      setModalOutEventNone(true) //모달 창 외부 버튼 클릭 허용
    }
  }

  const deleteDiary = id => {
    const remainDiary = diary.filter(remove => remove.id !== id)
    setDiary(remainDiary)
  }

  const editDiary = (id, content) => {
    setDiary(diary.map(edit => (edit.id === id ? {...edit, content: content} : edit)))
  }

  const allDivDiaryBtn = () => {
    setCategoryDiary(diary)
  }

  const routineDivDiaryBtn = () => {
    const routineDiary = diary.filter(divide => divide.name === 'routine')
    setCategoryDiary(routineDiary)
  }

  const studyDivDiaryBtn = () => {
    const studyDiary = diary.filter(divide => divide.name === 'study')
    setCategoryDiary(studyDiary)
  }

  const travelDivDiaryBtn = () => {
    const travelDiary = diary.filter(divide => divide.name === 'travel')
    setCategoryDiary(travelDiary)
  }

  const gameDivDiaryBtn = () => {
    const gameDiary = diary.filter(divide => divide.name === 'game')
    setCategoryDiary(gameDiary)
  }

  const openModal = event => {
    setModalBool(true) //모달 창 열기
    setModalOutEventNone(false) //모달 창 외부 버튼 클릭 방지
    event.stopPropagation()
  }

  const closeModal = () => {
    setModalBool(false) //모달 창 닫기
    setModalOutEventNone(true) //모달 창 외부 버튼 클릭 허용
    setCategoryValue('none')
  }

  const modalOutClick = () => { //모달 창 외부 클릭 시 모달창 닫기
    setModalBool(false)
    setModalOutEventNone(true)
    setCategoryValue('none')
  }

  useEffect(() => {
    localStorage.setItem('Diary', JSON.stringify(diary))
    localStorage.setItem('DiaryKey', parseInt(diaryKey.current))
    setCategoryDiary(diary)
  }, [diary])

  return (
    <div className={modalBool ? 'diaryDiv' : null} onClick={modalOutClick}>
      <div>
        <Button
          className="openModalButton"
          name={'글쓰기'}
          onClick={modalOutEventNone ? openModal : null}
        />
      </div>
      <div onClick={event => event.stopPropagation()}>
        {modalBool ? (
          <DiaryWrite
            addDiary={addDiary}
            closeModal={closeModal}
            categoryValue={categoryValue}
            changeCategory={changeCategoryValue}
          />
        ) : null}
      </div>
      <div>
        <CategoryButton
          allDivDiaryBtn={modalOutEventNone ? allDivDiaryBtn : null}
          routineDivDiaryBtn={modalOutEventNone ? routineDivDiaryBtn : null}
          studyDivDiaryBtn={modalOutEventNone ? studyDivDiaryBtn : null}
          travelDivDiaryBtn={modalOutEventNone ? travelDivDiaryBtn : null}
          gameDivDiaryBtn={modalOutEventNone ? gameDivDiaryBtn : null}
        />
        {categoryDiary.map(diaryData => {
          return (
            <DiaryBlock
              key={diaryData.id}
              name={diaryData.name}
              date={diaryData.date}
              {...diaryData}
              deleteDiary={deleteDiary}
              editDiary={editDiary}
              modalOutEventNone={modalOutEventNone}
            />
          )
        })}
      </div>
    </div>
  )
}

export default Diary

 

Diary.css

.writeDiv {
  border: 1px solid black;
  display: inline-block;
  padding: 20px;
  position: absolute;
  background-color: white;
  left: 37%;
  top: 10%;
  box-shadow: rgba(0, 0, 0, 0.7) 0 0 0 9999px;
}

.writeMain {
  text-align: center;
  margin-left: 30px;
}

.writeTitleDiv {
  margin-top: 20px;
}

.writeTitleInput {
  width: 400px;
  height: 30px;
  font-size: 15px;
}

.writeContentDiv {
  margin-top: 20px;
}

.writeContentTextarea {
  width: 400px;
  height: 100px;
  resize: none;
  font-size: 15px;
}

.blockDiv {
  border: 1px solid rgb(211, 211, 211);
  background-color: rgb(211, 211, 211);
  width: 600px;
  padding: 10px;
  margin-top: 20px;
}

.blockTitleDiv {
  display: flex;
  justify-content: flex-start;
  align-items: center;
}

.blockTitle {
  margin-right: auto;
  margin-top: 10px;
  margin-bottom: 0;
}

.blockDateDiv {
  font-size: 15px;
  font-style: italic;
}

.diaryDiv {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  background-color: rgba(0, 0, 0, 0.4);
}

.openModalButton {
  margin-top: 10px;
  margin-right: 20px;
  padding: 7px 15px 7px 15px;
  background-color: black;
  color: white;
  border: none;
}

.closeModalButton {
  float: right;
  background: none;
  border: none;
  font-size: 20px;
}

.submitButton {
  margin-top: 20px;
  width: 400px;
  height: 40px;
  font-size: 20px;
}

.deleteButton {
  font-size: 15px;
  padding: 3px 10px 3px 10px;
  margin-right: 10px;
  margin-top: 10px;
  background-color: rgb(211, 211, 211);
}

.editButton {
  font-size: 15px;
  padding: 3px 10px 3px 10px;
  margin-right: 30px;
  margin-top: 10px;
  background-color: rgb(211, 211, 211);
}

 

 

'개인 공부 > 게시판' 카테고리의 다른 글

8. 일기 수정 모달 창  (0) 2024.02.15
7. 페이지 구성  (0) 2024.02.13
5. 카테고리, 작성 날짜  (0) 2024.01.31
4. EditDiary  (0) 2024.01.29
3. DeleteDiary, diaryKey 값 유지  (0) 2024.01.29