Skip to content

Commit

Permalink
feat: 프로젝트 검색 기능 추가 / 업로드 버튼 플로팅으로 변경 (#1279)
Browse files Browse the repository at this point in the history
* feat: 프로젝트 이름 검색 추가

* feat: 업로드 버튼 플로팅으로 변경
  • Loading branch information
juno7803 authored Jan 24, 2024
1 parent c287637 commit 404777e
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 42 deletions.
91 changes: 49 additions & 42 deletions src/components/projects/main/ProjectList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,44 @@ import { ImpressionArea } from '@toss/impression-area';
import { uniqBy as _uniqBy } from 'lodash-es';
import Link from 'next/link';

import Responsive from '@/components/common/Responsive';
import Text from '@/components/common/Text';
import useEventLogger from '@/components/eventLogger/hooks/useEventLogger';
import ProjectCard from '@/components/projects/main/ProjectCard';
import useGetProjectListQuery from '@/components/projects/upload/hooks/useGetProjectListQuery';
import { playgroundLink } from '@/constants/links';
import IconPen from '@/public/icons/icon-pen.svg';
import IconPlusWhite from '@/public/icons/icon-plus-black.svg';
import { MOBILE_MEDIA_QUERY } from '@/styles/mediaQuery';
import { textStyles } from '@/styles/typography';
import ProjectSearch from '@/components/projects/main/ProjectSearch';
import { StringParam, useQueryParam, withDefault } from 'use-query-params';
import { useDebounce } from '@toss/react';
import { fonts } from '@sopt-makers/fonts';
import { useState } from 'react';

const ProjectList = () => {
const [query, setQuery] = useQueryParam('name', withDefault(StringParam, undefined));
const [value, setValue] = useState(query);
const debouncedChangeName = useDebounce(setQuery, 300);
const { data, isLoading, fetchNextPage } = useGetProjectListQuery({
limit: 20,
name: query,
});
const { logClickEvent } = useEventLogger();

return (
<StyledContainer>
<StyledContent>
<TopWrapper>
<Title as='h1' typography='SUIT_32_B'>
✨ 솝트에서 진행된 프로젝트 둘러보기
</Title>
<Responsive only='desktop'>
<ProjectUploadButton
href={playgroundLink.projectUpload()}
onClick={() =>
logClickEvent('projectUpload', {
referral: 'projectPage',
})
}
>
<IconPlusWhite />
<Text typography='SUIT_18_B'>내 프로젝트 올리기</Text>
</ProjectUploadButton>
</Responsive>
</TopWrapper>
<ProjectSearch
value={value ?? query}
onValueChange={(value) => {
setValue(value);
debouncedChangeName(value);
}}
placeholder='프로젝트 검색'
/>

<LengthWrapper>
{data?.pages && <StyledLength typography='SUIT_18_M'>{data.pages[0].totalCount}개의 프로젝트</StyledLength>}
{data?.pages && <StyledLength typography='SUIT_18_M'>전체 {data.pages[0].totalCount}</StyledLength>}
<ProjectMobileUploadButton
onClick={() =>
logClickEvent('projectUpload', {
Expand All @@ -66,6 +64,10 @@ const ProjectList = () => {
</StyledGridContainer>
)}
</StyledContent>
<ProjectUploadButton href={playgroundLink.projectUpload()}>
<PlusIcon />
프로젝트 올리기
</ProjectUploadButton>
</StyledContainer>
);
};
Expand All @@ -92,21 +94,6 @@ const StyledContent = styled.div`
}
`;

const Title = styled(Text)`
${textStyles.SUIT_32_B};
@media ${MOBILE_MEDIA_QUERY} {
margin: 0 6px;
${textStyles.SUIT_20_B};
}
`;

const TopWrapper = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
`;

const ProjectMobileUploadButton = styled(Link)`
display: none;
@media ${MOBILE_MEDIA_QUERY} {
Expand All @@ -118,34 +105,54 @@ const ProjectMobileUploadButton = styled(Link)`

const ProjectUploadButton = styled(Link)`
display: flex;
gap: 12px;
position: fixed;
right: 60px;
bottom: 80px;
gap: 8px;
align-items: center;
border-radius: 10px;
border-radius: 18px;
background-color: ${colors.gray10};
padding: 18px 24px 18px 20px;
padding: 14px 30px 14px 27px;
color: ${colors.gray950};
${fonts.TITLE_20_SB};
&:hover {
background-color: ${colors.gray50};
}
@media ${MOBILE_MEDIA_QUERY} {
right: 16px;
bottom: 16px;
padding: 12px;
${fonts.LABEL_16_SB}
}
`;

const PlusIcon = () => (
<svg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M10.9208 2.58751C10.9208 2.07966 10.5091 1.66797 10.0013 1.66797C9.49345 1.66797 9.08176 2.07966 9.08176 2.58751V9.08176H2.58751C2.07966 9.08176 1.66797 9.49345 1.66797 10.0013C1.66797 10.5091 2.07966 10.9208 2.58751 10.9208H9.08176V17.4151C9.08176 17.9229 9.49345 18.3346 10.0013 18.3346C10.5091 18.3346 10.9208 17.9229 10.9208 17.4151V10.9208H17.4151C17.9229 10.9208 18.3346 10.5091 18.3346 10.0013C18.3346 9.49345 17.9229 9.08176 17.4151 9.08176H10.9208V2.58751Z'
fill='#0F0F12'
/>
</svg>
);

const LengthWrapper = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 48px;
margin-top: 20px;
@media ${MOBILE_MEDIA_QUERY} {
margin: 30px 6px 0;
}
`;

const StyledLength = styled(Text)`
${textStyles.SUIT_18_M};
${fonts.BODY_16_M};
@media ${MOBILE_MEDIA_QUERY} {
${textStyles.SUIT_16_M};
${fonts.BODY_14_M};
}
`;

Expand Down
70 changes: 70 additions & 0 deletions src/components/projects/main/ProjectSearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { MOBILE_MEDIA_QUERY } from '@/styles/mediaQuery';

import styled from '@emotion/styled';
import { colors } from '@sopt-makers/colors';
import { fonts } from '@sopt-makers/fonts';
import { Flex, width100 } from '@toss/emotion-utils';

interface ProjectSearchProps {
defaultValue?: string;
value?: string;
onValueChange?: (value: string) => void;
placeholder?: string;
}

const ProjectSearch = ({ value, defaultValue, onValueChange, placeholder }: ProjectSearchProps) => {
return (
<Container align='center' justify='space-between'>
<Input
placeholder={placeholder}
defaultValue={defaultValue}
value={value}
onChange={(e) => onValueChange?.(e.target.value)}
/>
<Icon />
</Container>
);
};

export default ProjectSearch;

const Container = styled(Flex)`
gap: 8px;
border-radius: 8px;
background-color: ${colors.gray800};
padding: 16px 14px;
${width100}
@media ${MOBILE_MEDIA_QUERY} {
border-radius: 6px;
padding: 9px 18px;
${fonts.BODY_14_M};
}
`;

const Input = styled.input`
color: ${colors.gray200};
${width100};
${fonts.BODY_16_M};
::placeholder {
${colors.gray200};
}
`;

const Icon = () => (
<svg width='23' height='23' viewBox='0 0 23 23' fill='none' xmlns='http://www.w3.org/2000/svg'>
<circle
cx='9'
cy='9'
r='7.75'
transform='matrix(-1 0 0 1 18 0.5)'
stroke='#808087'
stroke-width='2.5'
stroke-linecap='round'
stroke-linejoin='round'
/>
<path d='M21 21.5L15 15.5' stroke='#808087' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' />
</svg>
);

0 comments on commit 404777e

Please sign in to comment.