Skip to content

Commit

Permalink
Feat: 리스트 상세페이지 안쪽 레이아웃 퍼블리싱 (#15)
Browse files Browse the repository at this point in the history
* Design : 리스트 상세페이지 내부의 rankList 컴포넌트 레이아웃 생성

* Design : 리스트 상세페이지 내부의 rankList 컴포넌트 스타일 적용

* Fix : 리스트 상세페이지 내부의 rankList 컴포넌트 스타일 및 타입 수정

* Design : 리스트 상세페이지 내부의 rankList 컴포넌트 스타일 수정

* Design : 리스트 상세페이지 내부의 rankList 컴포넌트에 바텀시트 추가

* Chore : open-graph 라이브러리 설치

* Chore : open-graph 라이브러리 타입 설치

* Chore : cheerio 라이브러리 설치

* feat : 링크 미리보기 기능 일부 구현
(백엔드와 협의 필요)

* style : 링크 미리보기 코드 수정

* Design : rankList 스타일 변경

* Feature : 비디오 임베드 추가 및 스타일 변경

* Style : 주석 수정

* Chore : copy-to-clipboard 라이브러리 다운로드

* Feature : 링크 다운로드 기능 구현

* Chore : react-toastify 다운로드

* Feature : 토스트 구현

* Chore : html-to-image 라이브러리 다운로드

* Feature : 리스트상세의 이미지 저장 기능 구현 초안

* Feature : 카카오톡 공유기 초안

* Feature : OG태그 불러오는 로직을 프록시로 이동

* Fix : 목데이터 제거 및 코드 정리

* Fix : 프록시 설정
개발중이기에 localhost:3000 으로 적었으나 이후 배포서버도메인으로 수정하겠습니다.

* Fix : 컴포넌트명 변경
추후 Dropdown 컴포넌트가 생길것으로 예상되어 SelectComponent 로 변경했습니다.

* Fix : 컴포넌트명 변경
추후 Dropdown 컴포넌트가 생길것으로 예상되어 SelectComponent 로 변경했습니다.

* Style : css파일 컨벤션에 맞춰 수정 및 폴더 구조 변경

* fix : layout.tsx에 카카오 sdk, toastContainer 추가

* fix : 카카오톡 공유 템플릿 변경

* fix : 링크 미리보기 수정
일부 og:url 태그가 없는 경우 링크 이동이 안되는 것을 방지하기 위해 인자로받은 linkUrl 사용하도록 수정

* Design : rankList css 수정

* fix : img태그 -> Image로 수정

* Chore : yarn.lock 커밋

* fix : img태그 -> Image태그

* fix : 사용하지 않는 파일 삭제

* fix : Image -> img

* Design : 나현님 css에맞춰 수정

* Style : 불필요한 console.log 삭제

* Style : import 순서 변경

* Style : CI/CD에서 잡아준 에러로 수정했습니다.

* Style : TODO 주석 생성

* Style : CI/CD 에러 수정

* Style : 오타 수정

* Style : TODO 주석 생성

* Style : CI/CD에러 수정
  • Loading branch information
kanglocal authored Feb 5, 2024
1 parent 62ae89d commit c3b990f
Show file tree
Hide file tree
Showing 29 changed files with 1,264 additions and 10 deletions.
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "temp",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:3000",
"scripts": {
"dev": "next dev",
"build": "next build",
Expand Down Expand Up @@ -31,12 +32,19 @@
"@vanilla-extract/next-plugin": "^2.3.2",
"@yaireo/tagify": "^4.19.0",
"axios": "^1.6.5",
"cheerio": "^1.0.0-rc.12",
"copy-to-clipboard": "^3.3.3",
"html-to-image": "^1.11.11",
"http-proxy-middleware": "^2.0.6",
"next": "14.0.4",
"open-graph": "^0.2.6",
"react": "^18",
"react-beautiful-dnd": "^13.1.1",
"react-dom": "^18",
"react-hook-form": "^7.50.0",
"react-scripts": "^5.0.1",
"react-select": "^5.8.0",
"react-toastify": "^10.0.4",
"zustand": "^4.4.7"
},
"devDependencies": {
Expand All @@ -51,6 +59,7 @@
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/jest": "^29.5.11",
"@types/node": "^20",
"@types/open-graph": "^0.2.5",
"@types/react": "^18",
"@types/react-beautiful-dnd": "^13.1.8",
"@types/react-dom": "^18",
Expand Down
4 changes: 2 additions & 2 deletions public/icons/check_red.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/collect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/icons/crown.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/etc.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/icons/share.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { keyframes, style } from '@vanilla-extract/css';

export const backGround = style({
position: 'fixed',
top: 0,
left: 0,
bottom: 0,
right: 0,
background: 'rgba(0,0,0,0.3)',
zIndex: 999,
});

export const wrapper = style({
padding: '37px 0 43px',

position: 'fixed',
bottom: 0,
left: 0,
right: 0,

display: 'flex',
flexDirection: 'column',
justifyContent: 'center',

backgroundColor: '#ffffff',
borderTopLeftRadius: '25px',
borderTopRightRadius: '25px',

transitionProperty: 'all',
transitionDuration: '0.2s',
});

const slideIn = keyframes({
from: { transform: 'translateY(100%)' },
to: { transform: 'translateY(0)' },
});

export const sheetActive = style({
animation: `${slideIn} 0.2s ease-in-out`,
});

export const sheetItemWrapper = style({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',

':hover': {
backgroundColor: '#EFEFF0',
},
});

export const checkIcon = style({
display: 'none',
marginRight: '28px',

selectors: {
[`${sheetItemWrapper}:hover &`]: {
display: 'block',
},
},
});

export const sheetItem = style({
width: '100%',
fontSize: '1.4rem',
cursor: 'pointer',
padding: '2.5rem 2.8rem 2.5rem',

selectors: {
[`${sheetItemWrapper}:hover &`]: {
color: '#FF5454',
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { style } from '@vanilla-extract/css';

export const container = style({
width: '100%',

display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
});

export const shareAndOthers = style({
width: '100%',

display: 'flex',
flexDirection: 'row',
justifyContent: 'right',
alignItems: 'center',
gap: '20px',
});

export const buttonComponent = style({
cursor: 'pointer',
});
143 changes: 143 additions & 0 deletions src/app/[userNickname]/[listId]/_components/ListDetailInner/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
'use client';

import { useParams, useRouter } from 'next/navigation';
import { MouseEvent, useState } from 'react';
import BottomSheet from '@/app/[userNickname]/[listId]/_components/BottomSheet/BottomSheet';
import ModalPortal from '@/components/ModalPortal';
import saveImageFromHtml from '@/lib/utils/saveImageFromHtml';
import copyUrl from '@/lib/utils/copyUrl';
import toasting from '@/lib/utils/toasting';
import kakaotalkShare from '@/components/KakaotalkShare/kakaotalkShare';
import * as styles from './Footer.css';
import CollectIcon from '/public/icons/collect.svg';
import ShareIcon from '/public/icons/share.svg';
import EtcIcon from '/public/icons/etc.svg';

interface BottomSheetOptionsProps {
key: string;
title: string;
onClick: () => void;
}

interface SheetTypeProps {
type: 'share' | 'etc';
}

interface FooterProps {
category: string;
listId: string;
title: string;
description: string;
items: [];
collaborators: [];
ownerNickname: string;
}

function Footer({ data }: { data: FooterProps }) {
const router = useRouter();
const params = useParams<{ userNickname: string; listId: string }>();

const [isSheetActive, setSheetActive] = useState<boolean>(false);
const [sheetOptionList, setSheetOptionList] = useState<BottomSheetOptionsProps[]>([]);

const handleSheetOptionList = ({ type }: SheetTypeProps) => {
const listUrl = `${process.env.NEXT_PUBLIC_BASE_URL}/${params?.userNickname}/${params?.listId}`;

if (type === 'share') {
const optionList = [
{
key: 'copyLink',
title: '리스트 링크 복사하기',
onClick: () => {
copyUrl(listUrl);
setSheetActive(false);
},
},
{
key: 'kakaoShare',
title: '리스트 카카오톡으로 공유하기',
onClick: () => {
// TODO: image로 저장한다음에 해당 image를 보내줘야한다.
kakaotalkShare({
title: data.title,
description: data.description,
image:
'https://i.namu.wiki/i/-8Iah6PGZzzQuY1KtJIbj8_KBbX4whnbaq8AYShoqphdJOpfJDskZZ2Y3bU2I5Jpnx8aRi1LXTz1_e0v_fMrp172modjOmKRcxcME5dmM6IDAIgqktw5yIs75is2CgC1GrGoxZPwxpeTXudKIxWn2w.webp',
listItem: data.items,
collaborators: data.collaborators,
listId: data.listId,
userNickname: data.ownerNickname,
});
setSheetActive(false);
},
},
];
setSheetOptionList([...optionList]);
return;
}

if (type === 'etc') {
const optionList = [
{
key: 'saveToImg',
title: '리스트 이미지로 저장하기',
onClick: () => {
setSheetActive(false);
saveImageFromHtml({ filename: `${data.category}_${data.listId}` });
},
},
{
key: 'copyAndCreateList',
title: '이 리스트 템플릿으로 바로 리스트 작성하기',
onClick: () => {
toasting({ type: 'default', txt: '리스트 작성 페이지로 이동합니다.' });
router.push(`/create?title=${data.title}&category=${data.category}`);
},
},
];
setSheetOptionList([...optionList]);
return;
}
};

const handleSheetActive = ({ type }: SheetTypeProps) => {
handleSheetOptionList({ type });
setSheetActive((prev: boolean) => !prev);
};

const handleOutsideClick = (e: MouseEvent) => {
if (e.target === e.currentTarget) {
setSheetActive(false);
}
};

// TODO: 콜렉트 API생성되면 요청보내고 UI변경시키기
const handleCollect = () => {
console.log('콜렉트기능 미구현');
};

return (
<>
{isSheetActive && (
<ModalPortal>
<BottomSheet onClose={handleOutsideClick} isActive={isSheetActive} optionList={sheetOptionList} />
</ModalPortal>
)}
<div className={styles.container}>
<div className={styles.buttonComponent}>
<CollectIcon onClick={handleCollect} />
</div>
<div className={styles.shareAndOthers}>
<div className={styles.buttonComponent} onClick={() => handleSheetActive({ type: 'share' })}>
<ShareIcon />
</div>
<div className={styles.buttonComponent} onClick={() => handleSheetActive({ type: 'etc' })}>
<EtcIcon />
</div>
</div>
</div>
</>
);
}

export default Footer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { style } from '@vanilla-extract/css';

export const container = style({
width: '100%',

display: 'flex',
justifyContent: 'right',
alignItems: 'center',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as styles from './Header.css';
import SelectComponent from '@/components/SelectComponent/SelectComponent';

interface OptionsProps {
value: string;
label: string;
}

interface HeaderProps {
handleChangeListType: (target: OptionsProps) => void | undefined;
}

const dropdownOptions = [
{
value: 'simple',
label: '간단히',
},
{
value: 'detail',
label: '자세히',
},
];
function Header({ handleChangeListType }: HeaderProps) {
return (
<div className={styles.container}>
<SelectComponent name="listType" options={dropdownOptions} isSearchable={false} onChange={handleChangeListType} />
</div>
);
}

export default Header;
Loading

0 comments on commit c3b990f

Please sign in to comment.