diff --git a/components/Button/Button.styled.ts b/components/Button/Button.styled.ts index 0bf173229..f63a1270d 100644 --- a/components/Button/Button.styled.ts +++ b/components/Button/Button.styled.ts @@ -8,7 +8,7 @@ const buttonSize = { lg: '40', }; -export const Cta = styled.span` +export const Cta = styled.button` cursor: pointer; text-decoration: none; display: flex; diff --git a/components/Button/Button.tsx b/components/Button/Button.tsx index e79ffc861..9f3257bc5 100644 --- a/components/Button/Button.tsx +++ b/components/Button/Button.tsx @@ -6,9 +6,5 @@ export interface ButtonProps extends ButtonHTMLAttributes { } export function Button({ children, size }: ButtonProps) { - return ( - <> - {children} - - ); + return {children}; } diff --git a/components/Card/Card.styled.ts b/components/Card/Card.styled.ts index 673fabe42..25faf06e2 100644 --- a/components/Card/Card.styled.ts +++ b/components/Card/Card.styled.ts @@ -24,7 +24,7 @@ export const ItemImg = styled.div<{ image: string }>` background-position: center; &:hover { - background-size: 130%; + background-size: 150%; } `; @@ -56,7 +56,6 @@ export const StarIcon = styled.img` position: absolute; top: 1.5rem; right: 1.5rem; - z-index: 10; `; export const ItemInfo = styled.div` @@ -88,6 +87,7 @@ export const ItemDescription = styled.p` overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + padding-top: 0.2rem; a { text-decoration: none; diff --git a/components/Card/Card.tsx b/components/Card/Card.tsx index f6ba0626d..ec2731f4a 100644 --- a/components/Card/Card.tsx +++ b/components/Card/Card.tsx @@ -1,79 +1,71 @@ -import { useEffect, useRef, useState } from 'react'; +import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import { changeDate, calculateDate } from '../../util/util'; import * as S from './Card.styled'; import KebabMenu from '../KebabMenu/KebabMenu'; -import { Link } from '../../hooks/useGetFolder'; +import { LinkData } from '../../hooks/useGetFolder'; import Image from 'next/image'; +import Link from 'next/link'; -function Card({ item }: { item: Link }) { +function Card({ + item, + setUrl, +}: { + item: LinkData; + setUrl: Dispatch>; +}) { const [createdAt, setCreatedAt] = useState({ time: 0, result: '' }); const [fullDate, setFullDate] = useState(''); const { image_source } = item; - const [kebabView, setKebaView] = useState(false); + const [kebabView, setKebabView] = useState(false); const [like, setLike] = useState(false); - const kebabRef = useRef(null); - - const { url, description } = item; + const { url, description, id } = item; const createdText = `${createdAt.time} ${createdAt.result} ago`; useEffect(() => { const nowDate = new Date(); - let createdate = new Date(item.created_at); + const createdate = new Date(item.created_at); const date = (Number(nowDate) - Number(createdate)) / 1000; setCreatedAt(calculateDate(date)); setFullDate(changeDate(createdate)); }, [item]); - useEffect(() => { - function handleClickOutside(e: any) { - if ( - kebabView && - kebabRef.current && - !kebabRef.current.contains(e.target) - ) { - setKebaView(false); - } - } - - document.addEventListener('mousedown', handleClickOutside); - return () => { - document.removeEventListener('mousedown', handleClickOutside); - }; - }, [kebabView]); - return ( - - { - setLike(!like); - }} - /> - {image_source ? ( - - ) : ( - - 빈 이미지 - - )} - - setKebaView(!kebabView)} + + + { + setLike(!like); + e.preventDefault(); + }} /> - {createdText} - - + {image_source ? ( + + ) : ( + + 빈 이미지 + + )} + + { + setKebabView(!kebabView); + e.preventDefault(); + }} + /> + {createdText} + {description ? description : url} - - - {fullDate} - - {kebabView && } - + + {fullDate} + + {kebabView && } + + ); } diff --git a/components/ContentsContainer.tsx b/components/ContentsContainer.tsx index ae9905786..fd8b487b1 100644 --- a/components/ContentsContainer.tsx +++ b/components/ContentsContainer.tsx @@ -2,12 +2,14 @@ import { ReactNode } from 'react'; import styled from 'styled-components'; const Container = styled.div<{ $empty: number }>` + min-height: 40rem; gap: 2rem; display: grid; grid-template-columns: ${(props) => props.$empty > 0 ? 'repeat(3, 1fr)' : 'none'}; margin: 0 auto; position: relative; + margin-bottom: 10rem; @media (max-width: 1199px) { grid-template-columns: ${(props) => (props.$empty > 0 ? '1fr 1fr' : '1fr')}; diff --git a/components/FolderButton/FolderButton.styled.ts b/components/FolderButton/FolderButton.styled.ts index 8882bc8a2..ca1d95894 100644 --- a/components/FolderButton/FolderButton.styled.ts +++ b/components/FolderButton/FolderButton.styled.ts @@ -15,6 +15,11 @@ export const FolderName = styled.span<{ $select: string | boolean }>` white-space: nowrap; color: ${(props) => (props.$select === 'select' ? '#fff' : '#000')}; + &:hover { + background-color: var(--Primary); + color: #fff; + } + @media (max-width: 768px) { font-size: 1.2rem; } diff --git a/components/FolderButton/FolderButton.tsx b/components/FolderButton/FolderButton.tsx index 557126e20..cc0c2bbcb 100644 --- a/components/FolderButton/FolderButton.tsx +++ b/components/FolderButton/FolderButton.tsx @@ -4,22 +4,21 @@ import { Folder } from '@/hooks/useGetFolderList'; function FolderButton({ item, - setFolderId, - setFolderName, isSelected, handleMenuClick, index, + setOnSelect, }: { item: Folder; - setFolderId: React.Dispatch>; - setFolderName: React.Dispatch>; isSelected: string; handleMenuClick: (index: number) => void; index: number; + setOnSelect: React.Dispatch< + React.SetStateAction<{ id: number; name: string }> + >; }) { const changeFolder = () => { - setFolderId(item.id); - setFolderName(item.name); + setOnSelect({ id: item.id, name: item.name }); handleMenuClick(index); }; diff --git a/components/FolderButtonContainer/FolderButtonContainer.styled.ts b/components/FolderButtonContainer/FolderButtonContainer.styled.ts index 31fdef72a..ca41d0b61 100644 --- a/components/FolderButtonContainer/FolderButtonContainer.styled.ts +++ b/components/FolderButtonContainer/FolderButtonContainer.styled.ts @@ -12,7 +12,8 @@ export const FolderButtons = styled.div` display: flex; align-items: center; gap: 1.2rem; - flex-wrap: wrap; + overflow: auto; + width: 100%; `; export const TotalFolderButton = styled(FolderName)``; diff --git a/components/FolderButtonContainer/FolderButtonContainer.tsx b/components/FolderButtonContainer/FolderButtonContainer.tsx index d8eb9a372..dcb0a95d9 100644 --- a/components/FolderButtonContainer/FolderButtonContainer.tsx +++ b/components/FolderButtonContainer/FolderButtonContainer.tsx @@ -3,19 +3,21 @@ import * as S from './FolderButtonContainer.styled'; import FolderButton from '../FolderButton/FolderButton'; import { useModal } from '../../contexts/ModalContext'; import { Folders } from '../../hooks/useGetFolderList'; +import ModalPortal from '@/Portal'; +import AddFolderModal from '../Modal/AddFolderModal/AddFolderModal'; function FolderButtonContainer({ link, - setFolderName, - setFolderId, + setOnSelect, }: { link: Folders; - setFolderName: React.Dispatch>; - setFolderId: React.Dispatch>; + setOnSelect: React.Dispatch< + React.SetStateAction<{ id: number; name: string }> + >; }) { const [linkSelected, setLinkSelected] = useState([]); const [totalBtn, setTotalBtn] = useState(true); - const { openModal } = useModal(); + const { modalState, openModal } = useModal(); const handleMenuClick = (index: number) => { const booleanArr: string[] = new Array(link.length).fill('white'); @@ -27,8 +29,7 @@ function FolderButtonContainer({ const handleClickTotalButton = () => { const totalArr: string[] = new Array(link.length).fill('white'); setLinkSelected(totalArr); - setFolderId(0); - setFolderName(''); + setOnSelect({ id: 0, name: '' }); setTotalBtn(true); }; @@ -46,8 +47,7 @@ function FolderButtonContainer({ + {modalState.addFolder && ( + + + + )} ); } diff --git a/components/FolderModalContainer/FolderModals.styled.ts b/components/FolderModalContainer/FolderModals.styled.ts new file mode 100644 index 000000000..c903fa94f --- /dev/null +++ b/components/FolderModalContainer/FolderModals.styled.ts @@ -0,0 +1,16 @@ +import styled from 'styled-components'; + +export const FolderModal = styled.div` + display: flex; + align-items: center; + gap: 1.2rem; +`; + +export const FolderModalIcon = styled.div` + cursor: pointer; + display: flex; + align-items: center; + gap: 0.4rem; + color: var(--Linkbrary-gray60); + font-size: 1.4rem; +`; diff --git a/components/FolderModalContainer/FolderModals.tsx b/components/FolderModalContainer/FolderModals.tsx new file mode 100644 index 000000000..31548cb44 --- /dev/null +++ b/components/FolderModalContainer/FolderModals.tsx @@ -0,0 +1,62 @@ +import Image from 'next/image'; +import { ReactNode } from 'react'; +import * as S from './FolderModals.styled'; +import { useModal } from '@/contexts/ModalContext'; +import ModalPortal from '@/Portal'; +import ShareModal from '../Modal/ShareModal/ShareModal'; +import EditModal from '../Modal/EditModal/EditModal'; +import DeleteModal from '../Modal/DeleteModal/DeleteModal'; + +function FolderIcon({ + image, + children, + onOpen, + state, +}: { + image: string; + children: ReactNode; + onOpen: (modalName: string) => void; + state: string; +}) { + return ( + onOpen(`${state}`)}> + {`${image}`} + {children} + + ); +} + +function FolderModals({ id, name }: { id: number; name: string }) { + const { modalState, openModal, closeModal } = useModal(); + + return ( + + + 공유 + + + 이름 변경 + + + 삭제 + + {modalState.share && ( + + + + )} + {modalState.edit && ( + + + + )} + {modalState.delete && ( + + + + )} + + ); +} + +export default FolderModals; diff --git a/components/Footer/Footer.styled.ts b/components/Footer/Footer.styled.ts index 1eee3a5de..49547aae2 100644 --- a/components/Footer/Footer.styled.ts +++ b/components/Footer/Footer.styled.ts @@ -3,7 +3,7 @@ import { Description } from '../MainSectionCard/MainSectionCard.styled'; export const Footer = styled.div` width: 100%; - margin: 6rem auto 0; + margin: auto 0; background: var(--Footer-black); padding: 3.2rem 10.4rem 6.4rem; color: #cfcfcf; @@ -26,7 +26,7 @@ export const Footer__menu = styled.div` justify-content: space-between; `; -export const FooterDescrption = styled(Description)` +export const FooterDescription = styled(Description)` width: fit-content; @media (max-width: 768px) { diff --git a/components/Footer/Footer.tsx b/components/Footer/Footer.tsx index 0f50028b1..5ea60cd3d 100644 --- a/components/Footer/Footer.tsx +++ b/components/Footer/Footer.tsx @@ -17,9 +17,9 @@ function Footer() { return ( - ©codeit - 2023 + ©codeit - 2023 -

Privacy Polic

+

Privacy Policy

FAQ

diff --git a/components/Input/Input.styled.ts b/components/Input/Input.styled.ts index 5cdb80a6e..ceb6baed5 100644 --- a/components/Input/Input.styled.ts +++ b/components/Input/Input.styled.ts @@ -1,5 +1,4 @@ import styled from 'styled-components'; -import { InputProps } from './Input'; const inputSize = { sm: '28', @@ -7,22 +6,37 @@ const inputSize = { lg: '50', }; -const InputModal = styled.input` - display: flex; - width: ${({ size }) => inputSize[size]}rem; - padding: 1.8rem 1.5rem; - justify-content: center; - align-items: center; - border-radius: 0.8rem; - border: 1px solid - var(${({ error }) => (error ? '--ErrorMessage' : '--Linkbrary-gray20')}); - background: var(--Section-white); - font-size: 1.6rem; - line-height: 2.4rem; +export const InputModal = styled.div<{ + size: 'sm' | 'md' | 'lg'; + $error: boolean; +}>` + input { + display: flex; + width: ${({ size }) => inputSize[size]}rem; + padding: 1.8rem 1.5rem; + justify-content: center; + align-items: center; + border: 0.1rem solid + var(${({ $error }) => ($error ? '--ErrorMessage' : '--Linkbrary-gray20')}); + border-radius: 0.8rem; + background: var(--Section-white); + font-size: 1.6rem; + } &:focus { border: 1px solid var(--Primary); } `; -export default InputModal; +export const TextArea = styled.div` + height: 3rem; + width: 100%; + display: flex; + align-items: center; +`; + +export const WarningMessage = styled.p` + color: var(--ErrorMessage); + font-size: 1.4rem; + margin: 0; +`; diff --git a/components/Input/Input.tsx b/components/Input/Input.tsx index 50bfe8489..595cf78af 100644 --- a/components/Input/Input.tsx +++ b/components/Input/Input.tsx @@ -1,27 +1,38 @@ -import { ChangeEvent, HtmlHTMLAttributes } from 'react'; -import InputModal from './Input.styled'; +import { + ControllerRenderProps, + FieldError, + FieldValues, +} from 'react-hook-form'; +import * as S from './Input.styled'; +import { useEffect, useState } from 'react'; -export interface InputProps extends HtmlHTMLAttributes { +export interface InputProps { + placeholder: string; type: string; - onChange: (e: ChangeEvent) => void; - error: string; size: 'sm' | 'md' | 'lg'; + field: ControllerRenderProps; + id: string; + error: FieldError | undefined; } -function Input({ id, placeholder, type, onChange, error, size }: InputProps) { +function Input({ placeholder, type, size, field, error }: InputProps) { + const [errorMessage, setErrorMessage] = useState(false); + + useEffect(() => { + if (error) { + setErrorMessage(true); + } else { + setErrorMessage(false); + } + }, [error]); + return ( - <> - - + + + + {error && {error.message}} + + ); } diff --git a/components/KebabMenu/KebabMenu.tsx b/components/KebabMenu/KebabMenu.tsx index 12cb828d6..f0b162db8 100644 --- a/components/KebabMenu/KebabMenu.tsx +++ b/components/KebabMenu/KebabMenu.tsx @@ -1,23 +1,46 @@ -import React from 'react'; +import React, { Dispatch, SetStateAction, useRef } from 'react'; import { useModal } from '@/contexts/ModalContext'; import * as S from './KebabMenu.styled'; +import ModalPortal from '@/Portal'; +import DeleteLinkModal from '../Modal/DeleteLinkModal/DeleteLinkModal'; function KebabMenu({ - menuRef, + url, + id, + setUrl, }: { - menuRef: React.RefObject; + url: string; + id: number; + setUrl: Dispatch>; }) { - const { openModal } = useModal(); + const kebabRef = useRef(null); + const { modalState, openModal } = useModal(); + + const handleAddKebab = (e: React.MouseEvent) => { + e.preventDefault(); + openModal('add'); + setUrl(url); + }; + + const handleDeleteKebab = ( + e: React.MouseEvent + ) => { + e.preventDefault(); + openModal('deleteLink'); + }; return ( - - openModal('deleteLink')}> - 삭제하기 - - openModal('add')}> - 폴더에 추가 - - + <> + + 삭제하기 + 폴더에 추가 + + {modalState.deleteLink && ( + + + + )} + ); } diff --git a/components/Layout/Layout.tsx b/components/Layout/Layout.tsx deleted file mode 100644 index 81c990ea0..000000000 --- a/components/Layout/Layout.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React, { ReactNode } from 'react'; -import Footer from '../Footer/Footer'; -import Nav from '../Nav/Nav'; - -function Layout({ children }: { children: ReactNode }) { - return ( - <> -