Image Button

A compact image button.

Loading…

Utils.ts

import { ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
 
export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}
 

Packages

npm i lucide-react
image-button/demo.tsx
"use client";
 
import { cn } from "@/lib/utils";
import Image from "next/image";
import { X } from "lucide-react";
 
  interface ImageButtonProps {
  image: {
    dataUrl: string;
  };
  displayName: string;
  isExpanded: boolean;
  onToggle: () => void;
}
 
const ImageButton = ({
  image,
  displayName,
  isExpanded,
  onToggle,
}: ImageButtonProps) => (
  <div className="relative group flex-shrink-0">
    {/* Main button */}
    <button
      type="button"
      onClick={onToggle}
      aria-expanded={isExpanded}
      className={cn(
        "relative flex items-center overflow-hidden cursor-pointer rounded-[6px]",
        "bg-gradient-to-br from-zinc-50 to-zinc-100 dark:from-zinc-900 dark:to-zinc-950",
        "border border-zinc-200/80 dark:border-zinc-800",
        "shadow-[0_1px_3px_rgba(0,0,0,0.05),inset_0_1px_0_rgba(255,255,255,0.6)]",
        "dark:shadow-[0_1px_3px_rgba(0,0,0,0.3),inset_0_1px_0_rgba(255,255,255,0.05)]",
        "hover:shadow-[0_4px_12px_rgba(0,0,0,0.08),inset_0_1px_0_rgba(255,255,255,0.6)]",
        "dark:hover:shadow-[0_4px_12px_rgba(0,0,0,0.4),inset_0_1px_0_rgba(255,255,255,0.05)]",
        "transition-all duration-300 ease-out",
        isExpanded ? "w-44 h-32 p-0" : "w-40 h-10 pl-1.5 pr-9 gap-2.5"
      )}
    >
      {/* Expanded image view */}
      {isExpanded && (
        <div
          className={cn(
            "absolute inset-0 transition-opacity duration-200",
            "opacity-100 delay-75"
          )}
        >
          <div className="relative w-full h-full overflow-hidden">
            <Image
              src={image.dataUrl}
              alt={displayName}
              fill
              unoptimized
              className="object-cover"
            />
            <div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent" />
            {/* Shimmer effect */}
            <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/10 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-1000 ease-in-out" />
            <div className="absolute bottom-2 left-3 right-3">
              <p className="text-white text-xs font-medium truncate drop-shadow-xl">
                {displayName}
              </p>
            </div>
          </div>
        </div>
      )}
 
      {!isExpanded && (
        <span
        className={cn(
          "flex items-center gap-2.5 text-sm truncate leading-none transition-all duration-200",    
        )}
      >
        <span className="relative flex-shrink-0 w-7 h-7 rounded-[8px] overflow-hidden border border-zinc-200/50 dark:border-zinc-700/50 bg-background" >
          <Image
            src={image.dataUrl}
            alt={displayName}
            fill
            unoptimized
            className="object-cover"
          />
        </span>
        <span className="truncate font-medium text-zinc-700 dark:text-zinc-300">
          {displayName}
        </span>
      </span>
 
      )}
    </button>
  </div>
);
 
export ImageButton;