Skip to content

Commit 9e2590a

Browse files
committed
functioning
1 parent 9e61824 commit 9e2590a

10 files changed

Lines changed: 1090 additions & 336 deletions

File tree

components/portable-text.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const BlockTable = dynamic(() => import("@/components/block-table"));
2727
const BlockYoutube = dynamic(() =>
2828
import("@/components/youtube").then((mod) => mod.YouTube),
2929
);
30+
const YouTubeShorts = dynamic(() => import("./youtube-shorts").then((mod) => mod.YouTubeShorts));
3031

3132
export default function CustomPortableText({
3233
className,
@@ -43,6 +44,7 @@ export default function CustomPortableText({
4344
codepen: ({ value }) => <CodePenEmbed {...value} />,
4445
codesandbox: ({ value }) => <CodeSandboxEmbed {...value} />,
4546
youtube: ({ value }) => <div className="not-prose"><BlockYoutube {...value} /></div>,
47+
youtubeShorts: ({ value }) => <div className="not-prose"><YouTubeShorts {...value} /></div>,
4648
twitter: ({ value }) => <TwitterEmbed {...value} />,
4749
htmlBlock: ({ value }) => <HTMLEmbed {...value} />,
4850
quote: ({ value }) => <QuoteEmbed {...value} />,

components/youtube-short-embed.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"use client";
2+
3+
import { youtubeParser } from "@/lib/utils";
4+
import { useState } from "react";
5+
6+
export function YouTubeShortEmbed(props: {
7+
youtube: string;
8+
children?: React.ReactNode;
9+
}) {
10+
const { youtube, children } = props;
11+
const [loadEmbed, setLoadEmbed] = useState(false);
12+
const id = youtubeParser(youtube);
13+
14+
return (
15+
<div
16+
style={{
17+
aspectRatio: "9 / 16",
18+
position: "relative",
19+
isolation: "isolate",
20+
}}
21+
>
22+
<div
23+
className="absolute -z-10"
24+
style={{
25+
position: "absolute",
26+
zIndex: -1,
27+
top: 0,
28+
left: 0,
29+
height: "100%",
30+
width: "100%",
31+
}}
32+
>
33+
{children}
34+
<button
35+
type="button"
36+
onClick={() => setLoadEmbed(true)}
37+
aria-label="Play"
38+
style={{
39+
border: "0",
40+
background: "none",
41+
position: "absolute",
42+
top: 0,
43+
left: 0,
44+
display: "grid",
45+
width: "100%",
46+
height: "100%",
47+
placeItems: "center",
48+
}}
49+
>
50+
<svg version="1.1" viewBox="0 0 68 48" height="48" width="68">
51+
<path
52+
d="M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z"
53+
fill="#f00"
54+
/>
55+
<path d="M 45,24 27,14 27,34" fill="#fff" />
56+
</svg>
57+
</button>
58+
</div>
59+
{loadEmbed && (
60+
<iframe
61+
style={{ height: "100%", width: "100%", border: 0 }}
62+
src={`https://www.youtube-nocookie.com/embed/${id}?autoplay=1&fs=0`}
63+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
64+
allowFullScreen
65+
title="YouTube video player"
66+
/>
67+
)}
68+
</div>
69+
);
70+
}

components/youtube-short.tsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import Image from "next/image";
2+
import { youtubeParser } from "@/lib/utils";
3+
import type { CloudinaryAsset } from "@/sanity/types";
4+
import CoverImage from "@/components/cover-image";
5+
import { YouTubeShortEmbed } from "./youtube-short-embed";
6+
7+
export function YouTubeShort(props: {
8+
youtube: string;
9+
image?: CloudinaryAsset | null | undefined;
10+
className?: string;
11+
}) {
12+
const { youtube, image, className } = props;
13+
const id = youtubeParser(youtube);
14+
15+
return (
16+
<YouTubeShortEmbed youtube={youtube}>
17+
{image?.public_id ? (
18+
<CoverImage image={image} priority={true} className={className} />
19+
) : (
20+
<picture>
21+
<source
22+
type="image/webp"
23+
srcSet={[
24+
`https://i.ytimg.com/vi_webp/${id}/mqdefault.webp 320w`,
25+
`https://i.ytimg.com/vi_webp/${id}/hqdefault.webp 480w`,
26+
`https://i.ytimg.com/vi_webp/${id}/sddefault.webp 640w`,
27+
].join(", ")}
28+
/>
29+
<Image
30+
style={{ width: "100%", height: "100%", objectFit: "cover" }}
31+
src={`https://i.ytimg.com/vi/${id}/sddefault.jpg`}
32+
alt=""
33+
width={360}
34+
height={640}
35+
/>
36+
</picture>
37+
)}
38+
</YouTubeShortEmbed>
39+
);
40+
}

components/youtube-shorts.tsx

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use client'
2+
3+
import React from 'react'
4+
import { Card, CardContent } from "@/components/ui/card"
5+
import {
6+
Carousel,
7+
CarouselContent,
8+
CarouselItem,
9+
CarouselNext,
10+
CarouselPrevious,
11+
} from "@/components/ui/carousel"
12+
import { YouTubeShort } from './youtube-short'
13+
import Autoplay from "embla-carousel-autoplay"
14+
15+
export function YouTubeShorts(props: { shorts: string[] }) {
16+
const { shorts } = props
17+
18+
if (shorts.length === 1) {
19+
return (
20+
<div className='flex items-center justify-center w-full'>
21+
<div className="aspect-w-9 aspect-h-16 w-full max-w-xs">
22+
<YouTubeShort youtube={shorts[0]} />
23+
</div>
24+
</div>
25+
)
26+
}
27+
28+
return (
29+
<div className='flex items-center justify-center w-full'>
30+
31+
<Carousel
32+
className="w-full max-w-xs"
33+
plugins={[
34+
Autoplay({
35+
delay: 2000,
36+
}),
37+
]}
38+
>
39+
<CarouselContent>
40+
{shorts.map((short, index) => (
41+
<CarouselItem key={index}>
42+
43+
<YouTubeShort youtube={short} />
44+
45+
</CarouselItem>
46+
))}
47+
</CarouselContent>
48+
<CarouselPrevious />
49+
<CarouselNext />
50+
</Carousel>
51+
</div>
52+
)
53+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"clsx": "^2.1.1",
7070
"cmdk": "^1.1.1",
7171
"date-fns": "^4.1.0",
72+
"embla-carousel-autoplay": "^8.6.0",
7273
"embla-carousel-react": "^8.6.0",
7374
"feed": "^5.1.0",
7475
"input-otp": "^1.4.2",

pnpm-lock.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)