378 lines
13 KiB
Markdown
378 lines
13 KiB
Markdown
# Fab
|
||
|
||
Floating Action Button (FAB) component for primary actions with expandable menu options.
|
||
|
||
## Tag
|
||
|
||
`Fab`
|
||
|
||
## Props
|
||
|
||
| Prop | Type | Default | Description |
|
||
| :--- | :--- | :--- | :--- |
|
||
| `icon` | `string \| VNode \| Signal` | `-` | Main FAB icon |
|
||
| `label` | `string \| VNode \| Signal` | `-` | Text label for main button |
|
||
| `actions` | `Array<Action> \| Signal<Array>` | `[]` | Array of action buttons that expand from FAB |
|
||
| `position` | `string` | `'bottom-6 right-6'` | CSS position classes (e.g., 'bottom-6 left-6') |
|
||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||
|
||
### Action Structure
|
||
|
||
| Property | Type | Description |
|
||
| :--- | :--- | :--- |
|
||
| `label` | `string \| VNode` | Label text shown next to action button |
|
||
| `icon` | `string \| VNode` | Icon for the action button |
|
||
| `onclick` | `function` | Click handler |
|
||
| `class` | `string` | Additional CSS classes for the action button |
|
||
| `text` | `string` | Alternative text when no icon is provided |
|
||
|
||
## Styling
|
||
|
||
Fab uses **daisyUI Button classes** for styling the main button and action buttons:
|
||
|
||
| Category | Keywords | Description |
|
||
| :--- | :--- | :--- |
|
||
| Color | `btn-primary`, `btn-secondary`, `btn-accent`, `btn-info`, `btn-success`, `btn-warning`, `btn-error` | Visual color variants |
|
||
| Size | `btn-xs`, `btn-sm`, `btn-md`, `btn-lg`, `btn-xl` | Button scale |
|
||
| Shape | `btn-circle` | Circular button shape (default for FAB) |
|
||
|
||
> For further details, check the [daisyUI FAB Documentation](https://daisyui.com/components/fab) – Full reference for CSS classes.
|
||
|
||
### Example
|
||
|
||
```javascript
|
||
Fab({
|
||
icon: "➕",
|
||
class: "btn-primary btn-lg",
|
||
position: "bottom-6 right-6",
|
||
actions: [
|
||
{ icon: "📝", label: "New Note", onclick: () => console.log("Create note") }
|
||
]
|
||
});
|
||
```
|
||
|
||
## Live Examples
|
||
|
||
### Basic FAB
|
||
|
||
<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 min-h-[300px] relative"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const BasicDemo = () => {
|
||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
||
Fab({
|
||
icon: '➕',
|
||
actions: [
|
||
{ icon: '📝', label: 'New Note', onclick: () => Toast('Create note', 'alert-info', 2000) },
|
||
{ icon: '📷', label: 'Take Photo', onclick: () => Toast('Open camera', 'alert-info', 2000) },
|
||
{ icon: '📎', label: 'Attach File', onclick: () => Toast('Attach file', 'alert-info', 2000) }
|
||
]
|
||
})
|
||
]);
|
||
};
|
||
$mount(BasicDemo, '#demo-basic');
|
||
```
|
||
|
||
### With Label
|
||
|
||
<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-label" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const LabelDemo = () => {
|
||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
||
Fab({
|
||
label: 'Create',
|
||
icon: '✨',
|
||
actions: [
|
||
{ icon: '📝', label: 'Document', onclick: () => Toast('New document', 'alert-success', 2000) },
|
||
{ icon: '🎨', label: 'Design', onclick: () => Toast('New design', 'alert-success', 2000) },
|
||
{ icon: '📊', label: 'Spreadsheet', onclick: () => Toast('New spreadsheet', 'alert-success', 2000) }
|
||
]
|
||
})
|
||
]);
|
||
};
|
||
$mount(LabelDemo, '#demo-label');
|
||
```
|
||
|
||
### Different Positions
|
||
|
||
<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-positions" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[500px] relative"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const PositionsDemo = () => {
|
||
const position = $('bottom-6 right-6');
|
||
|
||
return Div({ class: 'relative h-[500px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
||
Div({ class: 'absolute top-4 left-4 z-20 bg-base-200 p-2 rounded-lg shadow' }, [
|
||
Select({
|
||
items: [
|
||
{ value: 'bottom-6 right-6', label: 'Bottom Right' },
|
||
{ value: 'bottom-6 left-6', label: 'Bottom Left' },
|
||
{ value: 'top-6 right-6', label: 'Top Right' },
|
||
{ value: 'top-6 left-6', label: 'Top Left' }
|
||
],
|
||
value: position,
|
||
onchange: (e) => position(e.target.value)
|
||
})
|
||
]),
|
||
Div({ class: 'absolute inset-0 flex items-center justify-center text-sm opacity-50 pointer-events-none' }, [
|
||
'FAB position changes relative to this container'
|
||
]),
|
||
Fab({
|
||
position: () => position(),
|
||
icon: '🧭',
|
||
actions: [
|
||
{ icon: '⬅️', label: 'Bottom Left', onclick: () => position('bottom-6 left-6') },
|
||
{ icon: '➡️', label: 'Bottom Right', onclick: () => position('bottom-6 right-6') },
|
||
{ icon: '⬆️', label: 'Top Right', onclick: () => position('top-6 right-6') },
|
||
{ icon: '⬇️', label: 'Top Left', onclick: () => position('top-6 left-6') }
|
||
]
|
||
})
|
||
]);
|
||
};
|
||
$mount(PositionsDemo, '#demo-positions');
|
||
```
|
||
|
||
### Color 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-colors" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const ColorsDemo = () => {
|
||
const variant = $('primary');
|
||
|
||
const variants = {
|
||
primary: { class: 'btn-primary', icon: '🔵' },
|
||
secondary: { class: 'btn-secondary', icon: '🟣' },
|
||
accent: { class: 'btn-accent', icon: '🔴' },
|
||
info: { class: 'btn-info', icon: '🔷' },
|
||
success: { class: 'btn-success', icon: '🟢' },
|
||
warning: { class: 'btn-warning', icon: '🟡' },
|
||
error: { class: 'btn-error', icon: '🔴' }
|
||
};
|
||
|
||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
||
Div({ class: 'absolute top-4 left-4 z-20 bg-base-200 p-2 rounded-lg shadow' }, [
|
||
Select({
|
||
items: Object.keys(variants).map(v => ({ value: v, label: v.charAt(0).toUpperCase() + v.slice(1) })),
|
||
value: variant,
|
||
onchange: (e) => variant(e.target.value)
|
||
})
|
||
]),
|
||
Fab({
|
||
class: variants[variant()].class,
|
||
icon: variants[variant()].icon,
|
||
actions: [
|
||
{ icon: '📝', label: 'Action 1', onclick: () => Toast('Action 1', 'alert-info', 2000) },
|
||
{ icon: '🎨', label: 'Action 2', onclick: () => Toast('Action 2', 'alert-info', 2000) },
|
||
{ icon: '⚙️', label: 'Action 3', onclick: () => Toast('Action 3', 'alert-info', 2000) }
|
||
]
|
||
})
|
||
]);
|
||
};
|
||
$mount(ColorsDemo, '#demo-colors');
|
||
```
|
||
|
||
### Reactive Actions
|
||
|
||
<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 min-h-[300px] relative"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const ReactiveActions = () => {
|
||
const count = $(0);
|
||
|
||
const actions = () => [
|
||
{
|
||
icon: '🔢',
|
||
label: `Count: ${count()}`,
|
||
onclick: () => {}
|
||
},
|
||
{
|
||
icon: '➕',
|
||
label: 'Increment',
|
||
onclick: () => count(count() + 1)
|
||
},
|
||
{
|
||
icon: '➖',
|
||
label: 'Decrement',
|
||
onclick: () => count(count() - 1)
|
||
},
|
||
{
|
||
icon: '🔄',
|
||
label: 'Reset',
|
||
onclick: () => count(0)
|
||
}
|
||
];
|
||
|
||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
||
Fab({
|
||
icon: () => count() > 0 ? `🔢 ${count()}` : '🎛️',
|
||
actions: actions
|
||
})
|
||
]);
|
||
};
|
||
$mount(ReactiveActions, '#demo-reactive');
|
||
```
|
||
|
||
### Document Actions
|
||
|
||
<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-document" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const DocumentActions = () => {
|
||
const saved = $(false);
|
||
|
||
const handleSave = () => {
|
||
saved(true);
|
||
Toast('Document saved!', 'alert-success', 2000);
|
||
setTimeout(() => saved(false), 3000);
|
||
};
|
||
|
||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
||
Div({ class: 'absolute inset-0 flex flex-col items-center justify-center' }, [
|
||
Div({ class: 'text-6xl mb-4' }, '📄'),
|
||
Div({ class: 'text-sm opacity-70' }, 'Untitled Document'),
|
||
() => saved() ? Div({ class: 'mt-4' }, Alert({ type: 'success', message: '✓ Saved successfully' })) : null
|
||
]),
|
||
Fab({
|
||
icon: '✏️',
|
||
actions: [
|
||
{ icon: '💾', label: 'Save', onclick: handleSave },
|
||
{ icon: '📋', label: 'Copy', onclick: () => Toast('Copied!', 'alert-info', 2000) },
|
||
{ icon: '✂️', label: 'Cut', onclick: () => Toast('Cut!', 'alert-info', 2000) },
|
||
{ icon: '📎', label: 'Share', onclick: () => Toast('Share dialog', 'alert-info', 2000) }
|
||
]
|
||
})
|
||
]);
|
||
};
|
||
$mount(DocumentActions, '#demo-document');
|
||
```
|
||
|
||
### Messaging FAB
|
||
|
||
<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-messaging" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const MessagingFAB = () => {
|
||
const unread = $(3);
|
||
|
||
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
||
Div({ class: 'absolute inset-0 flex flex-col items-center justify-center' }, [
|
||
Div({ class: 'text-6xl mb-4' }, '💬'),
|
||
Div({ class: 'text-sm opacity-70' }, 'Messages'),
|
||
() => unread() > 0 ? Div({ class: 'badge badge-error mt-2' }, `${unread()} unread`) : null
|
||
]),
|
||
Fab({
|
||
icon: () => `💬${unread() > 0 ? ` ${unread()}` : ''}`,
|
||
class: 'btn-primary',
|
||
actions: [
|
||
{
|
||
icon: '👤',
|
||
label: 'New Message',
|
||
onclick: () => Toast('Start new conversation', 'alert-info', 2000)
|
||
},
|
||
{
|
||
icon: '👥',
|
||
label: 'Group Chat',
|
||
onclick: () => Toast('Create group', 'alert-info', 2000)
|
||
},
|
||
{
|
||
icon: '📞',
|
||
label: 'Voice Call',
|
||
onclick: () => Toast('Start call', 'alert-info', 2000)
|
||
},
|
||
{
|
||
icon: '📹',
|
||
label: 'Video Call',
|
||
onclick: () => Toast('Start video call', 'alert-info', 2000)
|
||
},
|
||
{
|
||
icon: '🔔',
|
||
label: () => `Mark as read (${unread()})`,
|
||
onclick: () => {
|
||
unread(0);
|
||
Toast('All messages read', 'alert-success', 2000);
|
||
}
|
||
}
|
||
]
|
||
})
|
||
]);
|
||
};
|
||
$mount(MessagingFAB, '#demo-messaging');
|
||
```
|
||
|
||
### 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 min-h-[400px] relative"></div>
|
||
</div>
|
||
</div>
|
||
|
||
```javascript
|
||
const VariantsDemo = () => {
|
||
const actions = [
|
||
{ icon: '⭐', label: 'Favorite', onclick: () => Toast('Favorited', 'alert-info', 2000) },
|
||
{ icon: '🔔', label: 'Remind', onclick: () => Toast('Reminder set', 'alert-info', 2000) },
|
||
{ icon: '📅', label: 'Schedule', onclick: () => Toast('Scheduled', 'alert-info', 2000) }
|
||
];
|
||
|
||
return Div({ class: 'relative h-[400px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
|
||
Div({ class: 'grid grid-cols-2 gap-4 p-4 h-full' }, [
|
||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Primary'),
|
||
Fab({ icon: '🔵', class: 'btn-primary', actions, position: 'bottom-6 left-6' })
|
||
]),
|
||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Secondary'),
|
||
Fab({ icon: '🟣', class: 'btn-secondary', actions, position: 'bottom-6 right-6' })
|
||
]),
|
||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Accent'),
|
||
Fab({ icon: '🔴', class: 'btn-accent', actions, position: 'top-6 left-6' })
|
||
]),
|
||
Div({ class: 'relative border rounded-lg bg-base-200' }, [
|
||
Span({ class: 'absolute top-2 left-2 text-xs opacity-50' }, 'Success'),
|
||
Fab({ icon: '🟢', class: 'btn-success', actions, position: 'top-6 right-6' })
|
||
])
|
||
])
|
||
]);
|
||
};
|
||
$mount(VariantsDemo, '#demo-variants');
|
||
``` |