Skip to content

Commit

Permalink
feature-060: 카드 컴포넌트 리팩토링
Browse files Browse the repository at this point in the history
  • Loading branch information
gs0428 committed Feb 13, 2024
1 parent 2b2510c commit f7a9940
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 115 deletions.
6 changes: 3 additions & 3 deletions src/components/Home/InsightVideos.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { InsightVideosContainer } from '@/styles/HomepageStyle';
import Card from '../category/Card';
import { cardDummy } from '../category/Card';
import { IVideoProps } from '../category/Card';

interface InsightVideosProps {
username: string;
Expand All @@ -13,8 +13,8 @@ const InsightVideos: React.FC<InsightVideosProps> = ({
popularHashtags,
}) => {
const formattedHashtags = popularHashtags.map((tag) => '#' + tag);
const [categoryItems] = useState<cardDummy[]>([]);
const [checkedItems, setCheckedItems] = useState<boolean[]>([]);
const [categoryItems] = useState<IVideoProps[]>([]);
const [checkedItems, setCheckedItems] = useState<number[]>([]);

return (
<InsightVideosContainer>
Expand Down
90 changes: 31 additions & 59 deletions src/components/category/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React, { useEffect, useState } from 'react';
import VideoTag from '../common/videoTag';
import React, { useEffect } from 'react';
import * as CardStyles from '@/styles/category/Card.style';

export interface cardDummy {
export interface IVideoProps {
video_id: number;
category_id: number;
title: string;
Expand All @@ -15,87 +14,60 @@ export interface cardDummy {
}

interface ICardProps {
videos: cardDummy[];
checkedVideos: boolean[];
setCheckedVideos: (value: boolean[]) => void;
videos: IVideoProps[];
checkedVideos: number[];
setCheckedVideos: (value: number[]) => void;
}

const Card: React.FC<ICardProps> = ({
videos,
checkedVideos,
setCheckedVideos,
}) => {
const [isShadow, setIsShadow] = useState<boolean[]>(new Array(6).fill(false));
useEffect(() => {}, [checkedVideos]);

useEffect(() => {
if (checkedVideos.includes(true)) {
// 1개 이상 클릭 시 모든 hover event 활성화
setIsShadow(isShadow.map(() => true));
} else if (!isShadow.includes(false)) {
//모든 hover 활성화, 모든 체크 비활성화 시 모든 hover 활성화 제거
setIsShadow(isShadow.map(() => false));
}
}, [checkedVideos]);

const handleMouseEnter = (id: number) => {
const prev = checkedVideos.includes(true)
? [...isShadow]
: new Array(isShadow.length).fill(false);
// 체크박스 미선택 이동 시 isshadow 중복 작동으로 인해 방식 변경
prev[id] = true;
setIsShadow(prev);
};

const handleMouseLeave = (id: number) => {
if (!checkedVideos.includes(true)) {
// 선택되면 유지
const prev = [...isShadow];
prev[id] = false;
setIsShadow(prev);
const handleCheckBox = (videoId: number) => {
if (checkedVideos.includes(videoId)) {
setCheckedVideos(checkedVideos.filter((id) => id !== videoId));
} else {
setCheckedVideos([...checkedVideos, videoId]);
}
};

const checkBoxHandler = (id: number) => {
const prev = [...checkedVideos];
prev[id] = !prev[id];
setCheckedVideos(prev);
};
return (
<CardStyles.Container>
{videos.map((video, idx) => (
<CardStyles.Wrap
key={`${video.title}-wrap`}
onMouseEnter={() => handleMouseEnter(idx)}
onMouseLeave={() => handleMouseLeave(idx)}
>
<CardStyles.Image
src={video.image}
alt="썸네일 이미지"
key={`${video.title}-image`}
/>
{isShadow[idx] && (
<CardStyles.CheckBox
type="checkbox"
checked={checkedVideos[idx]}
onChange={() => checkBoxHandler(idx)}
/>
)}
<CardStyles.Content key={`${video.title}-card-content`}>
{videos.map((video) => (
<CardStyles.Wrap key={`${video.title}-wrap`}>
<CardStyles.Image source={video.image}>
<CardStyles.CheckBoxWrap
className={checkedVideos.length > 0 ? 'activated' : ''}
>
<CardStyles.CheckBox
type="checkbox"
checked={checkedVideos.includes(video.video_id)}
onChange={() => handleCheckBox(video.video_id)}
/>
</CardStyles.CheckBoxWrap>
</CardStyles.Image>

<CardStyles.Content
to={`/summary/${video.video_id}`}
key={`${video.title}-card-content`}
>
<CardStyles.Title key={`${video.title}`}>
{video.title}
</CardStyles.Title>
<CardStyles.Summary key={`${video.description}`}>
{video.description}
</CardStyles.Summary>
<CardStyles.ChipWrap key={`${video.title}-chip-wrap`}>
{video.tag.map((tag) => (
{/* {video.tag.map((tag) => (
<VideoTag
content={`# ${tag.name}`}
color={'gray400'}
typography="Caption1"
key={`${video.title}-${tag.name}`}
/>
))}
))} */}
</CardStyles.ChipWrap>
</CardStyles.Content>
</CardStyles.Wrap>
Expand Down
37 changes: 17 additions & 20 deletions src/pages/CategoryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import GarbageSvg from '@/assets/icons/garbage.svg?react';
import FolderSvg from '@/assets/icons/open-file.svg?react';
import CloseSvg from '@/assets/icons/close.svg?react';
import * as CategoryPageStyles from '@/styles/category/index.style';
import Card from '@/components/category/Card';
import Card, { IVideoProps } from '@/components/category/Card';
import axiosInstance from '@/apis/config/instance';
import { useRecoilValue } from 'recoil';
import { categoryState } from '@/stores/category';
Expand All @@ -18,9 +18,9 @@ const CategoryPage = () => {
const params = useParams();
const [name, setName] = useState('');
const [menus, setMenus] = useState<ISubFolderProps[]>([]);
const [videos, setVideos] = useState([]);
const [videos, setVideos] = useState<IVideoProps[]>([]);
const [recentRegisterMode, setRecentRegisterMode] = useState(false);
const [checkedVideos, setCheckedVideos] = useState<boolean[]>([]);
const [checkedVideos, setCheckedVideos] = useState<number[]>([]);
const categories = useRecoilValue(categoryState);

const toggleRecentRegisterMode = () =>
Expand All @@ -47,41 +47,38 @@ const CategoryPage = () => {
}, [categories, params.top_folder]);

const allCheckBtnHandler = () => {
if (checkedVideos.includes(false)) {
setCheckedVideos(checkedVideos.map(() => true));
if (checkedVideos.length === videos.length) {
// 삭제 API 요청
console.log('모두 삭제');
setCheckedVideos([]);
} else {
// 모두 삭제
console.log('모두 선택');
setCheckedVideos(videos.map((video) => video.video_id));
}
};

const dirMoveHanlder = () => {
checkedVideos.map((value, id) => {
if (value === true) {
console.log('이동해야할 index : ', id);
}
});
console.log(checkedVideos);
};

const garbageHandler = () => {
checkedVideos.map((value, id) => {
if (value === true) {
console.log('삭제해야할 index : ', id);
}
});
console.log(checkedVideos);
};

return (
<CategoryPageStyles.Container>
<CategoryTitle name={name} totalVideos={videos.length} />
<CategoryPageStyles.MenuWrap>
{checkedVideos.includes(true) ? (
{checkedVideos.length > 0 ? (
<>
<div>
<CategoryPageStyles.AllSelectBtn onClick={allCheckBtnHandler}>
{!checkedVideos.includes(false) ? '모두 삭제' : '모두 선택'}
{checkedVideos.length === videos.length
? '모두 삭제'
: '모두 선택'}
</CategoryPageStyles.AllSelectBtn>
<CategoryPageStyles.SelectedCount>
{checkedVideos.filter((bool) => bool === true).length}개 선택
{checkedVideos.length}개 선택
</CategoryPageStyles.SelectedCount>
</div>
<CategoryPageStyles.CardManagement>
Expand All @@ -101,7 +98,7 @@ const CategoryPage = () => {
width={28}
height={28}
onClick={() => {
setCheckedVideos(checkedVideos.map(() => false));
setCheckedVideos([]);
}}
/>
</CategoryPageStyles.ManagementBox>
Expand Down
84 changes: 51 additions & 33 deletions src/styles/category/Card.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,45 @@ import styled from 'styled-components';
import theme from '../theme';
import checkIcon from '@/assets/icons/check.svg';
import checkedIcon from '@/assets/icons/checked.svg';
import { Link } from 'react-router-dom';

export const CheckBoxWrap = styled.div`
background-color: rgba(0, 0, 0, 0.5);
display: none;
flex-direction: row-reverse;
width: 100%;
height: 100%;
&.activated {
display: flex;
}
`;

export const CheckBox = styled.input`
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 24px;
height: 24px;
border-radius: 100%;
background-color: ${theme.color.gray300};
background-image: url(${checkIcon});
background-repeat: no-repeat;
background-position: center;
border: 1.5px solid ${theme.color.white};
color: ${theme.color.white};
margin: 12px;
&:checked {
border: 1.5px solid ${theme.color.green300};
background-color: ${theme.color.green300};
background-image: url(${checkedIcon});
background-repeat: no-repeat;
background-position: center;
}
`;
export const Container = styled.div`
display: grid;
grid-template-columns: repeat(3, auto);
Expand All @@ -17,9 +55,16 @@ export const Wrap = styled.div`
border-radius: 16px;
overflow: hidden;
box-shadow: 0px 4px 40px 0px rgba(0, 0, 0, 0.05);
&:hover {
${CheckBoxWrap} {
display: flex;
}
}
`;

export const Content = styled.div`
export const Content = styled(Link)`
text-decoration: none;
display: flex;
flex-direction: column;
padding: 24px 20px;
Expand All @@ -42,36 +87,9 @@ export const ChipWrap = styled.div`
flex-wrap: wrap;
`;

export const CheckBox = styled.input`
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background-color: ${theme.color.gray300};
background-image: url(${checkIcon});
background-repeat: no-repeat;
background-position: center;
border: 1.5px solid ${theme.color.white};
color: ${theme.color.white};
position: absolute;
margin-left: 252px;
margin-top: 13px;
&:checked {
border: 1.5px solid ${theme.color.green300};
background-color: ${theme.color.green300};
background-image: url(${checkedIcon});
background-repeat: no-repeat;
background-position: center;
}
`;

export const Image = styled.img`
&:hover {
filter: brightness(50%);
}
export const Image = styled.div<{ source: string }>`
background-image: url(${(props) => props.source});
width: 290px;
height: 163px;
background-size: 100%;
`;

0 comments on commit f7a9940

Please sign in to comment.