[React] 구글 Keep만들기5

Jungmin Ji·2023년 8월 28일
0

구글 Keep 만들기

목록 보기
5/5
post-thumbnail

html-react-parser

리액트에서 요소를 생성할때
React.createElement('p',{},'Hello, World');하는것처럼
HTML string을 분리해서 parse함.
tag를 없애준다.

NoteCard.tsx

import parse from 'html-react-parser';
...
      <ContentBox>
        {parse(content)}
      </ContentBox>

글자수와 이미지처리

  const func = () => {
    const imgContent = content.includes("img");
    if(imgContent) {
      return content;
    } else {
      return content.length > 75 ? content.slice(0, 75) + "..." : content;
    }
  }
  ...
  <ContentBox>{parse(func())}</ContentBox>
  

Note Modal 생성하기

NoteCard에 클릭이벤트, 모달 넣기

NoteCard.tsx

...
return (
    <>
      {isRead && <ReadNoteModal type={type} note={note}/>}
      ...
        <ContentBox onClick={() => dispatch(readNote({ type, id }))}>
          {parse(func())}
        </ContentBox>
        ...
    </>

Note Modal

src > Modal > ReadNoteModal.tsx

interface ReadNoteModalProps {
  type: string,
  note: Note
}
const ReadNoteModal = ({ type, note }: ReadNoteModalProps) => {
  const dispatch = useAppDispatch();


  return (
    <FixedContainer>
      <Box style={{ backgroundColor: note.color }}>
        <DeleteBox
          onClick={() => dispatch(readNote({ type, id: note.id }))}
          className="readNote__close-btn"
        >
          <FaTimes />
        </DeleteBox>
        <div className="readNote__title">{note.title}</div>
        <div className="readNote__content">{parse(note.content)}</div>
      </Box>
    </FixedContainer>
  );
}

노트 정렬 모달 생성하기

핸들러 생성

pages > AllNotes.tsx
filterHandler에서 이벤트객체 받아서 filter 값(순위별, 날짜별) 가져와서 setFilter하고
cleaerHandler에서 필터값 비우기
FiltersModal에 props로 filterHandler, cleaerHandler, filter를 넘겨준다.

  const filterHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    setFilter(e.target.value);
  };

  const clearHandler = () => {
    setFilter("");
  };
  return (
    <Container>
      {viewFiltersModal && (
        <FiltersModal 
          handleFilter={filterHandler}
          handleClear={clearHandler}
          filter={filter}
        />
      )}
      ...

필터 모달 작성

FiltersModal.tsx

interface FiltersModalProps {
  handleFilter: (e: React.ChangeEvent<HTMLInputElement>) => void;
  handleClear: () => void;
  filter: string;
}
const FiltersModal = ({ handleFilter, handleClear, filter }: FiltersModalProps) => {
  const dispatch = useAppDispatch();

  return (
    <FixedContainer>
      <Container>
        <DeleteBox 
          onClick={() => dispatch(toggleFiltersModal(false))}
          className='filters__close'
        >
          <FaTimes />
        </DeleteBox>
        <TopBox>
          <div className="filters__title">정렬</div>
          <small onClick={handleClear} className="filters__delete">
            Clear
          </small>
        </TopBox>
        <Box>
          <div className="filters__subtitle">PRIORITY</div>
          <div className="filters__check">
            <input
              type="radio"
              name="filter"
              value="low"
              id="low"
              checked={filter === "low"}
              onChange={(e) => handleFilter(e)}
            />
            <label htmlFor="low">Low to High</label>
          </div>
          <div className="filters__check">
            <input
              type="radio"
              name="filter"
              value="high"
              id="high"
              checked={filter === "high"}
              onChange={(e) => handleFilter(e)}
            />
            <label htmlFor="high">High to Low</label>
          </div>
        </Box>
        <Box>
          <div className="filters__subtitle">DATE</div>
          <div className="filters__check">
            <input
              type="radio"
              name="filter"
              value="latest"
              id="new"
              checked={filter === "latest"}
              onChange={(e) => handleFilter(e)}
            />
            <label htmlFor="new">Sort by Latest</label>
          </div>
          <div className="filters__check">
            <input
              type="radio"
              name="filter"
              value="created"
              id="create"
              checked={filter === "created"}
              onChange={(e) => handleFilter(e)}
            />
            <label htmlFor="create">Sort by Created</label>
          </div>
          <div className="filters__check">
            <input
              type="radio"
              name="filter"
              value="edited"
              id="edit"
              checked={filter === "edited"}
              onChange={(e) => handleFilter(e)}
            />
            <label htmlFor="edit">Sort by Edited</label>
          </div>
        </Box>
      </Container>
    </FixedContainer>
  );
}

노트 정렬 기능 적용하기

Pinned 노트와 일반 노트 구별하여 보여주기

getAllNotes.tsx를 수정한다.
pinned에 isPinned true인것을 넣어주고 false인것은 normal에 넣는다.
그리고 다음 조건에 따라 리스트 뷰가 달라진다.
1. 일반노트가 0이 아니고 핀노트가 0일때(All Notes만 보여짐)
2. 일반노트가 0이고 핀노트가 0이 아닐때(Pinned Notes만 보여짐)
3. 일반노트와 핀노트가 0이 아닐때(각각 따로 보여짐)
4. 일반노트와 핀노트가 0일때(아무것도 없음 안내문구 보여짐)

const getAllNotes = (mainNotes: Note[], filter: string) => {

    const pinned = mainNotes.filter(({ isPinned }) => isPinned); // isPinned true인 notes들만
    const normal = mainNotes.filter(({ isPinned }) => !isPinned); // isPinned false

    if (normal.length !== 0 && pinned.length === 0) {
      return (
        <>
          <div className="allNotes__notes-type">
            All Notes <span>({normal.length})</span>
          </div>
          <NotesContainer>
            {normal.map((note) => (
              <NoteCard key={note.id} note={note} type="notes" />
            ))}
          </NotesContainer>
        </>
      );
    }
    
    if (normal.length === 0 && pinned.length !== 0) {
      return (
        <>
          <div className="allNotes__notes-type">
            Pinned Notes <span>({pinned.length})</span>
          </div>
          <NotesContainer>
            {pinned.map((note) => (
              <NoteCard key={note.id} note={note} type="notes" />
            ))}
          </NotesContainer>
        </>
      );
    }

    if (normal.length !== 0 && pinned.length !== 0) {
      return (
        <>
          <div>
              <div className="allNotes__notes-type">
                Pinned Notes <span>({pinned.length})</span>
              </div>
              <NotesContainer>
                {pinned.map((note) => (
                  <NoteCard key={note.id} note={note} type="notes" />
                ))}
              </NotesContainer>
          </div>
          <div>
              <div className="allNotes__notes-type">
                All Notes <span>({normal.length})</span>
              </div>
              <NotesContainer>
                {normal.map((note) => (
                  <NoteCard key={note.id} note={note} type="notes" />
                ))}
              </NotesContainer>
          </div>
        </>
      );
    }

    if (normal.length === 0 && pinned.length === 0) {
      return (
        <>
          <p>저장된 노트가 없습니다.</p>
        </>
      );
    }


}

sort() 속성 중 하나의 값을 기준으로 정렬하기

mdn web docs-Array.prototype.sort()

var items = [
  { name: "Edward", value: 21 },
  { name: "Sharpe", value: 37 },
  { name: "And", value: 45 },
  { name: "The", value: -12 },
  { name: "Magnetic", value: 13 },
  { name: "Zeros", value: 37 },
];

// value 기준으로 정렬 (최신방법)
items.sort(function (a, b) {
  if (a.value > b.value) {
    return 1;
  }
  if (a.value < b.value) {
    return -1;
  }
  // a must be equal to b
  return 0;
});

// value 기준으로 정렬 (이전방법)
items.sort((a,b) => a.value - b.value)); // value가 작은것부터 큰순서
items.sort((a,b) => b.value - a.value)); // value가 큰것부터 작은순서

