forked from Hashnode/starter-kit
-
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.
Adds co-author details below the post subtitle for hashnode theme
- Loading branch information
Showing
4 changed files
with
223 additions
and
9 deletions.
There are no files selected for viewing
86 changes: 86 additions & 0 deletions
86
packages/blog-starter-kit/themes/hashnode/components/co-authors-modal.tsx
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,86 @@ | ||
import Button from './hn-button'; | ||
import { CloseSVG } from './icons/svgs'; | ||
import * as DialogPrimitive from '@radix-ui/react-dialog'; | ||
import CustomScrollArea from './scroll-area'; | ||
import { DEFAULT_AVATAR } from '../utils/const/images'; | ||
import { ResizableImage } from './resizable-image'; | ||
import { useAppContext } from './contexts/appContext'; | ||
import { PostFullFragment } from '../generated/graphql'; | ||
|
||
type CoAuthorsModalProps = { | ||
closeModal: () => void; | ||
}; | ||
|
||
const AuthorCard = ({ author }: { author: PostFullFragment['author']; }) => { | ||
|
||
return ( | ||
<div className="flex flex-row items-center justify-between" key={author.id.toString()}> | ||
<div | ||
className="flex w-full flex-wrap items-center justify-between overflow-hidden px-0 py-2.5" | ||
> | ||
<div className="flex flex-wrap items-center overflow-hidden"> | ||
<a href={`https://hashnode.com/@${author.username}`} title={author.name} className="mr-2 w-8"> | ||
<ResizableImage | ||
src={author.profilePicture || DEFAULT_AVATAR} | ||
resize={{ w: 200, h: 200, c: 'face' }} | ||
className="mr-3 h-8 w-8 rounded-full" | ||
/> | ||
</a> | ||
<div className="flex flex-row items-center text-clip"> | ||
<a | ||
href={`https://hashnode.com/@${author.username}`} | ||
title={author.name} | ||
className="truncate font-sans text-sm font-medium text-slate-700 dark:text-slate-200" | ||
> | ||
{author.name} | ||
</a> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default function CoAuthorsModal({ closeModal }: CoAuthorsModalProps) { | ||
const { post } = useAppContext(); | ||
const authors = [post?.author, ...(post?.coAuthors || [])]; | ||
|
||
return ( | ||
<DialogPrimitive.Root open> | ||
<DialogPrimitive.Portal> | ||
<DialogPrimitive.Overlay | ||
onClick={closeModal} | ||
className="fixed inset-0 z-50 bg-slate-900 opacity-50 transition-opacity duration-300 ease-out dark:bg-slate-600" | ||
/> | ||
<DialogPrimitive.Content | ||
onEscapeKeyDown={closeModal} | ||
className="fixed bottom-0 left-0 right-0 z-50 flex w-full max-w-[1200px] flex-col items-center overflow-hidden rounded-b-none rounded-t-lg border-slate-200 bg-white text-slate-700 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-50 md:bottom-50 md:left-50 md:w-96 md:-translate-x-1/2 md:translate-y-1/2 md:rounded-xl lg:w-108 xl:w-116" | ||
> | ||
<DialogPrimitive.DialogTitle className="w-full px-6 py-4 text-lg font-semibold"> | ||
Authors in this article | ||
</DialogPrimitive.DialogTitle> | ||
<DialogPrimitive.Close className="absolute right-2 top-4 text-slate-900 dark:text-slate-50" asChild> | ||
<Button | ||
className="p-1 focus:outline-none focus-visible:ring focus-visible:ring-blue-600 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-800" | ||
onClick={closeModal} | ||
aria-label="Close sign in modal" | ||
variant="transparent" | ||
> | ||
<CloseSVG className="h-5 w-5 fill-current" /> | ||
</Button> | ||
</DialogPrimitive.Close> | ||
<CustomScrollArea> | ||
<div className="px-6 pb-8"> | ||
{authors.map((author) => { | ||
if (!author) { | ||
return null; | ||
} | ||
return <AuthorCard author={author} key={author.id.toString()} />; | ||
})} | ||
</div> | ||
</CustomScrollArea> | ||
</DialogPrimitive.Content> | ||
</DialogPrimitive.Portal> | ||
</DialogPrimitive.Root> | ||
); | ||
} |
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
67 changes: 67 additions & 0 deletions
67
packages/blog-starter-kit/themes/hashnode/components/progressive-image.tsx
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,67 @@ | ||
import React from 'react'; | ||
import { resizeImage } from '@starter-kit/utils/image'; | ||
|
||
import { DEFAULT_AVATAR } from '../utils/const/images'; | ||
import { twMerge } from 'tailwind-merge'; | ||
|
||
/** | ||
* Progressive Image Component which loads low resolution version image before loading original | ||
* @param {string} options.src Image source | ||
* @param {string} options.alt Image alt text | ||
* @param {string} options.className Classname string | ||
* @param {...[type]} options.restOfProps Rest of the props passed to the child | ||
*/ | ||
class ProgressiveImage extends React.Component<{ | ||
resize: any; | ||
src: string; | ||
alt: string; | ||
className: string; | ||
css: string; | ||
}> { | ||
image: HTMLImageElement | null = null; | ||
|
||
componentDidMount() { | ||
if (!(window as any).lazySizes && this.image) { | ||
this.image.setAttribute('src', this.image.getAttribute('data-src') || ''); | ||
} | ||
} | ||
|
||
// TODO: Improve type | ||
replaceBadImage = (e: any) => { | ||
// eslint-disable-next-line react/destructuring-assignment | ||
if (this.props.resize && this.props.resize.c !== 'face') { | ||
return; | ||
} | ||
e.target.onerror = null; | ||
e.target.src = DEFAULT_AVATAR; | ||
}; | ||
|
||
render() { | ||
const { src, alt, className, resize = {}, ...restOfProps } = this.props; | ||
|
||
if (!src || src.trim().length === 0) return null; | ||
|
||
const resizedImage = resizeImage(src, resize); | ||
|
||
return ( | ||
<img | ||
data-sizes="auto" | ||
loading="lazy" | ||
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" | ||
// eslint-disable-next-line no-return-assign | ||
ref={(c) => (this.image = c || null)} | ||
data-src={resizedImage} | ||
width={resize.w} | ||
height={resize.h} | ||
onError={this.replaceBadImage} | ||
alt={alt} | ||
className={twMerge('lazyload block w-full', className)} | ||
{...restOfProps} | ||
/> | ||
); | ||
} | ||
} | ||
|
||
export default ProgressiveImage; | ||
|
||
export { ProgressiveImage }; |
12 changes: 12 additions & 0 deletions
12
packages/blog-starter-kit/themes/hashnode/components/resizable-image.js
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,12 @@ | ||
import { ProgressiveImage } from './progressive-image'; | ||
|
||
function ResizableImage(props) { | ||
const { src, alt, resize, className, ...restOfTheProps } = props; | ||
|
||
return ( | ||
<ProgressiveImage alt={alt} src={src || props.default} resize={resize} className={className} {...restOfTheProps} /> | ||
); | ||
} | ||
|
||
export default ResizableImage; | ||
export { ResizableImage }; |