import React, { useMemo, useState } from "react"; import DDMenu, { DDSearchable, type MenuItem, type DDMenuSize, type DDSearchableSize, } from "@asafarim/dd-menu"; import { ThemeToggle } from "@asafarim/react-themes"; import { PackageLinks } from "@asafarim/shared"; type Theme = "auto" | "light" | "dark"; type Placement = | "bottom" | "bottom-start" | "bottom-end" | "top" | "top-start" | "top-end" | "right" | "left"; const makeNestedItems = (): MenuItem[] => [ { id: "home", label: "Home", icon: "๐Ÿ " }, { id: "products", label: "Products", icon: "๐Ÿ“ฆ", children: [ { id: "electronics", label: "Electronics", icon: "๐Ÿ”Œ" }, { id: "clothing", label: "Clothing", icon: "๐Ÿ‘•", children: [ { id: "men", label: "Men" }, { id: "women", label: "Women" }, { id: "kids", label: "Kids", children: [ { id: "toys", label: "Toys" }, { id: "shoes", label: "Shoes" }, ], }, ], }, { id: "books", label: "Books", icon: "๐Ÿ“š" }, ], }, { id: "about", label: "About Us", icon: "โ„น๏ธ" }, { id: "help", label: "Help", icon: "โ“", disabled: true }, ]; const placements: Placement[] = [ "bottom", "bottom-start", "bottom-end", "top", "top-start", "top-end", "right", "left", ]; type RoadmapFeature = { icon: string; title: string; version: string; status: "In Development" | "Planned" | "Under Review"; summary: string; specs: string[]; }; const roadmapFeatures: RoadmapFeature[] = [ { icon: "โ˜‘", title: "Multi-Select & Tagged Selection", version: "v0.2.0", status: "In Development", summary: "Support multiple selections with inline tag chips and individual chip-removal controls.", specs: [ "Add multiple?: boolean to DDSearchable", "Pass selected items through selectedItems?: MenuItem[]", "Render inline badge chips with design-token styling", ], }, { icon: "โ†ป", title: "Async Data Fetching & Remote Loading", version: "v0.2.0", status: "In Development", summary: "Built-in loader state and debounced callback support for fetching menu items on demand.", specs: [ "Support onLoadOptions(searchTerm): Promise", "Show a native loading spinner inside the input trigger", "Provide configurable loadingText?: string", ], }, { icon: "โ˜ท", title: "Grouped Items & Section Headers", version: "v0.3.0", status: "Planned", summary: "Visual section headers and item dividers to organize complex navigation menus.", specs: [ "Support groupHeader?: string or structured group arrays", "Keep keyboard navigation focused on selectable items", "Mark header items as aria-disabled=true", ], }, { icon: "โคข", title: "React Portal Support", version: "v0.3.0", status: "Planned", summary: "Escape container boundaries and overflow constraints by mounting menus to the document root.", specs: [ "Add usePortal?: boolean and portalContainer?: HTMLElement", "Calculate dynamic positioning relative to the viewport", "Prevent clipping inside modals and dense layouts", ], }, { icon: "โ—ˆ", title: "Design Tokens Ecosystem Alignment", version: "v0.4.0", status: "Under Review", summary: "Native support for @asafarim/design-tokens, replacing legacy standalone variables.", specs: [ "Map theme states to shared surface and spacing tokens", "Keep light, dark, and auto modes in sync", "Support nested ASafariM application wrappers", ], }, ]; function RoadmapView() { return (

The next chapter

Roadmap & Upcoming Features

Planned enhancements for @asafarim/dd-menu, informed by common dropdown and filter workflows.

05 initiatives

Preview props

See the direction

These controls illustrate proposed API behavior and are intentionally disabled.

Design Engineering + more
{roadmapFeatures.map((feature, index) => (
{feature.status}
0{index + 1}

{feature.title}

Target {feature.version}

{feature.summary}

    {feature.specs.map((spec) =>
  • {spec}
  • )}
))}
); } export default function App() { const [theme] = useState("auto"); const [view, setView] = useState<"live" | "roadmap">("live"); const [size, setSize] = useState("md"); const [hoverDelay, setHoverDelay] = useState(150); const [closeOnClick, setCloseOnClick] = useState(true); const [selectedSearchItem, setSelectedSearchItem] = useState(null); const baseItems = useMemo(() => makeNestedItems(), []); const onItemClick = (item: MenuItem) => alert(`Clicked: ${item.label}`); const minimalTrigger = ; const profileTrigger = (
JD
John Doe
); return (
ASafariM DD Menu

React dropdown primitives

Dropdowns, beautifully composed.

A minimal, elegant, and highly customizable dropdown menu React component.

๐ŸŽจ Beautiful theming with dark/light modes
๐Ÿ“ฑ Fully responsive and accessible
๐Ÿ”ง TypeScript support with full type safety
โšก Lightweight with smooth animations

Quick Installation

npm install @asafarim/dd-menu or pnpm add @asafarim/dd-menu
Explore the package
{view === "live" ? "Interactive Demo & Tutorial" : "Roadmap & Upcoming Features"}
{view === "live" &&
setHoverDelay(Number(e.target.value))} />
setCloseOnClick(e.target.checked)} />
}
{view === "live" ?

1. Default Variant

The most basic dropdown menu with a simple "Menu" button trigger. Perfect for general-purpose dropdowns.

๐Ÿ“ Usage Code
              {`import DDMenu from "@asafarim/dd-menu";

const items = [
  { id: "home", label: "Home", icon: "๐Ÿ " },
  { id: "about", label: "About", icon: "โ„น๏ธ" }
];

 console.log(item)}
/>`}
            

2. Minimal Variant (Custom Trigger)

Use your own custom trigger element. Great for action buttons, context menus, or when you need full control over the trigger appearance.

๐Ÿ“ Usage Code
              {`const customTrigger = (
  
);

 console.log(item)}
/>`}
            

3. Navbar Variant (Profile Menu)

Designed for navigation bars and headers. Typically used for user profile menus, account settings, or top-level navigation.

Dashboard Workspace
Overview Activity Settings
Team workspace
๐Ÿ“ Usage Code
              {`const profileTrigger = (
  
JD
John Doe
); `}

4. Sidebar Variant (Collapsible Navigation)

Perfect for vertical navigation menus. Supports nested items that expand/collapse on click. Ideal for admin panels and dashboards.

Workspace Navigation
Live

Expand items to explore nested navigation.

Content area

Your page content appears here.

๐Ÿ“ Usage Code
              {`const sidebarItems = [
  { id: "dashboard", label: "Dashboard", icon: "๐Ÿ“Š" },
  {
    id: "products",
    label: "Products",
    icon: "๐Ÿ“ฆ",
    children: [
      { id: "list", label: "Product List" },
      { id: "add", label: "Add Product" }
    ]
  }
];

`}
            

5. Placement Options

Control where the dropdown appears relative to the trigger. Choose from 8 different positions to fit your layout needs.

{placements.map((p) => ( {p}} onItemClick={onItemClick} closeOnClick={closeOnClick} hoverDelay={hoverDelay} /> ))}
๐Ÿ“ Usage Code
              {`// Available placements:
// "bottom" | "bottom-start" | "bottom-end"
// "top" | "top-start" | "top-end"
// "right" | "left"

Options}
/>

More}
/>`}
            

