Skip to content

Commit

Permalink
Merge pull request #266 from Kernel360/fix/mainpage
Browse files Browse the repository at this point in the history
네브바 UI +  유튜브 채널 UI  Fix
  • Loading branch information
eun-hak authored May 10, 2024
2 parents 3386bb7 + 213bf7a commit fe6f7a0
Show file tree
Hide file tree
Showing 24 changed files with 632 additions and 166 deletions.
77 changes: 40 additions & 37 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@
"*.ts",
"*.tsx"
],
"rules": {}
"rules": {
"max-len": ["error", { "code": 2000 }]
}
},
{
"files": [
Expand Down Expand Up @@ -117,42 +119,43 @@
"ts": "never",
"tsx": "never"
}
], // import 구문에서 파일 확장자를 생략해야합니다. 패키지를 import할 때는 이 규칙을 무시합니다.
"import/order": [ // import 순서를 지정합니다.
"error",
{
"groups": [ // 먼저 해당 groups의 순서를 갖습니다.
"type",
"builtin",
"external",
"internal",
"parent",
"sibling",
"index",
"unknown"
],
"pathGroups": [ // 기본 groups 외 특정 순서를 지정할 수 있습니다.
{
"pattern": "react*",
"group": "external",
"position": "before"
},
{
"pattern": "@/public/images/*",
"group": "unknown",
"position": "after"
}
],
"pathGroupsExcludedImportTypes": [
"type"
], // type 종류는 pathGroups를 적용하지 않습니다.
"newlines-between": "always", // import grouping 간에 한 줄을 추가합니다.
"alphabetize": { // 같은 grouping 내에선 알파벳 순서를 따릅니다.
"order": "asc",
"caseInsensitive": true
}
}
],
],
// // import 구문에서 파일 확장자를 생략해야합니다. 패키지를 import할 때는 이 규칙을 무시합니다.
// "import/order": [ // import 순서를 지정합니다.
// "error",
// {
// "groups": [ // 먼저 해당 groups의 순서를 갖습니다.
// "type",
// "builtin",
// "external",
// "internal",
// "parent",
// "sibling",
// "index",
// "unknown"
// ],
// "pathGroups": [ // 기본 groups 외 특정 순서를 지정할 수 있습니다.
// {
// "pattern": "react*",
// "group": "external",
// "position": "before"
// },
// {
// "pattern": "@/public/images/*",
// "group": "unknown",
// "position": "after"
// }
// ],
// "pathGroupsExcludedImportTypes": [
// "type"
// ], // type 종류는 pathGroups를 적용하지 않습니다.
// "newlines-between": "always", // import grouping 간에 한 줄을 추가합니다.
// "alphabetize": { // 같은 grouping 내에선 알파벳 순서를 따릅니다.
// "order": "asc",
// "caseInsensitive": true
// }
// }
// ],
"import/no-extraneous-dependencies": [ // 아래 devDependencies 내 파일들은 해당 룰에 영향을 받지 않습니다.
"error",
{
Expand Down
13 changes: 13 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 90,
"tabWidth": 2,
"jsxBracketSameLine": false,
"objectCurlySpacing": true,
"endOfLine": "auto",
"bracketSpacing": true,
"arrowParens": "avoid",
"proseWrap": "always"
}
17 changes: 10 additions & 7 deletions next.config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
/** @type {import('next').NextConfig} */
const path = require('path')
const path = require('path');

module.exports = {
output: 'standalone',
sassOptions: {
includePaths: [path.join(__dirname, 'styles')],
},
images: {
domains: ['i.ytimg.com', 'yt3.ggpht.com'],
},
// async rewrites() {
// return [
// {
Expand All @@ -17,17 +20,17 @@ module.exports = {
webpack: (config, context) => {
if (context?.isServer) {
if (Array.isArray(config.resolve.alias)) {
config.resolve.alias.push({ name: "msw/browser", alias: false })
config.resolve.alias.push({ name: 'msw/browser', alias: false });
} else {
config.resolve.alias["msw/browser"] = false
config.resolve.alias['msw/browser'] = false;
}
} else {
if (Array.isArray(config.resolve.alias)) {
config.resolve.alias.push({ name: "msw/node", alias: false })
config.resolve.alias.push({ name: 'msw/node', alias: false });
} else {
config.resolve.alias["msw/node"] = false
config.resolve.alias['msw/node'] = false;
}
}
return config
return config;
},
}
};
61 changes: 52 additions & 9 deletions src/app/channel/page.module.scss
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
.headerTitleWrapper {
display: flex;
box-sizing: border-box;
padding: 23px var(--default-padding) 12px;
flex-direction: row;
padding: 23px var(--default-padding) 2px;

.left {
margin: 7px 10px 0 0;
}
}

.mainContainer {
position: relative;
margin-bottom: 76px;

.headerFix {
position: sticky;
z-index: 100;
top: 0;
right: 0;
left: 0;
background-color: white;
}

.channelList {
display: flex;
box-sizing: border-box;
margin: 12px 0;
padding: 0 var(--default-padding);
padding: 0 4px;
overflow: auto;
white-space: nowrap;
gap: 5px;
Expand All @@ -19,18 +35,45 @@
display: none;
}

.channelImage {
display: block;
width: 50px;
height: 50px;
border-radius: 50%;
}

.imageNone {
display: none;
}

.channelButton {
padding: 5px 10px;
padding-bottom: 10px;
transition: 0.15s all ease-out;
border: 1px solid var(--primary200);
border-radius: 15px;
color: var(--primary200);
font-size: 12px;

// border: 1px solid var(--primary200);
// border-radius: 15px;
color: var(--gray500);
font-size: 16px;

&.selected {
border-color: var(--primary400);
background-color: var(--primary400);
color: var(--white);
position: relative;

// border-bottom: 2px solid var(--primary500);
&::after {
content: "";
position: absolute;
bottom: 0;
left: 50%; // 가운데 정렬
width: 80%; // 테두리의 길이를 조정
height: 2px;
transform: translateX(-50%); // 가운데 정렬
background-color: var(--primary500);
}

// border-color: var(--primary400);
// background-color: var(--primary400);
color: var(--primary500);
}
}
}
Expand Down
114 changes: 92 additions & 22 deletions src/app/channel/page.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
/* eslint-disable react/no-array-index-key */

/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */

'use client';

import { useState } from 'react';
import { useEffect, useState } from 'react';

import classNames from 'classnames/bind';

import LeftIcon from '@/components/shared/header/headerItems/LeftIcon';
import Title from '@components/shared/title/Title';
import useYoutubeList from '@remote/queries/channel/useYoutubeList';
import BottomNav from '@shared/bottom-nav/BottomNav';
import ChannelArticle from '@shared/channel-article/ChannelArticle';

import useChannel from '@/remote/queries/channel/useChannel';
import Image from 'next/image';
import Flex from '@/components/shared/flex/Flex';
import styles from './page.module.scss';

const cx = classNames.bind(styles);

const CHANNEL_LIST = {
export const CHANNEL_LIST = {
DetailWizard: 'UCJM63e_MydEL2o6dMuJ_teQ',
ShineFreak: 'UCoqiH2Ce3qc8wr_t2GvIWvw',
autogrm: 'UCKUHhKTlNTHRlwbjoFDKOfA',
Expand All @@ -27,34 +33,98 @@ function ChannelPage() {
const [selectedChannel, setSelectedChannel] = useState(Object.values(CHANNEL_LIST)[0]);

const { data: dataList, isError } = useYoutubeList(selectedChannel);
const [scroll, setScroll] = useState(true);

const handleChannel = (channelId: string) => {
setSelectedChannel(channelId);
};

const { data: channelList1 } = useChannel('UCJM63e_MydEL2o6dMuJ_teQ');
const { data: channelList2 } = useChannel('UCoqiH2Ce3qc8wr_t2GvIWvw');
const { data: channelList3 } = useChannel('UCKUHhKTlNTHRlwbjoFDKOfA');
const { data: channelList4 } = useChannel('UCHVO8oWoMCIIdQHXUB32X1w');
const { data: channelList5 } = useChannel('UCB22mXLQeRlCPn-H000OxYg');

const channelList = [
channelList1,
channelList2,
channelList3,
channelList4,
channelList5,
];

// console.log(channelList);
const [lastScroll, setLastScroll] = useState(0);

useEffect(() => {
const checkScrollTop = () => {
const currentScroll = document.documentElement.scrollTop;
if (currentScroll > 100 && lastScroll <= 100) {
setScroll(false);
setLastScroll(currentScroll);
} else if (currentScroll <= 100 && lastScroll > 100) {
setScroll(true);
setLastScroll(currentScroll);
}
};

const intervalId = setInterval(checkScrollTop, 100); // 1초에 한 번씩 checkScrollTop 함수를 호출

return () => {
clearInterval(intervalId); // 컴포넌트가 언마운트될 때 타이머를 정리
};
}, [lastScroll]);
return (
<>
<div className={cx('headerTitleWrapper')}>
<Title title="유튜브 추천 채널" titleSize="t3" />
</div>
<main className={cx('mainContainer')}>
<ul className={cx('channelList')}>
{Object.entries(CHANNEL_LIST).map(([channelName, channelId], idx) => {
return (
<li key={idx}>
<button aria-label="채널 버튼" className={cx('channelButton', selectedChannel === channelId ? 'selected' : '')} onClick={() => { return handleChannel(channelId); }}>
{channelName}
</button>
</li>
);
})}
</ul>

{isError && (<div className={cx('error')}>서버요청을 초과하였습니다.</div>)}
<div className={cx('headerFix')}>
<div className={cx('headerTitleWrapper')}>
<LeftIcon className={cx('left')} type="search" />
<Title title="추천 페이지" titleSize="t4" />
</div>
<ul className={cx('channelList')}>
{Object.entries(CHANNEL_LIST).map(([channelName, channelId], idx) => {
return (
<li
onClick={() => {
return handleChannel(channelId);
}}
key={idx}
>
<Flex direction="column" align="center" justify="center">
<Image
className={cx(scroll ? 'channelImage' : 'imageNone')}
src={
channelList[idx]?.items[0]?.snippet.thumbnails.high.url as string
}
alt="유튜브 채널 썸네일"
width={320}
height={180}
layout="cover"
objectFit="responsive"
/>
<button
aria-label="채널 버튼"
className={cx(
'channelButton',
selectedChannel === channelId ? 'selected' : '',
)}
onClick={() => {
return handleChannel(channelId);
}}
>
{channelName}
</button>
</Flex>
</li>
);
})}
</ul>
</div>
<div>{/* {channelList?.items?.thumbnails.high.url} */}</div>
{isError && <div className={cx('error')}>서버요청을 초과하였습니다.</div>}
{dataList?.items?.map((data, idx) => {
return (
<ChannelArticle key={idx} data={data} />
);
return <ChannelArticle key={idx} data={data} />;
})}
</main>
<BottomNav />
Expand Down
Loading

0 comments on commit fe6f7a0

Please sign in to comment.