getting closer 👀
This commit is contained in:
parent
8cb66a96dd
commit
6b0efea07c
|
@ -1,4 +1,8 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {}
|
const nextConfig = {
|
||||||
|
images: {
|
||||||
|
domains: ["cdn.sanity.io"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = nextConfig
|
module.exports = nextConfig;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.4",
|
"@radix-ui/react-dropdown-menu": "^2.0.4",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-navigation-menu": "^1.1.2",
|
"@radix-ui/react-navigation-menu": "^1.1.2",
|
||||||
|
"@radix-ui/react-tooltip": "^1.0.6",
|
||||||
"@sanity/image-url": "^1.0.2",
|
"@sanity/image-url": "^1.0.2",
|
||||||
"@sanity/vision": "^3.11.2",
|
"@sanity/vision": "^3.11.2",
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
|
@ -31,6 +32,7 @@
|
||||||
"lucide-react": "^0.220.0",
|
"lucide-react": "^0.220.0",
|
||||||
"next": "13.4.3",
|
"next": "13.4.3",
|
||||||
"next-sanity": "^4.3.3",
|
"next-sanity": "^4.3.3",
|
||||||
|
"next-sanity-image": "^6.0.0",
|
||||||
"postcss": "8.4.23",
|
"postcss": "8.4.23",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
|
|
@ -1,72 +1,80 @@
|
||||||
import {defineField, defineType} from 'sanity'
|
import { defineField, defineType } from "sanity";
|
||||||
|
|
||||||
export default defineType({
|
export default defineType({
|
||||||
name: 'post',
|
name: "post",
|
||||||
title: 'Post',
|
title: "Post",
|
||||||
type: 'document',
|
type: "document",
|
||||||
fields: [
|
fields: [
|
||||||
defineField({
|
defineField({
|
||||||
name: 'title',
|
name: "title",
|
||||||
title: 'Title',
|
title: "Title",
|
||||||
type: 'string',
|
type: "string",
|
||||||
}),
|
}),
|
||||||
defineField({
|
defineField({
|
||||||
name: 'slug',
|
name: "subtitle",
|
||||||
title: 'Slug',
|
title: "Subtitle",
|
||||||
type: 'slug',
|
type: "string",
|
||||||
|
}),
|
||||||
|
defineField({
|
||||||
|
name: "slug",
|
||||||
|
title: "Slug",
|
||||||
|
type: "slug",
|
||||||
options: {
|
options: {
|
||||||
source: 'title',
|
source: "title",
|
||||||
maxLength: 96,
|
maxLength: 96,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
defineField({
|
defineField({
|
||||||
name: 'author',
|
name: "author",
|
||||||
title: 'Author',
|
title: "Author",
|
||||||
type: 'reference',
|
type: "reference",
|
||||||
to: {type: 'author'},
|
to: { type: "author" },
|
||||||
}),
|
}),
|
||||||
defineField({
|
defineField({
|
||||||
name: 'mainImage',
|
name: "mainImage",
|
||||||
title: 'Main image',
|
title: "Main image",
|
||||||
type: 'image',
|
type: "image",
|
||||||
options: {
|
options: {
|
||||||
hotspot: true,
|
hotspot: true,
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'alt',
|
name: "alt",
|
||||||
type: 'string',
|
type: "string",
|
||||||
title: 'Alternative Text',
|
title: "Alternative Text",
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
}),
|
}),
|
||||||
defineField({
|
defineField({
|
||||||
name: 'categories',
|
name: "categories",
|
||||||
title: 'Categories',
|
title: "Categories",
|
||||||
type: 'array',
|
type: "array",
|
||||||
of: [{type: 'reference', to: {type: 'category'}}],
|
of: [{ type: "reference", to: { type: "category" } }],
|
||||||
}),
|
}),
|
||||||
defineField({
|
defineField({
|
||||||
name: 'publishedAt',
|
name: "publishedAt",
|
||||||
title: 'Published at',
|
title: "Published at",
|
||||||
type: 'datetime',
|
type: "datetime",
|
||||||
}),
|
}),
|
||||||
defineField({
|
defineField({
|
||||||
name: 'body',
|
name: "content",
|
||||||
title: 'Body',
|
title: "Body",
|
||||||
type: 'blockContent',
|
type: "markdown",
|
||||||
|
options: {
|
||||||
|
imageUrl: (imageAsset) => `${imageAsset.url}`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
preview: {
|
preview: {
|
||||||
select: {
|
select: {
|
||||||
title: 'title',
|
title: "title",
|
||||||
author: 'author.name',
|
author: "author.name",
|
||||||
media: 'mainImage',
|
media: "mainImage",
|
||||||
},
|
},
|
||||||
prepare(selection) {
|
prepare(selection) {
|
||||||
const {author} = selection
|
const { author } = selection;
|
||||||
return {...selection, subtitle: author && `by ${author}`}
|
return { ...selection, subtitle: author && `by ${author}` };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
|
@ -60,6 +60,9 @@ export default defineType({
|
||||||
name: "content",
|
name: "content",
|
||||||
title: "Body",
|
title: "Body",
|
||||||
type: "markdown",
|
type: "markdown",
|
||||||
|
options: {
|
||||||
|
imageUrl: (imageAsset) => `${imageAsset.url}`,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
122
src/app/@blogpost/(.)blog/[id]/page.tsx
Normal file
122
src/app/@blogpost/(.)blog/[id]/page.tsx
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import * as Dialog from "@radix-ui/react-dialog";
|
||||||
|
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { q } from "groqd";
|
||||||
|
import { client } from "../../../../../sanity/lib/client";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
|
import rehypeRaw from "rehype-raw";
|
||||||
|
import CodeBlock from "@/components/Codeblock";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { useNextSanityImage } from "next-sanity-image";
|
||||||
|
|
||||||
|
type BlogPost = {
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
slug: string;
|
||||||
|
publishedAt: Date;
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function BlogPostModal({
|
||||||
|
params: { id: slug },
|
||||||
|
}: {
|
||||||
|
params: {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
}) {
|
||||||
|
const router = useRouter();
|
||||||
|
const [post, setPost] = React.useState<BlogPost | null>(null);
|
||||||
|
const handleOpenChange = (open: boolean) => {
|
||||||
|
if (!open) {
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
async function getPost() {
|
||||||
|
const { query, schema } = q("*")
|
||||||
|
.filterByType("post")
|
||||||
|
.filter(`slug.current == "${slug}"`)
|
||||||
|
.grab$({
|
||||||
|
title: q.string(),
|
||||||
|
subtitle: q.string(),
|
||||||
|
slug: q.slug("slug"),
|
||||||
|
publishedAt: q.date(),
|
||||||
|
content: q.string(),
|
||||||
|
})
|
||||||
|
.slice(0, 1);
|
||||||
|
|
||||||
|
const post = schema.parse(await client.fetch(query));
|
||||||
|
|
||||||
|
setPost(post[0]);
|
||||||
|
}
|
||||||
|
getPost();
|
||||||
|
}, [slug]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog.Root open onOpenChange={handleOpenChange}>
|
||||||
|
<Dialog.Portal>
|
||||||
|
<Dialog.Overlay className="bg-zinc-900 opacity-75 data-[state=open]:animate-overlayShow fixed inset-0" />
|
||||||
|
<Dialog.Content className="data-[state=open]:animate-contentShow overflow-y-scroll fixed top-[50%] left-[50%] w-[90vw] max-h-[85vh] max-w-[50vw] translate-x-[-50%] translate-y-[-50%] rounded-[6px] bg-white dark:bg-zinc-800 px-8 py-12 shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] focus:outline-none">
|
||||||
|
<Dialog.Title
|
||||||
|
className={cn(
|
||||||
|
"dark:text-white text-indigo-600 m-0 text-6xl font-bold",
|
||||||
|
!post && "bg-gray-500 animate-pulse block w-52 h-5"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{post?.title}
|
||||||
|
</Dialog.Title>
|
||||||
|
<Dialog.Description
|
||||||
|
className={cn(
|
||||||
|
"dark:text-white text-indigo-500 font-semibold mt-[10px] mb-5 text-2xl leading-normal",
|
||||||
|
!post && "bg-gray-500 animate-pulse block w-72 h-5"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{post?.subtitle}
|
||||||
|
</Dialog.Description>
|
||||||
|
|
||||||
|
<article className="prose dark:prose-invert prose-zinc max-w-none lg:prose-xl">
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
|
rehypePlugins={[rehypeRaw]}
|
||||||
|
components={
|
||||||
|
{
|
||||||
|
code: CodeBlock,
|
||||||
|
img: ({ ...props }) => {
|
||||||
|
const { width, height } = props.src.match(
|
||||||
|
/(?<width>\d+)x(?<height>\d+)/
|
||||||
|
).groups;
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src={props.src}
|
||||||
|
alt={props.alt}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{post?.content || ""}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<Dialog.Close asChild>
|
||||||
|
<button
|
||||||
|
className="text-violet11 hover:bg-violet4 focus:shadow-violet7 absolute top-12 right-8 inline-flex h-[25px] w-[25px] appearance-none items-center justify-center rounded-full focus:shadow-[0_0_0_2px] focus:outline-none"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
<Cross2Icon />
|
||||||
|
</button>
|
||||||
|
</Dialog.Close>
|
||||||
|
</Dialog.Content>
|
||||||
|
</Dialog.Portal>
|
||||||
|
</Dialog.Root>
|
||||||
|
);
|
||||||
|
}
|
3
src/app/@blogpost/default.tsx
Normal file
3
src/app/@blogpost/default.tsx
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export default function Default() {
|
||||||
|
return null;
|
||||||
|
}
|
|
@ -11,13 +11,14 @@ import ReactMarkdown from "react-markdown";
|
||||||
import remarkGfm from "remark-gfm";
|
import remarkGfm from "remark-gfm";
|
||||||
import rehypeRaw from "rehype-raw";
|
import rehypeRaw from "rehype-raw";
|
||||||
import CodeBlock from "@/components/Codeblock";
|
import CodeBlock from "@/components/Codeblock";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { useNextSanityImage } from "next-sanity-image";
|
||||||
|
|
||||||
type Project = {
|
type Project = {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle: string;
|
subtitle: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
publishedAt: Date;
|
publishedAt: Date;
|
||||||
mainImage: string;
|
|
||||||
content: string;
|
content: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,7 +47,6 @@ export default function ProjectModal({
|
||||||
subtitle: q.string(),
|
subtitle: q.string(),
|
||||||
slug: q.slug("slug"),
|
slug: q.slug("slug"),
|
||||||
publishedAt: q.date(),
|
publishedAt: q.date(),
|
||||||
mainImage: q("mainImage").grabOne$("asset->url", q.string()),
|
|
||||||
content: q.string(),
|
content: q.string(),
|
||||||
})
|
})
|
||||||
.slice(0, 1);
|
.slice(0, 1);
|
||||||
|
@ -87,6 +87,19 @@ export default function ProjectModal({
|
||||||
components={
|
components={
|
||||||
{
|
{
|
||||||
code: CodeBlock,
|
code: CodeBlock,
|
||||||
|
img: ({ ...props }) => {
|
||||||
|
const { width, height } = props.src.match(
|
||||||
|
/(?<width>\d+)x(?<height>\d+)/
|
||||||
|
).groups;
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src={props.src}
|
||||||
|
alt={props.alt}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
} as any
|
} as any
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
51
src/app/about/page.tsx
Normal file
51
src/app/about/page.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import Logo from "@/components/Logo";
|
||||||
|
import SidecardList from "@/components/SidecardList";
|
||||||
|
import { IdCardIcon } from "@radix-ui/react-icons";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-5 gap-4 mx-auto max-w-7xl">
|
||||||
|
<div className="col-span-4 space-y-3">
|
||||||
|
<h1 className="font-black text-8xl">Hey! I'm Jack Merrill.</h1>
|
||||||
|
|
||||||
|
<p className="text-2xl">
|
||||||
|
I'm a software engineer, designer, and student from the United
|
||||||
|
States. I'm working to bring accessible designs to the masses.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full h-full col-span-1 row-span-3 px-8 py-4 space-y-12 rounded-md bg-violet-500">
|
||||||
|
<div className="flex justify-center w-full">
|
||||||
|
<div className="h-auto p-12 border-2 border-white rounded-md">
|
||||||
|
<Logo />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SidecardList />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-span-4 space-y-3">
|
||||||
|
<h2 className="text-6xl font-black">About Me</h2>
|
||||||
|
|
||||||
|
<p className="text-2xl">
|
||||||
|
I'm a Division II (sophomore) student at Hampshire College,
|
||||||
|
studying interaction design. I'm also a full-stack web developer
|
||||||
|
at{" "}
|
||||||
|
<Link
|
||||||
|
rel="noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
href="https://merch.co"
|
||||||
|
className="font-bold text-violet-500"
|
||||||
|
>
|
||||||
|
Merch
|
||||||
|
</Link>
|
||||||
|
.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-2xl"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
86
src/app/blog/[id]/page.tsx
Normal file
86
src/app/blog/[id]/page.tsx
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import { q } from "groqd";
|
||||||
|
import { client } from "../../../../sanity/lib/client";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import CodeBlock from "@/components/Codeblock";
|
||||||
|
import Image from "next/image";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
|
import rehypeRaw from "rehype-raw";
|
||||||
|
|
||||||
|
export default async function Page({
|
||||||
|
params: { id: slug },
|
||||||
|
}: {
|
||||||
|
params: { id: string };
|
||||||
|
}) {
|
||||||
|
const { query, schema } = q("*")
|
||||||
|
.filterByType("post")
|
||||||
|
.filter(`slug.current == "${slug}"`)
|
||||||
|
.grab$({
|
||||||
|
title: q.string(),
|
||||||
|
subtitle: q.string(),
|
||||||
|
slug: q.slug("slug"),
|
||||||
|
publishedAt: q.date(),
|
||||||
|
content: q.string(),
|
||||||
|
mainImage: q("mainImage").grabOne$("asset->url", q.string()),
|
||||||
|
})
|
||||||
|
.slice(0, 1);
|
||||||
|
|
||||||
|
const post = schema.parse(await client.fetch(query))[0];
|
||||||
|
|
||||||
|
const r = post.mainImage.match(/(?<width>\d+)x(?<height>\d+)/);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-screen py-2">
|
||||||
|
<div className="flex items-center justify-center w-full py-24">
|
||||||
|
{/* the mainimage with the text on top of it */}
|
||||||
|
<div className="relative w-full h-96">
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center w-full h-full bg-black bg-opacity-50">
|
||||||
|
<div className="flex flex-col items-center justify-center space-y-4">
|
||||||
|
<h1 className="text-4xl font-bold text-center text-white">
|
||||||
|
{post.title}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h2 className="text-2xl font-semibold text-center text-white">
|
||||||
|
{post.subtitle}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Image
|
||||||
|
className="object-cover w-full h-full"
|
||||||
|
src={post.mainImage}
|
||||||
|
alt={post.title}
|
||||||
|
width={parseInt(r?.groups?.width ?? "400")}
|
||||||
|
height={parseInt(r?.groups?.height ?? "400")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* the content */}
|
||||||
|
<article className="prose dark:prose-invert prose-zinc max-w-none lg:prose-xl">
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
|
rehypePlugins={[rehypeRaw]}
|
||||||
|
components={
|
||||||
|
{
|
||||||
|
code: CodeBlock,
|
||||||
|
img: ({ ...props }) => {
|
||||||
|
const { width, height } = props.src.match(
|
||||||
|
/(?<width>\d+)x(?<height>\d+)/
|
||||||
|
).groups;
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src={props.src}
|
||||||
|
alt={props.alt}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{post?.content || ""}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
86
src/app/blog/page.tsx
Normal file
86
src/app/blog/page.tsx
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import { q } from "groqd";
|
||||||
|
import { client } from "../../../sanity/lib/client";
|
||||||
|
import Twemoji from "@/components/Twemoji";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const { query, schema } = q("*")
|
||||||
|
.filterByType("post")
|
||||||
|
.order("publishedAt desc")
|
||||||
|
.grab$({
|
||||||
|
title: q.string(),
|
||||||
|
subtitle: q.string(),
|
||||||
|
slug: q.slug("slug"),
|
||||||
|
publishedAt: q.date(),
|
||||||
|
mainImage: q("mainImage").grabOne$("asset->url", q.string()),
|
||||||
|
categories: q("categories")
|
||||||
|
.filter()
|
||||||
|
.deref()
|
||||||
|
.grabOne$("title", q.string()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const posts = schema.parse(await client.fetch(query));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<section className="flex items-center px-6 py-48 mx-auto h-2/3 max-w-7xl">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h1 className="flex items-center text-6xl font-bold gap-x-4">
|
||||||
|
<Twemoji emoji="📰" ext="svg" /> Blog
|
||||||
|
</h1>
|
||||||
|
<h2 className="text-2xl font-semibold">
|
||||||
|
My thoughts on web, tech, and life. (other things too)
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className="dark:bg-zinc-900">
|
||||||
|
<section className="grid grid-cols-2 gap-4 py-8 mx-auto max-w-7xl">
|
||||||
|
{posts.map((post) => {
|
||||||
|
const r = post.mainImage.match(/(?<width>\d+)x(?<height>\d+)/);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={post.slug}
|
||||||
|
className="flex flex-col items-center justify-center pb-4 space-y-4 overflow-hidden transition-all duration-150 rounded-md dark:bg-zinc-800 hover:scale-105"
|
||||||
|
href={`/blog/${post.slug}`}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={post.mainImage}
|
||||||
|
alt={post.title}
|
||||||
|
width={parseInt(r?.groups?.width ?? "400")}
|
||||||
|
height={parseInt(r?.groups?.height ?? "400")}
|
||||||
|
/>
|
||||||
|
<h3 className="text-xl font-semibold">{post.title}</h3>
|
||||||
|
<p className="text-lg">{post.subtitle}</p>
|
||||||
|
|
||||||
|
<p className="text-md text-zinc-400">
|
||||||
|
Categories:
|
||||||
|
{post.categories.map((category) => (
|
||||||
|
<span
|
||||||
|
key={category}
|
||||||
|
className="px-2 py-1 ml-2 text-sm font-semibold text-white bg-indigo-500 rounded-md"
|
||||||
|
>
|
||||||
|
{category}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<time
|
||||||
|
className="text-sm text-gray-500"
|
||||||
|
dateTime={post.publishedAt.toISOString()}
|
||||||
|
>
|
||||||
|
Published at{" "}
|
||||||
|
{new Date(post.publishedAt).toLocaleDateString()}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
'use client'
|
"use client";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This route is responsible for the built-in authoring environment using Sanity Studio.
|
* This route is responsible for the built-in authoring environment using Sanity Studio.
|
||||||
|
@ -9,9 +9,11 @@
|
||||||
* https://github.com/sanity-io/next-sanity
|
* https://github.com/sanity-io/next-sanity
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NextStudio } from 'next-sanity/studio'
|
import { NextStudio } from "next-sanity/studio";
|
||||||
import config from '../../../../../sanity.config'
|
import config from "../../../../../sanity.config";
|
||||||
|
|
||||||
|
const isDev = process.env.NODE_ENV === "development" || !process.env.NODE_ENV;
|
||||||
|
|
||||||
export default function StudioPage() {
|
export default function StudioPage() {
|
||||||
return <NextStudio config={config} />
|
return isDev ? <NextStudio config={config} /> : null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,11 @@ export const metadata = {
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({
|
||||||
children,
|
children,
|
||||||
project,
|
project,
|
||||||
|
blogpost,
|
||||||
}: {
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
project?: React.ReactNode;
|
project?: React.ReactNode;
|
||||||
|
blogpost?: React.ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const { query: projectQuery, schema: projectSchema } = q("*")
|
const { query: projectQuery, schema: projectSchema } = q("*")
|
||||||
.filterByType("project")
|
.filterByType("project")
|
||||||
|
@ -37,6 +39,7 @@ export default async function RootLayout({
|
||||||
subtitle: q.string(),
|
subtitle: q.string(),
|
||||||
slug: q.slug("slug"),
|
slug: q.slug("slug"),
|
||||||
publishedAt: q.date(),
|
publishedAt: q.date(),
|
||||||
|
mainImage: q("mainImage").grabOne$("asset->url", q.string()),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { query: blogQuery, schema: blogSchema } = q("*")
|
const { query: blogQuery, schema: blogSchema } = q("*")
|
||||||
|
@ -110,6 +113,7 @@ export default async function RootLayout({
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
{project}
|
{project}
|
||||||
|
{blogpost}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,86 @@
|
||||||
export default function Project() {
|
import { q } from "groqd";
|
||||||
return <p>This is a placeholder for the project page.</p>;
|
import { client } from "../../../../sanity/lib/client";
|
||||||
|
import ReactMarkdown from "react-markdown";
|
||||||
|
import CodeBlock from "@/components/Codeblock";
|
||||||
|
import Image from "next/image";
|
||||||
|
import remarkGfm from "remark-gfm";
|
||||||
|
import rehypeRaw from "rehype-raw";
|
||||||
|
|
||||||
|
export default async function Page({
|
||||||
|
params: { id: slug },
|
||||||
|
}: {
|
||||||
|
params: { id: string };
|
||||||
|
}) {
|
||||||
|
const { query: projectQuery, schema: projectSchema } = q("*")
|
||||||
|
.filterByType("project")
|
||||||
|
.filter(`slug.current == "${slug}"`)
|
||||||
|
.grab$({
|
||||||
|
title: q.string(),
|
||||||
|
subtitle: q.string(),
|
||||||
|
slug: q.slug("slug"),
|
||||||
|
publishedAt: q.date(),
|
||||||
|
content: q.string(),
|
||||||
|
mainImage: q("mainImage").grabOne$("asset->url", q.string()),
|
||||||
|
})
|
||||||
|
.slice(0, 1);
|
||||||
|
|
||||||
|
const project = projectSchema.parse(await client.fetch(projectQuery))[0];
|
||||||
|
|
||||||
|
const r = project.mainImage.match(/(?<width>\d+)x(?<height>\d+)/);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-screen py-2">
|
||||||
|
<div className="flex items-center justify-center w-full py-24">
|
||||||
|
{/* the mainimage with the text on top of it */}
|
||||||
|
<div className="relative w-full h-96">
|
||||||
|
<div className="absolute inset-0 flex items-center justify-center w-full h-full bg-black bg-opacity-50">
|
||||||
|
<div className="flex flex-col items-center justify-center space-y-4">
|
||||||
|
<h1 className="text-4xl font-bold text-center text-white">
|
||||||
|
{project.title}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h2 className="text-2xl font-semibold text-center text-white">
|
||||||
|
{project.subtitle}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Image
|
||||||
|
className="object-cover w-full h-full"
|
||||||
|
src={project.mainImage}
|
||||||
|
alt={project.title}
|
||||||
|
width={parseInt(r?.groups?.width ?? "400")}
|
||||||
|
height={parseInt(r?.groups?.height ?? "400")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* the content */}
|
||||||
|
<article className="prose dark:prose-invert prose-zinc max-w-none lg:prose-xl">
|
||||||
|
<ReactMarkdown
|
||||||
|
remarkPlugins={[remarkGfm]}
|
||||||
|
rehypePlugins={[rehypeRaw]}
|
||||||
|
components={
|
||||||
|
{
|
||||||
|
code: CodeBlock,
|
||||||
|
img: ({ ...props }) => {
|
||||||
|
const { width, height } = props.src.match(
|
||||||
|
/(?<width>\d+)x(?<height>\d+)/
|
||||||
|
).groups;
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src={props.src}
|
||||||
|
alt={props.alt}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
} as any
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{project?.content || ""}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
86
src/app/projects/page.tsx
Normal file
86
src/app/projects/page.tsx
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import { q } from "groqd";
|
||||||
|
import { client } from "../../../sanity/lib/client";
|
||||||
|
import Twemoji from "@/components/Twemoji";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const { query: projectQuery, schema: projectSchema } = q("*")
|
||||||
|
.filterByType("project")
|
||||||
|
.order("publishedAt desc")
|
||||||
|
.grab$({
|
||||||
|
title: q.string(),
|
||||||
|
subtitle: q.string(),
|
||||||
|
slug: q.slug("slug"),
|
||||||
|
publishedAt: q.date(),
|
||||||
|
mainImage: q("mainImage").grabOne$("asset->url", q.string()),
|
||||||
|
categories: q("categories")
|
||||||
|
.filter()
|
||||||
|
.deref()
|
||||||
|
.grabOne$("title", q.string()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const projects = projectSchema.parse(await client.fetch(projectQuery));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<section className="flex items-center px-6 py-48 mx-auto h-2/3 max-w-7xl">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<h1 className="flex items-center text-6xl font-bold gap-x-4">
|
||||||
|
<Twemoji emoji="🛠" ext="svg" /> Projects
|
||||||
|
</h1>
|
||||||
|
<h2 className="text-2xl font-semibold">
|
||||||
|
The weird and wonderful things I work on.
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className="dark:bg-zinc-900">
|
||||||
|
<section className="grid grid-cols-4 gap-4 py-8 mx-auto max-w-7xl">
|
||||||
|
{projects.map((project) => {
|
||||||
|
const r = project.mainImage.match(/(?<width>\d+)x(?<height>\d+)/);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={project.slug}
|
||||||
|
className="flex flex-col items-center justify-center p-6 space-y-4 transition-all duration-150 rounded-md dark:bg-zinc-800 hover:scale-105"
|
||||||
|
href={`/projects/${project.slug}`}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={project.mainImage}
|
||||||
|
alt={project.title}
|
||||||
|
width={parseInt(r?.groups?.width ?? "400")}
|
||||||
|
height={parseInt(r?.groups?.height ?? "400")}
|
||||||
|
/>
|
||||||
|
<h3 className="text-xl font-semibold">{project.title}</h3>
|
||||||
|
<p className="text-lg">{project.subtitle}</p>
|
||||||
|
|
||||||
|
<p className="text-md text-zinc-400">
|
||||||
|
Categories:
|
||||||
|
{project.categories.map((category) => (
|
||||||
|
<span
|
||||||
|
key={category}
|
||||||
|
className="px-2 py-1 ml-2 text-sm font-semibold text-white bg-indigo-500 rounded-md"
|
||||||
|
>
|
||||||
|
{category}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<time
|
||||||
|
className="text-sm text-gray-500"
|
||||||
|
dateTime={project.publishedAt.toISOString()}
|
||||||
|
>
|
||||||
|
Published at{" "}
|
||||||
|
{new Date(project.publishedAt).toLocaleDateString()}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
9
src/components/ClientComponent.tsx
Normal file
9
src/components/ClientComponent.tsx
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
export default function ClientComponent({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
import { CopyIcon } from "@radix-ui/react-icons";
|
import { CopyIcon } from "@radix-ui/react-icons";
|
||||||
import { CopyCheckIcon } from "lucide-react";
|
import { CopyCheckIcon } from "lucide-react";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
|
|
@ -24,6 +24,7 @@ export default function Navbar({
|
||||||
title: string;
|
title: string;
|
||||||
subtitle: string;
|
subtitle: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
|
mainImage: string;
|
||||||
}[];
|
}[];
|
||||||
blogPosts: {
|
blogPosts: {
|
||||||
publishedAt: Date;
|
publishedAt: Date;
|
||||||
|
@ -51,13 +52,8 @@ export default function Navbar({
|
||||||
<NavigationMenu.Root className="relative z-[1] mx-auto w-full flex h-full justify-center font-bold ">
|
<NavigationMenu.Root className="relative z-[1] mx-auto w-full flex h-full justify-center font-bold ">
|
||||||
<NavigationMenu.List className="flex m-0 list-none transition-all duration-150 rounded-full center text-slate-900 dark:text-white bg-slate-300 dark:bg-gray-800 hover:px-2">
|
<NavigationMenu.List className="flex m-0 list-none transition-all duration-150 rounded-full center text-slate-900 dark:text-white bg-slate-300 dark:bg-gray-800 hover:px-2">
|
||||||
<NavigationMenu.Item>
|
<NavigationMenu.Item>
|
||||||
<NavigationMenu.Trigger asChild>
|
<NavigationMenu.Trigger className="hover:bg-gray-700 group flex select-none items-center justify-between gap-[2px] rounded-full h-full px-3 text-[15px] font-medium leading-none outline-none focus:shadow-[0_0_0_2px]">
|
||||||
<Link
|
Projects
|
||||||
href="/projects"
|
|
||||||
className="hover:bg-gray-700 group flex select-none items-center justify-between gap-[2px] rounded-full h-full px-3 text-[15px] font-medium leading-none outline-none focus:shadow-[0_0_0_2px]"
|
|
||||||
>
|
|
||||||
Projects
|
|
||||||
</Link>
|
|
||||||
</NavigationMenu.Trigger>
|
</NavigationMenu.Trigger>
|
||||||
<NavigationMenu.Content className="data-[motion=from-start]:animate-enterFromLeft data-[motion=from-end]:animate-enterFromRight data-[motion=to-start]:animate-exitToLeft data-[motion=to-end]:animate-exitToRight absolute top-0 left-0 w-full sm:w-auto">
|
<NavigationMenu.Content className="data-[motion=from-start]:animate-enterFromLeft data-[motion=from-end]:animate-enterFromRight data-[motion=to-start]:animate-exitToLeft data-[motion=to-end]:animate-exitToRight absolute top-0 left-0 w-full sm:w-auto">
|
||||||
<ul className="m-0 grid list-none gap-x-[10px] p-[22px] sm:w-[500px] sm:grid-cols-5">
|
<ul className="m-0 grid list-none gap-x-[10px] p-[22px] sm:w-[500px] sm:grid-cols-5">
|
||||||
|
@ -89,15 +85,30 @@ export default function Navbar({
|
||||||
<Link
|
<Link
|
||||||
href={`/projects/${project.slug}`}
|
href={`/projects/${project.slug}`}
|
||||||
className={
|
className={
|
||||||
"focus:shadow-[0_0_0_2px] focus:shadow-violet7 hover:bg-mauve3 block select-none rounded-[6px] p-3 text-[15px] leading-none no-underline outline-none transition-colors"
|
"focus:shadow-[0_0_0_2px] grid grid-cols-4 items-center focus:shadow-violet7 hover:bg-mauve3 select-none rounded-[6px] p-3 text-[15px] leading-none no-underline outline-none transition-colors"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="mb-[5px] font-bold leading-[1.2]">
|
<div className="col-span-3">
|
||||||
{project.title}
|
<div className="mb-[5px] font-bold leading-[1.2]">
|
||||||
|
{project.title}
|
||||||
|
</div>
|
||||||
|
<p className="font-medium leading-[1.4]">
|
||||||
|
{project.subtitle}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-medium leading-[1.4]">
|
|
||||||
{project.subtitle}
|
{/* project image */}
|
||||||
</p>
|
{project.mainImage && (
|
||||||
|
<div className="relative w-full h-full overflow-hidden rounded-md">
|
||||||
|
<Image
|
||||||
|
src={project.mainImage}
|
||||||
|
alt=""
|
||||||
|
fill
|
||||||
|
style={{ objectFit: "cover" }}
|
||||||
|
className="h-full rounded-md"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
</NavigationMenu.Link>
|
</NavigationMenu.Link>
|
||||||
</li>
|
</li>
|
||||||
|
@ -159,9 +170,9 @@ export default function Navbar({
|
||||||
<NavigationMenu.Item>
|
<NavigationMenu.Item>
|
||||||
<NavigationMenu.Link
|
<NavigationMenu.Link
|
||||||
className="hover:bg-gray-700 h-full items-center flex select-none rounded-full px-3 py-2 text-[15px] font-medium leading-none no-underline outline-none focus:shadow-[0_0_0_2px]"
|
className="hover:bg-gray-700 h-full items-center flex select-none rounded-full px-3 py-2 text-[15px] font-medium leading-none no-underline outline-none focus:shadow-[0_0_0_2px]"
|
||||||
href="https://github.com/radix-ui"
|
asChild
|
||||||
>
|
>
|
||||||
About
|
<Link href="/about">About</Link>
|
||||||
</NavigationMenu.Link>
|
</NavigationMenu.Link>
|
||||||
</NavigationMenu.Item>
|
</NavigationMenu.Item>
|
||||||
|
|
||||||
|
|
63
src/components/SidecardList.tsx
Normal file
63
src/components/SidecardList.tsx
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Crosshair2Icon, IdCardIcon, PersonIcon } from "@radix-ui/react-icons";
|
||||||
|
import * as Tooltip from "@radix-ui/react-tooltip";
|
||||||
|
|
||||||
|
export default function SidecardList() {
|
||||||
|
return (
|
||||||
|
<Tooltip.Provider>
|
||||||
|
<ul className="flex flex-col gap-4 mt-4">
|
||||||
|
<Tooltip.Root>
|
||||||
|
<Tooltip.Trigger asChild>
|
||||||
|
<li className="flex items-center gap-2 w-min">
|
||||||
|
<PersonIcon className="w-6 h-6" />
|
||||||
|
<span className="ml-2 font-semibold text-md">
|
||||||
|
<span className="sr-only">Age</span>
|
||||||
|
{new Date().getFullYear() - 2004}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
<Tooltip.Portal>
|
||||||
|
<Tooltip.Content className="data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade text-violet11 select-none rounded-[4px] bg-white dark:bg-zinc-600 px-[15px] py-[10px] text-[15px] leading-none shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] will-change-[transform,opacity]">
|
||||||
|
<span className="font-semibold">Age</span>
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Portal>
|
||||||
|
</Tooltip.Root>
|
||||||
|
|
||||||
|
<Tooltip.Root>
|
||||||
|
<Tooltip.Trigger asChild>
|
||||||
|
<li className="flex items-center gap-2 w-min">
|
||||||
|
<IdCardIcon className="w-6 h-6" />
|
||||||
|
<span className="ml-2 font-semibold text-md">
|
||||||
|
<span className="sr-only">Pronouns</span>
|
||||||
|
he/him
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
<Tooltip.Portal>
|
||||||
|
<Tooltip.Content className="data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade text-violet11 select-none rounded-[4px] bg-white dark:bg-zinc-600 px-[15px] py-[10px] text-[15px] leading-none shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] will-change-[transform,opacity]">
|
||||||
|
<span className="font-semibold">Pronouns</span>
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Portal>
|
||||||
|
</Tooltip.Root>
|
||||||
|
|
||||||
|
<Tooltip.Root>
|
||||||
|
<Tooltip.Trigger asChild>
|
||||||
|
<li className="flex items-center w-full gap-2">
|
||||||
|
<Crosshair2Icon className="w-6 h-6" />
|
||||||
|
<span className="ml-2 font-semibold text-md">
|
||||||
|
<span className="sr-only">Location</span>
|
||||||
|
Chicago, IL / Amherst, MA
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</Tooltip.Trigger>
|
||||||
|
<Tooltip.Portal>
|
||||||
|
<Tooltip.Content className="data-[state=delayed-open]:data-[side=top]:animate-slideDownAndFade data-[state=delayed-open]:data-[side=right]:animate-slideLeftAndFade data-[state=delayed-open]:data-[side=left]:animate-slideRightAndFade data-[state=delayed-open]:data-[side=bottom]:animate-slideUpAndFade text-violet11 select-none rounded-[4px] bg-white dark:bg-zinc-600 px-[15px] py-[10px] text-[15px] leading-none shadow-[hsl(206_22%_7%_/_35%)_0px_10px_38px_-10px,_hsl(206_22%_7%_/_20%)_0px_10px_20px_-15px] will-change-[transform,opacity]">
|
||||||
|
<span className="font-semibold">Location</span>
|
||||||
|
</Tooltip.Content>
|
||||||
|
</Tooltip.Portal>
|
||||||
|
</Tooltip.Root>
|
||||||
|
</ul>
|
||||||
|
</Tooltip.Provider>
|
||||||
|
);
|
||||||
|
}
|
|
@ -112,6 +112,22 @@ module.exports = {
|
||||||
from: { opacity: 1 },
|
from: { opacity: 1 },
|
||||||
to: { opacity: 0 },
|
to: { opacity: 0 },
|
||||||
},
|
},
|
||||||
|
slideDownAndFade: {
|
||||||
|
from: { opacity: 0, transform: "translateY(-2px)" },
|
||||||
|
to: { opacity: 1, transform: "translateY(0)" },
|
||||||
|
},
|
||||||
|
slideLeftAndFade: {
|
||||||
|
from: { opacity: 0, transform: "translateX(2px)" },
|
||||||
|
to: { opacity: 1, transform: "translateX(0)" },
|
||||||
|
},
|
||||||
|
slideUpAndFade: {
|
||||||
|
from: { opacity: 0, transform: "translateY(2px)" },
|
||||||
|
to: { opacity: 1, transform: "translateY(0)" },
|
||||||
|
},
|
||||||
|
slideRightAndFade: {
|
||||||
|
from: { opacity: 0, transform: "translateX(-2px)" },
|
||||||
|
to: { opacity: 1, transform: "translateX(0)" },
|
||||||
|
},
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
"accordion-down": "accordion-down 0.2s ease-out",
|
"accordion-down": "accordion-down 0.2s ease-out",
|
||||||
|
@ -125,6 +141,13 @@ module.exports = {
|
||||||
enterFromRight: "enterFromRight 250ms ease",
|
enterFromRight: "enterFromRight 250ms ease",
|
||||||
exitToLeft: "exitToLeft 250ms ease",
|
exitToLeft: "exitToLeft 250ms ease",
|
||||||
exitToRight: "exitToRight 250ms ease",
|
exitToRight: "exitToRight 250ms ease",
|
||||||
|
slideDownAndFade:
|
||||||
|
"slideDownAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)",
|
||||||
|
slideLeftAndFade:
|
||||||
|
"slideLeftAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)",
|
||||||
|
slideUpAndFade: "slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)",
|
||||||
|
slideRightAndFade:
|
||||||
|
"slideRightAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
101
yarn.lock
101
yarn.lock
|
@ -658,6 +658,11 @@
|
||||||
resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.6.tgz"
|
resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.6.tgz"
|
||||||
integrity sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==
|
integrity sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==
|
||||||
|
|
||||||
|
"@floating-ui/core@^1.3.0":
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.3.0.tgz#113bc85fa102cf890ae801668f43ee265c547a09"
|
||||||
|
integrity sha512-vX1WVAdPjZg9DkDkC+zEx/tKtnST6/qcNpwcjeBgco3XRNHz5PUA+ivi/yr6G3o0kMR60uKBJcfOdfzOFI7PMQ==
|
||||||
|
|
||||||
"@floating-ui/dom@^0.5.3":
|
"@floating-ui/dom@^0.5.3":
|
||||||
version "0.5.4"
|
version "0.5.4"
|
||||||
resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz"
|
resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz"
|
||||||
|
@ -672,6 +677,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@floating-ui/core" "^1.2.6"
|
"@floating-ui/core" "^1.2.6"
|
||||||
|
|
||||||
|
"@floating-ui/dom@^1.3.0":
|
||||||
|
version "1.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.3.0.tgz#69456f2164fc3d33eb40837686eaf71537235ac9"
|
||||||
|
integrity sha512-qIAwejE3r6NeA107u4ELDKkH8+VtgRKdXqtSPaKflL2S2V+doyN+Wt9s5oHKXPDo4E8TaVXaHT3+6BbagH31xw==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/core" "^1.3.0"
|
||||||
|
|
||||||
"@floating-ui/react-dom@0.7.2":
|
"@floating-ui/react-dom@0.7.2":
|
||||||
version "0.7.2"
|
version "0.7.2"
|
||||||
resolved "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.2.tgz"
|
resolved "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.2.tgz"
|
||||||
|
@ -687,6 +699,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@floating-ui/dom" "^1.1.0"
|
"@floating-ui/dom" "^1.1.0"
|
||||||
|
|
||||||
|
"@floating-ui/react-dom@^2.0.0":
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.1.tgz#7972a4fc488a8c746cded3cfe603b6057c308a91"
|
||||||
|
integrity sha512-rZtAmSht4Lry6gdhAJDrCp/6rKN7++JnL1/Anbr/DdeyYXQPxvg/ivrbYvJulbRf4vL8b212suwMM2lxbv+RQA==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/dom" "^1.3.0"
|
||||||
|
|
||||||
"@fullhuman/postcss-purgecss@^2.1.2":
|
"@fullhuman/postcss-purgecss@^2.1.2":
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz"
|
resolved "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz"
|
||||||
|
@ -930,6 +949,14 @@
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-primitive" "1.0.2"
|
"@radix-ui/react-primitive" "1.0.2"
|
||||||
|
|
||||||
|
"@radix-ui/react-arrow@1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d"
|
||||||
|
integrity sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
|
||||||
"@radix-ui/react-collection@1.0.2":
|
"@radix-ui/react-collection@1.0.2":
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.2.tgz"
|
||||||
|
@ -1166,6 +1193,23 @@
|
||||||
"@radix-ui/react-use-size" "1.0.0"
|
"@radix-ui/react-use-size" "1.0.0"
|
||||||
"@radix-ui/rect" "1.0.0"
|
"@radix-ui/rect" "1.0.0"
|
||||||
|
|
||||||
|
"@radix-ui/react-popper@1.1.2":
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.2.tgz#4c0b96fcd188dc1f334e02dba2d538973ad842e9"
|
||||||
|
integrity sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@floating-ui/react-dom" "^2.0.0"
|
||||||
|
"@radix-ui/react-arrow" "1.0.3"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-context" "1.0.1"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||||
|
"@radix-ui/react-use-rect" "1.0.1"
|
||||||
|
"@radix-ui/react-use-size" "1.0.1"
|
||||||
|
"@radix-ui/rect" "1.0.1"
|
||||||
|
|
||||||
"@radix-ui/react-portal@1.0.2":
|
"@radix-ui/react-portal@1.0.2":
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz"
|
||||||
|
@ -1248,6 +1292,25 @@
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-compose-refs" "1.0.1"
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
|
||||||
|
"@radix-ui/react-tooltip@^1.0.6":
|
||||||
|
version "1.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.6.tgz#87a7786cd9f2b4de957ac645afae1575339c58b0"
|
||||||
|
integrity sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/primitive" "1.0.1"
|
||||||
|
"@radix-ui/react-compose-refs" "1.0.1"
|
||||||
|
"@radix-ui/react-context" "1.0.1"
|
||||||
|
"@radix-ui/react-dismissable-layer" "1.0.4"
|
||||||
|
"@radix-ui/react-id" "1.0.1"
|
||||||
|
"@radix-ui/react-popper" "1.1.2"
|
||||||
|
"@radix-ui/react-portal" "1.0.3"
|
||||||
|
"@radix-ui/react-presence" "1.0.1"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
"@radix-ui/react-slot" "1.0.2"
|
||||||
|
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||||
|
"@radix-ui/react-visually-hidden" "1.0.3"
|
||||||
|
|
||||||
"@radix-ui/react-use-callback-ref@1.0.0":
|
"@radix-ui/react-use-callback-ref@1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz"
|
||||||
|
@ -1323,6 +1386,14 @@
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/rect" "1.0.0"
|
"@radix-ui/rect" "1.0.0"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-rect@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz#fde50b3bb9fd08f4a1cd204572e5943c244fcec2"
|
||||||
|
integrity sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/rect" "1.0.1"
|
||||||
|
|
||||||
"@radix-ui/react-use-size@1.0.0":
|
"@radix-ui/react-use-size@1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.0.tgz"
|
||||||
|
@ -1331,6 +1402,14 @@
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-use-layout-effect" "1.0.0"
|
"@radix-ui/react-use-layout-effect" "1.0.0"
|
||||||
|
|
||||||
|
"@radix-ui/react-use-size@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
|
||||||
|
integrity sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||||
|
|
||||||
"@radix-ui/react-visually-hidden@1.0.2":
|
"@radix-ui/react-visually-hidden@1.0.2":
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.2.tgz"
|
resolved "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.2.tgz"
|
||||||
|
@ -1339,6 +1418,14 @@
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-primitive" "1.0.2"
|
"@radix-ui/react-primitive" "1.0.2"
|
||||||
|
|
||||||
|
"@radix-ui/react-visually-hidden@1.0.3":
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz#51aed9dd0fe5abcad7dee2a234ad36106a6984ac"
|
||||||
|
integrity sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
|
||||||
"@radix-ui/rect@1.0.0":
|
"@radix-ui/rect@1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.0.tgz"
|
||||||
|
@ -1346,6 +1433,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
|
"@radix-ui/rect@1.0.1":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.0.1.tgz#bf8e7d947671996da2e30f4904ece343bc4a883f"
|
||||||
|
integrity sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.13.10"
|
||||||
|
|
||||||
"@rexxars/react-json-inspector@^8.0.1":
|
"@rexxars/react-json-inspector@^8.0.1":
|
||||||
version "8.0.1"
|
version "8.0.1"
|
||||||
resolved "https://registry.npmjs.org/@rexxars/react-json-inspector/-/react-json-inspector-8.0.1.tgz"
|
resolved "https://registry.npmjs.org/@rexxars/react-json-inspector/-/react-json-inspector-8.0.1.tgz"
|
||||||
|
@ -5537,6 +5631,13 @@ natural-compare@^1.4.0:
|
||||||
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
|
||||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||||
|
|
||||||
|
next-sanity-image@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/next-sanity-image/-/next-sanity-image-6.0.0.tgz#38968317aa294fd06c7ff2e9df64ef8c8d0b3255"
|
||||||
|
integrity sha512-U4fl8CfJRgsUh3sJGoxgVrJ4bovCZDmWsjwowzDYbf66ci97j1mjdB+i3pz2CGFz//qbfmG8v2XL6dsYx1xFBg==
|
||||||
|
dependencies:
|
||||||
|
"@sanity/image-url" "^1.0.2"
|
||||||
|
|
||||||
next-sanity@^4.3.3:
|
next-sanity@^4.3.3:
|
||||||
version "4.3.3"
|
version "4.3.3"
|
||||||
resolved "https://registry.npmjs.org/next-sanity/-/next-sanity-4.3.3.tgz"
|
resolved "https://registry.npmjs.org/next-sanity/-/next-sanity-4.3.3.tgz"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user