Skip to content

Commit baf9c80

Browse files
committed
feat: breadcrumbs and back-to-top button
1 parent 26d6a40 commit baf9c80

File tree

5 files changed

+167
-6
lines changed

5 files changed

+167
-6
lines changed

src/components/MarkdownViewer.tsx

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ import { useParams } from 'react-router-dom';
66
import remarkGfm from 'remark-gfm';
77
import CustomLink from './Link';
88
import GitHubButton from './ui/github-button';
9+
import Breadcrumb from './ui/breadcrumb';
10+
import BackToTop from './ui/back-to-top';
911

1012
const MarkdownViewer = () => {
1113
const { category, topic } = useParams();
1214
const [markdown, setMarkdown] = useState('');
1315
const [isLoading, setIsLoading] = useState(true);
16+
const [isVisible, setIsVisible] = useState(false);
1417

1518
useEffect(() => {
1619
const fetchMarkdown = async () => {
@@ -29,10 +32,33 @@ const MarkdownViewer = () => {
2932
setIsLoading(false);
3033
}, 1000);
3134

35+
const handleScroll = () => {
36+
if (window.scrollY > 200) {
37+
setIsVisible(true);
38+
} else {
39+
setIsVisible(false);
40+
}
41+
};
42+
43+
useEffect(() => {
44+
window.addEventListener('scroll', handleScroll);
45+
46+
return () => {
47+
window.removeEventListener('scroll', handleScroll);
48+
};
49+
}, []);
50+
3251
return (
3352
<div className='min-h-[100vh]'>
34-
<div className='container mx-auto py-1 leading-relaxed px-4 pb-12 dark:text-neutral-300'>
53+
<div className='container mx-auto py-1 leading-relaxed px-4 pb-28 dark:text-neutral-300'>
3554
<div className='max-w-3xl mx-auto mt-12 markdown-file flex flex-col gap-8'>
55+
<div>
56+
<Breadcrumb
57+
category={category}
58+
topic={topic}
59+
/>
60+
</div>
61+
3662
<ReactMarkdown
3763
remarkPlugins={[[remarkGfm]]}
3864
components={{
@@ -65,12 +91,16 @@ const MarkdownViewer = () => {
6591
<div className='flex items-end justify-end w-full'>
6692
{!isLoading ? (
6793
<GitHubButton
68-
to={`https://github.com/AmanuelCh/gopher-notes/src/data/${category}/${topic}.md`}
94+
to={`https://github.com/AmanuelCh/gopher-notes/blob/main/src/data/${category}/${topic}.md`}
6995
/>
7096
) : null}
7197
</div>
7298
</div>
7399
</div>
100+
101+
<div className='fixed bottom-8 right-4 lg:right-10'>
102+
{isVisible ? <BackToTop /> : null}
103+
</div>
74104
</div>
75105
);
76106
};

src/components/TopicList.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
import { Link, useParams } from 'react-router-dom';
22
import { topics } from '../utils/lists';
3+
import Breadcrumb from './ui/breadcrumb';
34

45
const TopicList = () => {
56
const { category } = useParams<{ category: string }>();
67
const topicList =
78
topics[category?.replaceAll('-', '_') as keyof typeof topics] || [];
89

910
return (
10-
<div className='container mx-auto px-4 py-1 dark:text-neutral-200'>
11-
<h1 className='z-50'>{category}</h1>
12-
<ul className='list-none'>
11+
<div className='container mx-auto py-1 leading-relaxed pb-12 dark:text-neutral-300 max-w-3xl mt-12 w-11/12'>
12+
<div>
13+
{' '}
14+
<Breadcrumb category={category} />
15+
</div>
16+
17+
<h1 className='my-6 z-50'>{category?.replaceAll('-', ' ')}</h1>
18+
<ul className='grid md:grid-cols-3 gap-6 list-none'>
1319
{topicList.map((topic) => (
14-
<li key={topic}>
20+
<li
21+
className='card'
22+
key={topic}
23+
>
1524
<Link to={`/${category}/${topic}`}>
1625
{topic.split('-').join(' ')}
1726
</Link>

src/components/ui/back-to-top.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
type Props = {};
2+
3+
const BackToTop = (props: Props) => {
4+
const handleScroll = () => {
5+
window.scrollTo({
6+
top: 0,
7+
behavior: 'smooth'
8+
});
9+
}
10+
11+
return (
12+
<button className='back-btn w-[50px] h-[50px] bg-slate-700 font-semibold flex items-center justify-center shadow-[0px_0px_0px_4px_rgba(180,160,255,0.253)] cursor-pointer duration-[0.3s] overflow-hidden relative rounded-[50%] border-[none] hover:w-[140px] hover:duration-[0.3s] hover:bg-[rgb(181,160,255)] hover:items-center hover:rounded-[50px] before:absolute before:content-["Back_to_Top"] before:text-[white] before:text-[0px] before:-bottom-5 hover:before:text-[13px] hover:before:opacity-100 hover:before:duration-[0.3s] hover:before:bottom-[unset] group' onClick={handleScroll}>
13+
<svg
14+
className='w-[12px] duration-300 group-hover:duration-300 group-hover:translate-y-[-200%] animate-pulse'
15+
viewBox='0 0 384 512'
16+
>
17+
<path
18+
d='M214.6 41.4c-12.5-12.5-32.8-12.5-45.3 0l-160 160c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L160 141.2V448c0 17.7 14.3 32 32 32s32-14.3 32-32V141.2L329.4 246.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3l-160-160z'
19+
fill='white'
20+
></path>
21+
</svg>
22+
</button>
23+
);
24+
};
25+
26+
export default BackToTop;

src/components/ui/breadcrumb.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { Link } from 'react-router-dom';
2+
3+
type Props = {
4+
category: string | undefined;
5+
topic?: string;
6+
};
7+
8+
const Breadcrumb = ({ category, topic }: Props) => {
9+
return (
10+
<nav className='flex px-5 py-3 text-gray-700 border border-gray-200 rounded-lg bg-slate-200 md:w-max dark:bg-gray-800 dark:border-gray-700'>
11+
<div className='flex flex-col gap-4 md:space-x-2 list-none pr-3 md:flex-row md:items-center md:gap-0'>
12+
<li className='flex items-center ml-[3px] md:ml-0'>
13+
<Link
14+
className='inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600 dark:text-gray-400 dark:hover:text-white'
15+
to='/'
16+
>
17+
<svg
18+
className='w-3 h-3 me-2.5'
19+
aria-hidden='true'
20+
xmlns='http://www.w3.org/2000/svg'
21+
fill='currentColor'
22+
viewBox='0 0 20 20'
23+
>
24+
<path d='m19.707 9.293-2-2-7-7a1 1 0 0 0-1.414 0l-7 7-2 2a1 1 0 0 0 1.414 1.414L2 10.414V18a2 2 0 0 0 2 2h3a1 1 0 0 0 1-1v-4a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v4a1 1 0 0 0 1 1h3a2 2 0 0 0 2-2v-7.586l.293.293a1 1 0 0 0 1.414-1.414Z' />
25+
</svg>
26+
Home
27+
</Link>
28+
</li>
29+
<li>
30+
<div className='flex items-center'>
31+
<svg
32+
className='rtl:rotate-180 block w-3 h-3 mx-1 text-gray-400 '
33+
aria-hidden='true'
34+
xmlns='http://www.w3.org/2000/svg'
35+
fill='none'
36+
viewBox='0 0 6 10'
37+
>
38+
<path
39+
stroke='currentColor'
40+
strokeLinecap='round'
41+
strokeLinejoin='round'
42+
strokeWidth='2'
43+
d='m1 9 4-4-4-4'
44+
/>
45+
</svg>
46+
<Link
47+
className='ms-1 text-sm font-medium text-gray-700 hover:text-blue-600 md:ms-2 dark:text-gray-400 dark:hover:text-white capitalize'
48+
to={`/${category}`}
49+
>
50+
{category?.replaceAll('-', ' ')}
51+
</Link>
52+
</div>
53+
</li>
54+
{topic ? (
55+
<li
56+
className='block md:flex'
57+
aria-current='page'
58+
>
59+
<div className='flex items-center'>
60+
<svg
61+
className='rtl:rotate-180 w-3 h-3 mx-1 text-gray-400'
62+
aria-hidden='true'
63+
xmlns='http://www.w3.org/2000/svg'
64+
fill='none'
65+
viewBox='0 0 6 10'
66+
>
67+
<path
68+
stroke='currentColor'
69+
strokeLinecap='round'
70+
strokeLinejoin='round'
71+
strokeWidth='2'
72+
d='m1 9 4-4-4-4'
73+
/>
74+
</svg>
75+
<span className='ms-1 text-sm font-medium text-neutral-900 capitalize md:ms-2 dark:text-neutral-100'>
76+
{topic?.replaceAll('-', ' ')}
77+
</span>
78+
</div>
79+
</li>
80+
) : null}
81+
</div>
82+
</nav>
83+
);
84+
};
85+
86+
export default Breadcrumb;

src/index.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,28 @@
2525
.IconContainer {
2626
@apply w-[25px] h-[25px] rounded-[50px] flex items-center justify-center overflow-hidden z-20 duration-300;
2727
}
28+
.card {
29+
@apply rounded-2xl p-4 dark:bg-slate-950 border border-transparent dark:border-white/[0.2] border-slate-700 relative z-20 capitalize;
30+
}
2831
}
2932

3033
body {
3134
font-family: Inter, sans-serif;
35+
overflow-x: hidden !important;
3236
}
3337

3438
h1 {
3539
font-size: 2.1em;
3640
font-weight: bold;
3741
margin: 20px 0 0 0;
3842
line-height: 3rem;
43+
text-transform: capitalize;
3944
}
4045

4146
h2 {
4247
font-size: 1.6em;
4348
font-weight: bold;
49+
text-transform: capitalize;
4450
/* margin: 20px 0; */
4551
}
4652

@@ -158,6 +164,10 @@ code {
158164
transition-duration: 0.3s;
159165
}
160166

167+
.back-btn::before {
168+
content: 'Back to top';
169+
}
170+
161171
@media (max-width: 375px) {
162172
h1 {
163173
font-size: 1.8em;

0 commit comments

Comments
 (0)