Himesh Dua

Component Preview

'use client';
import {HugeiconsIcon} from '@hugeicons/react';
import useMeasure from 'react-use-measure';
import {AnimatePresence, motion, MotionConfig} from 'motion/react';
import {Activity, Flash, Search, User} from '@hugeicons/core-free-icons';
import {useEffect, useRef, useState} from 'react';
import {cn} from '@/lib/utils';
import Link from 'next/link';
import {Button} from '@/components/ui/button';

const transition = {
  type: 'spring',
  bounce: 0.1,
  duration: 0.25,
};
const ITEMS = [
  {
    id: 1,
    label: 'Profile',
    title: <HugeiconsIcon icon={User} className="h-5 w-5" />,
    content: (
      <div className="flex flex-col gap-3">
        <div className="flex items-center gap-3">
          <div className="h-9 w-9 rounded-full bg-linear-to-br from-indigo-500 to-purple-500" />
          <div className="text-sm">
            <div className="font-medium text-zinc-800">Hems</div>
            <div className="text-zinc-500 text-xs">Full-stack developer</div>
          </div>
        </div>

        <Link href="/craft">
          <Button variant="outline" className="w-full" size="sm">
            Open dashboard
          </Button>
        </Link>
      </div>
    ),
  },

  {
    id: 2,
    label: 'Search',
    title: <HugeiconsIcon icon={Search} className="h-5 w-5" />,
    content: (
      <div className="flex flex-col gap-3">
        <input
          placeholder="Search components, docs..."
          className="h-8 w-full rounded-md border px-2 text-sm outline-none focus:ring-2"
        />

        <div className="text-xs text-zinc-500">Try: "buttons", "cards", "animations"</div>
      </div>
    ),
  },

  {
    id: 3,
    label: 'Activity',
    title: <HugeiconsIcon icon={Activity} className="h-5 w-5" />,
    content: (
      <div className="flex flex-col gap-2 text-sm text-zinc-600">
        <div>Updated Navbar component</div>
        <div>Pushed 3 commits</div>
        <div>Fixed animation jitter</div>

        <Button variant="outline" className="w-full mt-2" size="sm">
          View history
        </Button>
      </div>
    ),
  },

  {
    id: 4,
    label: 'Quick Actions',
    title: <HugeiconsIcon icon={Flash} className="h-5 w-5" />,
    content: (
      <div className="grid grid-cols-1 gap-2 text-xs">
        <Button variant="outline" size="xs">
          New Project
        </Button>
        <Button variant="outline" size="xs">
          Add Component
        </Button>
        <Button variant="outline" size="xs">
          Deploy
        </Button>
        <Button variant="outline" size="xs">
          Settings
        </Button>
      </div>
    ),
  },
];

function Toolbar() {
  const [active, setActive] = useState<number | null>(null);
  const [contentRef, {height: heightContent}] = useMeasure();
  const [menuRef, {width: widthContainer}] = useMeasure();
  const ref = useRef<HTMLDivElement>(null);
  const [isOpen, setIsOpen] = useState(false);
  const [maxWidth, setMaxWidth] = useState(0);

  useEffect(() => {
    const handleOutsideClick = (e: MouseEvent) => {
      if (!ref.current?.contains(e.target as Node)) {
        setIsOpen(false);
        setActive(null);
      }
    };

    window.addEventListener('mousedown', handleOutsideClick);
    return () => window.removeEventListener('mousedown', handleOutsideClick);
  }, []);

  useEffect(() => {
    if (!widthContainer || maxWidth > 0) return;

    setMaxWidth(widthContainer);
  }, [widthContainer, maxWidth]);

  return (
    <MotionConfig transition={transition as any}>
      <div className="absolute bottom-8" ref={ref}>
        <div className="h-full w-full rounded-xl border border-zinc-950/10 bg-white">
          <div className="overflow-hidden">
            <AnimatePresence initial={false} mode="sync">
              {isOpen ? (
                <motion.div
                  key="content"
                  initial={{height: 0}}
                  animate={{height: heightContent || 0}}
                  exit={{height: 0}}
                  style={{
                    width: maxWidth,
                  }}
                >
                  <div ref={contentRef} className="p-2">
                    {ITEMS.map(item => {
                      const isSelected = active === item.id;

                      return (
                        <motion.div
                          key={item.id}
                          initial={{opacity: 0}}
                          animate={{opacity: isSelected ? 1 : 0}}
                          exit={{opacity: 0}}
                        >
                          <div className={cn('px-2 pt-2 text-sm', isSelected ? 'block' : 'hidden')}>
                            {item.content}
                          </div>
                        </motion.div>
                      );
                    })}
                  </div>
                </motion.div>
              ) : null}
            </AnimatePresence>

            <div className="flex space-x-2 p-2" ref={menuRef}>
              {ITEMS.map(item => (
                <button
                  key={item.id}
                  aria-label={item.label}
                  className={cn(
                    'relative flex h-9 w-9 shrink-0 scale-100 select-none appearance-none items-center justify-center rounded-lg text-zinc-500 transition-colors hover:bg-zinc-100 hover:text-zinc-800 focus-visible:ring-2 active:scale-[0.98]',
                    active === item.id ? 'bg-zinc-100 text-zinc-800' : ''
                  )}
                  type="button"
                  onClick={() => {
                    if (!isOpen) setIsOpen(true);
                    if (active === item.id) {
                      setIsOpen(false);
                      setActive(null);
                      return;
                    }

                    setActive(item.id);
                  }}
                >
                  {item.title}
                </button>
              ))}
            </div>
          </div>
        </div>
      </div>
    </MotionConfig>
  );
}

export default Toolbar;

Toolbar

A compact floating toolbar with animated panels, built using Framer Motion. Supports multiple actions with a shared expandable panel, smooth transitions, and outside-click handling for a clean, responsive interaction model.

Framer MotionFloating UIToolbarPopoverAnimated PanelUI Patterns