mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-06-30 22:46:39 -04:00
all components should accept additional classnames
This commit is contained in:
@ -1,11 +1,12 @@
|
||||
import classNames from "classnames";
|
||||
import type { BlockquoteHTMLAttributes } from "react";
|
||||
|
||||
import styles from "./Blockquote.module.css";
|
||||
|
||||
type Props = BlockquoteHTMLAttributes<HTMLElement>;
|
||||
|
||||
const Blockquote = ({ children, ...rest }: Props) => (
|
||||
<blockquote className={styles.blockquote} {...rest}>
|
||||
const Blockquote = ({ children, className, ...rest }: Props) => (
|
||||
<blockquote className={classNames(styles.blockquote, className)} {...rest}>
|
||||
{children}
|
||||
</blockquote>
|
||||
);
|
||||
|
@ -5,24 +5,28 @@ import type { ReactNode } from "react";
|
||||
import styles from "./CodeBlock.module.css";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const CodeBlock = (props: Props) => {
|
||||
if (props.className?.split(" ").includes("code-highlight")) {
|
||||
const CodeBlock = ({ children, className, ...rest }: Props) => {
|
||||
if (className?.split(" ").includes("code-highlight")) {
|
||||
// full multi-line code blocks with prism highlighting and copy-to-clipboard button
|
||||
return (
|
||||
<div className={styles.block}>
|
||||
<CopyButton source={props.children} className={styles.copy_btn} />
|
||||
<code {...props} className={classNames(styles.code, props.className)}>
|
||||
{props.children}
|
||||
<CopyButton source={children} className={styles.copy_btn} />
|
||||
<code className={classNames(styles.code, className)} {...rest}>
|
||||
{children}
|
||||
</code>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
// inline code in paragraphs, headings, etc. (not highlighted)
|
||||
return <code className={classNames(styles.code, styles.inline)}>{props.children}</code>;
|
||||
return (
|
||||
<code className={classNames(styles.code, styles.inline, className)} {...rest}>
|
||||
{children}
|
||||
</code>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { memo } from "react";
|
||||
import Link from "next/link";
|
||||
import css from "styled-jsx/css";
|
||||
import classNames from "classnames";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
type Props = {
|
||||
@ -10,6 +11,7 @@ type Props = {
|
||||
darkColor: string;
|
||||
title?: string;
|
||||
external?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const getFancyLinkStyles = ({ lightColor, darkColor }: Partial<Props>) => {
|
||||
@ -39,23 +41,33 @@ const getFancyLinkStyles = ({ lightColor, darkColor }: Partial<Props>) => {
|
||||
`;
|
||||
};
|
||||
|
||||
const ColorfulLink = ({ href, title, lightColor, darkColor, external = false, children }: Props) => {
|
||||
const { className, styles } = getFancyLinkStyles({ lightColor, darkColor });
|
||||
const ColorfulLink = ({
|
||||
href,
|
||||
title,
|
||||
lightColor,
|
||||
darkColor,
|
||||
external = false,
|
||||
className,
|
||||
children,
|
||||
...rest
|
||||
}: Props) => {
|
||||
const { className: underlineClassName, styles: underlineStyles } = getFancyLinkStyles({ lightColor, darkColor });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link href={href} passHref={true} prefetch={false}>
|
||||
<a
|
||||
className={className}
|
||||
className={classNames(underlineClassName, className)}
|
||||
title={title}
|
||||
target={external ? "_blank" : undefined}
|
||||
rel={external ? "noopener noreferrer" : undefined}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
{styles}
|
||||
{underlineStyles}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { useTheme } from "next-themes";
|
||||
import classNames from "classnames";
|
||||
import { Giscus } from "@giscus/react";
|
||||
import { giscusConfig } from "../../lib/config";
|
||||
import type { GiscusProps } from "@giscus/react";
|
||||
@ -7,13 +8,14 @@ import styles from "./Comments.module.css";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Comments = ({ title }: Props) => {
|
||||
const Comments = ({ title, className }: Props) => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={classNames(styles.wrapper, className)}>
|
||||
<Giscus
|
||||
{...(giscusConfig as GiscusProps)}
|
||||
term={title}
|
||||
|
@ -11,6 +11,10 @@ import type { FormikHelpers } from "formik";
|
||||
import styles from "./ContactForm.module.css";
|
||||
const cx = classNames.bind(styles);
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
type Values = {
|
||||
name: string;
|
||||
email: string;
|
||||
@ -18,7 +22,7 @@ type Values = {
|
||||
"h-captcha-response": string;
|
||||
};
|
||||
|
||||
const ContactForm = () => {
|
||||
const ContactForm = ({ className }: Props) => {
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
// status/feedback:
|
||||
@ -97,7 +101,7 @@ const ContactForm = () => {
|
||||
}}
|
||||
>
|
||||
{({ setFieldValue, isSubmitting, touched, errors }) => (
|
||||
<Form className={styles.form} name="contact">
|
||||
<Form className={className} name="contact">
|
||||
<Field
|
||||
className={cx({ input: true, missing: errors.name && touched.name })}
|
||||
name="name"
|
||||
|
@ -50,11 +50,7 @@ const CopyButton = ({ source, timeout = 2000, className }: Props) => {
|
||||
onClick={handleCopy}
|
||||
disabled={!!copied}
|
||||
>
|
||||
{copied ? (
|
||||
<CheckOcticon className="icon" fill="currentColor" />
|
||||
) : (
|
||||
<ClipboardOcticon className="icon" fill="currentColor" />
|
||||
)}
|
||||
{copied ? <CheckOcticon fill="currentColor" /> : <ClipboardOcticon fill="currentColor" />}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Image from "../Image/Image";
|
||||
import innerText from "react-innertext";
|
||||
import classNames from "classnames";
|
||||
import type { ReactNode } from "react";
|
||||
import type { ImageProps as NextImageProps } from "next/image";
|
||||
|
||||
@ -8,11 +9,12 @@ import styles from "./Figure.module.css";
|
||||
type Props = Omit<NextImageProps, "alt"> & {
|
||||
children: ReactNode; // caption (can be in markdown, yay!!!)
|
||||
alt?: string; // becomes optional -- pulled from plaintext-ified caption if missing
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Figure = ({ children, alt, ...imageProps }: Props) => {
|
||||
const Figure = ({ children, alt, className, ...imageProps }: Props) => {
|
||||
return (
|
||||
<figure className={styles.figure}>
|
||||
<figure className={classNames(styles.figure, className)}>
|
||||
<Image alt={alt || innerText(children)} {...imageProps} />
|
||||
<figcaption className={styles.caption}>{children}</figcaption>
|
||||
</figure>
|
||||
|
@ -5,44 +5,45 @@ import styles from "./Heading.module.css";
|
||||
|
||||
type Props = HTMLAttributes<HTMLHeadingElement> & {
|
||||
as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Heading = ({ as: Component, children, ...rest }: Props) => {
|
||||
const Heading = ({ as: Component, children, className, ...rest }: Props) => {
|
||||
return (
|
||||
<Component className={classNames(styles.heading, styles[Component])} {...rest}>
|
||||
<Component className={classNames(styles.heading, styles[Component], className)} {...rest}>
|
||||
{children}
|
||||
</Component>
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: do this less manually...
|
||||
export const H1 = ({ children, ...props }: Props) => (
|
||||
<Heading as="h1" {...props}>
|
||||
export const H1 = ({ children, ...rest }: Props) => (
|
||||
<Heading as="h1" {...rest}>
|
||||
{children}
|
||||
</Heading>
|
||||
);
|
||||
export const H2 = ({ children, ...props }: Props) => (
|
||||
<Heading as="h2" {...props}>
|
||||
export const H2 = ({ children, ...rest }: Props) => (
|
||||
<Heading as="h2" {...rest}>
|
||||
{children}
|
||||
</Heading>
|
||||
);
|
||||
export const H3 = ({ children, ...props }: Props) => (
|
||||
<Heading as="h3" {...props}>
|
||||
export const H3 = ({ children, ...rest }: Props) => (
|
||||
<Heading as="h3" {...rest}>
|
||||
{children}
|
||||
</Heading>
|
||||
);
|
||||
export const H4 = ({ children, ...props }: Props) => (
|
||||
<Heading as="h4" {...props}>
|
||||
export const H4 = ({ children, ...rest }: Props) => (
|
||||
<Heading as="h4" {...rest}>
|
||||
{children}
|
||||
</Heading>
|
||||
);
|
||||
export const H5 = ({ children, ...props }: Props) => (
|
||||
<Heading as="h5" {...props}>
|
||||
export const H5 = ({ children, ...rest }: Props) => (
|
||||
<Heading as="h5" {...rest}>
|
||||
{children}
|
||||
</Heading>
|
||||
);
|
||||
export const H6 = ({ children, ...props }: Props) => (
|
||||
<Heading as="h6" {...props}>
|
||||
export const H6 = ({ children, ...rest }: Props) => (
|
||||
<Heading as="h6" {...rest}>
|
||||
{children}
|
||||
</Heading>
|
||||
);
|
||||
|
@ -2,7 +2,12 @@ import useSWR from "swr";
|
||||
import Loading from "../Loading/Loading";
|
||||
import { fetcher } from "../../lib/fetcher";
|
||||
|
||||
const HitCounter = ({ slug }) => {
|
||||
type Props = {
|
||||
slug: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const HitCounter = ({ slug, className }: Props) => {
|
||||
// start fetching repos from API immediately
|
||||
const { data, error } = useSWR(`/api/hits/?slug=${encodeURIComponent(slug)}`, fetcher, {
|
||||
// avoid double (or more) counting views
|
||||
@ -21,7 +26,7 @@ const HitCounter = ({ slug }) => {
|
||||
|
||||
// we have data!
|
||||
return (
|
||||
<span title={`${data.hits.toLocaleString("en-US")} ${data.hits === 1 ? "view" : "views"}`}>
|
||||
<span title={`${data.hits.toLocaleString("en-US")} ${data.hits === 1 ? "view" : "views"}`} className={className}>
|
||||
{data.hits.toLocaleString("en-US")}
|
||||
</span>
|
||||
);
|
||||
|
@ -1,5 +1,11 @@
|
||||
import classNames from "classnames";
|
||||
|
||||
import styles from "./HorizontalRule.module.css";
|
||||
|
||||
const HorizontalRule = () => <hr className={styles.hr} />;
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const HorizontalRule = ({ className, ...rest }: Props) => <hr className={classNames(styles.hr, className)} {...rest} />;
|
||||
|
||||
export default HorizontalRule;
|
||||
|
@ -1,3 +1,5 @@
|
||||
import classNames from "classnames";
|
||||
|
||||
import styles from "./IFrame.module.css";
|
||||
|
||||
type Props = {
|
||||
@ -7,11 +9,12 @@ type Props = {
|
||||
width?: number; // defaults to 100%
|
||||
allowScripts?: boolean;
|
||||
noScroll?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const IFrame = ({ src, title, height, width, allowScripts, noScroll, ...rest }: Props) => (
|
||||
const IFrame = ({ src, title, height, width, allowScripts, noScroll, className, ...rest }: Props) => (
|
||||
<iframe
|
||||
className={styles.frame}
|
||||
className={classNames(styles.frame, className)}
|
||||
src={src}
|
||||
title={title}
|
||||
sandbox={allowScripts ? "allow-same-origin allow-scripts allow-popups" : undefined}
|
||||
|
@ -1,11 +1,12 @@
|
||||
import NextImage from "next/image";
|
||||
import classNames from "classnames";
|
||||
import type { ImageProps as NextImageProps } from "next/image";
|
||||
|
||||
import styles from "./Image.module.css";
|
||||
|
||||
const Image = ({ src, width, height, alt, quality, priority }: NextImageProps) => {
|
||||
const Image = ({ src, width, height, alt, quality, priority, className, ...rest }: NextImageProps) => {
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={classNames(styles.wrapper, className)}>
|
||||
<NextImage
|
||||
src={(src as string).replace(/^\/public/g, "")}
|
||||
layout="intrinsic"
|
||||
@ -15,6 +16,7 @@ const Image = ({ src, width, height, alt, quality, priority }: NextImageProps) =
|
||||
quality={quality || 65}
|
||||
loading={priority ? "eager" : "lazy"}
|
||||
priority={!!priority}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,25 +1,27 @@
|
||||
import classNames from "classnames";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import styles from "./List.module.css";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const UnorderedList = ({ children, ...rest }: Props) => (
|
||||
<ul className={styles.unordered} {...rest}>
|
||||
export const UnorderedList = ({ children, className, ...rest }: Props) => (
|
||||
<ul className={classNames(styles.unordered, className)} {...rest}>
|
||||
{children}
|
||||
</ul>
|
||||
);
|
||||
export const OrderedList = ({ children, ...rest }: Props) => (
|
||||
<ol className={styles.ordered} {...rest}>
|
||||
export const OrderedList = ({ children, className, ...rest }: Props) => (
|
||||
<ol className={classNames(styles.ordered, className)} {...rest}>
|
||||
{children}
|
||||
</ol>
|
||||
);
|
||||
|
||||
// TODO: this is based on good faith that the children are all `<li>`s...
|
||||
export const ListItem = ({ children, ...rest }: Props) => (
|
||||
<li className={styles.item} {...rest}>
|
||||
export const ListItem = ({ children, className, ...rest }: Props) => (
|
||||
<li className={classNames(styles.item, className)} {...rest}>
|
||||
{children}
|
||||
</li>
|
||||
);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { memo } from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
import styles from "./Loading.module.css";
|
||||
|
||||
@ -6,9 +7,10 @@ type Props = {
|
||||
width: number; // of entire container, in pixels
|
||||
boxes?: number; // total number of boxes (default: 3)
|
||||
timing?: number; // staggered timing between each box's pulse, in seconds (default: 0.1s)
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Loading = ({ width, boxes = 3, timing = 0.1 }: Props) => {
|
||||
const Loading = ({ width, boxes = 3, timing = 0.1, className }: Props) => {
|
||||
// each box is just an empty div
|
||||
const divs = [];
|
||||
|
||||
@ -30,7 +32,7 @@ const Loading = ({ width, boxes = 3, timing = 0.1 }: Props) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={styles.wrapper}
|
||||
className={classNames(styles.wrapper, className)}
|
||||
style={{
|
||||
width: `${width}px`,
|
||||
height: `${width / 2}px`,
|
||||
|
@ -1,14 +1,16 @@
|
||||
import classNames from "classnames";
|
||||
import { OctocatOcticon } from "../Icons";
|
||||
|
||||
import styles from "./OctocatLink.module.css";
|
||||
|
||||
type Props = {
|
||||
repo: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const OctocatLink = (props: Props) => (
|
||||
<a className={styles.link} href={`https://github.com/${props.repo}`} target="_blank" rel="noopener noreferrer">
|
||||
<OctocatOcticon fill="currentColor" />
|
||||
const OctocatLink = ({ repo, className }: Props) => (
|
||||
<a className={styles.link} href={`https://github.com/${repo}`} target="_blank" rel="noopener noreferrer">
|
||||
<OctocatOcticon fill="currentColor" className={classNames("icon", className)} />
|
||||
</a>
|
||||
);
|
||||
|
||||
|
@ -1,19 +1,21 @@
|
||||
import { memo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import Link from "next/link";
|
||||
import classNames from "classnames";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import styles from "./PageTitle.module.css";
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const PageTitle = ({ children }: Props) => {
|
||||
const PageTitle = ({ children, className }: Props) => {
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<h1 className={styles.title}>
|
||||
<h1 className={classNames(styles.title, className)}>
|
||||
<Link href={router.asPath}>
|
||||
<a>{children}</a>
|
||||
</Link>
|
||||
|
@ -1,49 +1,54 @@
|
||||
import classNames from "classnames";
|
||||
import { intlFormat, formatDistanceToNowStrict } from "date-fns";
|
||||
import { StarOcticon, ForkOcticon } from "../Icons";
|
||||
import { RepoType } from "../../types";
|
||||
|
||||
import styles from "./RepositoryCard.module.css";
|
||||
|
||||
const RepositoryCard = (props: RepoType) => (
|
||||
<div className={styles.card}>
|
||||
<a className={styles.name} href={props.url} target="_blank" rel="noopener noreferrer">
|
||||
{props.name}
|
||||
type Props = RepoType & {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const RepositoryCard = ({ name, url, description, language, stars, forks, updatedAt, className }: Props) => (
|
||||
<div className={classNames(styles.card, className)}>
|
||||
<a className={styles.name} href={url} target="_blank" rel="noopener noreferrer">
|
||||
{name}
|
||||
</a>
|
||||
|
||||
{props.description && <p className={styles.description}>{props.description}</p>}
|
||||
{description && <p className={styles.description}>{description}</p>}
|
||||
|
||||
<div className={styles.meta}>
|
||||
{props.language && (
|
||||
{language && (
|
||||
<div className={styles.meta_item}>
|
||||
<span className={styles.language_color} style={{ backgroundColor: props.language.color }} />
|
||||
<span>{props.language.name}</span>
|
||||
<span className={styles.language_color} style={{ backgroundColor: language.color }} />
|
||||
<span>{language.name}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{props.stars > 0 && (
|
||||
{stars > 0 && (
|
||||
<div className={styles.meta_item}>
|
||||
<a
|
||||
href={`${props.url}/stargazers`}
|
||||
title={`${props.stars.toLocaleString("en-US")} ${props.stars === 1 ? "star" : "stars"}`}
|
||||
href={`${url}/stargazers`}
|
||||
title={`${stars.toLocaleString("en-US")} ${stars === 1 ? "star" : "stars"}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<StarOcticon fill="currentColor" className={styles.octicon} />
|
||||
<span>{props.stars.toLocaleString("en-US")}</span>
|
||||
<span>{stars.toLocaleString("en-US")}</span>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{props.forks > 0 && (
|
||||
{forks > 0 && (
|
||||
<div className={styles.meta_item}>
|
||||
<a
|
||||
href={`${props.url}/network/members`}
|
||||
title={`${props.forks.toLocaleString("en-US")} ${props.forks === 1 ? "fork" : "forks"}`}
|
||||
href={`${url}/network/members`}
|
||||
title={`${forks.toLocaleString("en-US")} ${forks === 1 ? "fork" : "forks"}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<ForkOcticon fill="currentColor" className={styles.octicon} />
|
||||
<span>{props.forks.toLocaleString("en-US")}</span>
|
||||
<span>{forks.toLocaleString("en-US")}</span>
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
@ -51,7 +56,7 @@ const RepositoryCard = (props: RepoType) => (
|
||||
<div
|
||||
className={styles.meta_item}
|
||||
title={intlFormat(
|
||||
new Date(props.updatedAt),
|
||||
new Date(updatedAt),
|
||||
{
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
@ -65,7 +70,7 @@ const RepositoryCard = (props: RepoType) => (
|
||||
}
|
||||
)}
|
||||
>
|
||||
<span>Updated {formatDistanceToNowStrict(new Date(props.updatedAt), { addSuffix: true })}</span>
|
||||
<span>Updated {formatDistanceToNowStrict(new Date(updatedAt), { addSuffix: true })}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,14 +2,18 @@ import Tweet from "react-tweet-embed";
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
className?: string;
|
||||
options?: object;
|
||||
};
|
||||
|
||||
const TweetEmbed = (props: Props) => (
|
||||
const TweetEmbed = ({ id, className, options }: Props) => (
|
||||
<Tweet
|
||||
id={props.id}
|
||||
className={className}
|
||||
id={id}
|
||||
options={{
|
||||
dnt: true,
|
||||
align: "center",
|
||||
...options,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
import classNames from "classnames";
|
||||
import ReactPlayer from "react-player/file";
|
||||
|
||||
import styles from "./Video.module.css";
|
||||
@ -8,9 +9,10 @@ type Props = {
|
||||
thumbnail?: string;
|
||||
subs?: string;
|
||||
autoplay?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Video = ({ webm, mp4, thumbnail, subs, autoplay }: Props) => {
|
||||
const Video = ({ webm, mp4, thumbnail, subs, autoplay, className, ...rest }: Props) => {
|
||||
const url = [
|
||||
webm && {
|
||||
src: webm,
|
||||
@ -53,9 +55,9 @@ const Video = ({ webm, mp4, thumbnail, subs, autoplay }: Props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={classNames(styles.wrapper, className)}>
|
||||
{/* @ts-ignore */}
|
||||
<ReactPlayer width="100%" height="100%" url={url} config={config} controls={!autoplay} />
|
||||
<ReactPlayer width="100%" height="100%" url={url} config={config} controls={!autoplay} {...rest} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,19 +1,22 @@
|
||||
import classNames from "classnames";
|
||||
import ReactPlayer from "react-player/youtube";
|
||||
|
||||
import styles from "./YouTubeEmbed.module.css";
|
||||
|
||||
type Props = {
|
||||
id: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const YouTubeEmbed = ({ id }: Props) => (
|
||||
<div className={styles.wrapper}>
|
||||
const YouTubeEmbed = ({ id, className, ...rest }: Props) => (
|
||||
<div className={classNames(styles.wrapper, className)}>
|
||||
<ReactPlayer
|
||||
width="100%"
|
||||
height="100%"
|
||||
url={`https://www.youtube-nocookie.com/watch?v=${id}`}
|
||||
light={`https://i.ytimg.com/vi/${id}/hqdefault.jpg`}
|
||||
controls
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -259,7 +259,7 @@ const Index = () => (
|
||||
</ColorfulLink>{" "}
|
||||
<sup className="monospace pgp_key">
|
||||
<ColorfulLink href="/pubkey.asc" title="My Public Key" lightColor="#757575" darkColor="#959595" external>
|
||||
<LockIcon className="icon" /> 2B0C 9CF2 51E6 9A39
|
||||
<LockIcon /> 2B0C 9CF2 51E6 9A39
|
||||
</ColorfulLink>
|
||||
</sup>
|
||||
,{" "}
|
||||
|
@ -6,7 +6,11 @@ import { ProjectsIcon } from "../components/Icons";
|
||||
import type { GetStaticProps } from "next";
|
||||
import { RepoType } from "../types";
|
||||
|
||||
const Projects = (props: { repos: RepoType[] }) => (
|
||||
type Props = {
|
||||
repos: RepoType[];
|
||||
};
|
||||
|
||||
const Projects = ({ repos }: Props) => (
|
||||
<>
|
||||
<NextSeo
|
||||
title="Projects"
|
||||
@ -20,7 +24,7 @@ const Projects = (props: { repos: RepoType[] }) => (
|
||||
</PageTitle>
|
||||
|
||||
<div className="wrapper">
|
||||
{props.repos.map((repo: RepoType) => (
|
||||
{repos.map((repo: RepoType) => (
|
||||
<div key={repo.name} className="card">
|
||||
<RepositoryCard {...repo} />
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user