portfolio/components/ui/Card.tsx

155 lines
4.1 KiB
TypeScript
Raw Normal View History

2023-02-13 20:05:01 +00:00
import {ElementType, ReactNode} from 'react'
2022-12-06 12:54:34 +00:00
import Link from 'next/link'
import clsx from 'clsx'
2023-02-07 23:39:38 +00:00
import {twMerge} from 'tailwind-merge'
2023-02-13 20:05:01 +00:00
import {ChevronRightIcon} from '@/components/icons/ChevronRightIcon'
2022-12-06 12:54:34 +00:00
2023-01-14 19:31:05 +00:00
type Card = {
as?: ElementType
2023-02-07 23:39:38 +00:00
variant?: string
2023-01-14 19:31:05 +00:00
className?: string
children: ReactNode
}
type CardLink = {
href: string
children: ReactNode
}
type CardTitle = {
as?: ElementType
href: string
children: ReactNode
}
type CardDescription = {
children: ReactNode
2023-01-27 23:33:49 +00:00
className?: string
2023-01-14 19:31:05 +00:00
}
type CardCta = {
children: ReactNode
}
type CardEyebrow = {
as: ElementType
dateTime: string
decorate: boolean
className?: string
children: ReactNode
}
export function Card({
as: Component = 'div',
2023-02-07 23:39:38 +00:00
variant = 'normal',
2023-01-14 19:31:05 +00:00
className,
children
}: Card) {
2023-02-07 23:39:38 +00:00
type VariantStyles = {
normal: string
inline: string
}
const variantStyles: VariantStyles = {
normal:
'flex-col',
inline:
'flex-col md:flex-row md:justify-between',
}
2023-01-05 20:03:09 +00:00
return (
<Component
2023-02-07 23:39:38 +00:00
className={twMerge(`
group
relative
flex
items-baseline
${variantStyles[variant as keyof VariantStyles]}
${className ?? ""}
`)}
2023-01-05 20:03:09 +00:00
>
{children}
</Component>
)
2022-12-06 12:54:34 +00:00
}
2023-01-14 19:31:05 +00:00
Card.Link = function CardLink({href, children}: CardLink) {
2023-01-05 20:03:09 +00:00
return (
<>
<div
className="absolute -inset-y-6 -inset-x-4 z-0 scale-95 bg-zinc-50 opacity-0 transition group-hover:scale-100 group-hover:opacity-100 dark:bg-zinc-800/50 sm:-inset-x-6 sm:rounded-2xl"/>
2023-01-14 19:31:05 +00:00
<Link href={href}>
2023-01-05 20:03:09 +00:00
<span className="absolute -inset-y-6 -inset-x-4 z-20 sm:-inset-x-6 sm:rounded-2xl"/>
<span className="relative z-10">{children}</span>
</Link>
</>
)
2022-12-06 12:54:34 +00:00
}
2023-01-14 19:31:05 +00:00
Card.Title = function CardTitle({as: Component = 'h2', href, children}: CardTitle) {
2023-01-05 20:03:09 +00:00
return (
2024-09-27 20:50:32 +00:00
<Component className="group-hover:text-indigo-500 text-base font-semibold tracking-tight text-zinc-800 dark:text-zinc-100">
2023-01-05 20:03:09 +00:00
{href ? <Card.Link href={href}>{children}</Card.Link> : children}
</Component>
)
2022-12-06 12:54:34 +00:00
}
2023-01-27 23:33:49 +00:00
Card.Description = function CardDescription({children, className}: CardDescription) {
2023-01-05 20:03:09 +00:00
return (
2023-02-08 21:38:41 +00:00
<p className={
twMerge(`
mt-2
text-sm
text-zinc-600
dark:text-zinc-400
relative
z-10
${className ?? ""}
`)}>
2023-01-05 20:03:09 +00:00
{children}
</p>
)
2022-12-06 12:54:34 +00:00
}
2023-01-14 19:31:05 +00:00
Card.Cta = function CardCta({children}: CardCta) {
2023-01-05 20:03:09 +00:00
return (
<div
aria-hidden="true"
className="relative z-10 mt-4 flex items-center text-sm font-medium text-zinc-400 group-hover:text-indigo-500 dark:text-zinc-200"
>
{children}
<ChevronRightIcon className="ml-1 h-4 w-4 stroke-current"/>
</div>
)
2022-12-06 12:54:34 +00:00
}
Card.Eyebrow = function CardEyebrow({
2023-01-05 20:03:09 +00:00
as: Component = 'p',
decorate = false,
className,
children,
...props
2023-01-14 19:31:05 +00:00
}: CardEyebrow) {
2023-01-05 20:03:09 +00:00
return (
<Component
className={clsx(
className,
2023-01-26 23:50:07 +00:00
'relative z-10 order-first flex items-center text-sm text-zinc-500 dark:text-zinc-400',
2023-01-05 20:03:09 +00:00
decorate && 'pl-3.5'
)}
{...props}
2022-12-06 12:54:34 +00:00
>
2023-01-05 20:03:09 +00:00
{decorate && (
<span
className="absolute inset-y-0 left-0 flex items-center"
aria-hidden="true"
>
<span className="h-4 w-0.5 rounded-full bg-zinc-200 dark:bg-zinc-500"/>
2022-12-06 12:54:34 +00:00
</span>
2023-01-05 20:03:09 +00:00
)}
{children}
</Component>
)
2022-12-06 12:54:34 +00:00
}