mdn docs를 찾아봤는데 sort방법이 바뀌어서 콘솔에 찍어봤더니... return값으로 정렬이 되는듯하다... 일단 강의대로 이전방법을 써볼것이다.

filter과 정렬

getAllNotes.tsx

const filteredNotes = (notes: Note[], filter: string) => {
    const lowPriority = notes.filter(({ priority }) => priority === "low");
    const highPriority = notes.filter(({ priority }) => priority === "high");

    if(filter === "low") {
        return [...lowPriority, ...highPriority];
    } else if(filter === "high") {
        return [...highPriority, ...lowPriority];
    } else if(filter === "latest") {
        return notes.sort((a, b) => b.createdTime - a.createdTime);
    } else if(filter === "created") {
        return notes.sort((a, b) => a.createdTime - b.createdTime);
    } else if(filter === "edited") {
        const editedNotes = notes.filter(({ editedTime })=> editedTime); // editedTime있는것
        const normalNotes = notes.filter(({ editedTime })=> !editedTime); // 없는것

        const sortEdited = editedNotes.sort((a, b) => ((b.editedTime as number) - (a.editedTime as number)));
        return [...sortEdited, ...normalNotes];
    } else {
        return notes;
    }
}

const getAllNotes = (mainNotes: Note[], filter: string) => {

    const pinned = mainNotes.filter(({ isPinned }) => isPinned); // isPinned true인 notes들만
    const normal = mainNotes.filter(({ isPinned }) => !isPinned); // isPinned false

    if (normal.length !== 0 && pinned.length === 0) {
      return (
        <>
          <div className="allNotes__notes-type">
            All Notes <span>({normal.length})</span>
          </div>
          <NotesContainer>
            {filteredNotes(normal, filter).map((note) => (
              <NoteCard key={note.id} note={note} type="notes" />
            ))}
          </NotesContainer>
        </>
      );
    }
    
    if (normal.length === 0 && pinned.length !== 0) {
      return (
        <>
          <div className="allNotes__notes-type">
            Pinned Notes <span>({pinned.length})</span>
          </div>
          <NotesContainer>
            {filteredNotes(pinned, filter).map((note) => (
              <NoteCard key={note.id} note={note} type="notes" />
            ))}
          </NotesContainer>
        </>
      );
    }

    if (normal.length !== 0 && pinned.length !== 0) {
      return (
        <>
          <div>
            <div className="allNotes__notes-type">
              Pinned Notes <span>({pinned.length})</span>
            </div>
            <NotesContainer>
              {filteredNotes(pinned, filter).map((note) => (
                <NoteCard key={note.id} note={note} type="notes" />
              ))}
            </NotesContainer>
          </div>
          <div>
            <div className="allNotes__notes-type">
              All Notes <span>({normal.length})</span>
            </div>
            <NotesContainer>
              {filteredNotes(normal, filter).map((note) => (
                <NoteCard key={note.id} note={note} type="notes" />
              ))}
            </NotesContainer>
          </div>
        </>
      );
    }

    if (normal.length === 0 && pinned.length === 0) {
      return (
        <>
          <p>저장된 노트가 없습니다.</p>
        </>
      );
    }


}

