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

302 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |
## Styling
Dropdown supports all **daisyUI Dropdown classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Position | `dropdown-end` | Align dropdown to the right |
| Direction | `dropdown-top`, `dropdown-bottom`, `dropdown-left`, `dropdown-right` | Dropdown open direction |
| Hover | `dropdown-hover` | Open on hover instead of click |
> For further details, check the [daisyUI Dropdown Documentation](https://daisyui.com/components/dropdown) Full reference for CSS classes.
### Example
```javascript
Dropdown({
label: "Menu",
class: "dropdown-end dropdown-hover",
items: menuItems
});
// Applies: right-aligned, opens on hover
```
## Live Examples
### Basic Dropdown (Items Array)
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-basic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
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)
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-icons" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
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)
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-actions" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
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)
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-user" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
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
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-reactive" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
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)
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-notifications" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
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
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-custom" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
</div>
</div>
```javascript
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
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
<div id="demo-variants" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-4 justify-center"></div>
</div>
</div>
```javascript
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');
```