1import Link from 'next/link'
2import { motion, useMotionTemplate, useMotionValue } from 'framer-motion'
3
4import { GridPattern } from '@/components/GridPattern'
5import { Heading } from '@/components/Heading'
6import { ChatBubbleIcon } from '@/components/icons/ChatBubbleIcon'
7import { EnvelopeIcon } from '@/components/icons/EnvelopeIcon'
8import { UserIcon } from '@/components/icons/UserIcon'
9import { UsersIcon } from '@/components/icons/UsersIcon'
10
11const resources = [
12 {
13 href: '/contacts',
14 name: 'Contacts',
15 description:
16 'Learn about the contact model and how to create, retrieve, update, delete, and list contacts.',
17 icon: UserIcon,
18 pattern: {
19 y: 16,
20 squares: [
21 [0, 1],
22 [1, 3],
23 ],
24 },
25 },
26 {
27 href: '/conversations',
28 name: 'Conversations',
29 description:
30 'Learn about the conversation model and how to create, retrieve, update, delete, and list conversations.',
31 icon: ChatBubbleIcon,
32 pattern: {
33 y: -6,
34 squares: [
35 [-1, 2],
36 [1, 3],
37 ],
38 },
39 },
40 {
41 href: '/messages',
42 name: 'Messages',
43 description:
44 'Learn about the message model and how to create, retrieve, update, delete, and list messages.',
45 icon: EnvelopeIcon,
46 pattern: {
47 y: 32,
48 squares: [
49 [0, 2],
50 [1, 4],
51 ],
52 },
53 },
54 {
55 href: '/groups',
56 name: 'Groups',
57 description:
58 'Learn about the group model and how to create, retrieve, update, delete, and list groups.',
59 icon: UsersIcon,
60 pattern: {
61 y: 22,
62 squares: [[0, 1]],
63 },
64 },
65]
66
67function ResourceIcon({ icon: Icon }) {
68 return (
69 <div className="flex h-7 w-7 items-center justify-center rounded-full 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-emerald-300/10 dark:group-hover:ring-emerald-400">
70 <Icon 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-emerald-300/10 dark:group-hover:stroke-emerald-400" />
71 </div>
72 )
73}
74
75function ResourcePattern({ mouseX, mouseY, ...gridProps }) {
76 let maskImage = useMotionTemplate`radial-gradient(180px at ${mouseX}px ${mouseY}px, white, transparent)`
77 let style = { maskImage, WebkitMaskImage: maskImage }
78
79 return (
80 <div className="pointer-events-none">
81 <div className="absolute inset-0 rounded-2xl transition duration-300 [mask-image:linear-gradient(white,transparent)] group-hover:opacity-50">
82 <GridPattern
83 width={72}
84 height={56}
85 x="50%"
86 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"
87 {...gridProps}
88 />
89 </div>
90 <motion.div
91 className="absolute inset-0 rounded-2xl bg-gradient-to-r from-[#D7EDEA] to-[#F4FBDF] opacity-0 transition duration-300 group-hover:opacity-100 dark:from-[#202D2E] dark:to-[#303428]"
92 style={style}
93 />
94 <motion.div
95 className="absolute inset-0 rounded-2xl opacity-0 mix-blend-overlay transition duration-300 group-hover:opacity-100"
96 style={style}
97 >
98 <GridPattern
99 width={72}
100 height={56}
101 x="50%"
102 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"
103 {...gridProps}
104 />
105 </motion.div>
106 </div>
107 )
108}
109
110function Resource({ resource }) {
111 let mouseX = useMotionValue(0)
112 let mouseY = useMotionValue(0)
113
114 function onMouseMove({ currentTarget, clientX, clientY }) {
115 let { left, top } = currentTarget.getBoundingClientRect()
116 mouseX.set(clientX - left)
117 mouseY.set(clientY - top)
118 }
119
120 return (
121 <div
122 key={resource.href}
123 onMouseMove={onMouseMove}
124 className="group relative flex rounded-2xl bg-zinc-50 transition-shadow hover:shadow-md hover:shadow-zinc-900/5 dark:bg-white/2.5 dark:hover:shadow-black/5"
125 >
126 <ResourcePattern {...resource.pattern} mouseX={mouseX} mouseY={mouseY} />
127 <div className="absolute inset-0 rounded-2xl 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" />
128 <div className="relative rounded-2xl px-4 pb-4 pt-16">
129 <ResourceIcon icon={resource.icon} />
130 <h3 className="mt-4 text-sm font-semibold leading-7 text-zinc-900 dark:text-white">
131 <Link href={resource.href}>
132 <span className="absolute inset-0 rounded-2xl" />
133 {resource.name}
134 </Link>
135 </h3>
136 <p className="mt-1 text-sm text-zinc-600 dark:text-zinc-400">
137 {resource.description}
138 </p>
139 </div>
140 </div>
141 )
142}
143
144export function Resources() {
145 return (
146 <div className="my-16 xl:max-w-none">
147 <Heading level={2} id="resources">
148 Resources
149 </Heading>
150 <div className="not-prose mt-4 grid grid-cols-1 gap-8 border-t border-zinc-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:grid-cols-4">
151 {resources.map((resource) => (
152 <Resource key={resource.href} resource={resource} />
153 ))}
154 </div>
155 </div>
156 )
157}