Docs

Speed Dial

Speed Dial

Installation

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 React, { useState } from "react";
import { Plus } from "lucide-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-all 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-all 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-all 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-all duration-300 hover:bg-slate-100`}
            >
              {action.icon}
            </button>
          </Tooltip>
        ))}
      </div>
    </div>
  );
}

Credits

Built by Sabin Shrestha