| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- import { ChevronRight, Loader } from 'lucide-react'
- import Link from 'next/link'
- import React, { cloneElement, PropsWithChildren } from 'react'
- import { cn } from 'ui'
- interface CardButtonProps {
- title?: string | React.ReactNode
- description?: string
- footer?: React.ReactNode
- url?: string
- linkHref?: string
- imgUrl?: string
- imgAlt?: string
- onClick?: () => void
- icon?: React.ReactNode
- loading?: boolean
- className?: string
- fixedHeight?: boolean
- hideChevron?: boolean
- titleClass?: string
- containerElement?: React.ReactNode
- }
- // Define separate interfaces for each type of container
- interface LinkContainerProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'title'> {
- href: string
- }
- interface UrlContainerProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'title'> {
- href: string
- }
- interface NonLinkContainerProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {}
- interface ButtonContainerProps extends Omit<
- React.ButtonHTMLAttributes<HTMLButtonElement>,
- 'title'
- > {}
- // Union of all container props
- type ContainerProps =
- | LinkContainerProps
- | UrlContainerProps
- | NonLinkContainerProps
- | ButtonContainerProps
- const CardButton = ({
- title,
- description,
- children,
- footer,
- url = '',
- linkHref = '',
- imgUrl,
- imgAlt,
- icon,
- className,
- loading = false,
- fixedHeight = true,
- hideChevron = false,
- titleClass = '',
- containerElement,
- ...props
- }: PropsWithChildren<CardButtonProps & ContainerProps>) => {
- const isLink = url || linkHref || props.onClick
- let Container: React.ElementType
- let containerProps: ContainerProps = {}
- const ContainerComponentOverride =
- containerElement && React.isValidElement(containerElement)
- ? (props: any) => cloneElement<any>(containerElement, { ...props })
- : undefined
- if (props.onClick) {
- Container = ContainerComponentOverride ?? 'button'
- containerProps = props
- } else if (linkHref) {
- Container = ContainerComponentOverride ?? Link
- containerProps = {
- href: linkHref,
- ...props,
- }
- } else if (url) {
- Container = ContainerComponentOverride ?? 'a'
- containerProps = {
- href: url,
- ...props,
- }
- } else {
- Container = ContainerComponentOverride ?? 'div'
- containerProps = props
- }
- let containerClasses = [
- 'group relative text-left',
- 'bg-surface-100',
- 'border border-surface',
- 'rounded-md p-5 flex flex-row',
- 'transition ease-in-out duration-150',
- ]
- if (isLink) {
- containerClasses = [
- ...containerClasses,
- 'cursor-pointer',
- 'hover:bg-surface-200',
- 'hover:border-control',
- ]
- }
- if (fixedHeight) {
- containerClasses = [...containerClasses, 'min-h-32 md:min-h-44']
- }
- const ImageContainer = ({ children }: { children: React.ReactNode }) => {
- return <div className="mr-4 flex flex-col">{children}</div>
- }
- const contents = (
- <>
- {imgUrl && (
- <ImageContainer>
- <img
- className="
- transition-all
- group-hover:scale-110
- "
- src={`${imgUrl}`}
- alt={`${imgAlt}`}
- width="26"
- />
- </ImageContainer>
- )}
- {icon && <ImageContainer>{icon}</ImageContainer>}
- <div className="flex h-full w-full flex-col space-y-2">
- {typeof title === 'string' ? (
- <h5 className={`text-foreground pr-5 ${titleClass}`}>{title}</h5>
- ) : (
- title
- )}
- {(children || description) && (
- <div className="flex w-full flex-1 flex-col">
- <p className="text-sm text-foreground-light">{description}</p>
- <div className="w-full">{children && children}</div>
- </div>
- )}
- {footer && <div className="w-full mt-auto!">{footer}</div>}
- </div>
- {isLink && (
- <div
- className="
- absolute
- right-4
- top-4
- text-foreground-lighter
- transition-all
- duration-200
- group-hover:right-3
- group-hover:text-foreground
- "
- >
- {loading ? <Loader className="animate-spin" /> : !hideChevron ? <ChevronRight /> : <></>}
- </div>
- )}
- </>
- )
- return (
- <Container {...containerProps} className={cn(containerClasses, className)}>
- {contents}
- </Container>
- )
- }
- export default CardButton
|