Files
sigpro-ui/docs/components_old/dropdown.md
2026-04-02 19:31:39 +02:00

18 KiB

Dropdown

Dropdown component for creating menus, selectors, and action panels that appear when triggered. Supports both array-based items and custom content.

Tag

Dropdown

Props

Prop Type Default Description
label string | VNode | Signal - Button label or content
icon string | VNode | Signal - Icon displayed next to label
items Array<MenuItem> | Signal<Array> - Array of menu items (alternative to children)
class string '' Additional CSS classes (DaisyUI + Tailwind)
children VNode | function - Custom dropdown content (alternative to items)

MenuItem Structure (when using items)

Property Type Description
label string | VNode Menu item text
icon string | VNode Optional icon for the menu item
onclick function Click handler
class string Additional CSS classes for the menu item

Live Examples

Basic Dropdown (Items Array)

Live Demo

const BasicDemo = () => {
  return Dropdown({
    label: 'Options',
    items: [
      { label: 'Profile', onclick: () => Toast('Profile clicked', 'alert-info', 2000) },
      { label: 'Settings', onclick: () => Toast('Settings clicked', 'alert-info', 2000) },
      { label: 'Logout', onclick: () => Toast('Logged out', 'alert-warning', 2000), class: 'text-error' }
    ]
  });
};
$mount(BasicDemo, '#demo-basic');

With Icons (Items Array)

Live Demo

const IconsDemo = () => {
  return Dropdown({
    label: 'Menu',
    icon: '☰',
    items: [
      { icon: '👤', label: 'Profile', onclick: () => Toast('Profile', 'alert-info', 2000) },
      { icon: '⭐', label: 'Favorites', onclick: () => Toast('Favorites', 'alert-info', 2000) },
      { icon: '📁', label: 'Documents', onclick: () => Toast('Documents', 'alert-info', 2000) },
      { icon: '⚙️', label: 'Settings', onclick: () => Toast('Settings', 'alert-info', 2000) }
    ]
  });
};
$mount(IconsDemo, '#demo-icons');

Action Dropdown (Items Array)

Live Demo

const ActionsDemo = () => {
  const handleAction = (action) => {
    Toast(`${action} action`, 'alert-info', 2000);
  };
  
  return Dropdown({
    label: 'Actions',
    class: 'dropdown-end',
    items: [
      { icon: '✏️', label: 'Edit', onclick: () => handleAction('Edit') },
      { icon: '📋', label: 'Copy', onclick: () => handleAction('Copy') },
      { icon: '🗑️', label: 'Delete', onclick: () => handleAction('Delete'), class: 'text-error' }
    ]
  });
};
$mount(ActionsDemo, '#demo-actions');

User Dropdown (Items Array)

Live Demo

const UserDropdown = () => {
  return Dropdown({
    label: Span({ class: 'flex items-center gap-2' }, [
      Div({ class: 'avatar placeholder' }, [
        Div({ class: 'bg-primary text-primary-content rounded-full w-8 h-8 flex items-center justify-center text-sm' }, 'JD')
      ]),
      'John Doe'
    ]),
    class: 'dropdown-end',
    items: [
      { label: 'Profile', onclick: () => Toast('Profile', 'alert-info', 2000) },
      { label: 'Settings', onclick: () => Toast('Settings', 'alert-info', 2000) },
      { label: 'Sign Out', onclick: () => Toast('Signed out', 'alert-warning', 2000), class: 'text-error' }
    ]
  });
};
$mount(UserDropdown, '#demo-user');

Reactive Items

Live Demo

const ReactiveDropdown = () => {
  const count = $(0);
  
  const items = () => [
    { label: `Count: ${count()}`, onclick: () => {} },
    { label: 'Increment', onclick: () => count(count() + 1) },
    { label: 'Decrement', onclick: () => count(count() - 1) },
    { label: 'Reset', onclick: () => count(0) }
  ];
  
  return Dropdown({
    label: () => `Counter (${count()})`,
    items: items
  });
};
$mount(ReactiveDropdown, '#demo-reactive');

Notification Dropdown (Custom Children)

Live Demo

