getting closer 👀

This commit is contained in:
Jack Merrill 2023-06-13 22:57:59 -05:00
parent 8cb66a96dd
commit 6b0efea07c
No known key found for this signature in database
GPG Key ID: B8E3CDF57DD80CA5
20 changed files with 827 additions and 65 deletions

View File

@ -1,4 +1,8 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}
const nextConfig = {
images: {
domains: ["cdn.sanity.io"],
},
};
module.exports = nextConfig
module.exports = nextConfig;

View File

@ -15,6 +15,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.4",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-navigation-menu": "^1.1.2",
"@radix-ui/react-tooltip": "^1.0.6",
"@sanity/image-url": "^1.0.2",
"@sanity/vision": "^3.11.2",
"@tailwindcss/typography": "^0.5.9",
@ -31,6 +32,7 @@
"lucide-react": "^0.220.0",
"next": "13.4.3",
"next-sanity": "^4.3.3",
"next-sanity-image": "^6.0.0",
"postcss": "8.4.23",
"react": "18.2.0",
"react-dom": "18.2.0",

View File

@ -1,72 +1,80 @@
import {defineField, defineType} from 'sanity'
import { defineField, defineType } from "sanity";
export default defineType({
name: 'post',
title: 'Post',
type: 'document',
name: "post",
title: "Post",
type: "document",
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
name: "title",
title: "Title",
type: "string",
}),
defineField({
name: 'slug',
title: 'Slug',
type: 'slug',
name: "subtitle",
title: "Subtitle",
type: "string",
}),
defineField({
name: "slug",
title: "Slug",
type: "slug",
options: {
source: 'title',
source: "title",
maxLength: 96,
},
}),
defineField({
name: 'author',
title: 'Author',
type: 'reference',
to: {type: 'author'},
name: "author",
title: "Author",
type: "reference",
to: { type: "author" },
}),
defineField({
name: 'mainImage',
title: 'Main image',
type: 'image',
name: "mainImage",
title: "Main image",
type: "image",
options: {
hotspot: true,
},
fields: [
{
name: 'alt',
type: 'string',
title: 'Alternative Text',
}
]
name: "alt",
type: "string",
title: "Alternative Text",
},
],
}),
defineField({
name: 'categories',
title: 'Categories',
type: 'array',
of: [{type: 'reference', to: {type: 'category'}}],
name: "categories",
title: "Categories",
type: "array",
of: [{ type: "reference", to: { type: "category" } }],
}),
defineField({
name: 'publishedAt',
title: 'Published at',
type: 'datetime',
name: "publishedAt",
title: "Published at",
type: "datetime",
}),
defineField({
name: 'body',
title: 'Body',
type: 'blockContent',
name: "content",
title: "Body",
type: "markdown",
options: {
imageUrl: (imageAsset) => `${imageAsset.url}`,
},
}),
],
preview: {
select: {
title: 'title',
author: 'author.name',
media: 'mainImage',
title: "title",
author: "author.name",
media: "mainImage",
},
prepare(selection) {
const {author} = selection
return {...selection, subtitle: author && `by ${author}`}
const { author } = selection;
return { ...selection, subtitle: author && `by ${author}` };
},
},
})
});

View File

@ -60,6 +60,9 @@ export default defineType({
name: "content",
title: "Body",
type: "markdown",
options: {
imageUrl: (imageAsset) => `${imageAsset.url}`,
},
}),
],

View 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>
);
}

View File

@ -0,0 +1,3 @@
export default function Default() {
return null;
}

View File

@ -11,13 +11,14 @@ 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 Project = {
title: string;
subtitle: string;
slug: string;
publishedAt: Date;
mainImage: string;
content: string;
};
@ -46,7 +47,6 @@ export default function ProjectModal({
subtitle: q.string(),
slug: q.slug("slug"),
publishedAt: q.date(),
mainImage: q("mainImage").grabOne$("asset->url", q.string()),
content: q.string(),
})
.slice(0, 1);
@ -87,6 +87,19 @@ export default function ProjectModal({
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
}
>

51
src/app/about/page.tsx Normal file
View 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&apos;m Jack Merrill.</h1>
<p className="text-2xl">
I&apos;m a software engineer, designer, and student from the United
States. I&apos;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&apos;m a Division II (sophomore) student at Hampshire College,
studying interaction design. I&apos;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>
);
}

View 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
View 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>
);
}

View File

@ -1,4 +1,4 @@
'use client'
"use client";
/**
* This route is responsible for the built-in authoring environment using Sanity Studio.
@ -9,9 +9,11 @@
* https://github.com/sanity-io/next-sanity
*/
import { NextStudio } from 'next-sanity/studio'
import config from '../../../../../sanity.config'
import { NextStudio } from "next-sanity/studio";
import config from "../../../../../sanity.config";
const isDev = process.env.NODE_ENV === "development" || !process.env.NODE_ENV;
export default function StudioPage() {
return <NextStudio config={config} />
return isDev ? <NextStudio config={config} /> : null;
}

View File

@ -24,9 +24,11 @@ export const metadata = {
export default async function RootLayout({
children,
project,
blogpost,
}: {
children: React.ReactNode;
project?: React.ReactNode;
blogpost?: React.ReactNode;
}) {
const { query: projectQuery, schema: projectSchema } = q("*")
.filterByType("project")
@ -37,6 +39,7 @@ export default async function RootLayout({
subtitle: q.string(),
slug: q.slug("slug"),
publishedAt: q.date(),
mainImage: q("mainImage").grabOne$("asset->url", q.string()),
});
const { query: blogQuery, schema: blogSchema } = q("*")
@ -110,6 +113,7 @@ export default async function RootLayout({
</footer>
{project}
{blogpost}
</body>
</html>
);

View File

@ -1,3 +1,86 @@
export default function Project() {
return <p>This is a placeholder for the project page.</p>;
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: 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
View 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>
);
}

View File

@ -0,0 +1,9 @@
"use client";
export default function ClientComponent({
children,
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}

View File

@ -1,3 +1,5 @@
"use client";
import { CopyIcon } from "@radix-ui/react-icons";
import { CopyCheckIcon } from "lucide-react";
import React, { useState } from "react";

View File

@ -24,6 +24,7 @@ export default function Navbar({
title: string;
subtitle: string;
slug: string;
mainImage: string;
}[];
blogPosts: {
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.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.Trigger asChild>
<Link
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 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
</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">
<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
href={`/projects/${project.slug}`}
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]">
{project.title}
<div className="col-span-3">
<div className="mb-[5px] font-bold leading-[1.2]">
{project.title}
</div>
<p className="font-medium leading-[1.4]">
{project.subtitle}
</p>
</div>
<p className="font-medium leading-[1.4]">
{project.subtitle}
</p>
{/* project image */}
{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>
</NavigationMenu.Link>
</li>
@ -159,9 +170,9 @@ export default function Navbar({
<NavigationMenu.Item>
<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]"
href="https://github.com/radix-ui"
asChild
>
About
<Link href="/about">About</Link>
</NavigationMenu.Link>
</NavigationMenu.Item>

View 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>
);
}

View File

@ -112,6 +112,22 @@ module.exports = {
from: { opacity: 1 },
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: {
"accordion-down": "accordion-down 0.2s ease-out",
@ -125,6 +141,13 @@ module.exports = {
enterFromRight: "enterFromRight 250ms ease",
exitToLeft: "exitToLeft 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
View File

@ -658,6 +658,11 @@
resolved "https://registry.npmjs.org/@floating-ui/core/-/core-1.2.6.tgz"
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":
version "0.5.4"
resolved "https://registry.npmjs.org/@floating-ui/dom/-/dom-0.5.4.tgz"
@ -672,6 +677,13 @@
dependencies:
"@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":
version "0.7.2"
resolved "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-0.7.2.tgz"
@ -687,6 +699,13 @@
dependencies:
"@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":
version "2.3.0"
resolved "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-2.3.0.tgz"
@ -930,6 +949,14 @@
"@babel/runtime" "^7.13.10"
"@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":
version "1.0.2"
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/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":
version "1.0.2"
resolved "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.2.tgz"
@ -1248,6 +1292,25 @@
"@babel/runtime" "^7.13.10"
"@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":
version "1.0.0"
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"
"@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":
version "1.0.0"
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"
"@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":
version "1.0.2"
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"
"@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":
version "1.0.0"
resolved "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.0.tgz"
@ -1346,6 +1433,13 @@
dependencies:
"@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":
version "8.0.1"
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"
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:
version "4.3.3"
resolved "https://registry.npmjs.org/next-sanity/-/next-sanity-4.3.3.tgz"