2024-07-22 11:49:47 +02:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
type MotionValue,
|
|
|
|
|
motion,
|
|
|
|
|
useMotionTemplate,
|
|
|
|
|
useMotionValue,
|
|
|
|
|
} from "framer-motion";
|
|
|
|
|
import Link from "next/link";
|
|
|
|
|
|
2024-07-23 02:25:45 +02:00
|
|
|
import { Icon } from "@iconify-icon/react";
|
|
|
|
|
import type { ComponentPropsWithoutRef, MouseEvent } from "react";
|
2024-07-22 11:49:47 +02:00
|
|
|
import { GridPattern } from "./GridPattern";
|
|
|
|
|
import { Heading } from "./Heading";
|
|
|
|
|
|
2024-07-23 02:25:45 +02:00
|
|
|
export interface ResourceType {
|
|
|
|
|
href?: string;
|
2024-07-22 11:49:47 +02:00
|
|
|
name: string;
|
|
|
|
|
description: string;
|
2024-07-23 02:25:45 +02:00
|
|
|
icon: string;
|
|
|
|
|
pattern?: Omit<
|
2024-07-22 11:49:47 +02:00
|
|
|
ComponentPropsWithoutRef<typeof GridPattern>,
|
|
|
|
|
"width" | "height" | "x"
|
|
|
|
|
>;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 02:25:45 +02:00
|
|
|
const resources: ResourceType[] = [
|
2024-07-22 11:49:47 +02:00
|
|
|
{
|
2024-07-22 13:26:09 +02:00
|
|
|
href: "/entities",
|
|
|
|
|
name: "Entities",
|
2024-07-22 11:49:47 +02:00
|
|
|
description:
|
2024-07-22 13:26:09 +02:00
|
|
|
"Learn how Entities work and how to use them to transmit federated data.",
|
2024-07-23 02:25:45 +02:00
|
|
|
icon: "tabler:code-asterisk",
|
2024-07-22 11:49:47 +02:00
|
|
|
pattern: {
|
|
|
|
|
y: 16,
|
|
|
|
|
squares: [
|
|
|
|
|
[0, 1],
|
|
|
|
|
[1, 3],
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
2024-08-24 14:34:25 +02:00
|
|
|
href: "/federation",
|
|
|
|
|
name: "Federation",
|
2024-07-22 11:49:47 +02:00
|
|
|
description:
|
2024-08-24 14:34:25 +02:00
|
|
|
"Learn how to federate data across the Versia federation network.",
|
2024-07-23 02:25:45 +02:00
|
|
|
icon: "tabler:building-bank",
|
2024-07-22 11:49:47 +02:00
|
|
|
pattern: {
|
|
|
|
|
y: -6,
|
|
|
|
|
squares: [
|
|
|
|
|
[-1, 2],
|
|
|
|
|
[1, 3],
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
2024-07-23 02:25:45 +02:00
|
|
|
function ResourceIcon({ icon }: { icon: string }) {
|
2024-07-22 11:49:47 +02:00
|
|
|
return (
|
2024-07-22 14:24:03 +02:00
|
|
|
<div className="flex h-7 w-7 items-center justify-center rounded-md bg-zinc-900/5 ring-1 ring-zinc-900/25 backdrop-blur-[2px] transition duration-300 group-hover:bg-white/50 group-hover:ring-zinc-900/25 dark:bg-white/7.5 dark:ring-white/15 dark:group-hover:bg-brand-300/10 dark:group-hover:ring-brand-400">
|
2024-07-23 02:25:45 +02:00
|
|
|
<Icon
|
|
|
|
|
icon={icon}
|
|
|
|
|
width="unset"
|
|
|
|
|
className="h-5 w-5 fill-zinc-700/10 stroke-zinc-700 transition-colors duration-300 group-hover:stroke-zinc-900 dark:fill-white/10 dark:stroke-zinc-400 dark:group-hover:fill-brand-300/10 dark:group-hover:stroke-brand-400"
|
|
|
|
|
/>
|
2024-07-22 11:49:47 +02:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ResourcePattern({
|
|
|
|
|
mouseX,
|
|
|
|
|
mouseY,
|
|
|
|
|
...gridProps
|
2024-07-23 02:25:45 +02:00
|
|
|
}: ResourceType["pattern"] & {
|
2024-07-22 11:49:47 +02:00
|
|
|
mouseX: MotionValue<number>;
|
|
|
|
|
mouseY: MotionValue<number>;
|
|
|
|
|
}) {
|
|
|
|
|
const maskImage = useMotionTemplate`radial-gradient(180px at ${mouseX}px ${mouseY}px, white, transparent)`;
|
|
|
|
|
const style = { maskImage, WebkitMaskImage: maskImage };
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="pointer-events-none">
|
2025-05-14 18:25:40 +02:00
|
|
|
<div className="absolute inset-0 rounded-md transition duration-300 mask-[linear-gradient(white,transparent)] group-hover:opacity-50">
|
2024-07-22 11:49:47 +02:00
|
|
|
<GridPattern
|
|
|
|
|
width={72}
|
|
|
|
|
height={56}
|
|
|
|
|
x="50%"
|
|
|
|
|
className="absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/[0.02] stroke-black/5 dark:fill-white/1 dark:stroke-white/2.5"
|
|
|
|
|
{...gridProps}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
<motion.div
|
2025-05-14 18:25:40 +02:00
|
|
|
className="absolute inset-0 rounded-md bg-linear-to-r from-brand-100 to-brand-50 opacity-0 transition duration-300 group-hover:opacity-100 dark:from-brand-900/30 dark:to-brand-950/30"
|
2024-07-22 11:49:47 +02:00
|
|
|
style={style}
|
|
|
|
|
/>
|
|
|
|
|
<motion.div
|
2024-07-22 14:24:03 +02:00
|
|
|
className="absolute inset-0 rounded-md opacity-0 mix-blend-overlay transition duration-300 group-hover:opacity-100"
|
2024-07-22 11:49:47 +02:00
|
|
|
style={style}
|
|
|
|
|
>
|
|
|
|
|
<GridPattern
|
|
|
|
|
width={72}
|
|
|
|
|
height={56}
|
|
|
|
|
x="50%"
|
|
|
|
|
className="absolute inset-x-0 inset-y-[-30%] h-[160%] w-full skew-y-[-18deg] fill-black/50 stroke-black/70 dark:fill-white/2.5 dark:stroke-white/10"
|
|
|
|
|
{...gridProps}
|
|
|
|
|
/>
|
|
|
|
|
</motion.div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 02:25:45 +02:00
|
|
|
export function Resource({ resource }: { resource: ResourceType }) {
|
2024-07-22 11:49:47 +02:00
|
|
|
const mouseX = useMotionValue(0);
|
|
|
|
|
const mouseY = useMotionValue(0);
|
|
|
|
|
|
|
|
|
|
function onMouseMove({
|
|
|
|
|
currentTarget,
|
|
|
|
|
clientX,
|
|
|
|
|
clientY,
|
|
|
|
|
}: MouseEvent<HTMLDivElement>) {
|
|
|
|
|
const { left, top } = currentTarget.getBoundingClientRect();
|
|
|
|
|
mouseX.set(clientX - left);
|
|
|
|
|
mouseY.set(clientY - top);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={resource.href}
|
|
|
|
|
onMouseMove={onMouseMove}
|
2024-07-22 14:24:03 +02:00
|
|
|
className="group relative flex rounded-md bg-zinc-50 transition-shadow hover:shadow-md hover:shadow-zinc-900/5 dark:bg-white/2.5 dark:hover:shadow-black/5"
|
2024-07-22 11:49:47 +02:00
|
|
|
>
|
|
|
|
|
<ResourcePattern
|
2024-07-23 02:25:45 +02:00
|
|
|
{...(resource.pattern ?? {
|
|
|
|
|
y: 0,
|
|
|
|
|
squares: [
|
|
|
|
|
[0, 1],
|
|
|
|
|
[1, 2],
|
|
|
|
|
],
|
|
|
|
|
})}
|
2024-07-22 11:49:47 +02:00
|
|
|
mouseX={mouseX}
|
|
|
|
|
mouseY={mouseY}
|
|
|
|
|
/>
|
2024-07-22 14:24:03 +02:00
|
|
|
<div className="absolute inset-0 rounded-md ring-1 ring-inset ring-zinc-900/7.5 group-hover:ring-zinc-900/10 dark:ring-white/10 dark:group-hover:ring-white/20" />
|
|
|
|
|
<div className="relative rounded-md px-4 pb-4 pt-16">
|
2024-07-22 11:49:47 +02:00
|
|
|
<ResourceIcon icon={resource.icon} />
|
|
|
|
|
<h3 className="mt-4 text-sm font-semibold leading-7 text-zinc-900 dark:text-white">
|
2024-07-23 02:25:45 +02:00
|
|
|
{resource.href ? (
|
|
|
|
|
<Link href={resource.href}>
|
|
|
|
|
<span className="absolute inset-0 rounded-md" />
|
|
|
|
|
{resource.name}
|
|
|
|
|
</Link>
|
|
|
|
|
) : (
|
|
|
|
|
resource.name
|
|
|
|
|
)}
|
2024-07-22 11:49:47 +02:00
|
|
|
</h3>
|
|
|
|
|
<p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
|
|
|
|
|
{resource.description}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function Resources() {
|
|
|
|
|
return (
|
|
|
|
|
<div className="my-16 xl:max-w-none">
|
|
|
|
|
<Heading level={2} id="resources">
|
|
|
|
|
Resources
|
|
|
|
|
</Heading>
|
|
|
|
|
<div className="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 sm:grid-cols-2 xl:grid-cols-4 dark:border-white/5">
|
|
|
|
|
{resources.map((resource) => (
|
|
|
|
|
<Resource key={resource.href} resource={resource} />
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|