-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(toc): 포스트 상세 화면에서 포스트 목차 노출 (#17)
- 포스트 상세 화면에서 화면 우측에 포스트 목차를 `sticky` position 형태로 노출하도록 함 - 포스트 목차를 폭이 넓은 스크린에서만 보여주도록 함 - 폭이 넓은 스크린에서 여백 공간을 활용하고자 lg, xl에 대한 브레이크포인트를 변경하고 그에 따라 테이블 목차(toc)의 margin을 다르게 설정함 Signed-off-by: chayeoi <chayeoikeem@gmail.com>
- Loading branch information
Showing
6 changed files
with
164 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/** @jsx jsx */ | ||
import { jsx } from '@emotion/core' | ||
import _ from 'lodash/fp' | ||
import { useCallback, useEffect, useRef } from 'react' | ||
|
||
interface Props { | ||
top?: number; | ||
} | ||
|
||
const Sticky: React.FC<Props> = ({ top = 0, ...otherProps }) => { | ||
const elementRef = useRef<HTMLDivElement>(null) | ||
const y = useRef(0) | ||
|
||
const handleScroll = useCallback(_.throttle(50, () => { | ||
const element = elementRef.current | ||
|
||
if (!element) { | ||
return | ||
} | ||
|
||
const fixed = element.style.position === 'fixed' | ||
const nextFixed = window.pageYOffset + top > y.current | ||
|
||
if (fixed !== nextFixed) { | ||
element.style.position = nextFixed ? 'fixed' : 'static' | ||
element.style.top = nextFixed ? `${top}px` : 'auto' | ||
} | ||
}), [top]) | ||
|
||
useEffect(() => { | ||
const element = elementRef.current | ||
|
||
if (!element) { | ||
return | ||
} | ||
|
||
const rect = element.getBoundingClientRect() | ||
|
||
y.current = rect.top + window.pageYOffset | ||
}, []) | ||
|
||
useEffect(() => { | ||
window.addEventListener('scroll', handleScroll) | ||
|
||
return () => { | ||
window.removeEventListener('scroll', handleScroll) | ||
} | ||
}, [handleScroll]) | ||
|
||
return ( | ||
<div | ||
ref={elementRef} | ||
{...otherProps} | ||
/> | ||
) | ||
} | ||
|
||
export default Sticky |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** @jsx jsx */ | ||
import { css, jsx, SerializedStyles } from '@emotion/core' | ||
import _ from 'lodash/fp' | ||
|
||
import { TOC_ITEM_SPACING } from '../constants' | ||
import { Theme } from '../models/Theme' | ||
import TocItem from '../models/TocItem' | ||
|
||
interface Props { | ||
toc: TocItem[]; | ||
} | ||
|
||
const TableOfContents: React.FC<Props> = ({ toc, ...otherProps }) => { | ||
return ( | ||
<div css={s.root} {...otherProps}> | ||
<ul css={s.toc}> | ||
{_.map(item => ( | ||
<li key={item.slug} css={s.item}> | ||
<a href={`#${item.slug as string}`}> | ||
{item.title} | ||
</a> | ||
{item.items && ( | ||
<ul style={{ marginLeft: TOC_ITEM_SPACING }}> | ||
{_.map(item => ( | ||
<li key={item.slug} css={s.item}> | ||
<a href={`#${item.slug as string}`}> | ||
{item.title} | ||
</a> | ||
{item.items && ( | ||
<ul style={{ marginLeft: TOC_ITEM_SPACING }}> | ||
{_.map(item => ( | ||
<li key={item.slug} css={s.item}> | ||
<a href={`#${item.slug as string}`}> | ||
{item.title} | ||
</a> | ||
</li> | ||
), item.items)} | ||
</ul> | ||
)} | ||
</li> | ||
), item.items)} | ||
</ul> | ||
)} | ||
</li> | ||
), toc)} | ||
</ul> | ||
</div> | ||
) | ||
} | ||
|
||
const s = { | ||
root: (theme: Theme): SerializedStyles => css` | ||
display: none; | ||
width: 240px; | ||
color: ${theme.palette.text.secondary}; | ||
font-size: ${theme.typography.pxToRem(14)}; | ||
${theme.breakpoints.media.lg} { | ||
display: block; | ||
} | ||
${theme.breakpoints.media.xl} { | ||
width: 256px; | ||
} | ||
`, | ||
toc: (theme: Theme): SerializedStyles => css` | ||
padding-left: 32px; | ||
a:hover, a:focus { | ||
color: ${theme.palette.primary.main}; | ||
} | ||
`, | ||
item: css` | ||
position: relative; | ||
`, | ||
} | ||
|
||
export default TableOfContents |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const TOC_ITEM_SPACING = 20 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters