mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-07-03 17:26:37 -04:00
move some non-post pages to mdx
This commit is contained in:
32
components/CodePen/CodePen.tsx
Normal file
32
components/CodePen/CodePen.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
export type CodePenProps = {
|
||||
username: string;
|
||||
id: string;
|
||||
height?: number;
|
||||
defaultTab?: string;
|
||||
preview?: boolean;
|
||||
editable?: boolean;
|
||||
};
|
||||
|
||||
const CodePen = ({
|
||||
username,
|
||||
id,
|
||||
height = 500,
|
||||
defaultTab = "html",
|
||||
preview = true,
|
||||
editable = false,
|
||||
}: CodePenProps) => {
|
||||
return (
|
||||
<iframe
|
||||
src={`https://codepen.io/${username}/embed/${id}/?${new URLSearchParams({
|
||||
"default-tab": `${defaultTab},result`,
|
||||
preview: `${!!preview}`,
|
||||
editable: `${!!editable}`,
|
||||
})}`}
|
||||
scrolling="no"
|
||||
sandbox="allow-same-origin allow-scripts allow-popups allow-top-navigation-by-user-activation"
|
||||
style={{ height: `${height}px`, width: "100%", border: "0" }}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodePen;
|
2
components/CodePen/index.ts
Normal file
2
components/CodePen/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./CodePen";
|
||||
export { default } from "./CodePen";
|
@ -1,3 +0,0 @@
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import IFrame from "../IFrame";
|
||||
|
||||
import styles from "./CodePenEmbed.module.css";
|
||||
|
||||
export type CodePenEmbedProps = {
|
||||
username: string;
|
||||
id: string;
|
||||
height?: number;
|
||||
defaultTab?: string;
|
||||
preview?: boolean;
|
||||
editable?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const CodePenEmbed = ({
|
||||
username,
|
||||
id,
|
||||
height = 500,
|
||||
defaultTab = "html",
|
||||
preview = true,
|
||||
editable = false,
|
||||
className,
|
||||
}: CodePenEmbedProps) => {
|
||||
return (
|
||||
<div className={clsx(styles.wrapper, className)} style={{ height }}>
|
||||
<IFrame
|
||||
src={`https://codepen.io/${username}/embed/${id}/?${new URLSearchParams({
|
||||
"default-tab": `${defaultTab},result`,
|
||||
preview: `${!!preview}`,
|
||||
editable: `${!!editable}`,
|
||||
})}`}
|
||||
height={height}
|
||||
allowScripts
|
||||
noScroll
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodePenEmbed;
|
@ -1,2 +0,0 @@
|
||||
export * from "./CodePenEmbed";
|
||||
export { default } from "./CodePenEmbed";
|
@ -1,6 +0,0 @@
|
||||
.comments {
|
||||
margin-top: 2em;
|
||||
padding-top: 2em;
|
||||
border-top: 2px solid var(--colors-light);
|
||||
min-height: 360px;
|
||||
}
|
@ -1,44 +1,37 @@
|
||||
"use client";
|
||||
|
||||
import Giscus from "@giscus/react";
|
||||
import clsx from "clsx";
|
||||
import useTheme from "../../hooks/useTheme";
|
||||
import config from "../../lib/config";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
import type { GiscusProps } from "@giscus/react";
|
||||
|
||||
import styles from "./Comments.module.css";
|
||||
|
||||
export type CommentsProps = ComponentPropsWithoutRef<"div"> & {
|
||||
export type CommentsProps = {
|
||||
title: string;
|
||||
};
|
||||
|
||||
const Comments = ({ title, className, ...rest }: CommentsProps) => {
|
||||
const { theme } = useTheme();
|
||||
|
||||
const Comments = ({ title }: CommentsProps) => {
|
||||
// fail silently if giscus isn't configured
|
||||
if (!config.giscusConfig) {
|
||||
console.warn("Giscus isn't configured in lib/config/index.js.");
|
||||
console.warn(
|
||||
"[giscus] not configured, ensure giscusConfig.repoId and giscusConfig.categoryId are set in lib/config/index.js"
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: use custom `<Loading />` spinner component during suspense
|
||||
return (
|
||||
<div className={clsx(styles.comments, className)} {...rest}>
|
||||
<Giscus
|
||||
repo={config.githubRepo as GiscusProps["repo"]}
|
||||
repoId={config.giscusConfig.repoId}
|
||||
term={title}
|
||||
category="Comments"
|
||||
categoryId={config.giscusConfig.categoryId}
|
||||
mapping="specific"
|
||||
reactionsEnabled="1"
|
||||
emitMetadata="0"
|
||||
inputPosition="top"
|
||||
loading="lazy"
|
||||
theme={theme === "dark" ? theme : "light"}
|
||||
/>
|
||||
</div>
|
||||
<Giscus
|
||||
repo={config.githubRepo as GiscusProps["repo"]}
|
||||
repoId={config.giscusConfig.repoId}
|
||||
term={title}
|
||||
category="Comments"
|
||||
categoryId={config.giscusConfig.categoryId}
|
||||
mapping="specific"
|
||||
reactionsEnabled="1"
|
||||
emitMetadata="0"
|
||||
inputPosition="top"
|
||||
loading="lazy"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
43
components/Gist/Gist.tsx
Normal file
43
components/Gist/Gist.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import Link from "../Link";
|
||||
|
||||
export type GistProps = {
|
||||
id: string;
|
||||
file?: string;
|
||||
};
|
||||
|
||||
const Gist = async ({ id, file }: GistProps) => {
|
||||
const iframeId = `gist-${id}${file ? `-${file}` : ""}`;
|
||||
|
||||
const scriptUrl = `https://gist.github.com/${id}.js${file ? `?file=${file}` : ""}`;
|
||||
const scriptResponse = await fetch(scriptUrl);
|
||||
|
||||
if (!scriptResponse.ok) {
|
||||
console.warn(`[gist] fetching js for https://gist.github.com/${id} failed:`, scriptResponse.statusText);
|
||||
|
||||
return (
|
||||
<p style={{ textAlign: "center" }}>
|
||||
Failed to load gist.{" "}
|
||||
<Link href={`https://gist.github.com/${id}${file ? `?file=${file}` : ""}`}>Try opening it manually?</Link>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
const script = await scriptResponse.text();
|
||||
|
||||
// https://github.com/tleunen/react-gist/blob/master/src/index.js#L29
|
||||
const iframeHtml = `<html><head><base target="_parent"></head><body onload="parent.document.getElementById('${iframeId}').style.height=document.body.scrollHeight + 'px'" style="margin:0"><script>${script}</script></body></html>`;
|
||||
|
||||
return (
|
||||
<iframe
|
||||
width="100%"
|
||||
scrolling="no"
|
||||
id={iframeId}
|
||||
srcDoc={iframeHtml}
|
||||
sandbox="allow-same-origin allow-scripts allow-popups allow-top-navigation-by-user-activation"
|
||||
style={{ border: "0", overflow: "hidden" }}
|
||||
suppressHydrationWarning
|
||||
></iframe>
|
||||
);
|
||||
};
|
||||
|
||||
export default Gist;
|
2
components/Gist/index.ts
Normal file
2
components/Gist/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./Gist";
|
||||
export { default } from "./Gist";
|
@ -1,37 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import Frame from "react-frame-component";
|
||||
import useHasMounted from "../../hooks/useHasMounted";
|
||||
|
||||
export type GistEmbedProps = {
|
||||
id: string;
|
||||
file?: string;
|
||||
};
|
||||
|
||||
const GistEmbed = ({ id, file }: GistEmbedProps) => {
|
||||
const hasMounted = useHasMounted();
|
||||
|
||||
const scriptUrl = `https://gist.github.com/${id}.js${file ? `?file=${file}` : ""}`;
|
||||
const iframeId = file ? `gist-${id}-${file}` : `gist-${id}`;
|
||||
// https://github.com/tleunen/react-gist/blob/master/src/index.js#L29
|
||||
const iframeHtml = `<html><head><base target="_parent"><style>*{font-size:12px;}</style></head><body onload="parent.document.getElementById('${iframeId}').style.height=document.body.scrollHeight + 'px'" style="margin:0;"><script type="text/javascript" src="${scriptUrl}"></script></body></html>`;
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasMounted && (
|
||||
<Frame
|
||||
width="100%"
|
||||
frameBorder={0}
|
||||
scrolling="no"
|
||||
id={iframeId}
|
||||
initialContent={iframeHtml}
|
||||
style={{ height: "0px", overflow: "hidden" }}
|
||||
>
|
||||
<></>
|
||||
</Frame>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GistEmbed;
|
@ -1,2 +0,0 @@
|
||||
export * from "./GistEmbed";
|
||||
export { default } from "./GistEmbed";
|
@ -1,7 +0,0 @@
|
||||
.iframe {
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin: 1em auto;
|
||||
border: 2px solid var(--colors-kindaLight);
|
||||
border-radius: var(--radii-corner);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
import styles from "./IFrame.module.css";
|
||||
|
||||
export type IFrameProps = ComponentPropsWithoutRef<"iframe"> & {
|
||||
src: string;
|
||||
height: number;
|
||||
width?: number; // defaults to 100%
|
||||
allowScripts?: boolean;
|
||||
noScroll?: boolean;
|
||||
};
|
||||
|
||||
const IFrame = ({ src, title, height, width, allowScripts, noScroll, className, style, ...rest }: IFrameProps) => {
|
||||
return (
|
||||
<iframe
|
||||
src={src}
|
||||
title={title}
|
||||
sandbox={allowScripts ? "allow-same-origin allow-scripts allow-popups" : undefined}
|
||||
scrolling={noScroll ? "no" : undefined}
|
||||
loading="lazy"
|
||||
className={clsx(styles.iframe, className)}
|
||||
style={{
|
||||
height: `${height}px`,
|
||||
maxWidth: width ? `${width}px` : "100%",
|
||||
...style,
|
||||
}}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default IFrame;
|
@ -1,2 +0,0 @@
|
||||
export * from "./IFrame";
|
||||
export { default } from "./IFrame";
|
@ -1,16 +0,0 @@
|
||||
.flex {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.default {
|
||||
width: 100%;
|
||||
padding: 1.5em;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: var(--sizes-maxLayoutWidth);
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import Header from "../Header";
|
||||
import Footer from "../Footer";
|
||||
import { SkipToContentLink, SkipToContentTarget } from "../SkipToContent";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
import styles from "./Layout.module.css";
|
||||
|
||||
export type LayoutProps = ComponentPropsWithoutRef<"div">;
|
||||
|
||||
const Layout = ({ className, children, ...rest }: LayoutProps) => {
|
||||
return (
|
||||
<>
|
||||
<SkipToContentLink />
|
||||
|
||||
<div className={clsx(styles.flex, className)} {...rest}>
|
||||
<Header />
|
||||
|
||||
<main className={styles.default}>
|
||||
<SkipToContentTarget />
|
||||
<div className={styles.container}>{children}</div>
|
||||
</main>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
@ -1,2 +0,0 @@
|
||||
export * from "./Layout";
|
||||
export { default } from "./Layout";
|
@ -2,18 +2,18 @@ import clsx from "clsx";
|
||||
import Link from "../Link";
|
||||
import type { Route } from "next";
|
||||
import type { IconType } from "react-icons";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
import styles from "./MenuItem.module.css";
|
||||
|
||||
export type MenuItemProps = {
|
||||
export type MenuItemProps = Omit<ComponentPropsWithoutRef<typeof Link>, "href"> & {
|
||||
text?: string;
|
||||
href?: Route;
|
||||
icon?: IconType;
|
||||
current?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const MenuItem = ({ text, href, icon, current, className }: MenuItemProps) => {
|
||||
const MenuItem = ({ text, href, icon, current, className, ...rest }: MenuItemProps) => {
|
||||
const Icon = icon;
|
||||
|
||||
const item = (
|
||||
@ -32,6 +32,7 @@ const MenuItem = ({ text, href, icon, current, className }: MenuItemProps) => {
|
||||
title={text}
|
||||
plain
|
||||
aria-label={text}
|
||||
{...rest}
|
||||
>
|
||||
{item}
|
||||
</Link>
|
||||
|
@ -1,16 +0,0 @@
|
||||
.octocatLink {
|
||||
margin: 0 0.4em;
|
||||
color: var(--colors-text) !important;
|
||||
}
|
||||
|
||||
.octocatLink:hover,
|
||||
.octocatLink:focus-visible {
|
||||
color: var(--colors-link) !important;
|
||||
}
|
||||
|
||||
.octocat {
|
||||
display: inline;
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
vertical-align: -0.2em;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import Link from "../Link";
|
||||
import { SiGithub } from "react-icons/si";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
import styles from "./OctocatLink.module.css";
|
||||
import clsx from "clsx";
|
||||
|
||||
export type OctocatLinkProps = Omit<ComponentPropsWithoutRef<typeof Link>, "href"> & {
|
||||
repo: string;
|
||||
};
|
||||
|
||||
const OctocatLink = ({ repo, className, ...rest }: OctocatLinkProps) => {
|
||||
return (
|
||||
<Link href={`https://github.com/${repo}`} plain className={styles.octocatLink} {...rest}>
|
||||
<SiGithub className={clsx(styles.octocat, className)} />
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default OctocatLink;
|
@ -1,2 +0,0 @@
|
||||
export * from "./OctocatLink";
|
||||
export { default } from "./OctocatLink";
|
@ -1,22 +1,22 @@
|
||||
"use client";
|
||||
|
||||
import useHasMounted from "../../hooks/useHasMounted";
|
||||
import { useHasMounted } from "../../hooks";
|
||||
import { formatDate, formatTimeAgo } from "../../lib/helpers/format-date";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
export type RelativeTimeProps = {
|
||||
export type RelativeTimeProps = ComponentPropsWithoutRef<"time"> & {
|
||||
date: string | number | Date;
|
||||
verb?: string; // optional "Updated", "Published", "Created", etc.
|
||||
staticFormat?: string; // format for the placeholder/fallback before client-side renders the relative time
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const RelativeTime = ({ date, verb, staticFormat, className }: RelativeTimeProps) => {
|
||||
const RelativeTime = ({ date, verb, staticFormat, ...rest }: RelativeTimeProps) => {
|
||||
// play nice with SSR -- only use relative time on the client, since it'll quickly become outdated on the server and
|
||||
// cause a react hydration mismatch error.
|
||||
const hasMounted = useHasMounted();
|
||||
|
||||
return (
|
||||
<time dateTime={formatDate(date)} title={formatDate(date, "MMM D, YYYY, h:mm A z")} className={className}>
|
||||
<time dateTime={formatDate(date)} title={formatDate(date, "MMM D, YYYY, h:mm A z")} {...rest}>
|
||||
{verb && `${verb} `}
|
||||
{hasMounted ? formatTimeAgo(date, { suffix: true }) : `on ${formatDate(date, staticFormat)}`}
|
||||
</time>
|
||||
|
@ -2,9 +2,7 @@
|
||||
|
||||
import { useEffect, useId } from "react";
|
||||
import { animated, Globals, useSpring, useReducedMotion } from "@react-spring/web";
|
||||
import useFirstMountState from "../../hooks/useFirstMountState";
|
||||
import useTheme from "../../hooks/useTheme";
|
||||
import useHasMounted from "../../hooks/useHasMounted";
|
||||
import { useFirstMountState, useHasMounted, useTheme } from "../../hooks";
|
||||
|
||||
import styles from "./ThemeToggle.module.css";
|
||||
|
||||
|
@ -5,14 +5,14 @@ import { getTweet } from "react-tweet/api";
|
||||
import clsx from "clsx";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
import styles from "./TweetEmbed.module.css";
|
||||
import styles from "./Tweet.module.css";
|
||||
|
||||
export type TweetEmbedProps = Omit<ComponentPropsWithoutRef<typeof EmbeddedTweet>, "tweet"> & {
|
||||
export type TweetProps = Omit<ComponentPropsWithoutRef<typeof EmbeddedTweet>, "tweet"> & {
|
||||
id: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const TweetEmbed = async ({ id, className, ...rest }: TweetEmbedProps) => {
|
||||
const Tweet = async ({ id, className, ...rest }: TweetProps) => {
|
||||
try {
|
||||
const tweet = await getTweet(id);
|
||||
|
||||
@ -40,8 +40,12 @@ const TweetEmbed = async ({ id, className, ...rest }: TweetEmbedProps) => {
|
||||
} catch (
|
||||
error // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
) {
|
||||
return <TweetNotFound />;
|
||||
return (
|
||||
<div className={clsx(styles.tweet, className)}>
|
||||
<TweetNotFound />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default TweetEmbed;
|
||||
export default Tweet;
|
2
components/Tweet/index.ts
Normal file
2
components/Tweet/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./Tweet";
|
||||
export { default } from "./Tweet";
|
@ -1,2 +0,0 @@
|
||||
export * from "./TweetEmbed";
|
||||
export { default } from "./TweetEmbed";
|
@ -4,42 +4,43 @@ import type { ComponentPropsWithoutRef } from "react";
|
||||
import styles from "./Video.module.css";
|
||||
|
||||
export type VideoProps = Omit<Partial<ComponentPropsWithoutRef<"video">>, "src"> & {
|
||||
src: {
|
||||
// at least one is required:
|
||||
webm?: string;
|
||||
mp4?: string;
|
||||
// optional:
|
||||
vtt?: string;
|
||||
};
|
||||
src: string[];
|
||||
poster?: string;
|
||||
autoplay?: boolean;
|
||||
responsive?: boolean;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const Video = ({ src, poster, autoplay = false, responsive = true, className, ...rest }: VideoProps) => {
|
||||
if (!src || (!src.mp4 && !src.webm)) {
|
||||
throw new Error("'src' prop must include either 'mp4' or 'webm' URL.");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clsx(styles.wrapper, responsive && styles.responsive, className)}>
|
||||
<video
|
||||
width="100%"
|
||||
height="100%"
|
||||
className={styles.player}
|
||||
preload={autoplay ? "auto" : "metadata"}
|
||||
controls={!autoplay}
|
||||
autoPlay={autoplay || undefined}
|
||||
playsInline={autoplay} // safari autoplay workaround
|
||||
loop={autoplay || undefined}
|
||||
muted={autoplay || undefined}
|
||||
poster={poster}
|
||||
{...(autoplay
|
||||
? {
|
||||
preload: "auto",
|
||||
controls: false,
|
||||
autoPlay: true,
|
||||
playsInline: true, // safari autoplay workaround
|
||||
loop: true,
|
||||
muted: true,
|
||||
}
|
||||
: {
|
||||
preload: "metadata",
|
||||
controls: true,
|
||||
})}
|
||||
{...rest}
|
||||
>
|
||||
{src.webm && <source key={src.webm} src={src.webm} type="video/webm" />}
|
||||
{src.mp4 && <source key={src.mp4} src={src.mp4} type="video/mp4" />}
|
||||
{src.vtt && <track key={src.vtt} kind="subtitles" src={src.vtt} srcLang="en" label="English" default />}
|
||||
{src.map((file) => {
|
||||
const extension = file.split(".").pop();
|
||||
if (extension === "vtt") {
|
||||
return <track key={file} kind="subtitles" src={file} srcLang="en" label="English" default />;
|
||||
} else {
|
||||
return <source key={file} src={file} type={`video/${file.split(".").pop()}`} />;
|
||||
}
|
||||
})}
|
||||
</video>
|
||||
</div>
|
||||
);
|
||||
|
3
components/YouTube/YouTube.module.css
Normal file
3
components/YouTube/YouTube.module.css
Normal file
@ -0,0 +1,3 @@
|
||||
.wrapper :global(lite-youtube) {
|
||||
margin: 0 auto;
|
||||
}
|
17
components/YouTube/YouTube.tsx
Normal file
17
components/YouTube/YouTube.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
import { YouTubeEmbed } from "@next/third-parties/google";
|
||||
|
||||
import styles from "./YouTube.module.css";
|
||||
|
||||
export type YouTubeProps = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const YouTube = ({ id }: YouTubeProps) => {
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<YouTubeEmbed videoid={id} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default YouTube;
|
2
components/YouTube/index.ts
Normal file
2
components/YouTube/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./YouTube";
|
||||
export { default } from "./YouTube";
|
@ -1,15 +0,0 @@
|
||||
.wrapper {
|
||||
position: relative;
|
||||
padding-top: 56.25%; /* ratio of 1280x720 */
|
||||
}
|
||||
|
||||
.player {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.player .react-player__preview,
|
||||
.player iframe {
|
||||
border-radius: var(--radii-corner);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
import clsx from "clsx";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
|
||||
import styles from "./YouTubeEmbed.module.css";
|
||||
|
||||
export type YouTubeEmbedProps = ComponentPropsWithoutRef<"div"> & {
|
||||
id: string;
|
||||
};
|
||||
|
||||
const YouTubeEmbed = ({ id, className, ...rest }: YouTubeEmbedProps) => {
|
||||
return (
|
||||
<div className={clsx(styles.wrapper, className)} {...rest}>
|
||||
<iframe
|
||||
src={`https://www.youtube-nocookie.com/embed/${id}`}
|
||||
className={styles.player}
|
||||
width="100%"
|
||||
height="100%"
|
||||
frameBorder="0"
|
||||
loading="lazy"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default YouTubeEmbed;
|
@ -1,2 +0,0 @@
|
||||
export * from "./YouTubeEmbed";
|
||||
export { default } from "./YouTubeEmbed";
|
Reference in New Issue
Block a user