Skip to content

Commit

Permalink
feature-074: Indicator 및 스크롤 기능 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
whistleJs committed Feb 19, 2024
1 parent 2727c81 commit 24711bc
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 64 deletions.
113 changes: 71 additions & 42 deletions src/components/SearchPage/SearchResultBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,77 @@ import { userInfoState } from '@/stores/user';
import { useRecoilValue } from 'recoil';

interface SearchResultProp {
video : IVideo;
tags : string[];
video: IVideo;
tags: string[];
}

const SearchResultBox : React.FC<SearchResultProp>= ({video, tags}) => {
const nav = useNavigate();
const userName = useRecoilValue(userInfoState);
const date = video.created_at.toString().split('T')[0].split('-');
const handleImg = (event : React.SyntheticEvent<HTMLImageElement, Event>) => {
const target = event.target as HTMLImageElement;
target.style.display = 'none';
}
const handleOnclick = () => {
nav(`/summary/${video.video_id}?insight=${userName?.name === video.user}`);
}

return (
<Styled.VideoCard style={{width : '910px', height : '254px'}} onClick={handleOnclick}>
<div className="main" style={{width : '670px', height : '254px'}}>
<div className="user" style={{width : '114px', height : '20px'}}>
<span className="userName" style={{ height : '19px'}}>{video.user}</span>
<span className='contour' style={{width : '0px', height : '12px'}}></span>
<span className="userDate" style={{height :'19px'}}>
{`${date[0]}${date[1]}${date[2]}일`}
</span>
</div>
<div className="content" style={{width : '648px', height : '108px'}}>
<div className="title" style={{width : '648px', height : '26px'}} dangerouslySetInnerHTML={{__html : video.title}}></div>
<div className="subtitle" style={{width : '648px', height : '22px'}} dangerouslySetInnerHTML={{__html : video.description}}></div>
<div className="subcontent" style={{width : '648px' , height : '44px'}} dangerouslySetInnerHTML={{__html : video.content}}></div>
</div>
<div className='hashtag' style={{maxWidth : '648px',height : '31px'}}>
{video.tag.map((item, index) =>
<Styled.hashtagBox key = {index} style={{backgroundColor : tags.includes(`#${item.name}`) ? '#E9FF3F':''}}>{item.name}</Styled.hashtagBox>
)}
</div>
</div>
<div className="imgBox">
<img src={video.image} onError={(event) => handleImg(event)}></img>
</div>
</Styled.VideoCard>
);
}
const SearchResultBox: React.FC<SearchResultProp> = ({ video, tags }) => {
const nav = useNavigate();
const userName = useRecoilValue(userInfoState);
const date = video.created_at.toString().split('T')[0].split('-');
const handleImg = (event: React.SyntheticEvent<HTMLImageElement, Event>) => {
const target = event.target as HTMLImageElement;
target.style.display = 'none';
};
const handleOnclick = () => {
nav(`/summary/${video.video_id}?insight=${userName?.name === video.user}`);
};

return (
<Styled.VideoCard
style={{ width: '910px', height: '254px' }}
onClick={handleOnclick}
>
<div className="main" style={{ width: '670px', height: '254px' }}>
<div className="user" style={{ width: '114px', height: '20px' }}>
<span className="userName" style={{ height: '19px' }}>
{video.user}
</span>
<span
className="contour"
style={{ width: '0px', height: '12px' }}
></span>
<span className="userDate" style={{ height: '19px' }}>
{`${date[0]}${date[1]}${date[2]}일`}
</span>
</div>
<div className="content" style={{ width: '648px', height: '108px' }}>
<div
className="title"
style={{ width: '648px', height: '26px' }}
dangerouslySetInnerHTML={{ __html: video.title }}
></div>
<div
className="subtitle"
style={{ width: '648px', height: '22px' }}
dangerouslySetInnerHTML={{ __html: video.description }}
></div>
<div
className="subcontent"
style={{ width: '648px', height: '44px' }}
dangerouslySetInnerHTML={{ __html: video.content }}
></div>
</div>
<div className="hashtag" style={{ maxWidth: '648px', height: '31px' }}>
{video.tag.map((item, index) => (
<Styled.hashtagBox
key={index}
style={{
backgroundColor: tags.includes(`#${item.name}`)
? '#E9FF3F'
: '',
}}
>
# {item.name}
</Styled.hashtagBox>
))}
</div>
</div>
<div className="imgBox">
<img src={video.image} onError={(event) => handleImg(event)}></img>
</div>
</Styled.VideoCard>
);
};

