From 5a411a6dcf2fb3611659d195acb1395abc453d29 Mon Sep 17 00:00:00 2001 From: JJongBin Date: Mon, 21 Mar 2022 13:03:31 +0900 Subject: [PATCH 001/171] feat: button event name of prop edit - feedback event -> clickEvent --- src/components/Button/index.tsx | 6 +++--- src/components/ReviewWrite/index.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index c080941..282fb62 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -6,7 +6,7 @@ interface ButtonChecker { color: string; disabled?: boolean; forwardRef?: any; - event?: () => void; + clickEvent?: () => void; } const Button = ({ @@ -15,11 +15,11 @@ const Button = ({ color, disabled, forwardRef, - event, + clickEvent, }: ButtonChecker) => { return ( { color={theme.colors.white} // disabled forwardRef={disabledRef} - event={check} + clickEvent={check} > 리뷰 올리기 From 7515245e5fbc43641901baf52a45e4a815c5e40b Mon Sep 17 00:00:00 2001 From: JJongBin Date: Mon, 21 Mar 2022 19:10:10 +0900 Subject: [PATCH 002/171] feat: success db insert --- src/components/ReviewWrite/index.tsx | 30 ++++++++++++++++++++-------- src/firebase/request.ts | 30 ++++++++++++++++++++++++++++ src/firebase/type.ts | 9 +++++++++ 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/components/ReviewWrite/index.tsx b/src/components/ReviewWrite/index.tsx index 7ca6a92..4fca1f0 100644 --- a/src/components/ReviewWrite/index.tsx +++ b/src/components/ReviewWrite/index.tsx @@ -19,9 +19,10 @@ import { faPlus, faXmark } from '@fortawesome/free-solid-svg-icons'; import { Button } from '@/components'; import theme from '@/styles/theme'; import { useState, useEffect, useRef, forwardRef } from 'react'; +import { getReviewDocs, postReviewDocs } from '@/firebase/request'; const ReviewWrite = () => { - const [selectScore, setSelectScore] = useState('good'); + const [selectScore, setSelectScore] = useState(null); const [prevSelectScore, setPrevSelectScore] = useState(null); const disabledRef = useRef(null); @@ -75,11 +76,24 @@ const ReviewWrite = () => { URL.revokeObjectURL(fileName); }; - const check = () => { - console.log('fileImage', fileImage); - console.log('selectScore', selectScore); + const check = async () => { const text = document.querySelector('textarea')?.value; - console.log('text', text); + + postReviewDocs({ + postId: '222', + userId: '111', + date: '2022-03-24', + images: fileImage, + text: text || '', + score: +(selectScore || 0), + }); + }; + + // 리뷰 db에서 불러오기 + const reviewLoad = () => { + getReviewDocs().then((res) => { + console.log(res); + }); }; return ( @@ -92,7 +106,7 @@ const ReviewWrite = () => {
  • @@ -102,7 +116,7 @@ const ReviewWrite = () => {
  • @@ -112,7 +126,7 @@ const ReviewWrite = () => {
  • diff --git a/src/firebase/request.ts b/src/firebase/request.ts index 3797859..764387f 100644 --- a/src/firebase/request.ts +++ b/src/firebase/request.ts @@ -3,9 +3,11 @@ import { collection, DocumentData, CollectionReference, + addDoc, } from 'firebase/firestore'; import { db } from '@/firebase'; import { Posts } from './type'; +import { Reviews } from './type'; const createCollection = (collectionName: string) => { return collection(db, collectionName) as CollectionReference; @@ -18,3 +20,31 @@ export const getPostDocs = async () => { const postData = postDocs.docs.map((x) => x.data()); return postData; }; + +const reviewsCol = createCollection('reviews'); +export const getReviewDocs = async () => { + const reviewDocs = await getDocs(reviewsCol); + const reviewData = reviewDocs.docs.map((x) => x.data()); + return reviewData; +}; + +export const postReviewDocs = async ({ + userId, + postId, + date, + score, + images, + text, +}: Reviews) => { + // Add a new document with a generated id. + + const docRef = await addDoc(collection(db, 'reviews'), { + userId, + postId, + date, + score, + images, + text, + }); + console.log('Document written with ID: ', docRef.id); +}; diff --git a/src/firebase/type.ts b/src/firebase/type.ts index 8e4ab99..8458098 100644 --- a/src/firebase/type.ts +++ b/src/firebase/type.ts @@ -4,3 +4,12 @@ export interface Posts { name: string; score: number; } + +export interface Reviews { + userId: string; + postId: string; + date: string; + score: number; + images: Array; + text: string; +} From b9306db464a7ca3ce9c38a91273fd3320cc24bf2 Mon Sep 17 00:00:00 2001 From: JJongBin Date: Mon, 21 Mar 2022 22:53:16 +0900 Subject: [PATCH 003/171] feat: firebase storage image insert success but! not done loading inserted images --- src/components/ReviewWrite/index.tsx | 33 +++++++++++++++++----------- src/firebase/request.ts | 15 +++++++++++-- src/firebase/type.ts | 2 +- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/components/ReviewWrite/index.tsx b/src/components/ReviewWrite/index.tsx index 4fca1f0..3ea6dbf 100644 --- a/src/components/ReviewWrite/index.tsx +++ b/src/components/ReviewWrite/index.tsx @@ -19,7 +19,7 @@ import { faPlus, faXmark } from '@fortawesome/free-solid-svg-icons'; import { Button } from '@/components'; import theme from '@/styles/theme'; import { useState, useEffect, useRef, forwardRef } from 'react'; -import { getReviewDocs, postReviewDocs } from '@/firebase/request'; +import { getReviewDocs, postReviewDocs, postImage } from '@/firebase/request'; const ReviewWrite = () => { const [selectScore, setSelectScore] = useState(null); @@ -61,32 +61,39 @@ const ReviewWrite = () => { }; //파일 미리볼 url을 저장해줄 state - const [fileImage, setFileImage] = useState>([]); + const [fileImage, setFileImage] = useState>([]); + const [fileImageSrc, setFileImageSrc] = useState>([]); // 파일 저장 const saveFileImage = (e: any) => { - setFileImage([...fileImage, URL.createObjectURL(e.target.files[0])]); + console.log(e.target.files[0]); + + // setFileImage([...fileImage, URL.createObjectURL(e.target.files[0])]); + setFileImage([...fileImage, e.target.files[0]]); + setFileImageSrc([...fileImageSrc, URL.createObjectURL(e.target.files[0])]); e.target.value = ''; }; // 파일 삭제 const deleteFileImage = (e: any) => { const fileName = e.currentTarget.parentNode.parentNode.dataset.id; - setFileImage([...fileImage].filter((file: any) => fileName !== file)); + setFileImageSrc([...fileImageSrc].filter((file: any) => fileName !== file)); URL.revokeObjectURL(fileName); }; const check = async () => { const text = document.querySelector('textarea')?.value; - postReviewDocs({ - postId: '222', - userId: '111', - date: '2022-03-24', - images: fileImage, - text: text || '', - score: +(selectScore || 0), - }); + const images = fileImage.map((file) => postImage(file)); + + // await postReviewDocs({ + // postId: '222', + // userId: '111', + // date: '2022-03-24', + // images: images, + // text: text || '', + // score: +(selectScore || 0), + // }); }; // 리뷰 db에서 불러오기 @@ -140,7 +147,7 @@ const ReviewWrite = () => { > - {fileImage.map((file, idx) => { + {fileImageSrc.map((file, idx) => { return ( diff --git a/src/firebase/request.ts b/src/firebase/request.ts index 764387f..aadcded 100644 --- a/src/firebase/request.ts +++ b/src/firebase/request.ts @@ -8,6 +8,7 @@ import { import { db } from '@/firebase'; import { Posts } from './type'; import { Reviews } from './type'; +import { getStorage, ref, uploadBytes } from 'firebase/storage'; const createCollection = (collectionName: string) => { return collection(db, collectionName) as CollectionReference; @@ -36,8 +37,6 @@ export const postReviewDocs = async ({ images, text, }: Reviews) => { - // Add a new document with a generated id. - const docRef = await addDoc(collection(db, 'reviews'), { userId, postId, @@ -48,3 +47,15 @@ export const postReviewDocs = async ({ }); console.log('Document written with ID: ', docRef.id); }; + +// storage (이미지) +const storage = getStorage(); + +export const postImage = async (file: any) => { + const storageRef = ref(storage, file.name); + + await uploadBytes(storageRef, file).then((snapshot) => { + console.log('Uploaded a blob or file!'); + console.log(storageRef); + }); +}; diff --git a/src/firebase/type.ts b/src/firebase/type.ts index 8458098..8f9b138 100644 --- a/src/firebase/type.ts +++ b/src/firebase/type.ts @@ -10,6 +10,6 @@ export interface Reviews { postId: string; date: string; score: number; - images: Array; + images: Array; text: string; } From e3fc88c64402a05555447b326cc34102329596c6 Mon Sep 17 00:00:00 2001 From: ywc8851 Date: Tue, 22 Mar 2022 12:50:19 +0900 Subject: [PATCH 004/171] feat: #108 profileIcon position by scroll length --- src/components/Header/Header.styled.tsx | 7 ++++--- src/components/Header/index.tsx | 1 + src/components/ProfileIcon/ProfileIcon.styled.tsx | 9 +++++++-- src/components/ProfileIcon/index.tsx | 9 +++++++-- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/Header/Header.styled.tsx b/src/components/Header/Header.styled.tsx index 531d3d3..b3f9ce1 100644 --- a/src/components/Header/Header.styled.tsx +++ b/src/components/Header/Header.styled.tsx @@ -11,14 +11,15 @@ export const StyledHeader = styled.header` display: flex; align-items: center; box-sizing: border-box; - box-shadow: 0 4px 11px rgb(0 0 0 / 10%); + border-bottom: ${({ isMain, theme }) => + isMain ? 'none' : `${theme.colors.gray900}`}; + box-shadow: ${({ isMain }) => + isMain ? 'none' : '0 4px 11px rgb(0 0 0 / 10%)'}; position: fixed; width: 100%; top: 0; left: 0; z-index: 900; - border-bottom: 0; - box-shadow: none; background-color: ${({ isScroll, isMain }) => isMain ? (isScroll ? 'white' : 'transparent') : 'white'}; diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index f99969a..852fa3c 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -107,6 +107,7 @@ const Header = () => { )} diff --git a/src/components/ProfileIcon/ProfileIcon.styled.tsx b/src/components/ProfileIcon/ProfileIcon.styled.tsx index e04fee5..b5fde27 100644 --- a/src/components/ProfileIcon/ProfileIcon.styled.tsx +++ b/src/components/ProfileIcon/ProfileIcon.styled.tsx @@ -1,17 +1,22 @@ import styled from '@emotion/styled'; import { css } from '@emotion/react'; -export const ModalContainer = styled.div` +interface modalContainerProps { + scroll: number; +} + +export const ModalContainer = styled.div` width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; + position: relative; > div { position: absolute; right: 20px; - top: 70px; + top: ${({ scroll }) => `${scroll + 70}px`}; width: 320px; height: 534px; diff --git a/src/components/ProfileIcon/index.tsx b/src/components/ProfileIcon/index.tsx index e035915..dcd4b48 100644 --- a/src/components/ProfileIcon/index.tsx +++ b/src/components/ProfileIcon/index.tsx @@ -18,9 +18,14 @@ import { modalActions } from '@/store/modal/modal-slice'; interface ProfileIconProps { onClickToggleModal: () => void; isLogin: boolean; + scroll: number; } -const ProfileIcon = ({ onClickToggleModal, isLogin }: ProfileIconProps) => { +const ProfileIcon = ({ + onClickToggleModal, + isLogin, + scroll, +}: ProfileIconProps) => { const dispatch = useAppDispatch(); const { isSocialModalOpen } = useAppSelector(({ modal }) => modal); @@ -79,7 +84,7 @@ const ProfileIcon = ({ onClickToggleModal, isLogin }: ProfileIconProps) => { }; return ( - +
      From acbf1aba65f3ee4b4843111232006380f638edac Mon Sep 17 00:00:00 2001 From: ywc8851 Date: Tue, 22 Mar 2022 12:53:57 +0900 Subject: [PATCH 005/171] style : add main header box-shadow when scrolling --- src/components/Header/Header.styled.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Header/Header.styled.tsx b/src/components/Header/Header.styled.tsx index b3f9ce1..6502fe4 100644 --- a/src/components/Header/Header.styled.tsx +++ b/src/components/Header/Header.styled.tsx @@ -13,8 +13,12 @@ export const StyledHeader = styled.header` box-sizing: border-box; border-bottom: ${({ isMain, theme }) => isMain ? 'none' : `${theme.colors.gray900}`}; - box-shadow: ${({ isMain }) => - isMain ? 'none' : '0 4px 11px rgb(0 0 0 / 10%)'}; + box-shadow: ${({ isMain, isScroll }) => + isMain + ? isScroll + ? '0 4px 11px rgb(0 0 0 / 10%)' + : 'none' + : '0 4px 11px rgb(0 0 0 / 10%)'}; position: fixed; width: 100%; top: 0; From 9d29a3e2d03fc69b98a65e1105fc09860caaaa4f Mon Sep 17 00:00:00 2001 From: JJongBin Date: Tue, 22 Mar 2022 14:14:58 +0900 Subject: [PATCH 006/171] feat: reviews and images insert into firebase --- src/components/ReviewWrite/index.tsx | 80 ++++++++++++++++++---------- src/firebase/request.ts | 16 ++++-- 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/src/components/ReviewWrite/index.tsx b/src/components/ReviewWrite/index.tsx index 3ea6dbf..e6ee384 100644 --- a/src/components/ReviewWrite/index.tsx +++ b/src/components/ReviewWrite/index.tsx @@ -18,13 +18,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faPlus, faXmark } from '@fortawesome/free-solid-svg-icons'; import { Button } from '@/components'; import theme from '@/styles/theme'; -import { useState, useEffect, useRef, forwardRef } from 'react'; -import { getReviewDocs, postReviewDocs, postImage } from '@/firebase/request'; +import { useState, useRef } from 'react'; +import { postReviewDocs, postImage } from '@/firebase/request'; +import { useAppSelector } from '@/store/hooks'; const ReviewWrite = () => { + const userId = useAppSelector(({ auth }) => auth.status.uid); const [selectScore, setSelectScore] = useState(null); const [prevSelectScore, setPrevSelectScore] = useState(null); const disabledRef = useRef(null); + const textRef = useRef(null); + const fileRef = useRef(null); const selectScoreHandler = (e: any) => { changeScore(e.currentTarget); @@ -60,18 +64,21 @@ const ReviewWrite = () => { : true; }; - //파일 미리볼 url을 저장해줄 state + // 이미지 미리보기 할 url을 저장해줄 state const [fileImage, setFileImage] = useState>([]); const [fileImageSrc, setFileImageSrc] = useState>([]); // 파일 저장 const saveFileImage = (e: any) => { - console.log(e.target.files[0]); - - // setFileImage([...fileImage, URL.createObjectURL(e.target.files[0])]); - setFileImage([...fileImage, e.target.files[0]]); - setFileImageSrc([...fileImageSrc, URL.createObjectURL(e.target.files[0])]); - e.target.value = ''; + if (fileRef.current?.files) { + // console.log(fileRef.current?.files[0]); + setFileImage([...fileImage, fileRef.current?.files[0]]); + setFileImageSrc([ + ...fileImageSrc, + URL.createObjectURL(fileRef.current?.files[0]), + ]); + e.target.value = ''; + } }; // 파일 삭제 @@ -81,27 +88,42 @@ const ReviewWrite = () => { URL.revokeObjectURL(fileName); }; - const check = async () => { - const text = document.querySelector('textarea')?.value; - - const images = fileImage.map((file) => postImage(file)); - - // await postReviewDocs({ - // postId: '222', - // userId: '111', - // date: '2022-03-24', - // images: images, - // text: text || '', - // score: +(selectScore || 0), - // }); + const craeteReview = async () => { + // 현재 시간 + const today = new Date(); + const date = `${today.getFullYear()}-${ + today.getMonth() + 1 + }-${today.getDate()}-${today.getHours()}:${today.getMinutes()}:${today.getSeconds()}`; + + // 이미지 + const images = await Promise.all( + fileImage.map(async (file) => await postImage(file)), + ); + + // text + const text = textRef.current?.value || ''; + + // 점수 + const score = +(selectScore || 0); + + // firebase insert + await postReviewDocs({ + // 현재 포스트의 아이디를 넣어주자 + postId: '222', + userId, + date, + images, + text, + score, + }); }; // 리뷰 db에서 불러오기 - const reviewLoad = () => { - getReviewDocs().then((res) => { - console.log(res); - }); - }; + // const reviewLoad = () => { + // getReviewDocs().then((res) => { + // console.log(res); + // }); + // }; return ( @@ -142,6 +164,7 @@ const ReviewWrite = () => { @@ -169,6 +192,7 @@ const ReviewWrite = () => { type="file" accept="image/*" onChange={saveFileImage} + ref={fileRef} /> @@ -182,7 +206,7 @@ const ReviewWrite = () => { color={theme.colors.white} // disabled forwardRef={disabledRef} - clickEvent={check} + clickEvent={craeteReview} > 리뷰 올리기 diff --git a/src/firebase/request.ts b/src/firebase/request.ts index aadcded..203cd73 100644 --- a/src/firebase/request.ts +++ b/src/firebase/request.ts @@ -8,6 +8,7 @@ import { import { db } from '@/firebase'; import { Posts } from './type'; import { Reviews } from './type'; +import { getErrorMessage } from '@/utils'; import { getStorage, ref, uploadBytes } from 'firebase/storage'; const createCollection = (collectionName: string) => { @@ -52,10 +53,15 @@ export const postReviewDocs = async ({ const storage = getStorage(); export const postImage = async (file: any) => { - const storageRef = ref(storage, file.name); + try { + const storageRef = ref(storage, file.name); - await uploadBytes(storageRef, file).then((snapshot) => { - console.log('Uploaded a blob or file!'); - console.log(storageRef); - }); + await uploadBytes(storageRef, file).then((snapshot) => { + console.log('Uploaded a blob or file!'); + }); + + return file.name; + } catch (e) { + console.log(getErrorMessage('파일이 정상적으로 업로드되지 않았습니다.')); + } }; From 8cc7f9d0051f91b7da7688f65810035a37d1d1ee Mon Sep 17 00:00:00 2001 From: ywc8851 Date: Tue, 22 Mar 2022 14:53:35 +0900 Subject: [PATCH 007/171] feat: #107 add search back modal state to redux --- src/store/modal/modal-slice.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/store/modal/modal-slice.ts b/src/store/modal/modal-slice.ts index 04f1f1e..5aee537 100644 --- a/src/store/modal/modal-slice.ts +++ b/src/store/modal/modal-slice.ts @@ -3,17 +3,27 @@ import { createSlice } from '@reduxjs/toolkit'; const initialState = { isSocialModalOpen: false, isOverlayModalOpen: false, + isSearchBackModalOpen: false, }; +const handleBodyOverflow = (element: boolean) => { + document.body.style.overflow = element ? 'hidden' : 'unset'; +}; const modalSlice = createSlice({ name: 'modal', initialState, reducers: { handleSocialModal(state) { state.isSocialModalOpen = !state.isSocialModalOpen; + handleBodyOverflow(state.isSocialModalOpen); }, handleOverlayModal(state) { state.isOverlayModalOpen = !state.isOverlayModalOpen; + handleBodyOverflow(state.isOverlayModalOpen); + }, + handleSearchBackModal(state) { + state.isSearchBackModalOpen = !state.isSearchBackModalOpen; + handleBodyOverflow(state.isSearchBackModalOpen); }, }, }); From c4a918d3a47a88d0236837f7ce4d62ced8d88b8c Mon Sep 17 00:00:00 2001 From: ywc8851 Date: Tue, 22 Mar 2022 14:54:30 +0900 Subject: [PATCH 008/171] style : change components z-index --- src/components/Header/Header.styled.tsx | 2 +- src/components/Modal/Modal.styled.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Header/Header.styled.tsx b/src/components/Header/Header.styled.tsx index 6502fe4..b9aca72 100644 --- a/src/components/Header/Header.styled.tsx +++ b/src/components/Header/Header.styled.tsx @@ -23,7 +23,7 @@ export const StyledHeader = styled.header` width: 100%; top: 0; left: 0; - z-index: 900; + z-index: 100; background-color: ${({ isScroll, isMain }) => isMain ? (isScroll ? 'white' : 'transparent') : 'white'}; diff --git a/src/components/Modal/Modal.styled.tsx b/src/components/Modal/Modal.styled.tsx index bc290bd..a654d99 100644 --- a/src/components/Modal/Modal.styled.tsx +++ b/src/components/Modal/Modal.styled.tsx @@ -5,7 +5,7 @@ export const modalBackground = css` height: 100vh; position: fixed; top: 0; - z-index: 9997; + z-index: 200; background-color: rgba(0, 0, 0, 0.6); `; export const loginModalBackground = css` From 9118e044088c865954a7c51166a875b17dccb7dd Mon Sep 17 00:00:00 2001 From: ywc8851 Date: Tue, 22 Mar 2022 14:56:04 +0900 Subject: [PATCH 009/171] feat : #107 add search modal to header & apply redux to search Component --- src/components/Header/index.tsx | 20 +++++++++-- src/components/Portal/index.tsx | 18 ++-------- src/components/Search/SearchModal.tsx | 1 - src/components/Search/index.tsx | 51 +++++++++++++++++---------- 4 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 852fa3c..0d4c9d3 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -1,6 +1,7 @@ import { useState, useCallback, Fragment, useEffect } from 'react'; import { useLocation, Link } from 'react-router-dom'; import ProfileIcon from '@/components/ProfileIcon'; +import Portal from '@/components/Portal'; import logo from '@/assets/img/logo.svg'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -30,13 +31,19 @@ const Header = () => { useEffect(() => { window.addEventListener('scroll', updateScroll); }); + + const { isSearchBackModalOpen } = useAppSelector(({ modal }) => modal); + + const onClickToggleSearchBackModal = useCallback(() => { + dispatch(modalActions.handleSearchBackModal()); + }, [dispatch]); const onClickToggleModal = useCallback(() => { dispatch(modalActions.handleOverlayModal()); }, [dispatch]); const { pathname } = useLocation(); - console.log(pathname); - console.log(isLogin); + // console.log(pathname); + // console.log(isLogin); const updateScroll = () => { setScrollPosition(window.scrollY || document.documentElement.scrollTop); @@ -70,7 +77,14 @@ const Header = () => { - + + + {isSearchBackModalOpen && ( + + )}
      • diff --git a/src/components/Portal/index.tsx b/src/components/Portal/index.tsx index 532bfc4..bc419d5 100644 --- a/src/components/Portal/index.tsx +++ b/src/components/Portal/index.tsx @@ -1,25 +1,13 @@ import { createPortal } from 'react-dom'; -import { css } from '@emotion/react'; +import { modalBackground } from '../Modal/Modal.styled'; -const darkTheme = css` - background: rgba(0, 0, 0, 0.7); - position: absolute; - top: 0; - width: 100vw; - height: 1000vh; -`; type SetState = { - // setModalOpen: React.Dispatch>; - setModalOpen: (isOpen: boolean) => void; + setModalOpen: () => void; onClick?: () => void; }; const Background = ({ setModalOpen }: SetState) => { - const onClick = () => { - setModalOpen(false); - document.body.style.overflow = 'unset'; - }; - return
        ; + return
        ; }; const Portal = ({ setModalOpen }: SetState) => { diff --git a/src/components/Search/SearchModal.tsx b/src/components/Search/SearchModal.tsx index 033420c..f2da453 100644 --- a/src/components/Search/SearchModal.tsx +++ b/src/components/Search/SearchModal.tsx @@ -6,7 +6,6 @@ import SearchKeyword from './SearchKeyword'; const openNavBox = css` position: absolute; width: 110%; - z-index: 1000; background: white; color: black; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 4a5e325..1bf8294 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -16,9 +16,14 @@ import { spanDisplay, none, } from './SearchBox.styled'; +import { useAppDispatch, useAppSelector } from '@/store/hooks'; +import { modalActions } from '@/store/modal/modal-slice'; const SearchBox = () => { - const [modalOpen, setModalOpen] = useState(false); + const dispatch = useAppDispatch(); + const { isSearchBackModalOpen } = useAppSelector(({ modal }) => modal); + + // const [modalOpen, setModalOpen] = useState(false); const [inputValue, setInputValue] = useState(''); useEffect(() => { @@ -29,40 +34,42 @@ const SearchBox = () => { const searchInput = useRef(null); let navigate = useNavigate(); + const handleSearchBackModal = () => { + dispatch(modalActions.handleSearchBackModal()); + }; + const onSubmit = (e: any) => { e.preventDefault(); - if (!inputValue) { + + if (inputValue) { + handleSearchBackModal(); + navigate(`/search/${inputValue}`); + } else { alert('검색어를 입력 해주세요!'); - e.preventDefault(); } }; const onKeyUp = (e: any) => { if (e.key === 'Enter' && !inputValue) { alert('검색어를 입력 해주세요!'); - setModalOpen(false); - document.body.style.overflow = 'unset'; return; } if (e.key === 'Enter' || e.key === 'Escape') { if (e.key === 'Escape') { - setModalOpen(false); - document.body.style.overflow = 'unset'; + handleSearchBackModal(); return; - } - if (inputValue) { + } else if (inputValue) { console.log('Clicked key: ', e.key); + handleSearchBackModal(); navigate(`/search/${inputValue}`); } else { - setModalOpen(false); - document.body.style.overflow = 'unset'; + alert('검색어를 입력 해주세요!'); } } }; const onFocus = () => { - setModalOpen(true); - document.body.style.overflow = 'hidden'; + handleSearchBackModal(); }; const onChange = (e: any) => { @@ -74,7 +81,7 @@ const SearchBox = () => { }; return ( -
        +
        @@ -99,12 +106,20 @@ const SearchBox = () => { onKeyUp={onKeyUp} onChange={onChange} > - {modalOpen && ( - + {isSearchBackModalOpen && ( + )} - {modalOpen && } + {/* {isSearchBackModalOpen && ( + + )} */}
        - +
        From 7e71e0d367bdd23152cca51ea791f6182b49e464 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Tue, 22 Mar 2022 22:37:46 +0900 Subject: [PATCH 010/171] feat: delete don't use modules - delete styled component in SearchBox --- src/components/Search/SearchBox.styled.tsx | 7 ------- src/components/Search/index.tsx | 1 - 2 files changed, 8 deletions(-) diff --git a/src/components/Search/SearchBox.styled.tsx b/src/components/Search/SearchBox.styled.tsx index 16d4021..9426774 100644 --- a/src/components/Search/SearchBox.styled.tsx +++ b/src/components/Search/SearchBox.styled.tsx @@ -81,13 +81,6 @@ export const search__input = css` } `; -export const search__link = css` - width: 100%; - height: 100%; - - text-decoration-line: none; - color: white; -`; export const search__btn = css` width: 100%; height: 100%; diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 4a5e325..5e12f0c 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -10,7 +10,6 @@ import { searchBar__contents, search__input, search__btn, - search__link, position, findImgStyle, spanDisplay, From 40298d5a859df619ba034f18a4e8371736b72dad Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Wed, 23 Mar 2022 00:27:07 +0900 Subject: [PATCH 011/171] feat: #70 update search-keyword component - add list rendering - seperate css files --- .../Search/SearchKeyword.styled.tsx | 23 ++++++++++ src/components/Search/SearchKeyword.tsx | 42 ++++--------------- 2 files changed, 30 insertions(+), 35 deletions(-) create mode 100644 src/components/Search/SearchKeyword.styled.tsx diff --git a/src/components/Search/SearchKeyword.styled.tsx b/src/components/Search/SearchKeyword.styled.tsx new file mode 100644 index 0000000..b8957f9 --- /dev/null +++ b/src/components/Search/SearchKeyword.styled.tsx @@ -0,0 +1,23 @@ +import { css } from '@emotion/react'; + +export const keywordStyle = css` + margin-top: 10px; + display: flex; + align-items: center; + width: 100%; + font-size: 25px; + img { + opacity: 0.6; + padding: 10px; + box-sizing: content-box; + margin-right: 10px; + padding: 10px; + } + :hover { + cursor: pointer; + opacity: 0.6; + } + button { + background-color: white; + } +`; diff --git a/src/components/Search/SearchKeyword.tsx b/src/components/Search/SearchKeyword.tsx index 78a790a..a5f8726 100644 --- a/src/components/Search/SearchKeyword.tsx +++ b/src/components/Search/SearchKeyword.tsx @@ -1,50 +1,22 @@ import React from 'react'; import { useNavigate } from 'react-router-dom'; -import { css } from '@emotion/react'; +import { keywordStyle } from './SearchKeyword.styled'; import glassSolid from '@/assets/icons/glass-solid.svg'; -const keywordStyle = css` - margin-top: 10px; - display: flex; - align-items: center; - width: 100%; - font-size: 25px; - img { - opacity: 0.6; - padding: 10px; - box-sizing: content-box; - margin-right: 10px; - padding: 10px; - } - :hover { - cursor: pointer; - opacity: 0.6; - } - button { - background-color: white; - } -`; - -interface KeywordPropsType { - setModalOpen: React.Dispatch>; - // setModalOpen: (state: boolean) => void; - keyword: string; -} - -const SearchKeyword = ({ keyword, setModalOpen }: KeywordPropsType) => { - let navigate = useNavigate(); +const SearchKeyword = ({ suggest }: any) => { + const URLTEXT = suggest.replace(' ', '').trim(); + const navigate = useNavigate(); const onClick = () => { - navigate(`/search/${keyword}`); + navigate(`/search/${URLTEXT}`); }; const onKeyUp = (e: any) => { - console.log(e.key); - if (e.key === 'Escape') setModalOpen(false); + if (e.key === 'Escape') console.log('Escape'); }; return (
      • glass-solid - +
      • ); }; From 51b50481e16e87cea496df451ebe18a22617c311 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Wed, 23 Mar 2022 00:29:47 +0900 Subject: [PATCH 012/171] feat: #70 delete DOM API - DOM API to list rendering --- src/components/Search/SearchBox.styled.tsx | 59 ++++++++++- src/components/Search/SearchModal.tsx | 115 +++++---------------- 2 files changed, 83 insertions(+), 91 deletions(-) diff --git a/src/components/Search/SearchBox.styled.tsx b/src/components/Search/SearchBox.styled.tsx index 9426774..dbb86ca 100644 --- a/src/components/Search/SearchBox.styled.tsx +++ b/src/components/Search/SearchBox.styled.tsx @@ -97,9 +97,6 @@ export const search__btn = css` align-items: center; `; -export const none = css` - display: none; -`; export const spanDisplay = css` color: rgba(0, 0, 0, 0.4); margin-right: 20px; @@ -114,3 +111,59 @@ export const position = css` width: 100%; height: 100%; `; + +// SearchModal +export const openNavBox = css` + position: absolute; + width: 110%; + + z-index: 1000; + background: white; + color: black; + margin: 0 auto; + + .keyword-suggester img { + width: 20px; + height: 20px; + margin-left: 10px; + } +`; + +export const UlContainer = css` + list-style-type: none; + margin: 10px 0; + padding: 0 30px; + display: flex; + justify-content: space-between; + + li { + padding: 10px 20px; + cursor: pointer; + } + li:hover { + opacity: 0.6; + } +`; + +interface searchKeywordProps { + isSelectedMenu: string; + key: string; +} + +export const selectedMenu = css` + color: #ff7100; + border-bottom: 3px solid #ff7100; +`; + +export const None = css` + display: none; +`; + +// export const List = styled.li(({ isSelectedMenu, key }) => +// isSelectedMenu === key +// ? { +// color: '#ff7100', +// borderBottom: '3px solid #ff7100', +// } +// : { color: 'inherit', borderBottom: '3px solid inherit' }, +// ); diff --git a/src/components/Search/SearchModal.tsx b/src/components/Search/SearchModal.tsx index 033420c..83c26dc 100644 --- a/src/components/Search/SearchModal.tsx +++ b/src/components/Search/SearchModal.tsx @@ -1,47 +1,12 @@ -import React, { useState, MouseEvent } from 'react'; +import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { css } from '@emotion/react'; import SearchKeyword from './SearchKeyword'; - -const openNavBox = css` - position: absolute; - width: 110%; - - z-index: 1000; - background: white; - color: black; - margin: 0 auto; - - .keyword-suggester img { - width: 20px; - height: 20px; - margin-left: 10px; - } -`; - -const ulStyle = css` - list-style-type: none; - margin: 10px 0; - padding: 0 30px; - display: flex; - justify-content: space-between; - - li { - padding: 10px 20px; - cursor: pointer; - } - li:hover { - opacity: 0.6; - } - .search-selected { - color: #ff7100; - border-bottom: 3px solid #ff7100; - } -`; - -const none = css` - display: none; -`; +import { + UlContainer, + openNavBox, + selectedMenu, + None, +} from './SearchBox.styled'; type Display = { modalOpen: boolean; @@ -49,63 +14,37 @@ type Display = { }; const SearchModal = ({ modalOpen, setModalOpen }: Display) => { - let navigate = useNavigate(); + const searchMenuKeywords = ['추천 검색어', '인기 검색어', '최근 검색어']; + const searchSuggestKeywords = ['강남 맛집', '수원 맛집', '분당 맛집']; - const onClick = (e: any) => { - const $list = e.target.parentNode.childNodes; + const [isSelectedMenu, setIsSelectedMenu] = useState('추천 검색어'); + const navigate = useNavigate(); - if (!e.target.classList.contains('search-selected')) { - $list.forEach((elem: any) => { - if (!elem.classList) return; - if (elem.classList.contains('search-selected')) { - elem.classList.remove('search-selected'); - } - }); - } - e.target.classList.add('search-selected'); - }; - - const ulClick = ({ target }: any) => { - // 이미 fetch 받은 데이터에서 filtering 해주는 방식으로 - // console.log(e.target); + const onClick = (e: any) => { + setIsSelectedMenu(e.target.textContent); }; const onKeywordClick = ({ target }: any) => { - // 클릭된 것이 li라면 해당 textContent를 - // img일 경우 parentNode의 textContent - console.log(target); - // if (target.matches('.keyword-suggester > li')) { - // navigate(`/search/${target.textContent}`); - // } else { - // navigate(`/search/${target.parentNode.textContent}`); - // } navigate(`/search/${target.textContent}`); }; return ( -
    From f593295ef2f19f6f2edbf376ebbd6101b3414497 Mon Sep 17 00:00:00 2001 From: Minsung Kim Date: Wed, 23 Mar 2022 01:15:06 +0900 Subject: [PATCH 014/171] feat: add redux logic in search bar - delete props in Keyword and Modal components - add redux in Search Component --- public/index.html | 1 - src/components/Search/SearchKeyword.tsx | 6 +++++- src/components/Search/SearchModal.tsx | 19 ++++++++++++------- src/components/Search/index.tsx | 17 ++++------------- 4 files changed, 21 insertions(+), 22 deletions(-) diff --git a/public/index.html b/public/index.html index 07881bd..175620d 100644 --- a/public/index.html +++ b/public/index.html @@ -29,7 +29,6 @@
    -
    diff --git a/src/components/Search/SearchKeyword.tsx b/src/components/Search/SearchKeyword.tsx index a5f8726..49e1212 100644 --- a/src/components/Search/SearchKeyword.tsx +++ b/src/components/Search/SearchKeyword.tsx @@ -3,7 +3,11 @@ import { useNavigate } from 'react-router-dom'; import { keywordStyle } from './SearchKeyword.styled'; import glassSolid from '@/assets/icons/glass-solid.svg'; -const SearchKeyword = ({ suggest }: any) => { +interface PropType { + suggest: string; +} + +const SearchKeyword = ({ suggest }: PropType) => { const URLTEXT = suggest.replace(' ', '').trim(); const navigate = useNavigate(); diff --git a/src/components/Search/SearchModal.tsx b/src/components/Search/SearchModal.tsx index 83c26dc..a6ce792 100644 --- a/src/components/Search/SearchModal.tsx +++ b/src/components/Search/SearchModal.tsx @@ -8,28 +8,33 @@ import { None, } from './SearchBox.styled'; -type Display = { - modalOpen: boolean; - setModalOpen: React.Dispatch>; -}; +import { useAppDispatch, useAppSelector } from '@/store/hooks'; +import { modalActions } from '@/store/modal/modal-slice'; -const SearchModal = ({ modalOpen, setModalOpen }: Display) => { +const SearchModal = () => { const searchMenuKeywords = ['추천 검색어', '인기 검색어', '최근 검색어']; const searchSuggestKeywords = ['강남 맛집', '수원 맛집', '분당 맛집']; - const [isSelectedMenu, setIsSelectedMenu] = useState('추천 검색어'); + const { isSearchBackModalOpen } = useAppSelector(({ modal }) => modal); + + const dispatch = useAppDispatch(); const navigate = useNavigate(); + const handleSearchBackModal = () => { + dispatch(modalActions.handleSearchBackModal()); + }; + const onClick = (e: any) => { setIsSelectedMenu(e.target.textContent); }; const onKeywordClick = ({ target }: any) => { navigate(`/search/${target.textContent}`); + handleSearchBackModal(); }; return ( -