const NotificationsDropdown = () => {
  const notifications = $([
    { id: 1, title: 'New message', time: '5 min ago', read: false },
    { id: 2, title: 'Update available', time: '1 hour ago', read: false },
    { id: 3, title: 'Task completed', time: '2 hours ago', read: true }
  ]);
  
  const markAsRead = (id) => {
    notifications(notifications().map(n => 
      n.id === id ? { ...n, read: true } : n
    ));
  };
  
  const unreadCount = () => notifications().filter(n => !n.read).length;
  
  return Dropdown({
    label: Span({ class: 'relative' }, [
      '🔔',
      () => unreadCount() > 0 ? Span({ class: 'badge badge-xs badge-error absolute -top-1 -right-2' }, unreadCount()) : null
    ]),
    class: 'dropdown-end',
    children: () => Div({ class: 'w-80' }, [
      Div({ class: 'p-3 border-b border-base-300 font-bold' }, `Notifications (${unreadCount()} unread)`),
      Div({ class: 'max-h-64 overflow-y-auto' }, notifications().map(notif =>
        Div({ 
          class: `p-3 border-b border-base-300 cursor-pointer hover:bg-base-200 ${!notif.read ? 'bg-primary/5' : ''}`,
          onclick: () => markAsRead(notif.id)
        }, [
          Div({ class: 'font-medium' }, notif.title),
          Div({ class: 'text-xs opacity-60' }, notif.time),
          !notif.read && Span({ class: 'badge badge-xs badge-primary mt-1' }, 'New')
        ])
      )),
      Div({ class: 'p-2 text-center' }, [
        Button({ 
          class: 'btn btn-xs btn-ghost w-full',
          onclick: () => notifications([])
        }, 'Clear all')
      ])
    ])
  });
};
$mount(NotificationsDropdown, '#demo-notifications');

Custom Content Dropdown

Live Demo

const CustomDropdown = () => {
  const selected = $('Option 1');
  
  return Dropdown({
    label: () => selected(),
    children: () => Div({ class: 'p-4 min-w-48' }, [
      Div({ class: 'text-sm font-bold mb-2' }, 'Select an option'),
      Div({ class: 'flex flex-col gap-1' }, [
        Button({ 
          class: 'btn btn-ghost btn-sm justify-start',
          onclick: () => selected('Option 1')
        }, 'Option 1'),
        Button({ 
          class: 'btn btn-ghost btn-sm justify-start',
          onclick: () => selected('Option 2')
        }, 'Option 2'),
        Button({ 
          class: 'btn btn-ghost btn-sm justify-start',
          onclick: () => selected('Option 3')
        }, 'Option 3')
      ])
    ])
  });
};
$mount(CustomDropdown, '#demo-custom');

All Variants

Live Demo