6. Item States (Active & Disabled)

Show current selection with active states and prevent interaction with disabled items. Perfect for navigation menus and conditional actions.

๐Ÿ“ Usage Code
              {`const menuItems = [
  {
    id: "current",
    label: "Current Page",
    icon: "๐Ÿ“",
    active: true        // Highlights as current/selected
  },
  {
    id: "restricted",
    label: "Premium Feature",
    icon: "๐Ÿ”’",
    disabled: true      // Grayed out, not clickable
  },
  {
    id: "normal",
    label: "Available Option",
    icon: "โœ…"
  }
];

`}
            

7. Persistent Menu (closeOnClick = false)

Keep the menu open after clicking items. Perfect for multi-select scenarios, filter menus, or menus with checkboxes and toggles.

closeOnClick = false} onItemClick={onItemClick} hoverDelay={hoverDelay} />
๐Ÿ“ Usage Code
              {`const [selectedFilters, setSelectedFilters] = useState([]);

const filterItems = [
  { id: "category1", label: "Electronics" },
  { id: "category2", label: "Clothing" },
  { id: "category3", label: "Books" }
];

const handleFilterClick = (item) => {
  setSelectedFilters(prev => 
    prev.includes(item.id)
      ? prev.filter(id => id !== item.id)
      : [...prev, item.id]
  );
};

Filters}
/>`}
            

