NEW
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
Learn more
Skip to content
Docs

Speed Dial

Speed Dial

Loading...

Installation

CLI

pnpm dlx shadcn@latest add https://animata.design/r/fabs/speed-dial.json

Manual

Install dependencies

npm install lucide-react

Run the following command

It will create a new file speed-dial.tsx inside the components/animata/fabs directory.

mkdir -p components/animata/fabs && touch components/animata/fabs/speed-dial.tsx

Paste the code

Open the newly created file and paste the following code:

"use client";
 
import { Plus } from "lucide-react";
import type React from "react";
import { useState } from "react";
 
interface SpeedialProps {
  direction: string;
  actionButtons: Array<{
    icon: React.ReactNode;
    label: string;
    key: string;
    action: () => void;
  }>;
}
 
interface TooltipProps {
  text: string;
  children: React.ReactNode;
  direction: string;
}
 
const Tooltip: React.FC<TooltipProps> = ({ text, children, direction }) => {
  const [visible, setVisible] = useState(false);
 
  const showTooltip = () => setVisible(true);
  const hideTooltip = () => setVisible(false);
 
  return (
    <div onMouseEnter={showTooltip} onMouseLeave={hideTooltip} className="relative inline-block">
      {children}
      {visible && (
        <div
          className={` ${
            direction === "up" || direction === "down"
              ? "absolute left-full top-1/2 z-10 ml-2 -translate-y-1/2 transform rounded bg-gray-800 px-2 py-1 text-sm text-white"
              : "absolute bottom-full left-1/2 z-10 mb-2 -translate-x-1/2 transform rounded bg-gray-800 px-2 py-1 text-sm text-white"
          } `}
        >
          {text}
        </div>
      )}
    </div>
  );
};
 
export default function Speeddial({ direction, actionButtons }: SpeedialProps) {
  const [isHovered, setIsHovered] = useState(false);
 
  const getAnimation = () => {
    switch (direction) {
      case "up":
        return "origin-bottom flex-col order-0";
      case "down":
        return "origin-top flex-col order-2";
      case "left":
        return "origin-right order-0";
      case "right":
        return "origin-left order-2";
      default:
        return "";
    }
  };
 
  const handleMouseEnter = () => setIsHovered(true);
  const handleMouseLeave = () => setIsHovered(false);
 
  const getGlassyClasses = () => {
    return "backdrop-filter backdrop-blur-xl bg-white border border-white rounded-xl shadow-lg transition duration-300";
  };
 
  //customize your action buttons here
 
  return (
    <div
      onMouseLeave={handleMouseLeave}
      className={`relative mb-3 flex w-fit items-center gap-3 ${
        direction === "up" || direction === "down" ? "flex-col" : "flex-row"
      }`}
    >
      <button
        onMouseEnter={handleMouseEnter}
        className={`${getGlassyClasses()} order-0 order-1 flex items-center p-3 text-gray-800 transition duration-300 hover:bg-slate-100`}
      >
        <Plus size={20} />
      </button>
 
      {/* Speed Dial Actions */}
      <div
        className={`${
          isHovered ? "scale-100 opacity-100" : "scale-0 opacity-0"
        } flex items-center gap-3 transition duration-500 ease-in-out ${getAnimation()}`}
      >
        {actionButtons.map((action, index) => (
          <Tooltip text={action.label} key={index} direction={direction}>
            <button
              key={index}
              onClick={action.action}
              className={`${getGlassyClasses()} flex items-center p-3 text-gray-800 transition duration-300 hover:bg-slate-100`}
            >
              {action.icon}
            </button>
          </Tooltip>
        ))}
      </div>
    </div>
  );
}

Credits

Built by Sabin Shrestha