Componentnavigation0.99 KB gzipZero dependencies
Flyout V2
Hover flyout menu v2 with keyboard support.
Preview
Live Preview
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add flyout-v2Or install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { FlyoutV2 } from "@/components/ui/flyout-v2";
<FlyoutV2 />Usage guidance
Use Flyout V2 when a ticket stub surface needs Hover flyout menu v2 with keyboard support. Keep props minimal and prefer composition with sibling stubs.
Avoid Flyout V2 for long form flows or dense data grids where a dedicated layout block fits better.
Source code
flyout-v2.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface Flyout2Item {
id: string;
label: string;
children?: { id: string; label: string; href?: string }[];
}
export interface FlyoutV2Props extends Omit<React.HTMLAttributes<HTMLUListElement>, "onSelect"> {
items: Flyout2Item[];
}
/** Hover flyout navigation menu v2 with right-flyout submenus. */
export function FlyoutV2({ items, className, ...props }: FlyoutV2Props) {
return (
<ul className={cn("w-44 rounded-md border border-border bg-card p-1 shadow-lg", className)} role="menu" {...props}>
<style>{`@keyframes fl2In { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: translateX(0); } }`}</style>
{items.map((item, idx) => (
<li key={item.id} className="group relative animate-[fl2In_0.2s_ease-out_both] motion-reduce:animate-none" style={{ animationDelay: `${idx * 35}ms` }}>
<button
role="menuitem"
className="flex w-full items-center justify-between rounded-sm px-2.5 py-1.5 text-start text-xs transition-colors hover:bg-secondary motion-reduce:transition-none focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
>
{item.label}
{item.children && item.children.length > 0 && (
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" className="text-muted-foreground" aria-hidden="true"><path d="M9 6l6 6-6 6" /></svg>
)}
</button>
{item.children && item.children.length > 0 && (
<ul className="invisible absolute left-full top-0 z-10 ms-0.5 w-40 rounded-md border border-border bg-card p-1 opacity-0 shadow-lg transition-all duration-150 group-hover:visible group-hover:opacity-100 motion-reduce:transition-none">
{item.children.map((child) => (
<li key={child.id}>
<a role="menuitem" href={child.href ?? "#"} className="block rounded-sm px-2.5 py-1.5 text-xs transition-colors hover:bg-secondary motion-reduce:transition-none">
{child.label}
</a>
</li>
))}
</ul>
)}
</li>
))}
</ul>
);
}
Props
| Prop | Type | Description |
|---|---|---|
| items | Flyout2Item[] |