const VariantsDemo = () => {
  const commonItems = [
    { label: 'Item 1', onclick: () => Toast('Item 1', 'alert-info', 2000) },
    { label: 'Item 2', onclick: () => Toast('Item 2', 'alert-info', 2000) },
    { label: 'Item 3', onclick: () => Toast('Item 3', 'alert-info', 2000) }
  ];
  
  return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
    Dropdown({ label: 'Default', items: commonItems }),
    Dropdown({ label: 'With Icon', icon: '☰', items: commonItems }),
    Dropdown({ label: 'End Position', class: 'dropdown-end', items: commonItems })
  ]);
};
$mount(VariantsDemo, '#demo-variants');
<script> (function() { const initDropdownExamples = () => { // 1. Basic Dropdown (Items Array) const basicTarget = document.querySelector('#demo-basic'); if (basicTarget && !basicTarget.hasChildNodes()) { const BasicDemo = () => { return Dropdown({ label: 'Options', items: [ { label: 'Profile', onclick: () => Toast('Profile clicked', 'alert-info', 2000) }, { label: 'Settings', onclick: () => Toast('Settings clicked', 'alert-info', 2000) }, { label: 'Logout', onclick: () => Toast('Logged out', 'alert-warning', 2000), class: 'text-error' } ] }); }; $mount(BasicDemo, basicTarget); } // 2. With Icons (Items Array) const iconsTarget = document.querySelector('#demo-icons'); if (iconsTarget && !iconsTarget.hasChildNodes()) { const IconsDemo = () => { return Dropdown({ label: 'Menu', icon: '☰', items: [ { icon: '👤', label: 'Profile', onclick: () => Toast('Profile', 'alert-info', 2000) }, { icon: '', label: 'Favorites', onclick: () => Toast('Favorites', 'alert-info', 2000) }, { icon: '📁', label: 'Documents', onclick: () => Toast('Documents', 'alert-info', 2000) }, { icon: '⚙️', label: 'Settings', onclick: () => Toast('Settings', 'alert-info', 2000) } ] }); }; $mount(IconsDemo, iconsTarget); } // 3. Action Dropdown (Items Array) const actionsTarget = document.querySelector('#demo-actions'); if (actionsTarget && !actionsTarget.hasChildNodes()) { const ActionsDemo = () => { const handleAction = (action) => { Toast(`${action} action`, 'alert-info', 2000); }; return Dropdown({ label: 'Actions', class: 'dropdown-end', items: [ { icon: '✏️', label: 'Edit', onclick: () => handleAction('Edit') }, { icon: '📋', label: 'Copy', onclick: () => handleAction('Copy') }, { icon: '🗑️', label: 'Delete', onclick: () => handleAction('Delete'), class: 'text-error' } ] }); }; $mount(ActionsDemo, actionsTarget); } // 4. User Dropdown (Items Array) const userTarget = document.querySelector('#demo-user'); if (userTarget && !userTarget.hasChildNodes()) { const UserDropdown = () => { return Dropdown({ label: Span({ class: 'flex items-center gap-2' }, [ Div({ class: 'avatar placeholder' }, [ Div({ class: 'bg-primary text-primary-content rounded-full w-8 h-8 flex items-center justify-center text-sm' }, 'JD') ]), 'John Doe' ]), class: 'dropdown-end', items: [ { label: 'Profile', onclick: () => Toast('Profile', 'alert-info', 2000) }, { label: 'Settings', onclick: () => Toast('Settings', 'alert-info', 2000) }, { label: 'Sign Out', onclick: () => Toast('Signed out', 'alert-warning', 2000), class: 'text-error' } ] }); }; $mount(UserDropdown, userTarget); } // 5. Reactive Items const reactiveTarget = document.querySelector('#demo-reactive'); if (reactiveTarget && !reactiveTarget.hasChildNodes()) { const ReactiveDropdown = () => { const count = $(0); const items = () => [ { label: `Count: ${count()}`, onclick: () => {} }, { label: 'Increment', onclick: () => count(count() + 1) }, { label: 'Decrement', onclick: () => count(count() - 1) }, { label: 'Reset', onclick: () => count(0) } ]; return Dropdown({ label: () => `Counter (${count()})`, items: items }); }; $mount(ReactiveDropdown, reactiveTarget); } // 6. Notification Dropdown (Custom Children) const notifTarget = document.querySelector('#demo-notifications'); if (notifTarget && !notifTarget.hasChildNodes()) { const NotificationsDropdown = () => { const notifications = $([ { id: 1, title: 'New message', time: '5 min ago', read: false }, { id: 2, title: 'Update available', time: '1 hour ago', read: false }, { id: 3, title: 'Task completed', time: '2 hours ago', read: true } ]); const markAsRead = (id) => { notifications(notifications().map(n => n.id === id ? { ...n, read: true } : n )); }; const unreadCount = () => notifications().filter(n => !n.read).length; return Dropdown({ label: Span({ class: 'relative' }, [ '🔔', () => unreadCount() > 0 ? Span({ class: 'badge badge-xs badge-error absolute -top-1 -right-2' }, unreadCount()) : null ]), class: 'dropdown-end', children: () => Div({ class: 'w-80' }, [ Div({ class: 'p-3 border-b border-base-300 font-bold' }, `Notifications (${unreadCount()} unread)`), Div({ class: 'max-h-64 overflow-y-auto' }, notifications().map(notif => Div({ class: `p-3 border-b border-base-300 cursor-pointer hover:bg-base-200 ${!notif.read ? 'bg-primary/5' : ''}`, onclick: () => markAsRead(notif.id) }, [ Div({ class: 'font-medium' }, notif.title), Div({ class: 'text-xs opacity-60' }, notif.time), !notif.read && Span({ class: 'badge badge-xs badge-primary mt-1' }, 'New') ]) )), Div({ class: 'p-2 text-center' }, [ Button({ class: 'btn btn-xs btn-ghost w-full', onclick: () => notifications([]) }, 'Clear all') ]) ]) }); }; $mount(NotificationsDropdown, notifTarget); } // 7. Custom Content Dropdown const customTarget = document.querySelector('#demo-custom'); if (customTarget && !customTarget.hasChildNodes()) { const CustomDropdown = () => { const selected = $('Option 1'); return Dropdown({ label: () => selected(), children: () => Div({ class: 'p-4 min-w-48' }, [ Div({ class: 'text-sm font-bold mb-2' }, 'Select an option'), Div({ class: 'flex flex-col gap-1' }, [ Button({ class: 'btn btn-ghost btn-sm justify-start', onclick: () => selected('Option 1') }, 'Option 1'), Button({ class: 'btn btn-ghost btn-sm justify-start', onclick: () => selected('Option 2') }, 'Option 2'), Button({ class: 'btn btn-ghost btn-sm justify-start', onclick: () => selected('Option 3') }, 'Option 3') ]) ]) }); }; $mount(CustomDropdown, customTarget); } // 8. All Variants const variantsTarget = document.querySelector('#demo-variants'); if (variantsTarget && !variantsTarget.hasChildNodes()) { const VariantsDemo = () => { const commonItems = [ { label: 'Item 1', onclick: () => Toast('Item 1', 'alert-info', 2000) }, { label: 'Item 2', onclick: () => Toast('Item 2', 'alert-info', 2000) }, { label: 'Item 3', onclick: () => Toast('Item 3', 'alert-info', 2000) } ]; return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [ Dropdown({ label: 'Default', items: commonItems }), Dropdown({ label: 'With Icon', icon: '☰', items: commonItems }), Dropdown({ label: 'End Position', class: 'dropdown-end', items: commonItems }) ]); }; $mount(VariantsDemo, variantsTarget); } }; initDropdownExamples(); if (window.$docsify) { window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => { hook.doneEach(initDropdownExamples); }); } })(); </script>