Archive 페이지 생성하기

src > pages > ArchiveNotes

리스트 페이지

ArchiveNotes.tsx

const ArchiveNotes = () => {
  const { archiveNotes } = useAppSelector((state) => state.notesList);

  return (
    <Container>
      {archiveNotes.length === 0 ? (
        <EmptyMsgBox>노트가 없습니다.</EmptyMsgBox>
      ) : (
        <MainWrapper 
          notes={archiveNotes}
          type="archive"
        />
      )}
    </Container>
  )
}

MainWrapper에서 note map으로 뿌려주기

src > components > MainWrapper.tsx

interface MainWrapperProps {
  notes: Note[];
  type: string;
}
const MainWrapper = ({ notes, type }: MainWrapperProps) => {
  return (
    <NotesContainer>
      {notes.map((note) => (
        <NoteCard 
          key={note.id}
          note={note}
          type={type}
        />
      ))}
    </NotesContainer>
  )
}

Trash 페이지 생성하기

src > pages > TrashNotes.tsx
TrashNotes 페이지는 ArchiveNotes와 똑같다.

const TrashNotes = () => {
  const { trashNotes } = useAppSelector((state) => state.notesList);

  return (
    <Container>
      {trashNotes.length === 0 ? (
        <EmptyMsgBox>노트가 없습니다.</EmptyMsgBox>
      ) : (
        <MainWrapper 
          notes={trashNotes}
          type="trash"
        />
      )}
    </Container>
  )
}

TagNote 페이지 생성하기

태그이름을 useParams에서 받아와서
태그이름이 같은 것만 모아서 새로운 notes에 push하고
그 notes를 뿌려준다.
src > pages >TagNotes.tsx

const TagNotes = () => {
  const { name } = useParams() as {name: string}; // App.tsx에서 지정한 route에 parameter 이름을 name으로 지정함
  const { mainNotes } = useAppSelector((state) => state.notesList);

  let notes: Note[] = [];
  mainNotes.forEach((note) => {
    if(note.tags.find(({tag}) => tag === name)) {
      notes.push(note);
    }
  })
  return (
    <Container>
      {notes.length === 0 ? (
        <EmptyMsgBox>노트가 없습니다.</EmptyMsgBox>
      ) : (
        <MainWrapper 
          notes={notes}
          type={name}
        />
      )}
    </Container>
  )
}
profile
FE DEV/디블리셔

0개의 댓글

관련 채용 정보