8. Advanced: Event Handlers

Listen to hover and font size changes with custom event handlers. Perfect for analytics, tooltips, or dynamic UI updates.

{ console.log("Menu hover state:", isHovering); }} onFontSizeChange={(fontSize) => { console.log("Font size changed to:", fontSize); }} closeOnClick={closeOnClick} hoverDelay={hoverDelay} />
๐Ÿ“ Usage Code
              {` {
    console.log('Item clicked:', item);
    // Handle navigation, state updates, etc.
  }}
  onHoverChange={(isHovering) => {
    console.log('Menu hover state:', isHovering);
    // Show/hide tooltips, track engagement, etc.
  }}
  onFontSizeChange={(fontSize) => {
    console.log('Font size changed to:', fontSize);
    // Update related UI elements, save preferences, etc.
  }}
/>`}
            

9. Advanced: Custom Styling

Apply custom CSS classes and inline styles for complete visual control. Combine with theme variables for consistent styling.

๐Ÿ“ Usage Code
              {`// Custom CSS class
.my-custom-menu {
  --dd-accent: #ff6b6b;
  --dd-radius-md: 12px;
  border: 2px solid var(--dd-accent);
}

// Apply custom styling
`}
            

3. Custom Search Configuration

Configure search behavior: case sensitivity, minimum search length, debounce timing, and custom search keys.

console.log("Search term:", term)} theme={theme} size={size as DDSearchableSize} />
๐Ÿ“ Usage Code
              {` console.log("Search:", term)}
/>`}
            

4. Allow Custom Values

Enable users to add custom values that don't exist in the predefined list.

alert(`Custom value added: "${value}"`)} onItemSelect={(item: MenuItem | null) => alert(`Selected: ${item?.label}`)} theme={theme} size={size as DDSearchableSize} />
๐Ÿ“ Usage Code
              {` {
    // Handle custom value
    console.log('Custom value:', value);
    // You could add it to your items list
  }}
  placeholder="Type anything..."
/>`}
            

5. Controlled Component

Control the selected item externally and clear the selection.

๐Ÿ“ Usage Code
              {`const [selectedItem, setSelectedItem] = useState(null);

`}
            

6. Nested Items Support

Search through nested menu structures with hierarchical display.

๐Ÿ“ Usage Code
              {`const nestedItems = [
  {
    id: "products",
    label: "Products",
    children: [
      { id: "electronics", label: "Electronics" },
      { id: "clothing", label: "Clothing" }
    ]
  }
];

`}
            

๐Ÿš€ Getting Started

1

Install the package

                  npm install @asafarim/dd-menu
                
2

Import the components

                  {`import DDMenu, { DDSearchable } from "@asafarim/dd-menu";`}
                
3

Define your menu items

                  {`const items = [
  { id: "home", label: "Home", icon: "๐Ÿ " },
  { id: "about", label: "About", icon: "โ„น๏ธ" }
];`}
                
4

Use the components

                  {` console.log(item)}
/>

 console.log(item)}
/>`}
                
: }
); function TT() { return ( ); } }