228 lines
5.5 KiB
TypeScript
228 lines
5.5 KiB
TypeScript
import * as React from 'react'
|
|
import { Link, navigate } from 'gatsby'
|
|
import kebabCase from 'lodash/kebabCase'
|
|
import styled, { css } from 'styled-components'
|
|
import { FaRss } from 'react-icons/fa'
|
|
import { pathPrefix, siteMetadata } from '../../gatsby-config'
|
|
|
|
import { StyledHeading } from '../styledComponents/blog'
|
|
import { SolidButton } from '../components/shared/styledComponents'
|
|
|
|
import type { PageContext, RecentPost, TagCount } from '../types'
|
|
|
|
const rssFeedUrl = `${siteMetadata.siteUrl}rss.xml`
|
|
|
|
const styles = css`
|
|
color: #314351;
|
|
opacity: 0.8;
|
|
font:
|
|
13px/1.65em 'HelveticaNeue',
|
|
'Helvetica Neue',
|
|
Helvetica,
|
|
Arial,
|
|
sans-serif;
|
|
font-size: 0.9rem;
|
|
`
|
|
|
|
const linkStyles = css`
|
|
${styles}
|
|
text-decoration: none;
|
|
display: block;
|
|
&:hover {
|
|
color: #314351;
|
|
opacity: 1;
|
|
text-decoration: underline;
|
|
}
|
|
`
|
|
const ArchiveLink = styled(Link)`
|
|
${linkStyles}
|
|
`
|
|
const RssAnchor = styled.a`
|
|
${linkStyles}
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
`
|
|
const RecentPostLink = styled(Link)`
|
|
${linkStyles}
|
|
padding: 3px;
|
|
border-bottom: 3px solid #e1e1e1;
|
|
font-style: italic;
|
|
&:last-child {
|
|
border-bottom: none;
|
|
}
|
|
`
|
|
const SideBarSection = styled.div`
|
|
margin-bottom: 20px;
|
|
`
|
|
|
|
const StyledInput = styled.input`
|
|
border-color: #e1e1e1;
|
|
background-color: #f8f8f8;
|
|
color: #919191;
|
|
&:focus {
|
|
background-color: #f8f8f8;
|
|
box-shadow: none;
|
|
}
|
|
`
|
|
|
|
const StyledPara = styled.p`
|
|
${styles}
|
|
`
|
|
|
|
interface ArchivesProps {
|
|
archives: Record<string, number>
|
|
basePath?: string
|
|
}
|
|
|
|
const Archives: React.FC<ArchivesProps> = ({ archives, basePath = '' }) => (
|
|
<>
|
|
{Object.keys(archives)
|
|
.sort()
|
|
.reverse()
|
|
.map((year) => (
|
|
<ArchiveLink key={year} to={`${basePath}/${year}/`}>
|
|
{year} ({archives[year]})
|
|
</ArchiveLink>
|
|
))}
|
|
</>
|
|
)
|
|
|
|
interface CategoriesProps {
|
|
tags: TagCount[]
|
|
basePath?: string
|
|
}
|
|
|
|
const Categories: React.FC<CategoriesProps> = ({ tags, basePath = '' }) => (
|
|
<>
|
|
{tags.map((tag, i) => (
|
|
<ArchiveLink key={i} to={`${basePath}/category/${kebabCase(tag.name)}/`}>
|
|
{tag.name} ({tag.totalCount})
|
|
</ArchiveLink>
|
|
))}
|
|
</>
|
|
)
|
|
|
|
interface RecentPostsProps {
|
|
posts: RecentPost[]
|
|
}
|
|
|
|
const RecentPosts: React.FC<RecentPostsProps> = ({ posts }) => (
|
|
<>
|
|
{posts.map((post) => (
|
|
<RecentPostLink key={post.slug} to={post.slug}>
|
|
{post.title}
|
|
</RecentPostLink>
|
|
))}
|
|
</>
|
|
)
|
|
|
|
interface SideBarProps {
|
|
pageContext: PageContext
|
|
location: Location
|
|
notFoundPage?: boolean
|
|
basePath?: string
|
|
}
|
|
|
|
export const SideBar: React.FC<SideBarProps> = ({
|
|
pageContext,
|
|
location,
|
|
notFoundPage = false,
|
|
basePath = ''
|
|
}) => {
|
|
const params = new URLSearchParams(location.search.substring(1))
|
|
const queryUrl = params?.get('s') || ''
|
|
const [query, setQuery] = React.useState(queryUrl)
|
|
React.useEffect(() => {
|
|
setQuery(queryUrl)
|
|
}, [queryUrl])
|
|
|
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
event.preventDefault()
|
|
navigate('/search?s=' + query)
|
|
}
|
|
|
|
const _handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
if (event.key === 'Enter') {
|
|
navigate('/search?s=' + query)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<SideBarSection>
|
|
{notFoundPage && (
|
|
<>
|
|
<StyledPara>
|
|
<b>Nothing Found</b>
|
|
</StyledPara>
|
|
<StyledPara>
|
|
Sorry, the post you are looking for is not available. Maybe you
|
|
want to perform a search?
|
|
</StyledPara>
|
|
</>
|
|
)}
|
|
<div className="input-group mb-3">
|
|
<StyledInput
|
|
type="text"
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
onKeyDown={_handleKeyDown}
|
|
className="form-control"
|
|
placeholder="Search"
|
|
aria-label="Search"
|
|
aria-describedby="button-addon2"
|
|
/>
|
|
<SolidButton
|
|
theme="dark"
|
|
onClick={() => navigate('/search?s=' + query)}
|
|
>
|
|
Search
|
|
</SolidButton>
|
|
<form onSubmit={handleSubmit}></form>
|
|
</div>
|
|
{notFoundPage && (
|
|
<>
|
|
<StyledPara>
|
|
For best search results, mind the following suggestions:
|
|
</StyledPara>
|
|
<StyledPara>
|
|
<ul>
|
|
<li>Always double check your spelling.</li>
|
|
<li>
|
|
Try similar keywords, for example: tablet instead of laptop.
|
|
</li>
|
|
<li>Try using more than one keyword.</li>
|
|
</ul>
|
|
</StyledPara>
|
|
</>
|
|
)}
|
|
<StyledHeading>
|
|
{notFoundPage
|
|
? 'Feel like browsing some posts instead?'
|
|
: 'Recent Posts'}
|
|
</StyledHeading>
|
|
<RecentPosts posts={pageContext.recentPosts} />
|
|
</SideBarSection>
|
|
{!notFoundPage && (
|
|
<>
|
|
<SideBarSection>
|
|
<StyledHeading>Archives</StyledHeading>
|
|
<Archives archives={pageContext.archives} basePath={basePath} />
|
|
</SideBarSection>
|
|
<SideBarSection>
|
|
<StyledHeading>Categories</StyledHeading>
|
|
<Categories tags={pageContext.tags} basePath={basePath} />
|
|
</SideBarSection>
|
|
<SideBarSection>
|
|
<StyledHeading>Subscribe</StyledHeading>
|
|
<RssAnchor href={rssFeedUrl} target="_blank" rel="noopener">
|
|
<FaRss /> RSS Feed
|
|
</RssAnchor>
|
|
</SideBarSection>
|
|
</>
|
|
)}
|
|
</>
|
|
)
|
|
}
|