Resources.jsx

  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}