256 lines
8.2 KiB
Markdown
256 lines
8.2 KiB
Markdown
# Dropdown
|
||
|
||
Dropdown component for creating menus that appear when triggered. Uses DaisyUI's native details/summary pattern.
|
||
|
||
## 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>` | Required | Array of menu items |
|
||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||
|
||
### MenuItem Structure
|
||
|
||
| 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: [
|
||
{ label: "Profile", onclick: () => console.log("Profile") },
|
||
{ label: "Settings", onclick: () => console.log("Settings") }
|
||
]
|
||
});
|
||
```
|
||
|
||
## Live Examples
|
||
|
||
### Basic 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-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
|
||
|
||
<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
|
||
|
||
<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
|
||
|
||
<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);
|
||
$watch(()=>console.log(count()));
|
||
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
|
||
|
||
<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 unreadCount = () => notifications().filter(n => !n.read).length;
|
||
|
||
const markAsRead = (id) => {
|
||
notifications(notifications().map(n =>
|
||
n.id === id ? { ...n, read: true } : n
|
||
));
|
||
};
|
||
|
||
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',
|
||
items: () => notifications().map(notif => ({
|
||
label: Div({ class: 'flex flex-col' }, [
|
||
Span({ class: 'font-medium' }, notif.title),
|
||
Span({ class: 'text-xs opacity-60' }, notif.time)
|
||
]),
|
||
class: notif.read ? '' : 'bg-primary/5',
|
||
onclick: () => markAsRead(notif.id)
|
||
}))
|
||
});
|
||
};
|
||
|
||
$mount(NotificationsDropdown, '#demo-notifications');
|
||
```
|
||
|
||
### 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');
|
||
``` |