export default SearchResultBox;
export default SearchResultBox;
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const ScriptViewer = ({ keyword }: Props) => {
return (
<div className="script-container">
{scriptList.map((script) => (
<div key={script.id}>
<div key={script.id} className="script-box">
<div
style={{
display: 'flex',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ const SummaryScriptBox = ({ onRefresh }: Props) => {
<ScriptBox style={{ width }}>
<ToolBox onRefresh={onRefresh} onChangeKeyword={handleChangeKeyword} />

<div ref={ref} style={{ height: 'calc(100% - 78px)', overflowY: 'auto' }}>
<div
ref={ref}
id="script-box"
style={{ height: 'calc(100% - 78px)', overflowY: 'auto' }}
>
{isEditingView ? <ScriptEditor /> : <ScriptViewer keyword={keyword} />}
</div>

Expand Down
75 changes: 62 additions & 13 deletions src/components/SummaryPage/SummaryScriptBox/ToolBox/Indicator.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,71 @@
// 임시 타입
interface Item {
id: number;
}

type Props = {
list: Item[];
focusId: number;
onChange: (focusId: number) => void;
};
import { useEffect, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';

import { IVideo } from '@/models/video';

import {
summaryPlaySubHeadingIdState,
summaryVideoState,
} from '@/stores/summary';

const Indicator = () => {
const summaryVideo = useRecoilValue(summaryVideoState) as IVideo;
const [playSubHeadingId, setPlaySubHeadingId] = useRecoilState(
summaryPlaySubHeadingIdState,
);

const [focusId, setFocusId] = useState(summaryVideo.subHeading[0].id);

useEffect(() => {
const handleScroll = () => {
const { top: containerTop } = container.getBoundingClientRect();
const list = Array.from(document.querySelectorAll('.script-box'))
.map((el) => el.getBoundingClientRect().top - containerTop)
.filter((top) => top < 100);
// window.innerHeight * 0.3
const { id } = summaryVideo.subHeading[list.length - 1];

setFocusId(id);
};

const container = document.querySelector('#script-box') as HTMLDivElement;
container.addEventListener('scroll', handleScroll);

return () => {
container.removeEventListener('scroll', handleScroll);
};
}, [summaryVideo]);

useEffect(() => {
if (playSubHeadingId < 0) return;

const findIdx = summaryVideo.subHeading.findIndex(
(s) => s.id === playSubHeadingId,
);

if (findIdx > -1) {
const container = document.querySelector('#script-box') as HTMLDivElement;
const element = document.querySelectorAll('.script-box')[findIdx];

const { top: containerTop } = container.getBoundingClientRect();
const { top } = element.getBoundingClientRect();

container.scrollTo({
top: container.scrollTop + top - containerTop,
behavior: 'smooth',
});
}

setFocusId(playSubHeadingId);
}, [playSubHeadingId, summaryVideo]);

const Indicator = ({ list, focusId, onChange }: Props) => {
return (
<div className="indicator">
{list.map((item) => (
{summaryVideo.subHeading.map((item) => (
<div
key={item.id}
className={`indicator-item ${item.id === focusId && 'active'}`}
onClick={() => onChange(item.id)}
onClick={() => setPlaySubHeadingId(item.id)}
/>
))}
</div>
Expand Down
12 changes: 5 additions & 7 deletions src/components/SummaryPage/SummaryScriptBox/ToolBox/ToolBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { getVideoAPI, updateVideoAPI } from '@/apis/videos';

import ModifyIcon from '@/assets/icons/modify.svg?react';

import useCreateToast from '@/hooks/useCreateToast';

import { IVideo } from '@/models/video';

import {
Expand All @@ -17,7 +19,6 @@ import {
import Indicator from './Indicator';
import { SearchKeyword } from './SearchKeyword';
import { ChangeKeyword } from './ChangeKeyword';
import useCreateToast from '@/hooks/useCreateToast';

type Props = {
onRefresh: () => void;
Expand All @@ -33,10 +34,11 @@ const ToolBox = ({ onRefresh, onChangeKeyword }: Props) => {
const [isEditingView, setIsEditingView] = useRecoilState(
summaryIsEditingViewState,
);
const { createToast } = useCreateToast();

const [originalSummary, setOriginalSummary] = useState<IVideo | null>(null);

const { createToast } = useCreateToast();

const handleClickModifyIcon = () => {
setPlaySubHeadingId(-1);
setIsEditingView(true);
Expand Down Expand Up @@ -125,11 +127,7 @@ const ToolBox = ({ onRefresh, onChangeKeyword }: Props) => {
</>
) : (
<>
<Indicator
list={summaryVideo.subHeading}
focusId={1}
onChange={() => {}}
/>
<Indicator />

<div style={{ display: 'flex', gap: 8 }}>
<SearchKeyword onChange={onChangeKeyword} />
Expand Down

0 comments on commit 24711bc

Please sign in to comment.