Docs Updated

This commit is contained in:
2026-04-03 01:41:07 +02:00
parent b0629ef3d0
commit 257107e198
42 changed files with 1529 additions and 6255 deletions

View File

@@ -28,19 +28,6 @@ Accordion supports all **daisyUI Collapse classes**:
> For further details, check the [daisyUI Collapse Documentation](https://daisyui.com/components/collapse) Full reference for CSS classes.
### Example
```javascript
Accordion({
title: "Click to expand",
class: "collapse-arrow bg-base-100",
open: isOpen,
onclick: () => isOpen(!isOpen())
}, [
Div({ class: "p-4" }, "Hidden content here")
]);
```
## Live Examples
### Basic Accordion

View File

@@ -29,18 +29,6 @@ Autocomplete wraps a **daisyUI Input component** internally. All Input styling c
> For further details, check the [daisyUI Input Documentation](https://daisyui.com/components/input) Full reference for CSS classes.
### Example
```javascript
Autocomplete({
class: "input-primary input-lg",
items: fruits,
value: selected,
onSelect: (value) => selected(value)
});
// Applies: primary color, large size
```
## Live Examples
### Basic Autocomplete

View File

@@ -25,16 +25,6 @@ Colorpicker is a custom component built with Tailwind CSS. The button supports s
> For further details, check the [daisyUI Button Documentation](https://daisyui.com/components/button) The color picker button accepts standard button styling classes.
### Example
```javascript
Colorpicker({
class: "btn-outline btn-sm",
label: "Pick color",
value: selectedColor
});
```
## Live Examples
### Basic Colorpicker

View File

@@ -29,17 +29,6 @@ Datepicker wraps a **daisyUI Input component** internally. All Input styling cla
> For further details, check the [daisyUI Input Documentation](https://daisyui.com/components/input) Full reference for CSS classes.
### Example
```javascript
Datepicker({
class: "input-primary input-lg",
label: "Select date",
value: selectedDate
});
// Applies: primary color, large size
```
## Live Examples
### Basic Datepicker

View File

@@ -27,19 +27,6 @@ Drawer supports all **daisyUI Drawer classes**:
> For further details, check the [daisyUI Drawer Documentation](https://daisyui.com/components/drawer) Full reference for CSS classes.
### Example
```javascript
Drawer({
id: "my-drawer",
open: isOpen,
class: "drawer-end",
side: SidebarContent,
content: MainContent
});
// Applies: drawer opens from right side
```
## Live Examples
### Basic Drawer

View File

@@ -168,7 +168,6 @@ $mount(UserDropdown, '#demo-user');
```javascript
const ReactiveDropdown = () => {
const count = $(0);
$watch(()=>console.log(count()));
const items = () => [
{ label: `Count: ${count()}`, onclick: () => {} },
{ label: 'Increment', onclick: () => count(count() + 1) },

View File

@@ -1,6 +1,6 @@
# Fab
Floating Action Button (FAB) component for primary actions with expandable menu options. Each example uses a container with `position: relative` and explicit height to position the FAB correctly.
Floating Action Button (FAB) component for primary actions with expandable menu options.
## Tag
@@ -8,22 +8,48 @@ Floating Action Button (FAB) component for primary actions with expandable menu
## 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) |
| 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 |
| 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
@@ -94,13 +120,13 @@ const PositionsDemo = () => {
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({
value: position,
options: [
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)
})
]),
@@ -148,8 +174,8 @@ const ColorsDemo = () => {
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,
options: Object.keys(variants).map(v => ({ value: v, label: v.charAt(0).toUpperCase() + v.slice(1) })),
onchange: (e) => variant(e.target.value)
})
]),
@@ -310,36 +336,6 @@ const MessagingFAB = () => {
$mount(MessagingFAB, '#demo-messaging');
```
### Flower Style 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-flower" class="bg-base-100 p-6 rounded-xl border border-base-300 min-h-[300px] relative"></div>
</div>
</div>
```javascript
const FlowerDemo = () => {
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
Div({ class: 'absolute inset-0 flex items-center justify-center text-sm opacity-50' }, [
'Flower style FAB (quarter circle arrangement)'
]),
Fab({
icon: '🌸',
class: 'fab-flower',
actions: [
{ icon: '📷', label: 'Camera', onclick: () => Toast('Camera', 'alert-info', 2000) },
{ icon: '🎨', label: 'Gallery', onclick: () => Toast('Gallery', 'alert-info', 2000) },
{ icon: '🎤', label: 'Voice', onclick: () => Toast('Voice', 'alert-info', 2000) },
{ icon: '📍', label: 'Location', onclick: () => Toast('Location', 'alert-info', 2000) }
]
})
]);
};
$mount(FlowerDemo, '#demo-flower');
```
### All Variants
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
@@ -379,310 +375,4 @@ const VariantsDemo = () => {
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initFabExamples = () => {
// 1. Basic FAB
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
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, basicTarget);
}
// 2. With Label
const labelTarget = document.querySelector('#demo-label');
if (labelTarget && !labelTarget.hasChildNodes()) {
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, labelTarget);
}
// 3. Different Positions
const positionsTarget = document.querySelector('#demo-positions');
if (positionsTarget && !positionsTarget.hasChildNodes()) {
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({
value: position,
options: [
{ 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' }
],
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, positionsTarget);
}
// 4. Color Variants
const colorsTarget = document.querySelector('#demo-colors');
if (colorsTarget && !colorsTarget.hasChildNodes()) {
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({
value: variant,
options: Object.keys(variants).map(v => ({ value: v, label: v.charAt(0).toUpperCase() + v.slice(1) })),
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, colorsTarget);
}
// 5. Reactive Actions
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
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, reactiveTarget);
}
// 6. Document Actions
const documentTarget = document.querySelector('#demo-document');
if (documentTarget && !documentTarget.hasChildNodes()) {
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, documentTarget);
}
// 7. Messaging FAB
const messagingTarget = document.querySelector('#demo-messaging');
if (messagingTarget && !messagingTarget.hasChildNodes()) {
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, messagingTarget);
}
// 8. Flower Style FAB
const flowerTarget = document.querySelector('#demo-flower');
if (flowerTarget && !flowerTarget.hasChildNodes()) {
const FlowerDemo = () => {
return Div({ class: 'relative h-[300px] w-full bg-base-100 rounded-lg overflow-hidden' }, [
Div({ class: 'absolute inset-0 flex items-center justify-center text-sm opacity-50' }, [
'Flower style FAB (quarter circle arrangement)'
]),
Fab({
icon: '🌸',
class: 'fab-flower',
actions: [
{ icon: '📷', label: 'Camera', onclick: () => Toast('Camera', 'alert-info', 2000) },
{ icon: '🎨', label: 'Gallery', onclick: () => Toast('Gallery', 'alert-info', 2000) },
{ icon: '🎤', label: 'Voice', onclick: () => Toast('Voice', 'alert-info', 2000) },
{ icon: '📍', label: 'Location', onclick: () => Toast('Location', 'alert-info', 2000) }
]
})
]);
};
$mount(FlowerDemo, flowerTarget);
}
// 9. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
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, variantsTarget);
}
};
initFabExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initFabExamples);
});
}
})();
</script>
```

View File

@@ -8,11 +8,35 @@ Fieldset component for grouping form fields with optional legend and consistent
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
| `legend` | `string \| VNode \| Signal` | `-` | Fieldset legend/title |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `VNode \| Array<VNode>` | Required | Form fields or content inside the fieldset |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `legend` | `string \| VNode \| Signal` | `-` | Fieldset legend/title |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `VNode \| Array<VNode>` | Required | Form fields or content inside the fieldset |
## Styling
Fieldset supports all **daisyUI Fieldset classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `fieldset` | Base fieldset styling |
| Color | `fieldset-primary`, `fieldset-secondary`, `fieldset-accent` | Border color variants |
| Size | `fieldset-xs`, `fieldset-sm`, `fieldset-md`, `fieldset-lg` | Fieldset scale |
> For further details, check the [daisyUI Fieldset Documentation](https://daisyui.com/components/fieldset) Full reference for CSS classes.
### Example
```javascript
Fieldset({
legend: "Personal Information",
class: "fieldset-primary w-full max-w-md"
}, [
Input({ label: "Name", placeholder: "Enter your name" }),
Input({ label: "Email", type: "email" })
]);
```
## Live Examples
@@ -112,7 +136,7 @@ const AddressDemo = () => {
Select({
label: 'Country',
value: country,
options: [
items: [
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' },
{ value: 'mx', label: 'Mexico' }
@@ -147,9 +171,9 @@ const PaymentDemo = () => {
}, [
Div({ class: 'space-y-4' }, [
Div({ class: 'flex gap-4' }, [
Radio({ label: 'Credit Card', value: method, radioValue: 'credit', onclick: () => method('credit') }),
Radio({ label: 'PayPal', value: method, radioValue: 'paypal', onclick: () => method('paypal') }),
Radio({ label: 'Bank Transfer', value: method, radioValue: 'bank', onclick: () => method('bank') })
Radio({ label: 'Credit Card', value: method, inputValue: 'credit', onclick: () => method('credit') }),
Radio({ label: 'PayPal', value: method, inputValue: 'paypal', onclick: () => method('paypal') }),
Radio({ label: 'Bank Transfer', value: method, inputValue: 'bank', onclick: () => method('bank') })
]),
() => method() === 'credit' ? Div({ class: 'space-y-4' }, [
Input({ label: 'Card Number', value: cardNumber, placeholder: '1234 5678 9012 3456', oninput: (e) => cardNumber(e.target.value) }),
@@ -189,15 +213,15 @@ const PreferencesDemo = () => {
Div({ class: 'form-control' }, [
Span({ class: 'label-text mb-2' }, 'Theme'),
Div({ class: 'flex gap-4' }, [
Radio({ label: 'Light', value: theme, radioValue: 'light', onclick: () => theme('light') }),
Radio({ label: 'Dark', value: theme, radioValue: 'dark', onclick: () => theme('dark') }),
Radio({ label: 'System', value: theme, radioValue: 'system', onclick: () => theme('system') })
Radio({ label: 'Light', value: theme, inputValue: 'light', onclick: () => theme('light') }),
Radio({ label: 'Dark', value: theme, inputValue: 'dark', onclick: () => theme('dark') }),
Radio({ label: 'System', value: theme, inputValue: 'system', onclick: () => theme('system') })
])
]),
Select({
label: 'Language',
value: language,
options: [
items: [
{ value: 'en', label: 'English' },
{ value: 'es', label: 'Español' },
{ value: 'fr', label: 'Français' }
@@ -297,253 +321,4 @@ const VariantsDemo = () => {
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initFieldsetExamples = () => {
// 1. Basic Fieldset
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
return Fieldset({
legend: 'User Information',
class: 'w-full max-w-md mx-auto'
}, [
Div({ class: 'space-y-4' }, [
Input({ label: 'Full Name', placeholder: 'Enter your name' }),
Input({ label: 'Email', type: 'email', placeholder: 'user@example.com' }),
Input({ label: 'Phone', type: 'tel', placeholder: '+1 234 567 890' })
])
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. With Reactive Legend
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const name = $('');
const email = $('');
const isValid = () => name().length > 0 && email().includes('@');
return Fieldset({
legend: () => isValid() ? '✓ Valid Form' : '✗ Incomplete Form',
class: 'w-full max-w-md mx-auto'
}, [
Div({ class: 'space-y-4' }, [
Input({
label: 'Full Name',
value: name,
oninput: (e) => name(e.target.value),
placeholder: 'Enter your name'
}),
Input({
label: 'Email',
type: 'email',
value: email,
oninput: (e) => email(e.target.value),
placeholder: 'user@example.com'
}),
() => isValid()
? Alert({ type: 'success', message: 'Form is ready to submit' })
: Alert({ type: 'warning', message: 'Please fill all required fields' })
])
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 3. Address Form
const addressTarget = document.querySelector('#demo-address');
if (addressTarget && !addressTarget.hasChildNodes()) {
const AddressDemo = () => {
const address = $('');
const city = $('');
const zip = $('');
const country = $('us');
return Fieldset({
legend: Span({ class: 'flex items-center gap-2' }, ['📍', 'Shipping Address']),
class: 'w-full max-w-md mx-auto'
}, [
Div({ class: 'space-y-4' }, [
Input({ label: 'Street Address', value: address, placeholder: '123 Main St', oninput: (e) => address(e.target.value) }),
Div({ class: 'grid grid-cols-2 gap-4' }, [
Input({ label: 'City', value: city, placeholder: 'City', oninput: (e) => city(e.target.value) }),
Input({ label: 'ZIP Code', value: zip, placeholder: 'ZIP', oninput: (e) => zip(e.target.value) })
]),
Select({
label: 'Country',
value: country,
options: [
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' },
{ value: 'mx', label: 'Mexico' }
],
onchange: (e) => country(e.target.value)
})
])
]);
};
$mount(AddressDemo, addressTarget);
}
// 4. Payment Method
const paymentTarget = document.querySelector('#demo-payment');
if (paymentTarget && !paymentTarget.hasChildNodes()) {
const PaymentDemo = () => {
const method = $('credit');
const cardNumber = $('');
const expiry = $('');
const cvv = $('');
return Fieldset({
legend: Span({ class: 'flex items-center gap-2' }, ['💳', 'Payment Details']),
class: 'w-full max-w-md mx-auto'
}, [
Div({ class: 'space-y-4' }, [
Div({ class: 'flex gap-4' }, [
Radio({ label: 'Credit Card', value: method, radioValue: 'credit', onclick: () => method('credit') }),
Radio({ label: 'PayPal', value: method, radioValue: 'paypal', onclick: () => method('paypal') }),
Radio({ label: 'Bank Transfer', value: method, radioValue: 'bank', onclick: () => method('bank') })
]),
() => method() === 'credit' ? Div({ class: 'space-y-4' }, [
Input({ label: 'Card Number', value: cardNumber, placeholder: '1234 5678 9012 3456', oninput: (e) => cardNumber(e.target.value) }),
Div({ class: 'grid grid-cols-2 gap-4' }, [
Input({ label: 'Expiry Date', value: expiry, placeholder: 'MM/YY', oninput: (e) => expiry(e.target.value) }),
Input({ label: 'CVV', type: 'password', value: cvv, placeholder: '123', oninput: (e) => cvv(e.target.value) })
])
]) : null,
() => method() === 'paypal' ? Alert({ type: 'info', message: 'You will be redirected to PayPal after confirming.' }) : null,
() => method() === 'bank' ? Alert({ type: 'warning', message: 'Bank transfer details will be sent via email.' }) : null
])
]);
};
$mount(PaymentDemo, paymentTarget);
}
// 5. Preferences Panel
const preferencesTarget = document.querySelector('#demo-preferences');
if (preferencesTarget && !preferencesTarget.hasChildNodes()) {
const PreferencesDemo = () => {
const theme = $('light');
const language = $('en');
const notifications = $(true);
return Fieldset({
legend: Span({ class: 'flex items-center gap-2' }, ['⚙️', 'Preferences']),
class: 'w-full max-w-md mx-auto'
}, [
Div({ class: 'space-y-4' }, [
Div({ class: 'form-control' }, [
Span({ class: 'label-text mb-2' }, 'Theme'),
Div({ class: 'flex gap-4' }, [
Radio({ label: 'Light', value: theme, radioValue: 'light', onclick: () => theme('light') }),
Radio({ label: 'Dark', value: theme, radioValue: 'dark', onclick: () => theme('dark') }),
Radio({ label: 'System', value: theme, radioValue: 'system', onclick: () => theme('system') })
])
]),
Select({
label: 'Language',
value: language,
options: [
{ value: 'en', label: 'English' },
{ value: 'es', label: 'Español' },
{ value: 'fr', label: 'Français' }
],
onchange: (e) => language(e.target.value)
}),
Checkbox({
label: 'Enable notifications',
value: notifications,
onclick: () => notifications(!notifications())
})
])
]);
};
$mount(PreferencesDemo, preferencesTarget);
}
// 6. Registration Form
const registrationTarget = document.querySelector('#demo-registration');
if (registrationTarget && !registrationTarget.hasChildNodes()) {
const RegistrationDemo = () => {
const username = $('');
const email = $('');
const password = $('');
const confirmPassword = $('');
const accepted = $(false);
const passwordsMatch = () => password() === confirmPassword();
const isFormValid = () => username() && email().includes('@') && password().length >= 6 && passwordsMatch() && accepted();
const handleSubmit = () => {
if (isFormValid()) {
Toast('Registration successful!', 'alert-success', 2000);
}
};
return Fieldset({
legend: Span({ class: 'flex items-center gap-2' }, ['📝', 'Create Account']),
class: 'w-full max-w-md mx-auto'
}, [
Div({ class: 'space-y-4' }, [
Input({ label: 'Username', value: username, placeholder: 'Choose a username', oninput: (e) => username(e.target.value) }),
Input({ label: 'Email', type: 'email', value: email, placeholder: 'your@email.com', oninput: (e) => email(e.target.value) }),
Input({ label: 'Password', type: 'password', value: password, placeholder: 'Min. 6 characters', oninput: (e) => password(e.target.value) }),
Input({
label: 'Confirm Password',
type: 'password',
value: confirmPassword,
error: () => confirmPassword() && !passwordsMatch() ? 'Passwords do not match' : '',
oninput: (e) => confirmPassword(e.target.value)
}),
Checkbox({
label: 'I accept the Terms and Conditions',
value: accepted,
onclick: () => accepted(!accepted())
}),
() => !isFormValid() && (username() || email() || password()) ? Alert({ type: 'warning', message: 'Please complete all fields correctly' }) : null,
Button({
class: 'btn btn-primary w-full',
onclick: handleSubmit,
disabled: () => !isFormValid()
}, 'Register')
])
]);
};
$mount(RegistrationDemo, registrationTarget);
}
// 7. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
const commonContent = Div({ class: 'space-y-4' }, [
Input({ label: 'Field 1', placeholder: 'Enter value' }),
Input({ label: 'Field 2', placeholder: 'Enter value' }),
Button({ class: 'btn btn-primary' }, 'Submit')
]);
return Div({ class: 'flex flex-col gap-4' }, [
Fieldset({ legend: 'Default Fieldset', class: 'w-full' }, [commonContent]),
Fieldset({ legend: 'With Shadow', class: 'w-full shadow-lg' }, [commonContent]),
Fieldset({ legend: 'With Background', class: 'w-full bg-base-100' }, [commonContent])
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initFieldsetExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initFieldsetExamples);
});
}
})();
</script>
```

View File

@@ -8,12 +8,31 @@ Indicator component for adding badges, status markers, or notifications to eleme
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
| `badge` | `string \| VNode \| Signal` | `-` | Content to display as indicator |
| `badgeClass` | `string` | `''` | Additional CSS classes for the badge |
| `class` | `string` | `''` | Additional CSS classes for the container |
| `children` | `VNode` | `-` | Element to attach the indicator to |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `value` | `string \| VNode \| Signal` | `-` | Content to display as indicator |
| `class` | `string` | `''` | Additional CSS classes for the badge |
| `children` | `VNode` | `-` | Element to attach the indicator to |
## Styling
Indicator uses **daisyUI Indicator and Badge classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Position | `indicator-start`, `indicator-end`, `indicator-top`, `indicator-bottom` | Indicator position (default: top-end) |
| Badge Color | `badge-primary`, `badge-secondary`, `badge-accent`, `badge-info`, `badge-success`, `badge-warning`, `badge-error` | Badge visual variants |
| Badge Size | `badge-xs`, `badge-sm`, `badge-md`, `badge-lg` | Badge scale |
> For further details, check the [daisyUI Indicator Documentation](https://daisyui.com/components/indicator) Full reference for CSS classes.
### Example
```javascript
Indicator({ value: "3", class: "badge-primary" },
Button({ class: "btn" }, "Notifications")
);
```
## Live Examples
@@ -29,15 +48,15 @@ Indicator component for adding badges, status markers, or notifications to eleme
```javascript
const BasicDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Indicator({ value: '3', ui: 'badge-primary' }, [
Indicator({ value: '3', class: 'badge-primary' },
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '📦')
]),
Indicator({ value: '99+', ui: 'badge-secondary' }, [
),
Indicator({ value: '99+', class: 'badge-secondary' },
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '🔔')
]),
Indicator({ value: 'New', ui: 'badge-accent' }, [
),
Indicator({ value: 'New', class: 'badge-accent' },
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '✨')
])
)
]);
};
$mount(BasicDemo, '#demo-basic');
@@ -55,21 +74,21 @@ $mount(BasicDemo, '#demo-basic');
```javascript
const StatusDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Indicator({ value: '●', ui: 'badge-success badge-xs' }, [
Indicator({ value: '●', class: 'badge-success badge-xs' },
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'JD')
])
]),
Indicator({ value: '●', ui: 'badge-warning badge-xs' }, [
),
Indicator({ value: '●', class: 'badge-warning badge-xs' },
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'JS')
])
]),
Indicator({ value: '●', ui: 'badge-error badge-xs' }, [
),
Indicator({ value: '●', class: 'badge-error badge-xs' },
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'BC')
])
])
)
]);
};
$mount(StatusDemo, '#demo-status');
@@ -90,14 +109,14 @@ const ReactiveDemo = () => {
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Indicator({
badge: () => count() > 0 ? count() : null,
badgeClass: 'badge-primary'
}, [
value: () => count() > 0 ? count() : null,
class: 'badge-primary'
},
Button({
class: 'btn btn-lg btn-primary',
onclick: () => count(count() + 1)
}, 'Notifications')
]),
),
Div({ class: 'flex gap-2' }, [
Button({
class: 'btn btn-sm',
@@ -145,11 +164,11 @@ const CartDemo = () => {
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex justify-between items-center' }, [
Indicator({
badge: () => cart().length,
badgeClass: 'badge-primary'
}, [
value: () => cart().length,
class: 'badge-primary'
},
Button({ class: 'btn', onclick: addItem }, '🛒 Add to Cart')
]),
),
Span({ class: 'text-lg font-bold' }, () => `Total: $${total()}`)
]),
cart().length === 0
@@ -202,11 +221,11 @@ const InboxDemo = () => {
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex justify-between items-center' }, [
Indicator({
badge: () => unread(),
badgeClass: 'badge-primary'
}, [
value: () => unread(),
class: 'badge-primary'
},
Span({ class: 'text-lg font-bold' }, 'Inbox')
]),
),
Button({
class: 'btn btn-sm btn-ghost',
onclick: () => {
@@ -231,47 +250,6 @@ const InboxDemo = () => {
$mount(InboxDemo, '#demo-inbox');
```
### Custom Position
<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 flex flex-wrap gap-8 justify-center"></div>
</div>
</div>
```javascript
const PositionsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Top-Left'),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Top-Right'),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Bottom-Left'),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Bottom-Right'),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
])
])
]);
};
$mount(PositionsDemo, '#demo-positions');
```
### All Variants
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
@@ -284,273 +262,21 @@ $mount(PositionsDemo, '#demo-positions');
```javascript
const VariantsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Indicator({ badge: '3', badgeClass: 'badge-primary badge-sm' }, [
Indicator({ value: '3', class: 'badge-primary badge-sm' },
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '📧')
]),
Indicator({ badge: '99+', badgeClass: 'badge-secondary badge-md' }, [
),
Indicator({ value: '99+', class: 'badge-secondary badge-md' },
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '🔔')
]),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
),
Indicator({ value: '●', class: 'badge-success badge-xs' },
Div({ class: 'avatar' }, [
Div({ class: 'w-10 h-10 rounded-full bg-primary' })
])
]),
Indicator({ badge: '!', badgeClass: 'badge-error badge-sm' }, [
),
Indicator({ value: '!', class: 'badge-error badge-sm' },
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '⚠️')
])
)
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initIndicatorExamples = () => {
// 1. Basic Indicator
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Indicator({ badge: '3', badgeClass: 'badge-primary' }, [
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '📦')
]),
Indicator({ badge: '99+', badgeClass: 'badge-secondary' }, [
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '🔔')
]),
Indicator({ badge: 'New', badgeClass: 'badge-accent' }, [
Div({ class: 'w-16 h-16 bg-base-300 rounded-lg flex items-center justify-center' }, '✨')
])
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. Online Status Indicator
const statusTarget = document.querySelector('#demo-status');
if (statusTarget && !statusTarget.hasChildNodes()) {
const StatusDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'JD')
])
]),
Indicator({ badge: '●', badgeClass: 'badge-warning badge-xs' }, [
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'JS')
])
]),
Indicator({ badge: '●', badgeClass: 'badge-error badge-xs' }, [
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-neutral text-neutral-content rounded-full w-12 h-12 flex items-center justify-center' }, 'BC')
])
])
]);
};
$mount(StatusDemo, statusTarget);
}
// 3. Reactive Counter
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const count = $(0);
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Indicator({
badge: () => count() > 0 ? count() : null,
badgeClass: 'badge-primary'
}, [
Button({
class: 'btn btn-lg btn-primary',
onclick: () => count(count() + 1)
}, 'Notifications')
]),
Div({ class: 'flex gap-2' }, [
Button({
class: 'btn btn-sm',
onclick: () => count(Math.max(0, count() - 1))
}, 'Decrease'),
Button({
class: 'btn btn-sm btn-ghost',
onclick: () => count(0)
}, 'Clear')
])
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 4. Shopping Cart
const cartTarget = document.querySelector('#demo-cart');
if (cartTarget && !cartTarget.hasChildNodes()) {
const CartDemo = () => {
const cart = $([
{ id: 1, name: 'Product 1', price: 29 },
{ id: 2, name: 'Product 2', price: 49 }
]);
const addItem = () => {
const newId = Math.max(...cart().map(i => i.id), 0) + 1;
cart([...cart(), { id: newId, name: `Product ${newId}`, price: Math.floor(Math.random() * 100) + 10 }]);
Toast('Item added to cart', 'alert-success', 1500);
};
const removeItem = (id) => {
cart(cart().filter(item => item.id !== id));
Toast('Item removed', 'alert-info', 1500);
};
const total = () => cart().reduce((sum, item) => sum + item.price, 0);
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex justify-between items-center' }, [
Indicator({
badge: () => cart().length,
badgeClass: 'badge-primary'
}, [
Button({ class: 'btn', onclick: addItem }, '🛒 Add to Cart')
]),
Span({ class: 'text-lg font-bold' }, () => `Total: $${total()}`)
]),
cart().length === 0
? Div({ class: 'alert alert-soft text-center' }, 'Cart is empty')
: Div({ class: 'flex flex-col gap-2' }, cart().map(item =>
Div({ class: 'flex justify-between items-center p-2 bg-base-200 rounded-lg' }, [
Span({}, item.name),
Div({ class: 'flex gap-2 items-center' }, [
Span({ class: 'text-sm font-bold' }, `$${item.price}`),
Button({
class: 'btn btn-xs btn-ghost btn-circle',
onclick: () => removeItem(item.id)
}, '✕')
])
])
))
]);
};
$mount(CartDemo, cartTarget);
}
// 5. Email Inbox
const inboxTarget = document.querySelector('#demo-inbox');
if (inboxTarget && !inboxTarget.hasChildNodes()) {
const InboxDemo = () => {
const unread = $(3);
const messages = $([
{ id: 1, from: 'john@example.com', subject: 'Meeting tomorrow', read: false },
{ id: 2, from: 'jane@example.com', subject: 'Project update', read: false },
{ id: 3, from: 'bob@example.com', subject: 'Question about design', read: false },
{ id: 4, from: 'alice@example.com', subject: 'Weekly report', read: true }
]);
const markAsRead = (id) => {
const msg = messages().find(m => m.id === id);
if (!msg.read) {
msg.read = true;
messages([...messages()]);
unread(unread() - 1);
}
};
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex justify-between items-center' }, [
Indicator({
badge: () => unread(),
badgeClass: 'badge-primary'
}, [
Span({ class: 'text-lg font-bold' }, 'Inbox')
]),
Button({
class: 'btn btn-sm btn-ghost',
onclick: () => {
messages().forEach(m => m.read = true);
messages([...messages()]);
unread(0);
}
}, 'Mark all read')
]),
Div({ class: 'flex flex-col gap-2' }, messages().map(msg =>
Div({
class: `p-3 rounded-lg cursor-pointer transition-all ${msg.read ? 'bg-base-200 opacity-60' : 'bg-primary/10 border-l-4 border-primary'}`,
onclick: () => markAsRead(msg.id)
}, [
Div({ class: 'font-medium' }, msg.from),
Div({ class: 'text-sm' }, msg.subject),
!msg.read && Span({ class: 'badge badge-xs badge-primary mt-1' }, 'New')
])
))
]);
};
$mount(InboxDemo, inboxTarget);
}
// 6. Custom Position
const positionsTarget = document.querySelector('#demo-positions');
if (positionsTarget && !positionsTarget.hasChildNodes()) {
const PositionsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Top-Left'),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Top-Right'),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Bottom-Left'),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Bottom-Right'),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg' })
])
])
]);
};
$mount(PositionsDemo, positionsTarget);
}
// 7. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Indicator({ badge: '3', badgeClass: 'badge-primary badge-sm' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '📧')
]),
Indicator({ badge: '99+', badgeClass: 'badge-secondary badge-md' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '🔔')
]),
Indicator({ badge: '●', badgeClass: 'badge-success badge-xs' }, [
Div({ class: 'avatar' }, [
Div({ class: 'w-10 h-10 rounded-full bg-primary' })
])
]),
Indicator({ badge: '!', badgeClass: 'badge-error badge-sm' }, [
Div({ class: 'w-12 h-12 bg-base-300 rounded-lg flex items-center justify-center' }, '⚠️')
])
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initIndicatorExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initIndicatorExamples);
});
}
})();
</script>
```

View File

@@ -1,4 +1,3 @@
# List
List component with custom item rendering, headers, and reactive data binding.
@@ -9,13 +8,25 @@ List component with custom item rendering, headers, and reactive data binding.
## Props
| Prop | Type | Default | Description |
| :----------- | :-------------------------------------- | :--------------- | :----------------------------------------------- |
| `items` | `Array \| Signal<Array>` | `[]` | Data array to display |
| `header` | `string \| VNode \| Signal` | `-` | Optional header content |
| `render` | `function(item, index)` | `-` | Custom render function for each item |
| `keyFn` | `function(item, index)` | `(item, idx) => idx` | Unique key function for items |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `items` | `Array \| Signal<Array>` | `[]` | Data array to display |
| `header` | `string \| VNode \| Signal` | `-` | Optional header content |
| `render` | `function(item, index)` | Required | Custom render function for each item |
| `keyFn` | `function(item, index)` | `(item, idx) => idx` | Unique key function for items |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
## Styling
List supports all **daisyUI List classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `list` | Base list styling |
| Variant | `list-row` | Row styling for list items |
| Background | `bg-base-100` | Background color |
> For further details, check the [daisyUI List Documentation](https://daisyui.com/components/list) Full reference for CSS classes.
## Live Examples
@@ -184,7 +195,7 @@ const InteractiveDemo = () => {
$mount(InteractiveDemo, '#demo-interactive');
```
### Reactive List
### Reactive List (Todo App)
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
<div class="card-body">
@@ -328,274 +339,4 @@ const VariantsDemo = () => {
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initListExamples = () => {
// 1. Basic List
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const items = ['Apple', 'Banana', 'Orange', 'Grape', 'Mango'];
return List({
items: items,
render: (item) => Div({ class: 'p-3 hover:bg-base-200 transition-colors' }, [
Span({ class: 'font-medium' }, item)
])
});
};
$mount(BasicDemo, basicTarget);
}
// 2. With Header
const headerTarget = document.querySelector('#demo-header');
if (headerTarget && !headerTarget.hasChildNodes()) {
const HeaderDemo = () => {
const users = [
{ name: 'John Doe', email: 'john@example.com', status: 'active' },
{ name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
{ name: 'Bob Johnson', email: 'bob@example.com', status: 'active' }
];
return List({
items: users,
header: Div({ class: 'p-3 bg-primary/10 font-bold border-b border-base-300' }, 'Active Users'),
render: (user) => Div({ class: 'p-3 border-b border-base-300 hover:bg-base-200' }, [
Div({ class: 'font-medium' }, user.name),
Div({ class: 'text-sm opacity-70' }, user.email),
Span({ class: `badge badge-sm ${user.status === 'active' ? 'badge-success' : 'badge-ghost'} mt-1` }, user.status)
])
});
};
$mount(HeaderDemo, headerTarget);
}
// 3. With Icons
const iconsTarget = document.querySelector('#demo-icons');
if (iconsTarget && !iconsTarget.hasChildNodes()) {
const IconsDemo = () => {
const settings = [
{ icon: '🔊', label: 'Sound', description: 'Adjust volume and notifications' },
{ icon: '🌙', label: 'Display', description: 'Brightness and dark mode' },
{ icon: '🔒', label: 'Privacy', description: 'Security settings' },
{ icon: '🌐', label: 'Network', description: 'WiFi and connections' }
];
return List({
items: settings,
render: (item) => Div({ class: 'flex gap-3 p-3 hover:bg-base-200 transition-colors cursor-pointer' }, [
Div({ class: 'text-2xl' }, item.icon),
Div({ class: 'flex-1' }, [
Div({ class: 'font-medium' }, item.label),
Div({ class: 'text-sm opacity-60' }, item.description)
]),
Span({ class: 'opacity-40' }, '→')
])
});
};
$mount(IconsDemo, iconsTarget);
}
// 4. With Badges
const badgesTarget = document.querySelector('#demo-badges');
if (badgesTarget && !badgesTarget.hasChildNodes()) {
const BadgesDemo = () => {
const notifications = [
{ id: 1, message: 'New comment on your post', time: '5 min ago', unread: true },
{ id: 2, message: 'Your order has been shipped', time: '1 hour ago', unread: true },
{ id: 3, message: 'Welcome to the platform!', time: '2 days ago', unread: false },
{ id: 4, message: 'Weekly digest available', time: '3 days ago', unread: false }
];
return List({
items: notifications,
render: (item) => Div({ class: `flex justify-between items-center p-3 border-b border-base-300 hover:bg-base-200 ${item.unread ? 'bg-primary/5' : ''}` }, [
Div({ class: 'flex-1' }, [
Div({ class: 'font-medium' }, item.message),
Div({ class: 'text-xs opacity-50' }, item.time)
]),
item.unread ? Span({ class: 'badge badge-primary badge-sm' }, 'New') : null
])
});
};
$mount(BadgesDemo, badgesTarget);
}
// 5. Interactive List
const interactiveTarget = document.querySelector('#demo-interactive');
if (interactiveTarget && !interactiveTarget.hasChildNodes()) {
const InteractiveDemo = () => {
const selected = $(null);
const items = [
{ id: 1, name: 'Project Alpha', status: 'In Progress' },
{ id: 2, name: 'Project Beta', status: 'Planning' },
{ id: 3, name: 'Project Gamma', status: 'Completed' },
{ id: 4, name: 'Project Delta', status: 'Review' }
];
const statusColors = {
'In Progress': 'badge-warning',
'Planning': 'badge-info',
'Completed': 'badge-success',
'Review': 'badge-accent'
};
return Div({ class: 'flex flex-col gap-4' }, [
List({
items: items,
render: (item) => Div({
class: `p-3 cursor-pointer transition-all hover:bg-base-200 ${selected() === item.id ? 'bg-primary/10 border-l-4 border-primary' : 'border-l-4 border-transparent'}`,
onclick: () => selected(item.id)
}, [
Div({ class: 'flex justify-between items-center' }, [
Div({ class: 'font-medium' }, item.name),
Span({ class: `badge ${statusColors[item.status]}` }, item.status)
])
])
}),
() => selected()
? Div({ class: 'alert alert-info' }, `Selected: ${items.find(i => i.id === selected()).name}`)
: Div({ class: 'alert alert-soft' }, 'Select a project to see details')
]);
};
$mount(InteractiveDemo, interactiveTarget);
}
// 6. Reactive List
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const todos = $([
{ id: 1, text: 'Complete documentation', done: false },
{ id: 2, text: 'Review pull requests', done: false },
{ id: 3, text: 'Deploy to production', done: false }
]);
const newTodo = $('');
const addTodo = () => {
if (newTodo().trim()) {
const newId = Math.max(...todos().map(t => t.id), 0) + 1;
todos([...todos(), { id: newId, text: newTodo(), done: false }]);
newTodo('');
}
};
const toggleTodo = (id) => {
todos(todos().map(t =>
t.id === id ? { ...t, done: !t.done } : t
));
};
const deleteTodo = (id) => {
todos(todos().filter(t => t.id !== id));
};
const pendingCount = () => todos().filter(t => !t.done).length;
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex gap-2' }, [
Input({
placeholder: 'Add new task...',
value: newTodo,
class: 'flex-1',
oninput: (e) => newTodo(e.target.value),
onkeypress: (e) => e.key === 'Enter' && addTodo()
}),
Button({ class: 'btn btn-primary', onclick: addTodo }, 'Add')
]),
List({
items: todos,
render: (todo) => Div({ class: `flex items-center gap-3 p-2 border-b border-base-300 hover:bg-base-200 ${todo.done ? 'opacity-60' : ''}` }, [
Checkbox({
value: todo.done,
onclick: () => toggleTodo(todo.id)
}),
Span({
class: `flex-1 ${todo.done ? 'line-through' : ''}`,
onclick: () => toggleTodo(todo.id)
}, todo.text),
Button({
class: 'btn btn-ghost btn-xs btn-circle',
onclick: () => deleteTodo(todo.id)
}, '✕')
])
}),
Div({ class: 'text-sm opacity-70 mt-2' }, () => `${pendingCount()} tasks remaining`)
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 7. Avatar List
const avatarTarget = document.querySelector('#demo-avatar');
if (avatarTarget && !avatarTarget.hasChildNodes()) {
const AvatarDemo = () => {
const contacts = [
{ name: 'Alice Johnson', role: 'Developer', avatar: 'A', online: true },
{ name: 'Bob Smith', role: 'Designer', avatar: 'B', online: false },
{ name: 'Charlie Brown', role: 'Manager', avatar: 'C', online: true },
{ name: 'Diana Prince', role: 'QA Engineer', avatar: 'D', online: false }
];
return List({
items: contacts,
render: (contact) => Div({ class: 'flex gap-3 p-3 hover:bg-base-200 transition-colors' }, [
Div({ class: `avatar ${contact.online ? 'online' : 'offline'}`, style: 'width: 48px' }, [
Div({ class: 'rounded-full bg-primary text-primary-content flex items-center justify-center w-12 h-12 font-bold' }, contact.avatar)
]),
Div({ class: 'flex-1' }, [
Div({ class: 'font-medium' }, contact.name),
Div({ class: 'text-sm opacity-60' }, contact.role)
]),
Div({ class: `badge badge-sm ${contact.online ? 'badge-success' : 'badge-ghost'}` }, contact.online ? 'Online' : 'Offline')
])
});
};
$mount(AvatarDemo, avatarTarget);
}
// 8. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
const items = ['Item 1', 'Item 2', 'Item 3'];
return Div({ class: 'flex flex-col gap-6' }, [
Div({ class: 'text-sm font-bold' }, 'Default List'),
List({
items: items,
render: (item) => Div({ class: 'p-2' }, item)
}),
Div({ class: 'text-sm font-bold mt-2' }, 'With Shadow'),
List({
items: items,
render: (item) => Div({ class: 'p-2' }, item),
class: 'shadow-lg'
}),
Div({ class: 'text-sm font-bold mt-2' }, 'Rounded Corners'),
List({
items: items,
render: (item) => Div({ class: 'p-2' }, item),
class: 'rounded-box overflow-hidden'
})
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initListExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initListExamples);
});
}
})();
</script>
```

View File

@@ -1,597 +0,0 @@
# Loading
Loading spinner component for indicating loading states with customizable size and colors.
## Tag
`Loading`
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :--------------- | :----------------------------------------------- |
| `$show` | `boolean \| Signal<boolean>` | `true` | Show/hide the loading spinner |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI loading variants)|
## Live Examples
### Basic Loading
<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 flex-wrap gap-8 justify-center items-center"></div>
</div>
</div>
```javascript
const BasicDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
Loading({ $show: true, class: 'loading-spinner' }),
Loading({ $show: true, class: 'loading-dots' }),
Loading({ $show: true, class: 'loading-ring' }),
Loading({ $show: true, class: 'loading-ball' }),
Loading({ $show: true, class: 'loading-bars' }),
Loading({ $show: true, class: 'loading-infinity' })
]);
};
$mount(BasicDemo, '#demo-basic');
```
### Loading Sizes
<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-sizes" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-wrap gap-8 justify-center items-center"></div>
</div>
</div>
```javascript
const SizesDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
Div({ class: 'text-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-xs' }),
Div({ class: 'text-xs mt-2' }, 'Extra Small')
]),
Div({ class: 'text-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-sm' }),
Div({ class: 'text-xs mt-2' }, 'Small')
]),
Div({ class: 'text-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-md' }),
Div({ class: 'text-xs mt-2' }, 'Medium')
]),
Div({ class: 'text-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-lg' }),
Div({ class: 'text-xs mt-2' }, 'Large')
])
]);
};
$mount(SizesDemo, '#demo-sizes');
```
### Loading Colors
<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 flex flex-wrap gap-8 justify-center items-center"></div>
</div>
</div>
```javascript
const ColorsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
Loading({ $show: true, class: 'loading-spinner text-primary' }),
Loading({ $show: true, class: 'loading-spinner text-secondary' }),
Loading({ $show: true, class: 'loading-spinner text-accent' }),
Loading({ $show: true, class: 'loading-spinner text-info' }),
Loading({ $show: true, class: 'loading-spinner text-success' }),
Loading({ $show: true, class: 'loading-spinner text-warning' }),
Loading({ $show: true, class: 'loading-spinner text-error' })
]);
};
$mount(ColorsDemo, '#demo-colors');
```
### Button Loading State
<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-button" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4 items-center"></div>
</div>
</div>
```javascript
const ButtonDemo = () => {
const isLoading = $(false);
const handleClick = async () => {
isLoading(true);
await new Promise(resolve => setTimeout(resolve, 2000));
isLoading(false);
Toast('Operation completed!', 'alert-success', 2000);
};
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Button({
class: 'btn btn-primary',
loading: isLoading,
onclick: handleClick
}, 'Submit'),
Div({ class: 'text-sm opacity-70' }, 'Click the button to see loading state')
]);
};
$mount(ButtonDemo, '#demo-button');
```
### Async Data Loading
<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-async" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
</div>
</div>
```javascript
const AsyncDemo = () => {
const loading = $(false);
const data = $(null);
const error = $(null);
const loadData = async () => {
loading(true);
error(null);
data(null);
try {
await new Promise(resolve => setTimeout(resolve, 2000));
const result = {
users: 1234,
posts: 5678,
comments: 9012
};
data(result);
Toast('Data loaded successfully!', 'alert-success', 2000);
} catch (err) {
error('Failed to load data');
Toast('Error loading data', 'alert-error', 2000);
} finally {
loading(false);
}
};
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: loadData,
disabled: () => loading()
}, loading() ? 'Loading...' : 'Load Data')
]),
Div({ class: 'relative min-h-[200px] flex items-center justify-center' }, [
() => loading() ? Loading({ $show: true, class: 'loading-spinner loading-lg' }) : null,
() => data() ? Div({ class: 'grid grid-cols-3 gap-4 w-full' }, [
Div({ class: 'stat' }, [
Div({ class: 'stat-title' }, 'Users'),
Div({ class: 'stat-value' }, data().users)
]),
Div({ class: 'stat' }, [
Div({ class: 'stat-title' }, 'Posts'),
Div({ class: 'stat-value' }, data().posts)
]),
Div({ class: 'stat' }, [
Div({ class: 'stat-title' }, 'Comments'),
Div({ class: 'stat-value' }, data().comments)
])
]) : null,
() => error() ? Alert({ type: 'error', message: error() }) : null
])
]);
};
$mount(AsyncDemo, '#demo-async');
```
### Full Page Loading
<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-fullpage" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
</div>
</div>
```javascript
const FullpageDemo = () => {
const isLoading = $(false);
const simulatePageLoad = () => {
isLoading(true);
setTimeout(() => {
isLoading(false);
Toast('Page loaded!', 'alert-success', 2000);
}, 2000);
};
return Div({ class: 'relative' }, [
Div({ class: 'flex justify-center p-8' }, [
Button({
class: 'btn btn-primary',
onclick: simulatePageLoad
}, 'Simulate Page Load')
]),
() => isLoading() ? Loading({ $show: true, class: 'loading-spinner loading-lg' }) : null
]);
};
$mount(FullpageDemo, '#demo-fullpage');
```
### Conditional Loading
<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-conditional" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
</div>
</div>
```javascript
const ConditionalDemo = () => {
const loadingState = $('idle');
const content = $('');
const simulateAction = (action) => {
loadingState('loading');
setTimeout(() => {
content(`Action: ${action} completed at ${new Date().toLocaleTimeString()}`);
loadingState('success');
setTimeout(() => loadingState('idle'), 1500);
}, 1500);
};
const loadingStates = {
idle: null,
loading: Loading({ $show: true, class: 'loading-spinner text-primary' }),
success: Alert({ type: 'success', message: '✓ Done!' })
};
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex gap-2 justify-center' }, [
Button({
class: 'btn btn-sm',
onclick: () => simulateAction('Save')
}, 'Save'),
Button({
class: 'btn btn-sm',
onclick: () => simulateAction('Update')
}, 'Update'),
Button({
class: 'btn btn-sm',
onclick: () => simulateAction('Delete')
}, 'Delete')
]),
Div({ class: 'flex justify-center min-h-[100px] items-center' }, [
() => loadingStates[loadingState()],
() => content() && loadingState() === 'idle' ? Div({ class: 'text-center' }, content()) : null
])
]);
};
$mount(ConditionalDemo, '#demo-conditional');
```
### 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-col gap-6"></div>
</div>
</div>
```javascript
const VariantsDemo = () => {
return Div({ class: 'flex flex-col gap-6' }, [
Div({ class: 'text-center' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Spinner'),
Div({ class: 'flex gap-4 justify-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-xs' }),
Loading({ $show: true, class: 'loading-spinner loading-sm' }),
Loading({ $show: true, class: 'loading-spinner loading-md' }),
Loading({ $show: true, class: 'loading-spinner loading-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Dots'),
Div({ class: 'flex gap-4 justify-center' }, [
Loading({ $show: true, class: 'loading-dots loading-xs' }),
Loading({ $show: true, class: 'loading-dots loading-sm' }),
Loading({ $show: true, class: 'loading-dots loading-md' }),
Loading({ $show: true, class: 'loading-dots loading-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Ring'),
Div({ class: 'flex gap-4 justify-center' }, [
Loading({ $show: true, class: 'loading-ring loading-xs' }),
Loading({ $show: true, class: 'loading-ring loading-sm' }),
Loading({ $show: true, class: 'loading-ring loading-md' }),
Loading({ $show: true, class: 'loading-ring loading-lg' })
])
])
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initLoadingExamples = () => {
// 1. Basic Loading
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
Loading({ $show: true, class: 'loading-spinner' }),
Loading({ $show: true, class: 'loading-dots' }),
Loading({ $show: true, class: 'loading-ring' }),
Loading({ $show: true, class: 'loading-ball' }),
Loading({ $show: true, class: 'loading-bars' }),
Loading({ $show: true, class: 'loading-infinity' })
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. Loading Sizes
const sizesTarget = document.querySelector('#demo-sizes');
if (sizesTarget && !sizesTarget.hasChildNodes()) {
const SizesDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
Div({ class: 'text-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-xs' }),
Div({ class: 'text-xs mt-2' }, 'Extra Small')
]),
Div({ class: 'text-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-sm' }),
Div({ class: 'text-xs mt-2' }, 'Small')
]),
Div({ class: 'text-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-md' }),
Div({ class: 'text-xs mt-2' }, 'Medium')
]),
Div({ class: 'text-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-lg' }),
Div({ class: 'text-xs mt-2' }, 'Large')
])
]);
};
$mount(SizesDemo, sizesTarget);
}
// 3. Loading Colors
const colorsTarget = document.querySelector('#demo-colors');
if (colorsTarget && !colorsTarget.hasChildNodes()) {
const ColorsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
Loading({ $show: true, class: 'loading-spinner text-primary' }),
Loading({ $show: true, class: 'loading-spinner text-secondary' }),
Loading({ $show: true, class: 'loading-spinner text-accent' }),
Loading({ $show: true, class: 'loading-spinner text-info' }),
Loading({ $show: true, class: 'loading-spinner text-success' }),
Loading({ $show: true, class: 'loading-spinner text-warning' }),
Loading({ $show: true, class: 'loading-spinner text-error' })
]);
};
$mount(ColorsDemo, colorsTarget);
}
// 4. Button Loading State
const buttonTarget = document.querySelector('#demo-button');
if (buttonTarget && !buttonTarget.hasChildNodes()) {
const ButtonDemo = () => {
const isLoading = $(false);
const handleClick = async () => {
isLoading(true);
await new Promise(resolve => setTimeout(resolve, 2000));
isLoading(false);
Toast('Operation completed!', 'alert-success', 2000);
};
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Button({
class: 'btn btn-primary',
loading: isLoading,
onclick: handleClick
}, 'Submit'),
Div({ class: 'text-sm opacity-70' }, 'Click the button to see loading state')
]);
};
$mount(ButtonDemo, buttonTarget);
}
// 5. Async Data Loading
const asyncTarget = document.querySelector('#demo-async');
if (asyncTarget && !asyncTarget.hasChildNodes()) {
const AsyncDemo = () => {
const loading = $(false);
const data = $(null);
const error = $(null);
const loadData = async () => {
loading(true);
error(null);
data(null);
try {
await new Promise(resolve => setTimeout(resolve, 2000));
const result = {
users: 1234,
posts: 5678,
comments: 9012
};
data(result);
Toast('Data loaded successfully!', 'alert-success', 2000);
} catch (err) {
error('Failed to load data');
Toast('Error loading data', 'alert-error', 2000);
} finally {
loading(false);
}
};
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: loadData,
disabled: () => loading()
}, loading() ? 'Loading...' : 'Load Data')
]),
Div({ class: 'relative min-h-[200px] flex items-center justify-center' }, [
() => loading() ? Loading({ $show: true, class: 'loading-spinner loading-lg' }) : null,
() => data() ? Div({ class: 'grid grid-cols-3 gap-4 w-full' }, [
Div({ class: 'stat' }, [
Div({ class: 'stat-title' }, 'Users'),
Div({ class: 'stat-value' }, data().users)
]),
Div({ class: 'stat' }, [
Div({ class: 'stat-title' }, 'Posts'),
Div({ class: 'stat-value' }, data().posts)
]),
Div({ class: 'stat' }, [
Div({ class: 'stat-title' }, 'Comments'),
Div({ class: 'stat-value' }, data().comments)
])
]) : null,
() => error() ? Alert({ type: 'error', message: error() }) : null
])
]);
};
$mount(AsyncDemo, asyncTarget);
}
// 6. Full Page Loading
const fullpageTarget = document.querySelector('#demo-fullpage');
if (fullpageTarget && !fullpageTarget.hasChildNodes()) {
const FullpageDemo = () => {
const isLoading = $(false);
const simulatePageLoad = () => {
isLoading(true);
setTimeout(() => {
isLoading(false);
Toast('Page loaded!', 'alert-success', 2000);
}, 2000);
};
return Div({ class: 'relative' }, [
Div({ class: 'flex justify-center p-8' }, [
Button({
class: 'btn btn-primary',
onclick: simulatePageLoad
}, 'Simulate Page Load')
]),
() => isLoading() ? Loading({ $show: true, class: 'loading-spinner loading-lg' }) : null
]);
};
$mount(FullpageDemo, fullpageTarget);
}
// 7. Conditional Loading
const conditionalTarget = document.querySelector('#demo-conditional');
if (conditionalTarget && !conditionalTarget.hasChildNodes()) {
const ConditionalDemo = () => {
const loadingState = $('idle');
const content = $('');
const simulateAction = (action) => {
loadingState('loading');
setTimeout(() => {
content(`Action: ${action} completed at ${new Date().toLocaleTimeString()}`);
loadingState('success');
setTimeout(() => loadingState('idle'), 1500);
}, 1500);
};
const loadingStates = {
idle: null,
loading: Loading({ $show: true, class: 'loading-spinner text-primary' }),
success: Alert({ type: 'success', message: '✓ Done!' })
};
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex gap-2 justify-center' }, [
Button({
class: 'btn btn-sm',
onclick: () => simulateAction('Save')
}, 'Save'),
Button({
class: 'btn btn-sm',
onclick: () => simulateAction('Update')
}, 'Update'),
Button({
class: 'btn btn-sm',
onclick: () => simulateAction('Delete')
}, 'Delete')
]),
Div({ class: 'flex justify-center min-h-[100px] items-center' }, [
() => loadingStates[loadingState()],
() => content() && loadingState() === 'idle' ? Div({ class: 'text-center' }, content()) : null
])
]);
};
$mount(ConditionalDemo, conditionalTarget);
}
// 8. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
return Div({ class: 'flex flex-col gap-6' }, [
Div({ class: 'text-center' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Spinner'),
Div({ class: 'flex gap-4 justify-center' }, [
Loading({ $show: true, class: 'loading-spinner loading-xs' }),
Loading({ $show: true, class: 'loading-spinner loading-sm' }),
Loading({ $show: true, class: 'loading-spinner loading-md' }),
Loading({ $show: true, class: 'loading-spinner loading-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Dots'),
Div({ class: 'flex gap-4 justify-center' }, [
Loading({ $show: true, class: 'loading-dots loading-xs' }),
Loading({ $show: true, class: 'loading-dots loading-sm' }),
Loading({ $show: true, class: 'loading-dots loading-md' }),
Loading({ $show: true, class: 'loading-dots loading-lg' })
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Ring'),
Div({ class: 'flex gap-4 justify-center' }, [
Loading({ $show: true, class: 'loading-ring loading-xs' }),
Loading({ $show: true, class: 'loading-ring loading-sm' }),
Loading({ $show: true, class: 'loading-ring loading-md' }),
Loading({ $show: true, class: 'loading-ring loading-lg' })
])
])
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initLoadingExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initLoadingExamples);
});
}
})();
</script>

View File

@@ -8,21 +8,34 @@ Menu component for creating navigation menus, sidebars, and dropdowns with suppo
## Props
| Prop | Type | Default | Description |
| :----------- | :-------------------------------------- | :---------- | :----------------------------------------------- |
| `items` | `Array<MenuItem>` | `[]` | Menu items configuration |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `items` | `Array<MenuItem>` | `[]` | Menu items configuration |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
### MenuItem Structure
| Property | Type | Description |
| :---------- | :--------------------------- | :----------------------------------------------- |
| `label` | `string \| VNode` | Menu item text or content |
| `icon` | `string \| VNode` | Optional icon to display |
| `active` | `boolean \| Signal<boolean>` | Active state highlighting |
| `onclick` | `function` | Click handler |
| `children` | `Array<MenuItem>` | Nested submenu items |
| `open` | `boolean` | Whether submenu is open (for nested items) |
| Property | Type | Description |
| :--- | :--- | :--- |
| `label` | `string \| VNode` | Menu item text or content |
| `icon` | `string \| VNode` | Optional icon to display |
| `active` | `boolean \| Signal<boolean>` | Active state highlighting |
| `onclick` | `function` | Click handler |
| `children` | `Array<MenuItem>` | Nested submenu items |
| `open` | `boolean` | Whether submenu is open (for nested items) |
## Styling
Menu supports all **daisyUI Menu classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Direction | `menu-vertical` (default), `menu-horizontal` | Menu orientation |
| Size | `menu-xs`, `menu-sm`, `menu-md`, `menu-lg` | Menu scale |
| Style | `menu-compact` | Reduced padding |
| Background | `bg-base-100`, `bg-base-200` | Background colors |
> For further details, check the [daisyUI Menu Documentation](https://daisyui.com/components/menu) Full reference for CSS classes.
## Live Examples
@@ -296,7 +309,7 @@ $mount(SidebarDemo, '#demo-sidebar');
```javascript
const AccountDemo = () => {
const [notifications, setNotifications] = $(3);
const notifications = $(3);
return Menu({
class: 'rounded-box w-56',
@@ -309,7 +322,6 @@ const AccountDemo = () => {
{
icon: '📧',
label: 'Messages',
badge: '3',
onclick: () => Toast('Messages opened', 'alert-info', 2000)
},
{
@@ -317,7 +329,7 @@ const AccountDemo = () => {
label: 'Notifications',
badge: () => notifications(),
onclick: () => {
setNotifications(0);
notifications(0);
Toast('Notifications cleared', 'alert-success', 2000);
}
},
@@ -411,350 +423,4 @@ const VariantsDemo = () => {
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initMenuExamples = () => {
// 1. Basic Menu
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const activeItem = $('home');
return Menu({
items: [
{
label: 'Home',
active: () => activeItem() === 'home',
onclick: () => activeItem('home')
},
{
label: 'About',
active: () => activeItem() === 'about',
onclick: () => activeItem('about')
},
{
label: 'Contact',
active: () => activeItem() === 'contact',
onclick: () => activeItem('contact')
}
]
});
};
$mount(BasicDemo, basicTarget);
}
// 2. With Icons
const iconsTarget = document.querySelector('#demo-icons');
if (iconsTarget && !iconsTarget.hasChildNodes()) {
const IconsDemo = () => {
const activeItem = $('dashboard');
return Menu({
items: [
{
icon: '🏠',
label: 'Dashboard',
active: () => activeItem() === 'dashboard',
onclick: () => activeItem('dashboard')
},
{
icon: '📊',
label: 'Analytics',
active: () => activeItem() === 'analytics',
onclick: () => activeItem('analytics')
},
{
icon: '⚙️',
label: 'Settings',
active: () => activeItem() === 'settings',
onclick: () => activeItem('settings')
},
{
icon: '👤',
label: 'Profile',
active: () => activeItem() === 'profile',
onclick: () => activeItem('profile')
}
]
});
};
$mount(IconsDemo, iconsTarget);
}
// 3. Nested Menu
const nestedTarget = document.querySelector('#demo-nested');
if (nestedTarget && !nestedTarget.hasChildNodes()) {
const NestedDemo = () => {
const activeItem = $('products');
return Menu({
items: [
{
label: 'Dashboard',
onclick: () => activeItem('dashboard'),
active: () => activeItem() === 'dashboard'
},
{
label: 'Products',
icon: '📦',
open: true,
children: [
{
label: 'All Products',
onclick: () => activeItem('all-products'),
active: () => activeItem() === 'all-products'
},
{
label: 'Add New',
onclick: () => activeItem('add-product'),
active: () => activeItem() === 'add-product'
},
{
label: 'Categories',
children: [
{
label: 'Electronics',
onclick: () => activeItem('electronics'),
active: () => activeItem() === 'electronics'
},
{
label: 'Clothing',
onclick: () => activeItem('clothing'),
active: () => activeItem() === 'clothing'
}
]
}
]
},
{
label: 'Orders',
icon: '📋',
onclick: () => activeItem('orders'),
active: () => activeItem() === 'orders'
},
{
label: 'Settings',
icon: '⚙️',
onclick: () => activeItem('settings'),
active: () => activeItem() === 'settings'
}
]
});
};
$mount(NestedDemo, nestedTarget);
}
// 4. Horizontal Menu
const horizontalTarget = document.querySelector('#demo-horizontal');
if (horizontalTarget && !horizontalTarget.hasChildNodes()) {
const HorizontalDemo = () => {
const activeItem = $('home');
return Menu({
class: 'menu-horizontal rounded-box',
items: [
{
label: 'Home',
active: () => activeItem() === 'home',
onclick: () => activeItem('home')
},
{
label: 'Products',
children: [
{ label: 'Electronics', onclick: () => activeItem('electronics') },
{ label: 'Clothing', onclick: () => activeItem('clothing') },
{ label: 'Books', onclick: () => activeItem('books') }
]
},
{
label: 'About',
onclick: () => activeItem('about'),
active: () => activeItem() === 'about'
},
{
label: 'Contact',
onclick: () => activeItem('contact'),
active: () => activeItem() === 'contact'
}
]
});
};
$mount(HorizontalDemo, horizontalTarget);
}
// 5. Sidebar Menu
const sidebarTarget = document.querySelector('#demo-sidebar');
if (sidebarTarget && !sidebarTarget.hasChildNodes()) {
const SidebarDemo = () => {
const activeItem = $('dashboard');
return Div({ class: 'flex' }, [
Div({ class: 'w-64' }, [
Menu({
class: 'rounded-box w-full',
items: [
{
icon: '📊',
label: 'Dashboard',
active: () => activeItem() === 'dashboard',
onclick: () => activeItem('dashboard')
},
{
icon: '👥',
label: 'Users',
children: [
{
label: 'All Users',
onclick: () => activeItem('all-users'),
active: () => activeItem() === 'all-users'
},
{
label: 'Add User',
onclick: () => activeItem('add-user'),
active: () => activeItem() === 'add-user'
}
]
},
{
icon: '📁',
label: 'Files',
onclick: () => activeItem('files'),
active: () => activeItem() === 'files'
},
{
icon: '⚙️',
label: 'Settings',
onclick: () => activeItem('settings'),
active: () => activeItem() === 'settings'
}
]
})
]),
Div({ class: 'flex-1 p-4' }, [
Div({ class: 'alert alert-info' }, () => `Current page: ${activeItem()}`)
])
]);
};
$mount(SidebarDemo, sidebarTarget);
}
// 6. Account Menu
const accountTarget = document.querySelector('#demo-account');
if (accountTarget && !accountTarget.hasChildNodes()) {
const AccountDemo = () => {
const [notifications, setNotifications] = $(3);
return Menu({
class: 'rounded-box w-56',
items: [
{
icon: '👤',
label: 'My Profile',
onclick: () => Toast('Profile clicked', 'alert-info', 2000)
},
{
icon: '📧',
label: 'Messages',
badge: '3',
onclick: () => Toast('Messages opened', 'alert-info', 2000)
},
{
icon: '🔔',
label: 'Notifications',
badge: () => notifications(),
onclick: () => {
setNotifications(0);
Toast('Notifications cleared', 'alert-success', 2000);
}
},
{
icon: '⚙️',
label: 'Settings',
onclick: () => Toast('Settings opened', 'alert-info', 2000)
},
{
icon: '🚪',
label: 'Logout',
onclick: () => Toast('Logged out', 'alert-warning', 2000)
}
]
});
};
$mount(AccountDemo, accountTarget);
}
// 7. Collapsible Sidebar
const collapsibleTarget = document.querySelector('#demo-collapsible');
if (collapsibleTarget && !collapsibleTarget.hasChildNodes()) {
const CollapsibleDemo = () => {
const collapsed = $(false);
const activeItem = $('dashboard');
return Div({ class: 'flex gap-4' }, [
Div({ class: `transition-all duration-300 ${collapsed() ? 'w-16' : 'w-64'}` }, [
Button({
class: 'btn btn-ghost btn-sm mb-2 w-full',
onclick: () => collapsed(!collapsed())
}, collapsed() ? '→' : '←'),
Menu({
class: `rounded-box ${collapsed() ? 'menu-compact' : ''}`,
items: [
{ icon: '📊', label: collapsed() ? '' : 'Dashboard', active: () => activeItem() === 'dashboard', onclick: () => activeItem('dashboard') },
{ icon: '👥', label: collapsed() ? '' : 'Users', active: () => activeItem() === 'users', onclick: () => activeItem('users') },
{ icon: '📁', label: collapsed() ? '' : 'Files', active: () => activeItem() === 'files', onclick: () => activeItem('files') },
{ icon: '⚙️', label: collapsed() ? '' : 'Settings', active: () => activeItem() === 'settings', onclick: () => activeItem('settings') }
]
})
]),
Div({ class: 'flex-1' }, [
Div({ class: 'alert alert-info' }, () => `Selected: ${activeItem()}`)
])
]);
};
$mount(CollapsibleDemo, collapsibleTarget);
}
// 8. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
const items = [
{ label: 'Item 1' },
{ label: 'Item 2' },
{ label: 'Item 3', children: [
{ label: 'Subitem 1' },
{ label: 'Subitem 2' }
]}
];
return Div({ class: 'flex flex-wrap gap-8' }, [
Div({ class: 'w-48' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Default'),
Menu({ items })
]),
Div({ class: 'w-48' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Compact'),
Menu({ items, class: 'menu-compact' })
]),
Div({ class: 'w-48' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'With Shadow'),
Menu({ items, class: 'shadow-lg' })
])
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initMenuExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initMenuExamples);
});
}
})();
</script>
```

View File

@@ -1,6 +1,6 @@
# Modal
Modal dialog component for displaying content in an overlay with customizable actions and backdrop.
Modal dialog component for displaying content in an overlay with customizable actions and backdrop. Uses the native HTML `<dialog>` element for better accessibility and performance.
## Tag
@@ -8,13 +8,24 @@ Modal dialog component for displaying content in an overlay with customizable ac
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
| `open` | `boolean \| Signal<boolean>` | `false` | Modal visibility state |
| `title` | `string \| VNode \| Signal` | `-` | Modal title text |
| `buttons` | `Array<VNode> \| VNode` | `-` | Action buttons to display (auto-closes on click) |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `string \| VNode` | `-` | Modal content |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `open` | `boolean \| Signal<boolean>` | `false` | Modal visibility state |
| `title` | `string \| VNode \| Signal` | `-` | Modal title text |
| `buttons` | `Array<VNode> \| VNode` | `-` | Action buttons to display (auto-closes on click) |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `string \| VNode` | `-` | Modal content |
## Styling
Modal supports all **daisyUI Modal classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Size | `modal-sm`, `modal-md`, `modal-lg`, `modal-xl` | Modal scale |
| Position | `modal-top`, `modal-middle`, `modal-bottom` | Modal vertical alignment |
> For further details, check the [daisyUI Modal Documentation](https://daisyui.com/components/modal) Full reference for CSS classes.
## Live Examples
@@ -34,14 +45,21 @@ const BasicDemo = () => {
return Div({ class: 'flex justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: () => isOpen(true)
onclick: () => {
isOpen(true);
}
}, 'Open Modal'),
Modal({
open: isOpen,
title: 'Basic Modal',
buttons: Button({ onclick: () => isOpen(false) }, 'Close')
buttons: Button({
class: 'btn-primary',
onclick: () => {
isOpen(false);
}
}, 'Custom Action')
}, [
Div({ class: 'py-4' }, 'This is a basic modal dialog. You can put any content here.')
Div({ class: 'py-4' }, 'This is a basic modal dialog. Press ESC or click Close.')
])
]);
};
@@ -357,311 +375,4 @@ const CustomDemo = () => {
]);
};
$mount(CustomDemo, '#demo-custom');
```
<script>
(function() {
const initModalExamples = () => {
// 1. Basic Modal
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const isOpen = $(false);
return Div({ class: 'flex justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: () => isOpen(true)
}, 'Open Modal'),
Modal({
open: isOpen,
title: 'Basic Modal',
buttons: Button({ onclick: () => isOpen(false) }, 'Close')
}, [
Div({ class: 'py-4' }, 'This is a basic modal dialog. You can put any content here.')
])
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. Modal with Actions
const actionsTarget = document.querySelector('#demo-actions');
if (actionsTarget && !actionsTarget.hasChildNodes()) {
const ActionsDemo = () => {
const isOpen = $(false);
const confirmed = $(false);
const handleConfirm = () => {
confirmed(true);
isOpen(false);
Toast('Action confirmed!', 'alert-success', 2000);
};
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Button({
class: 'btn btn-primary',
onclick: () => isOpen(true)
}, 'Confirm Action'),
() => confirmed() ? Alert({
type: 'success',
message: 'You confirmed the action!'
}) : null,
Modal({
open: isOpen,
title: 'Confirm Action',
buttons: [
Button({
class: 'btn btn-ghost',
onclick: () => isOpen(false)
}, 'Cancel'),
Button({
class: 'btn btn-primary',
onclick: handleConfirm
}, 'Confirm')
]
}, [
Div({ class: 'py-4' }, 'Are you sure you want to perform this action? This cannot be undone.')
])
]);
};
$mount(ActionsDemo, actionsTarget);
}
// 3. Form Modal
const formTarget = document.querySelector('#demo-form');
if (formTarget && !formTarget.hasChildNodes()) {
const FormModal = () => {
const isOpen = $(false);
const name = $('');
const email = $('');
const submitted = $(false);
const handleSubmit = () => {
if (name() && email()) {
submitted(true);
isOpen(false);
Toast(`Welcome ${name()}!`, 'alert-success', 2000);
setTimeout(() => submitted(false), 3000);
}
};
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Button({
class: 'btn btn-primary',
onclick: () => isOpen(true)
}, 'Sign Up'),
() => submitted() ? Alert({
type: 'success',
message: 'Account created successfully!'
}) : null,
Modal({
open: isOpen,
title: 'Create Account',
buttons: [
Button({
class: 'btn btn-ghost',
onclick: () => isOpen(false)
}, 'Cancel'),
Button({
class: 'btn btn-primary',
onclick: handleSubmit
}, 'Sign Up')
]
}, [
Div({ class: 'flex flex-col gap-4 py-2' }, [
Input({
label: 'Full Name',
value: name,
placeholder: 'Enter your name',
oninput: (e) => name(e.target.value)
}),
Input({
label: 'Email',
type: 'email',
value: email,
placeholder: 'Enter your email',
oninput: (e) => email(e.target.value)
})
])
])
]);
};
$mount(FormModal, formTarget);
}
// 4. Confirmation Modal
const confirmTarget = document.querySelector('#demo-confirm');
if (confirmTarget && !confirmTarget.hasChildNodes()) {
const ConfirmDemo = () => {
const isOpen = $(false);
const items = $([
{ id: 1, name: 'Document A' },
{ id: 2, name: 'Document B' },
{ id: 3, name: 'Document C' }
]);
const pendingDelete = $(null);
const confirmDelete = (item) => {
pendingDelete(item);
isOpen(true);
};
const handleDelete = () => {
items(items().filter(i => i.id !== pendingDelete().id));
isOpen(false);
Toast(`Deleted: ${pendingDelete().name}`, 'alert-info', 2000);
};
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex flex-col gap-2' }, items().map(item =>
Div({ class: 'flex justify-between items-center p-3 bg-base-200 rounded-lg' }, [
Span({}, item.name),
Button({
class: 'btn btn-xs btn-error',
onclick: () => confirmDelete(item)
}, 'Delete')
])
)),
Modal({
open: isOpen,
title: 'Delete Confirmation',
buttons: [
Button({
class: 'btn btn-ghost',
onclick: () => isOpen(false)
}, 'Cancel'),
Button({
class: 'btn btn-error',
onclick: handleDelete
}, 'Delete')
]
}, [
Div({ class: 'py-4' }, () => `Are you sure you want to delete "${pendingDelete()?.name}"? This action cannot be undone.`)
])
]);
};
$mount(ConfirmDemo, confirmTarget);
}
// 5. Large Content Modal
const largeTarget = document.querySelector('#demo-large');
if (largeTarget && !largeTarget.hasChildNodes()) {
const LargeDemo = () => {
const isOpen = $(false);
return Div({ class: 'flex justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: () => isOpen(true)
}, 'Open Large Modal'),
Modal({
open: isOpen,
title: 'Terms and Conditions',
buttons: Button({
class: 'btn btn-primary',
onclick: () => isOpen(false)
}, 'I Agree')
}, [
Div({ class: 'py-4 max-h-96 overflow-y-auto' }, [
Div({ class: 'space-y-4' }, [
Div({ class: 'text-sm' }, [
Div({ class: 'font-bold mb-2' }, '1. Introduction'),
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.'
]),
Div({ class: 'text-sm' }, [
Div({ class: 'font-bold mb-2' }, '2. User Obligations'),
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident.'
]),
Div({ class: 'text-sm' }, [
Div({ class: 'font-bold mb-2' }, '3. Privacy Policy'),
'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam.'
]),
Div({ class: 'text-sm' }, [
Div({ class: 'font-bold mb-2' }, '4. Termination'),
'At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores.'
])
])
])
])
]);
};
$mount(LargeDemo, largeTarget);
}
// 6. Multiple Modals
const multipleTarget = document.querySelector('#demo-multiple');
if (multipleTarget && !multipleTarget.hasChildNodes()) {
const MultipleDemo = () => {
const modal1 = $(false);
const modal2 = $(false);
const modal3 = $(false);
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
Button({ class: 'btn', onclick: () => modal1(true) }, 'Info Modal'),
Button({ class: 'btn', onclick: () => modal2(true) }, 'Success Modal'),
Button({ class: 'btn', onclick: () => modal3(true) }, 'Warning Modal'),
Modal({
open: modal1,
title: 'Information',
buttons: Button({ onclick: () => modal1(false) }, 'OK')
}, 'This is an informational message.'),
Modal({
open: modal2,
title: 'Success!',
buttons: Button({ onclick: () => modal2(false) }, 'Great!')
}, 'Your operation was completed successfully.'),
Modal({
open: modal3,
title: 'Warning',
buttons: Button({ onclick: () => modal3(false) }, 'Understood')
}, 'Please review your input before proceeding.')
]);
};
$mount(MultipleDemo, multipleTarget);
}
// 7. Custom Styled Modal
const customTarget = document.querySelector('#demo-custom');
if (customTarget && !customTarget.hasChildNodes()) {
const CustomDemo = () => {
const isOpen = $(false);
return Div({ class: 'flex justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: () => isOpen(true)
}, 'Open Custom Modal'),
Modal({
open: isOpen,
class: 'bg-gradient-to-r from-primary to-secondary text-primary-content'
}, [
Div({ class: 'text-center py-8' }, [
Div({ class: 'text-6xl mb-4' }, '🎉'),
Div({ class: 'text-2xl font-bold mb-2' }, 'Congratulations!'),
Div({ class: 'mb-6' }, 'You\'ve successfully completed the tutorial.'),
Button({
class: 'btn btn-ghost bg-white/20 hover:bg-white/30',
onclick: () => isOpen(false)
}, 'Get Started')
])
])
]);
};
$mount(CustomDemo, customTarget);
}
};
initModalExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initModalExamples);
});
}
})();
</script>
```

View File

@@ -8,10 +8,23 @@ Navigation bar component for creating responsive headers with logo, navigation l
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `VNode \| Array<VNode>` | `-` | Navbar content (should contain left, center, right sections) |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `VNode \| Array<VNode>` | `-` | Navbar content (should contain left, center, right sections) |
## Styling
Navbar supports all **daisyUI Navbar classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `navbar` | Base navbar styling |
| Sections | `navbar-start`, `navbar-center`, `navbar-end` | Content alignment sections |
| Background | `bg-base-100`, `bg-base-200`, `bg-primary`, etc. | Background colors |
| Shadow | `shadow`, `shadow-lg`, `shadow-md` | Box shadow variants |
> For further details, check the [daisyUI Navbar Documentation](https://daisyui.com/components/navbar) Full reference for CSS classes.
## Live Examples
@@ -82,47 +95,6 @@ const LinksDemo = () => {
$mount(LinksDemo, '#demo-links');
```
### With Dropdown Menu
<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-dropdown" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
</div>
</div>
```javascript
const DropdownDemo = () => {
return Navbar({ class: 'rounded-box' }, [
Div({ class: 'navbar-start' }, [
Div({ class: 'dropdown' }, [
Div({ tabindex: 0, role: 'button', class: 'btn btn-ghost lg:hidden' }, [
Span({ class: 'sr-only' }, 'Open menu'),
Icons.iconInfo
]),
Div({ tabindex: 0, class: 'dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow' }, [
Div({ class: 'menu-item' }, 'Home'),
Div({ class: 'menu-item' }, 'About'),
Div({ class: 'menu-item' }, 'Contact')
])
]),
Span({ class: 'text-xl font-bold' }, 'MyApp')
]),
Div({ class: 'navbar-center hidden lg:flex' }, [
Div({ class: 'menu menu-horizontal px-1' }, [
Div({ class: 'menu-item' }, 'Home'),
Div({ class: 'menu-item' }, 'About'),
Div({ class: 'menu-item' }, 'Contact')
])
]),
Div({ class: 'navbar-end' }, [
Button({ class: 'btn btn-primary btn-sm' }, 'Login')
])
]);
};
$mount(DropdownDemo, '#demo-dropdown');
```
### With Search
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
@@ -176,9 +148,9 @@ const AvatarDemo = () => {
]),
Div({ class: 'navbar-center hidden lg:flex' }, [
Div({ class: 'menu menu-horizontal px-1' }, [
Div({ class: 'menu-item' }, 'Dashboard'),
Div({ class: 'menu-item' }, 'Projects'),
Div({ class: 'menu-item' }, 'Tasks')
Span({ class: 'text-sm' }, 'Dashboard'),
Span({ class: 'text-sm' }, 'Projects'),
Span({ class: 'text-sm' }, 'Tasks')
])
]),
Div({ class: 'navbar-end' }, [
@@ -187,9 +159,9 @@ const AvatarDemo = () => {
Div({ class: 'w-8 rounded-full bg-primary text-primary-content flex items-center justify-center' }, 'JD')
]),
Div({ tabindex: 0, class: 'dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow' }, [
Div({ class: 'menu-item' }, 'Profile'),
Div({ class: 'menu-item' }, 'Settings'),
Div({ class: 'menu-item' }, 'Logout')
Span({ class: 'menu-item' }, 'Profile'),
Span({ class: 'menu-item' }, 'Settings'),
Span({ class: 'menu-item' }, 'Logout')
])
])
])
@@ -301,251 +273,4 @@ const VariantsDemo = () => {
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initNavbarExamples = () => {
// 1. Basic Navbar
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
return Navbar({ class: 'rounded-box' }, [
Div({ class: 'navbar-start' }, [
Span({ class: 'text-xl font-bold' }, 'Logo')
]),
Div({ class: 'navbar-center hidden lg:flex' }, [
Span({ class: 'text-sm' }, 'Navigation Items')
]),
Div({ class: 'navbar-end' }, [
Button({ class: 'btn btn-ghost btn-sm' }, 'Login')
])
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. With Navigation Links
const linksTarget = document.querySelector('#demo-links');
if (linksTarget && !linksTarget.hasChildNodes()) {
const LinksDemo = () => {
const active = $('home');
return Navbar({ class: 'rounded-box' }, [
Div({ class: 'navbar-start' }, [
Span({ class: 'text-xl font-bold' }, 'MyApp')
]),
Div({ class: 'navbar-center hidden lg:flex' }, [
Div({ class: 'menu menu-horizontal px-1' }, [
Button({
class: `btn btn-ghost btn-sm ${active() === 'home' ? 'btn-active' : ''}`,
onclick: () => active('home')
}, 'Home'),
Button({
class: `btn btn-ghost btn-sm ${active() === 'about' ? 'btn-active' : ''}`,
onclick: () => active('about')
}, 'About'),
Button({
class: `btn btn-ghost btn-sm ${active() === 'contact' ? 'btn-active' : ''}`,
onclick: () => active('contact')
}, 'Contact')
])
]),
Div({ class: 'navbar-end' }, [
Button({ class: 'btn btn-primary btn-sm' }, 'Sign Up')
])
]);
};
$mount(LinksDemo, linksTarget);
}
// 3. With Dropdown Menu
const dropdownTarget = document.querySelector('#demo-dropdown');
if (dropdownTarget && !dropdownTarget.hasChildNodes()) {
const DropdownDemo = () => {
return Navbar({ class: 'rounded-box' }, [
Div({ class: 'navbar-start' }, [
Div({ class: 'dropdown' }, [
Div({ tabindex: 0, role: 'button', class: 'btn btn-ghost lg:hidden' }, [
Span({ class: 'sr-only' }, 'Open menu'),
Icons.iconInfo
]),
Div({ tabindex: 0, class: 'dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow' }, [
Div({ class: 'menu-item' }, 'Home'),
Div({ class: 'menu-item' }, 'About'),
Div({ class: 'menu-item' }, 'Contact')
])
]),
Span({ class: 'text-xl font-bold' }, 'MyApp')
]),
Div({ class: 'navbar-center hidden lg:flex' }, [
Div({ class: 'menu menu-horizontal px-1' }, [
Div({ class: 'menu-item' }, 'Home'),
Div({ class: 'menu-item' }, 'About'),
Div({ class: 'menu-item' }, 'Contact')
])
]),
Div({ class: 'navbar-end' }, [
Button({ class: 'btn btn-primary btn-sm' }, 'Login')
])
]);
};
$mount(DropdownDemo, dropdownTarget);
}
// 4. With Search
const searchTarget = document.querySelector('#demo-search');
if (searchTarget && !searchTarget.hasChildNodes()) {
const SearchDemo = () => {
const searchQuery = $('');
return Navbar({ class: 'rounded-box' }, [
Div({ class: 'navbar-start' }, [
Span({ class: 'text-xl font-bold' }, 'MyApp')
]),
Div({ class: 'navbar-center' }, [
Div({ class: 'form-control' }, [
Input({
placeholder: 'Search...',
value: searchQuery,
class: 'input input-bordered w-48 md:w-auto',
oninput: (e) => searchQuery(e.target.value)
})
])
]),
Div({ class: 'navbar-end' }, [
Button({ class: 'btn btn-ghost btn-circle' }, '🔔'),
Button({ class: 'btn btn-ghost btn-circle' }, '👤')
])
]);
};
$mount(SearchDemo, searchTarget);
}
// 5. With Avatar and Dropdown
const avatarTarget = document.querySelector('#demo-avatar');
if (avatarTarget && !avatarTarget.hasChildNodes()) {
const AvatarDemo = () => {
return Navbar({ class: 'rounded-box' }, [
Div({ class: 'navbar-start' }, [
Span({ class: 'text-xl font-bold' }, 'MyApp')
]),
Div({ class: 'navbar-center hidden lg:flex' }, [
Div({ class: 'menu menu-horizontal px-1' }, [
Div({ class: 'menu-item' }, 'Dashboard'),
Div({ class: 'menu-item' }, 'Projects'),
Div({ class: 'menu-item' }, 'Tasks')
])
]),
Div({ class: 'navbar-end' }, [
Div({ class: 'dropdown dropdown-end' }, [
Div({ tabindex: 0, role: 'button', class: 'btn btn-ghost btn-circle avatar' }, [
Div({ class: 'w-8 rounded-full bg-primary text-primary-content flex items-center justify-center' }, 'JD')
]),
Div({ tabindex: 0, class: 'dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow' }, [
Div({ class: 'menu-item' }, 'Profile'),
Div({ class: 'menu-item' }, 'Settings'),
Div({ class: 'menu-item' }, 'Logout')
])
])
])
]);
};
$mount(AvatarDemo, avatarTarget);
}
// 6. Responsive Navbar
const responsiveTarget = document.querySelector('#demo-responsive');
if (responsiveTarget && !responsiveTarget.hasChildNodes()) {
const ResponsiveDemo = () => {
const isOpen = $(false);
return Div({ class: 'flex flex-col' }, [
Navbar({ class: 'rounded-box' }, [
Div({ class: 'navbar-start' }, [
Button({
class: 'btn btn-ghost btn-circle lg:hidden',
onclick: () => isOpen(!isOpen())
}, '☰')
]),
Div({ class: 'navbar-center' }, [
Span({ class: 'text-xl font-bold' }, 'MyApp')
]),
Div({ class: 'navbar-end' }, [
Button({ class: 'btn btn-ghost btn-circle' }, '🔔')
])
]),
() => isOpen() ? Div({ class: 'flex flex-col gap-2 p-4 bg-base-200 rounded-box mt-2' }, [
Button({ class: 'btn btn-ghost justify-start' }, 'Home'),
Button({ class: 'btn btn-ghost justify-start' }, 'About'),
Button({ class: 'btn btn-ghost justify-start' }, 'Services'),
Button({ class: 'btn btn-ghost justify-start' }, 'Contact')
]) : null
]);
};
$mount(ResponsiveDemo, responsiveTarget);
}
// 7. With Brand and Actions
const brandTarget = document.querySelector('#demo-brand');
if (brandTarget && !brandTarget.hasChildNodes()) {
const BrandDemo = () => {
return Navbar({ class: 'rounded-box bg-primary text-primary-content' }, [
Div({ class: 'navbar-start' }, [
Span({ class: 'text-xl font-bold' }, 'Brand')
]),
Div({ class: 'navbar-center hidden lg:flex' }, [
Div({ class: 'menu menu-horizontal px-1' }, [
Span({ class: 'text-sm' }, 'Features'),
Span({ class: 'text-sm' }, 'Pricing'),
Span({ class: 'text-sm' }, 'About')
])
]),
Div({ class: 'navbar-end' }, [
Button({ class: 'btn btn-ghost btn-sm' }, 'Login'),
Button({ class: 'btn btn-outline btn-sm ml-2' }, 'Sign Up')
])
]);
};
$mount(BrandDemo, brandTarget);
}
// 8. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'text-sm font-bold' }, 'Default Navbar'),
Navbar({ class: 'rounded-box' }, [
Div({ class: 'navbar-start' }, [Span({}, 'Start')]),
Div({ class: 'navbar-center' }, [Span({}, 'Center')]),
Div({ class: 'navbar-end' }, [Span({}, 'End')])
]),
Div({ class: 'text-sm font-bold mt-2' }, 'With Shadow'),
Navbar({ class: 'rounded-box shadow-lg' }, [
Div({ class: 'navbar-start' }, [Span({}, 'Logo')]),
Div({ class: 'navbar-end' }, [Button({ class: 'btn btn-sm' }, 'Button')])
]),
Div({ class: 'text-sm font-bold mt-2' }, 'With Background'),
Navbar({ class: 'rounded-box bg-base-300' }, [
Div({ class: 'navbar-start' }, [Span({ class: 'font-bold' }, 'Colored')]),
Div({ class: 'navbar-end' }, [Span({ class: 'text-sm' }, 'Info')])
])
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initNavbarExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initNavbarExamples);
});
}
})();
</script>
```

View File

@@ -8,16 +8,27 @@ Radio button component with label, tooltip support, and reactive group selection
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
| `label` | `string` | `-` | Label text for the radio button |
| `value` | `string \| Signal<string>` | `-` | Selected value signal for the group |
| `radioValue` | `string` | `-` | Value of this radio button |
| `name` | `string` | `-` | Group name (all radios in group should share this) |
| `tooltip` | `string` | `-` | Tooltip text on hover |
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `onclick` | `function` | `-` | Click event handler |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `label` | `string` | `-` | Label text for the radio button |
| `value` | `string \| Signal<string>` | `-` | Selected value signal for the group |
| `inputValue` | `string` | `-` | Value of this radio button |
| `name` | `string` | `-` | Group name (all radios in group should share this) |
| `tooltip` | `string` | `-` | Tooltip text on hover |
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `onclick` | `function` | `-` | Click event handler |
## Styling
Radio supports all **daisyUI Radio classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Color | `radio-primary`, `radio-secondary`, `radio-accent`, `radio-info`, `radio-success`, `radio-warning`, `radio-error` | Radio color variants |
| Size | `radio-xs`, `radio-sm`, `radio-md`, `radio-lg` | Radio scale |
> For further details, check the [daisyUI Radio Documentation](https://daisyui.com/components/radio) Full reference for CSS classes.
## Live Examples
@@ -39,21 +50,21 @@ const BasicDemo = () => {
label: 'Option 1',
name: 'basic-group',
value: selected,
radioValue: 'option1',
inputValue: 'option1',
onclick: () => selected('option1')
}),
Radio({
label: 'Option 2',
name: 'basic-group',
value: selected,
radioValue: 'option2',
inputValue: 'option2',
onclick: () => selected('option2')
}),
Radio({
label: 'Option 3',
name: 'basic-group',
value: selected,
radioValue: 'option3',
inputValue: 'option3',
onclick: () => selected('option3')
}),
Div({ class: 'mt-2 text-sm opacity-70' }, () => `Selected: ${selected()}`)
@@ -80,7 +91,7 @@ const TooltipDemo = () => {
label: 'Light mode',
name: 'theme-group',
value: selected,
radioValue: 'light',
inputValue: 'light',
tooltip: 'Light theme with white background',
onclick: () => selected('light')
}),
@@ -88,7 +99,7 @@ const TooltipDemo = () => {
label: 'Dark mode',
name: 'theme-group',
value: selected,
radioValue: 'dark',
inputValue: 'dark',
tooltip: 'Dark theme with black background',
onclick: () => selected('dark')
})
@@ -115,14 +126,14 @@ const DisabledDemo = () => {
label: 'Enabled option',
name: 'disabled-group',
value: selected,
radioValue: 'enabled',
inputValue: 'enabled',
onclick: () => selected('enabled')
}),
Radio({
label: 'Disabled option (cannot select)',
name: 'disabled-group',
value: selected,
radioValue: 'disabled',
inputValue: 'disabled',
disabled: true,
onclick: () => selected('disabled')
})
@@ -164,7 +175,7 @@ const ReactiveDemo = () => {
label: s.label,
name: 'size-group',
value: size,
radioValue: s.value,
inputValue: s.value,
onclick: () => size(s.value)
})
)),
@@ -174,7 +185,7 @@ const ReactiveDemo = () => {
label: c.label,
name: 'color-group',
value: color,
radioValue: c.value,
inputValue: c.value,
onclick: () => color(c.value)
})
)),
@@ -211,21 +222,21 @@ const PaymentDemo = () => {
label: '💳 Credit Card',
name: 'payment-group',
value: method,
radioValue: 'credit',
inputValue: 'credit',
onclick: () => method('credit')
}),
Radio({
label: '🏦 Bank Transfer',
name: 'payment-group',
value: method,
radioValue: 'bank',
inputValue: 'bank',
onclick: () => method('bank')
}),
Radio({
label: '📱 Digital Wallet',
name: 'payment-group',
value: method,
radioValue: 'wallet',
inputValue: 'wallet',
onclick: () => method('wallet')
})
]),
@@ -265,7 +276,7 @@ const VariantsDemo = () => {
label: 'Option A',
name: 'primary-group',
value: primary,
radioValue: 'primary1',
inputValue: 'primary1',
class: 'radio-primary',
onclick: () => primary('primary1')
}),
@@ -273,7 +284,7 @@ const VariantsDemo = () => {
label: 'Option B',
name: 'primary-group',
value: primary,
radioValue: 'primary2',
inputValue: 'primary2',
class: 'radio-primary',
onclick: () => primary('primary2')
})
@@ -286,7 +297,7 @@ const VariantsDemo = () => {
label: 'Option A',
name: 'secondary-group',
value: secondary,
radioValue: 'secondary1',
inputValue: 'secondary1',
class: 'radio-secondary',
onclick: () => secondary('secondary1')
}),
@@ -294,7 +305,7 @@ const VariantsDemo = () => {
label: 'Option B',
name: 'secondary-group',
value: secondary,
radioValue: 'secondary2',
inputValue: 'secondary2',
class: 'radio-secondary',
onclick: () => secondary('secondary2')
})
@@ -307,7 +318,7 @@ const VariantsDemo = () => {
label: 'Option A',
name: 'accent-group',
value: accent,
radioValue: 'accent1',
inputValue: 'accent1',
class: 'radio-accent',
onclick: () => accent('accent1')
}),
@@ -315,7 +326,7 @@ const VariantsDemo = () => {
label: 'Option B',
name: 'accent-group',
value: accent,
radioValue: 'accent2',
inputValue: 'accent2',
class: 'radio-accent',
onclick: () => accent('accent2')
})
@@ -359,7 +370,7 @@ const DynamicDemo = () => {
label: 'Cars',
name: 'category-group',
value: category,
radioValue: 'cars',
inputValue: 'cars',
onclick: () => {
category('cars');
selected('');
@@ -369,7 +380,7 @@ const DynamicDemo = () => {
label: 'Colors',
name: 'category-group',
value: category,
radioValue: 'colors',
inputValue: 'colors',
onclick: () => {
category('colors');
selected('');
@@ -384,7 +395,7 @@ const DynamicDemo = () => {
label: opt.label,
name: 'dynamic-option-group',
value: selected,
radioValue: opt.value,
inputValue: opt.value,
onclick: () => selected(opt.value)
})
)
@@ -395,346 +406,4 @@ const DynamicDemo = () => {
]);
};
$mount(DynamicDemo, '#demo-dynamic');
```
<script>
(function() {
const initRadioExamples = () => {
// 1. Basic Radio Group
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const selected = $('option1');
return Div({ class: 'flex flex-col gap-3' }, [
Radio({
label: 'Option 1',
name: 'basic-group',
value: selected,
radioValue: 'option1',
onclick: () => selected('option1')
}),
Radio({
label: 'Option 2',
name: 'basic-group',
value: selected,
radioValue: 'option2',
onclick: () => selected('option2')
}),
Radio({
label: 'Option 3',
name: 'basic-group',
value: selected,
radioValue: 'option3',
onclick: () => selected('option3')
}),
Div({ class: 'mt-2 text-sm opacity-70' }, () => `Selected: ${selected()}`)
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. With Tooltip
const tooltipTarget = document.querySelector('#demo-tooltip');
if (tooltipTarget && !tooltipTarget.hasChildNodes()) {
const TooltipDemo = () => {
const selected = $('light');
return Div({ class: 'flex flex-col gap-3' }, [
Radio({
label: 'Light mode',
name: 'theme-group',
value: selected,
radioValue: 'light',
tooltip: 'Light theme with white background',
onclick: () => selected('light')
}),
Radio({
label: 'Dark mode',
name: 'theme-group',
value: selected,
radioValue: 'dark',
tooltip: 'Dark theme with black background',
onclick: () => selected('dark')
})
]);
};
$mount(TooltipDemo, tooltipTarget);
}
// 3. Disabled State
const disabledTarget = document.querySelector('#demo-disabled');
if (disabledTarget && !disabledTarget.hasChildNodes()) {
const DisabledDemo = () => {
const selected = $('enabled');
return Div({ class: 'flex flex-col gap-3' }, [
Radio({
label: 'Enabled option',
name: 'disabled-group',
value: selected,
radioValue: 'enabled',
onclick: () => selected('enabled')
}),
Radio({
label: 'Disabled option (cannot select)',
name: 'disabled-group',
value: selected,
radioValue: 'disabled',
disabled: true,
onclick: () => selected('disabled')
})
]);
};
$mount(DisabledDemo, disabledTarget);
}
// 4. Reactive Preview
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const size = $('medium');
const color = $('blue');
const sizes = [
{ value: 'small', label: 'Small' },
{ value: 'medium', label: 'Medium' },
{ value: 'large', label: 'Large' }
];
const colors = [
{ value: 'blue', label: 'Blue' },
{ value: 'green', label: 'Green' },
{ value: 'red', label: 'Red' }
];
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'text-sm font-bold' }, 'Select size'),
Div({ class: 'flex gap-4' }, sizes.map(s =>
Radio({
label: s.label,
name: 'size-group',
value: size,
radioValue: s.value,
onclick: () => size(s.value)
})
)),
Div({ class: 'text-sm font-bold mt-2' }, 'Select color'),
Div({ class: 'flex gap-4' }, colors.map(c =>
Radio({
label: c.label,
name: 'color-group',
value: color,
radioValue: c.value,
onclick: () => color(c.value)
})
)),
Div({
class: 'mt-4 p-4 rounded-lg text-center transition-all',
style: () => {
const sizeMap = { small: 'text-sm', medium: 'text-base', large: 'text-lg' };
const colorMap = { blue: '#3b82f6', green: '#10b981', red: '#ef4444' };
return `background-color: ${colorMap[color()]}; color: white; ${sizeMap[size()]}`;
}
}, () => `${size()} ${color()} preview`)
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 5. Payment Method Selection
const paymentTarget = document.querySelector('#demo-payment');
if (paymentTarget && !paymentTarget.hasChildNodes()) {
const PaymentDemo = () => {
const method = $('credit');
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'text-sm font-bold' }, 'Payment method'),
Div({ class: 'flex flex-col gap-3' }, [
Radio({
label: '💳 Credit Card',
name: 'payment-group',
value: method,
radioValue: 'credit',
onclick: () => method('credit')
}),
Radio({
label: '🏦 Bank Transfer',
name: 'payment-group',
value: method,
radioValue: 'bank',
onclick: () => method('bank')
}),
Radio({
label: '📱 Digital Wallet',
name: 'payment-group',
value: method,
radioValue: 'wallet',
onclick: () => method('wallet')
})
]),
Div({ class: 'alert alert-info mt-2' }, () => {
const messages = {
credit: 'You selected Credit Card. Enter your card details.',
bank: 'You selected Bank Transfer. Use the provided account number.',
wallet: 'You selected Digital Wallet. Scan the QR code to pay.'
};
return messages[method()];
})
]);
};
$mount(PaymentDemo, paymentTarget);
}
// 6. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
const primary = $('primary1');
const secondary = $('secondary1');
const accent = $('accent1');
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'card bg-base-200 p-4' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Primary variant'),
Div({ class: 'flex gap-4' }, [
Radio({
label: 'Option A',
name: 'primary-group',
value: primary,
radioValue: 'primary1',
class: 'radio-primary',
onclick: () => primary('primary1')
}),
Radio({
label: 'Option B',
name: 'primary-group',
value: primary,
radioValue: 'primary2',
class: 'radio-primary',
onclick: () => primary('primary2')
})
])
]),
Div({ class: 'card bg-base-200 p-4' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Secondary variant'),
Div({ class: 'flex gap-4' }, [
Radio({
label: 'Option A',
name: 'secondary-group',
value: secondary,
radioValue: 'secondary1',
class: 'radio-secondary',
onclick: () => secondary('secondary1')
}),
Radio({
label: 'Option B',
name: 'secondary-group',
value: secondary,
radioValue: 'secondary2',
class: 'radio-secondary',
onclick: () => secondary('secondary2')
})
])
]),
Div({ class: 'card bg-base-200 p-4' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Accent variant'),
Div({ class: 'flex gap-4' }, [
Radio({
label: 'Option A',
name: 'accent-group',
value: accent,
radioValue: 'accent1',
class: 'radio-accent',
onclick: () => accent('accent1')
}),
Radio({
label: 'Option B',
name: 'accent-group',
value: accent,
radioValue: 'accent2',
class: 'radio-accent',
onclick: () => accent('accent2')
})
])
])
]);
};
$mount(VariantsDemo, variantsTarget);
}
// 7. Dynamic Options
const dynamicTarget = document.querySelector('#demo-dynamic');
if (dynamicTarget && !dynamicTarget.hasChildNodes()) {
const DynamicDemo = () => {
const category = $('cars');
const selected = $('');
const options = {
cars: [
{ value: 'sedan', label: 'Sedan' },
{ value: 'suv', label: 'SUV' },
{ value: 'sports', label: 'Sports' }
],
colors: [
{ value: 'red', label: 'Red' },
{ value: 'blue', label: 'Blue' },
{ value: 'black', label: 'Black' }
]
};
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex gap-4' }, [
Radio({
label: 'Cars',
name: 'category-group',
value: category,
radioValue: 'cars',
onclick: () => {
category('cars');
selected('');
}
}),
Radio({
label: 'Colors',
name: 'category-group',
value: category,
radioValue: 'colors',
onclick: () => {
category('colors');
selected('');
}
})
]),
Div({ class: 'divider my-1' }),
Div({ class: 'text-sm font-bold' }, () => `Select ${category()}`),
Div({ class: 'flex flex-col gap-2' }, () =>
options[category()].map(opt =>
Radio({
label: opt.label,
name: 'dynamic-option-group',
value: selected,
radioValue: opt.value,
onclick: () => selected(opt.value)
})
)
),
() => selected()
? Div({ class: 'alert alert-success mt-2' }, () => `Selected ${category()}: ${selected()}`)
: null
]);
};
$mount(DynamicDemo, dynamicTarget);
}
};
initRadioExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initRadioExamples);
});
}
})();
</script>
```

258
docs/components/range.md Normal file
View File

@@ -0,0 +1,258 @@
# Range
Range slider component for selecting numeric values with labels, tooltips, and DaisyUI styling.
## Tag
`Range`
## Props
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `value` | `number \| Signal<number>` | `0` | Current slider value |
| `min` | `number` | `0` | Minimum value |
| `max` | `number` | `100` | Maximum value |
| `step` | `number` | `1` | Step increment |
| `label` | `string \| VNode \| Signal` | `-` | Label text for the slider |
| `tooltip` | `string` | `-` | Tooltip text on hover |
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `oninput` | `function` | `-` | Input event handler |
## Styling
Range supports all **daisyUI Range classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Color | `range-primary`, `range-secondary`, `range-accent`, `range-info`, `range-success`, `range-warning`, `range-error` | Range color variants |
| Size | `range-xs`, `range-sm`, `range-md`, `range-lg` | Range scale |
> For further details, check the [daisyUI Range Documentation](https://daisyui.com/components/range) Full reference for CSS classes.
## Live Examples
### Basic Range
<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 flex-col gap-4"></div>
</div>
</div>
```javascript
const BasicDemo = () => {
const value = $(50);
return Div({ class: 'flex flex-col gap-4' }, [
Range({
label: 'Volume',
value: value,
min: 0,
max: 100,
oninput: (e) => value(parseInt(e.target.value))
}),
Div({ class: 'text-center' }, () => `Value: ${value()}%`)
]);
};
$mount(BasicDemo, '#demo-basic');
```
### With Tooltip
<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-tooltip" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
</div>
</div>
```javascript
const TooltipDemo = () => {
const brightness = $(75);
return Div({ class: 'flex flex-col gap-4' }, [
Range({
label: 'Brightness',
value: brightness,
min: 0,
max: 100,
tooltip: () => `${brightness()}% brightness`,
oninput: (e) => brightness(parseInt(e.target.value))
}),
Div({ class: 'w-full h-20 rounded-lg transition-all', style: () => `background-color: hsl(0, 0%, ${brightness()}%)` })
]);
};
$mount(TooltipDemo, '#demo-tooltip');
```
### 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 flex flex-col gap-4"></div>
</div>
</div>
```javascript
const ColorsDemo = () => {
const primary = $(30);
const secondary = $(60);
const accent = $(90);
return Div({ class: 'flex flex-col gap-4' }, [
Range({ label: 'Primary', value: primary, class: 'range-primary', oninput: (e) => primary(e.target.value) }),
Range({ label: 'Secondary', value: secondary, class: 'range-secondary', oninput: (e) => secondary(e.target.value) }),
Range({ label: 'Accent', value: accent, class: 'range-accent', oninput: (e) => accent(e.target.value) })
]);
};
$mount(ColorsDemo, '#demo-colors');
```
### Size 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-sizes" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
</div>
</div>
```javascript
const SizesDemo = () => {
const xs = $(25);
const sm = $(50);
const md = $(75);
const lg = $(100);
return Div({ class: 'flex flex-col gap-4' }, [
Range({ label: 'Extra Small', value: xs, class: 'range-xs', oninput: (e) => xs(e.target.value) }),
Range({ label: 'Small', value: sm, class: 'range-sm', oninput: (e) => sm(e.target.value) }),
Range({ label: 'Medium', value: md, class: 'range-md', oninput: (e) => md(e.target.value) }),
Range({ label: 'Large', value: lg, class: 'range-lg', oninput: (e) => lg(e.target.value) })
]);
};
$mount(SizesDemo, '#demo-sizes');
```
### Price Range
<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-price" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
</div>
</div>
```javascript
const PriceDemo = () => {
const price = $(500);
const maxPrice = 1000;
return Div({ class: 'flex flex-col gap-4' }, [
Range({
label: 'Price Range',
value: price,
min: 0,
max: maxPrice,
step: 10,
oninput: (e) => price(parseInt(e.target.value))
}),
Div({ class: 'flex justify-between' }, [
Span({}, '$0'),
Span({}, () => `$${price()}`),
Span({}, `$${maxPrice}`)
]),
Div({ class: 'mt-2 text-sm opacity-70' }, () => {
if (price() < 250) return 'Budget friendly';
if (price() < 750) return 'Mid range';
return 'Premium';
})
]);
};
$mount(PriceDemo, '#demo-price');
```
### Audio Controls
<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-audio" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
</div>
</div>
```javascript
const AudioDemo = () => {
const volume = $(50);
const bass = $(40);
const treble = $(60);
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex items-center gap-3' }, [
Span({ class: 'w-12' }, '🔊'),
Range({
value: volume,
min: 0,
max: 100,
class: 'range-primary flex-1',
oninput: (e) => volume(e.target.value)
}),
Span({ class: 'w-12 text-right' }, () => `${volume()}%`)
]),
Div({ class: 'flex items-center gap-3' }, [
Span({ class: 'w-12' }, '🎵 Bass'),
Range({
value: bass,
min: 0,
max: 100,
class: 'range-secondary flex-1',
oninput: (e) => bass(e.target.value)
}),
Span({ class: 'w-12 text-right' }, () => `${bass()}%`)
]),
Div({ class: 'flex items-center gap-3' }, [
Span({ class: 'w-12' }, '🎶 Treble'),
Range({
value: treble,
min: 0,
max: 100,
class: 'range-accent flex-1',
oninput: (e) => treble(e.target.value)
}),
Span({ class: 'w-12 text-right' }, () => `${treble()}%`)
])
]);
};
$mount(AudioDemo, '#demo-audio');
```
### 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-col gap-4"></div>
</div>
</div>
```javascript
const VariantsDemo = () => {
const value = $(50);
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'text-sm font-bold' }, 'With Label'),
Range({ label: 'Label', value: $(50), oninput: (e) => {} }),
Div({ class: 'text-sm font-bold mt-2' }, 'With Tooltip'),
Range({ tooltip: 'Tooltip message', value: $(50), oninput: (e) => {} }),
Div({ class: 'text-sm font-bold mt-2' }, 'Disabled'),
Range({ disabled: true, value: $(50), oninput: (e) => {} })
]);
};
$mount(VariantsDemo, '#demo-variants');
```

View File

@@ -8,13 +8,27 @@ Star rating component with customizable count, icons, and read-only mode.
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :--------------- | :----------------------------------------------- |
| `value` | `number \| Signal<number>` | `0` | Current rating value |
| `count` | `number \| Signal<number>` | `5` | Number of stars/items |
| `mask` | `string` | `'mask-star'` | Mask shape (mask-star, mask-star-2, mask-heart) |
| `readonly` | `boolean \| Signal<boolean>` | `false` | Disable interaction |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `value` | `number \| Signal<number>` | `0` | Current rating value |
| `count` | `number \| Signal<number>` | `5` | Number of stars/items |
| `mask` | `string` | `'mask-star'` | Mask shape (`mask-star`, `mask-star-2`, `mask-heart`) |
| `readonly` | `boolean \| Signal<boolean>` | `false` | Disable interaction |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `onchange` | `function` | `-` | Callback when rating changes |
## Styling
Rating supports all **daisyUI Rating classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `rating` | Base rating container |
| Color | `rating-primary`, `rating-secondary`, `rating-accent`, `rating-info`, `rating-success`, `rating-warning`, `rating-error` | Rating color variants |
| Size | `rating-xs`, `rating-sm`, `rating-md`, `rating-lg` | Rating scale |
| Mask | `mask-star`, `mask-star-2`, `mask-heart` | Rating icon shapes |
> For further details, check the [daisyUI Rating Documentation](https://daisyui.com/components/rating) Full reference for CSS classes.
## Live Examples
@@ -120,40 +134,6 @@ const ReadonlyDemo = () => {
$mount(ReadonlyDemo, '#demo-readonly');
```
### Custom Count
<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 flex-col gap-4"></div>
</div>
</div>
```javascript
const CustomDemo = () => {
const rating = $(3);
const count = $(10);
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Div({ class: 'flex items-center gap-4' }, [
Span({ class: 'text-sm' }, 'Number of stars:'),
Input({
type: 'number',
value: count,
class: 'input input-sm w-24'
})
]),
Rating({
value: rating,
count: count,
onchange: (value) => rating(value)
}),
Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / ${count()}`)
]);
};
$mount(CustomDemo, '#demo-custom');
```
### Product Review
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
@@ -179,7 +159,6 @@ const ReviewDemo = () => {
Rating({
value: quality,
count: 5,
size: 'sm',
onchange: (v) => quality(v)
})
]),
@@ -188,7 +167,6 @@ const ReviewDemo = () => {
Rating({
value: price,
count: 5,
size: 'sm',
onchange: (v) => price(v)
})
]),
@@ -197,7 +175,6 @@ const ReviewDemo = () => {
Rating({
value: support,
count: 5,
size: 'sm',
onchange: (v) => support(v)
})
])
@@ -229,7 +206,7 @@ const VariantsDemo = () => {
Rating({ value: $(3), count: 5, mask: 'mask-star' })
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm mb-2' }, 'Mask Star 2 (yellow)' ),
Div({ class: 'text-sm mb-2' }, 'Mask Star 2 (yellow)'),
Rating({ value: $(4), count: 5, mask: 'mask-star-2', class: 'rating-warning' })
]),
Div({ class: 'text-center' }, [
@@ -257,7 +234,6 @@ $mount(VariantsDemo, '#demo-variants');
```javascript
const FeedbackDemo = () => {
const rating = $(0);
const feedback = $(false);
const messages = {
1: 'Very disappointed 😞',
@@ -275,7 +251,6 @@ const FeedbackDemo = () => {
count: 5,
onchange: (value) => {
rating(value);
feedback(true);
if (value >= 4) {
Toast('Thank you for your positive feedback!', 'alert-success', 2000);
} else if (value <= 2) {
@@ -294,242 +269,4 @@ const FeedbackDemo = () => {
]);
};
$mount(FeedbackDemo, '#demo-feedback');
```
<script>
(function() {
const initRatingExamples = () => {
// 1. Basic Rating
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const rating = $(3);
return Div({ class: 'flex flex-col gap-2 items-center' }, [
Rating({
value: rating,
count: 5,
onchange: (value) => rating(value)
}),
Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / 5`)
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. Heart Rating
const heartTarget = document.querySelector('#demo-heart');
if (heartTarget && !heartTarget.hasChildNodes()) {
const HeartDemo = () => {
const rating = $(4);
return Div({ class: 'flex flex-col gap-2 items-center' }, [
Rating({
value: rating,
count: 5,
mask: 'mask-heart',
onchange: (value) => rating(value)
}),
Div({ class: 'text-sm opacity-70' }, () => `${rating()} hearts`)
]);
};
$mount(HeartDemo, heartTarget);
}
// 3. Star with Outline
const star2Target = document.querySelector('#demo-star2');
if (star2Target && !star2Target.hasChildNodes()) {
const Star2Demo = () => {
const rating = $(2);
return Div({ class: 'flex flex-col gap-2 items-center' }, [
Rating({
value: rating,
count: 5,
mask: 'mask-star-2',
onchange: (value) => rating(value)
}),
Div({ class: 'text-sm opacity-70' }, () => `${rating()} stars`)
]);
};
$mount(Star2Demo, star2Target);
}
// 4. Read-only Rating
const readonlyTarget = document.querySelector('#demo-readonly');
if (readonlyTarget && !readonlyTarget.hasChildNodes()) {
const ReadonlyDemo = () => {
const rating = $(4.5);
return Div({ class: 'flex flex-col gap-2 items-center' }, [
Rating({
value: rating,
count: 5,
readonly: true
}),
Div({ class: 'text-sm opacity-70' }, 'Average rating: 4.5/5 (read-only)')
]);
};
$mount(ReadonlyDemo, readonlyTarget);
}
// 5. Custom Count
const customTarget = document.querySelector('#demo-custom');
if (customTarget && !customTarget.hasChildNodes()) {
const CustomDemo = () => {
const rating = $(3);
const count = $(10);
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Div({ class: 'flex items-center gap-4' }, [
Span({ class: 'text-sm' }, 'Number of stars:'),
Input({
type: 'number',
value: count,
class: 'input input-sm w-24',
oninput: (e) => count(parseInt(e.target.value) || 1)
})
]),
Rating({
value: rating,
count: count,
onchange: (value) => rating(value)
}),
Div({ class: 'text-sm opacity-70' }, () => `Rating: ${rating()} / ${count()}`)
]);
};
$mount(CustomDemo, customTarget);
}
// 6. Product Review
const reviewTarget = document.querySelector('#demo-review');
if (reviewTarget && !reviewTarget.hasChildNodes()) {
const ReviewDemo = () => {
const quality = $(4);
const price = $(3);
const support = $(5);
const average = () => Math.round(((quality() + price() + support()) / 3) * 10) / 10;
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'text-lg font-bold' }, 'Product Review'),
Div({ class: 'flex flex-col gap-2' }, [
Div({ class: 'flex justify-between items-center' }, [
Span({ class: 'text-sm w-24' }, 'Quality:'),
Rating({
value: quality,
count: 5,
size: 'sm',
onchange: (v) => quality(v)
})
]),
Div({ class: 'flex justify-between items-center' }, [
Span({ class: 'text-sm w-24' }, 'Price:'),
Rating({
value: price,
count: 5,
size: 'sm',
onchange: (v) => price(v)
})
]),
Div({ class: 'flex justify-between items-center' }, [
Span({ class: 'text-sm w-24' }, 'Support:'),
Rating({
value: support,
count: 5,
size: 'sm',
onchange: (v) => support(v)
})
])
]),
Div({ class: 'divider my-1' }),
Div({ class: 'flex justify-between items-center' }, [
Span({ class: 'font-bold' }, 'Overall:'),
Div({ class: 'text-2xl font-bold text-primary' }, () => average())
])
]);
};
$mount(ReviewDemo, reviewTarget);
}
// 7. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
return Div({ class: 'flex flex-col gap-6' }, [
Div({ class: 'text-center' }, [
Div({ class: 'text-sm mb-2' }, 'Mask Star'),
Rating({ value: $(3), count: 5, mask: 'mask-star' })
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm mb-2' }, 'Mask Star 2 (yellow)' ),
Rating({ value: $(4), count: 5, mask: 'mask-star-2', class: 'rating-warning' })
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm mb-2' }, 'Mask Heart'),
Rating({ value: $(5), count: 5, mask: 'mask-heart', class: 'rating-error' })
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm mb-2' }, 'Half Stars (read-only)'),
Rating({ value: $(3.5), count: 5, readonly: true })
])
]);
};
$mount(VariantsDemo, variantsTarget);
}
// 8. Interactive Feedback
const feedbackTarget = document.querySelector('#demo-feedback');
if (feedbackTarget && !feedbackTarget.hasChildNodes()) {
const FeedbackDemo = () => {
const rating = $(0);
const feedback = $(false);
const messages = {
1: 'Very disappointed 😞',
2: 'Could be better 😕',
3: 'Good 👍',
4: 'Very good 😊',
5: 'Excellent! 🎉'
};
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Div({ class: 'text-center' }, [
Div({ class: 'text-sm mb-2' }, 'How was your experience?'),
Rating({
value: rating,
count: 5,
onchange: (value) => {
rating(value);
feedback(true);
if (value >= 4) {
Toast('Thank you for your positive feedback!', 'alert-success', 2000);
} else if (value <= 2) {
Toast('We appreciate your feedback and will improve!', 'alert-warning', 2000);
} else {
Toast('Thanks for your rating!', 'alert-info', 2000);
}
}
})
]),
() => rating() > 0
? Div({ class: 'alert alert-soft text-center' }, [
messages[rating()] || `Rating: ${rating()} stars`
])
: null
]);
};
$mount(FeedbackDemo, feedbackTarget);
}
};
initRatingExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initRatingExamples);
});
}
})();
</script>
```

View File

@@ -8,14 +8,26 @@ Dropdown select component with full DaisyUI styling, reactive items, and form in
## Props
| Prop | Type | Default | Description |
| :----------- | :-------------------------------------- | :------------------ | :----------------------------------------------- |
| `label` | `string` | `-` | Label text above select |
| `items` | `Array<{value: string, label: string}>` | `[]` | Array of items with value and label |
| `value` | `string \| Signal<string>` | `''` | Selected value |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
| `onchange` | `function` | `-` | Change event handler |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `label` | `string` | `-` | Label text above select |
| `items` | `Array<{value: string, label: string}> \| Signal<Array>` | `[]` | Array of items with value and label |
| `value` | `string \| Signal<string>` | `''` | Selected value |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `disabled` | `boolean \| Signal<boolean>` | `false` | Disabled state |
| `onchange` | `function` | `-` | Change event handler |
## Styling
Select supports all **daisyUI Select classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Color | `select-primary`, `select-secondary`, `select-accent`, `select-info`, `select-success`, `select-warning`, `select-error` | Select color variants |
| Size | `select-xs`, `select-sm`, `select-md`, `select-lg` | Select scale |
| Style | `select-bordered` (default), `select-ghost` | Visual style variants |
> For further details, check the [daisyUI Select Documentation](https://daisyui.com/components/select) Full reference for CSS classes.
## Live Examples
@@ -70,9 +82,7 @@ const ReactiveDemo = () => {
value: selected,
onchange: (e) => selected(e.target.value)
}),
Div({ class: 'alert alert-info' }, [
() => `You selected: ${selected()}`
])
Div({ class: 'alert alert-info' }, () => `You selected: ${selected()}`)
]);
};
$mount(ReactiveDemo, '#demo-reactive');
@@ -103,18 +113,19 @@ const DisabledDemo = () => {
$mount(DisabledDemo, '#demo-disabled');
```
### Dynamic items
### Dynamic 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-dynamic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex items-center justify-center"></div>
<div id="demo-dynamic" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-4"></div>
</div>
</div>
```javascript
const DynamicDemo = () => {
const category = $('fruits');
const selectedItem = $('');
const items = {
fruits: [
@@ -135,14 +146,18 @@ const DynamicDemo = () => {
{ value: 'vegetables', label: 'Vegetables' }
],
value: category,
onchange: (e) => category(e.target.value)
onchange: (e) => {
category(e.target.value);
selectedItem('');
}
}),
Select({
label: 'Item',
items: () => items[category()] || [],
value: $(''),
onchange: (e) => console.log('Selected:', e.target.value)
})
value: selectedItem,
onchange: (e) => selectedItem(e.target.value)
}),
() => selectedItem() ? Div({ class: 'alert alert-success' }, `Selected: ${selectedItem()}`) : null
]);
};
$mount(DynamicDemo, '#demo-dynamic');
@@ -161,7 +176,7 @@ $mount(DynamicDemo, '#demo-dynamic');
const VariantsDemo = () => {
const primary = $('option1');
const secondary = $('option2');
const accent = $('');
const ghost = $('');
return Div({ class: 'flex flex-col gap-4' }, [
Select({
@@ -193,173 +208,10 @@ const VariantsDemo = () => {
{ value: 'opt1', label: 'Option 1' },
{ value: 'opt2', label: 'Option 2' }
],
value: accent,
onchange: (e) => accent(e.target.value)
value: ghost,
onchange: (e) => ghost(e.target.value)
})
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initSelectExamples = () => {
// 1. Basic Select
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const selected = $('apple');
return Select({
label: 'Choose a fruit',
items: [
{ value: 'apple', label: '🍎 Apple' },
{ value: 'banana', label: '🍌 Banana' },
{ value: 'orange', label: '🍊 Orange' },
{ value: 'grape', label: '🍇 Grape' }
],
value: selected,
onchange: (e) => selected(e.target.value)
});
};
$mount(BasicDemo, basicTarget);
}
// 2. Reactive Display
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const selected = $('small');
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Select({
label: 'Select size',
items: [
{ value: 'small', label: 'Small' },
{ value: 'medium', label: 'Medium' },
{ value: 'large', label: 'Large' }
],
value: selected,
onchange: (e) => selected(e.target.value)
}),
Div({ class: 'alert alert-info' }, [
`You selected: ${selected()}`
])
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 3. Disabled State
const disabledTarget = document.querySelector('#demo-disabled');
if (disabledTarget && !disabledTarget.hasChildNodes()) {
const DisabledDemo = () => {
return Select({
label: 'Country (disabled)',
items: [
{ value: 'mx', label: 'Mexico' },
{ value: 'us', label: 'United States' },
{ value: 'ca', label: 'Canada' }
],
value: 'mx',
disabled: true
});
};
$mount(DisabledDemo, disabledTarget);
}
// 4. Dynamic items
const dynamicTarget = document.querySelector('#demo-dynamic');
if (dynamicTarget && !dynamicTarget.hasChildNodes()) {
const DynamicDemo = () => {
const category = $('fruits');
const items = {
fruits: [
{ value: 'apple', label: '🍎 Apple' },
{ value: 'banana', label: '🍌 Banana' }
],
vegetables: [
{ value: 'carrot', label: '🥕 Carrot' },
{ value: 'broccoli', label: '🥦 Broccoli' }
]
};
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Select({
label: 'Category',
items: [
{ value: 'fruits', label: 'Fruits' },
{ value: 'vegetables', label: 'Vegetables' }
],
value: category,
onchange: (e) => category(e.target.value)
}),
Select({
label: 'Item',
items: () => items[category()] || [],
value: $(''),
onchange: (e) => console.log('Selected:', e.target.value)
})
]);
};
$mount(DynamicDemo, dynamicTarget);
}
// 5. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
const primary = $('option1');
const secondary = $('option2');
const accent = $('');
return Div({ class: 'flex flex-col gap-4' }, [
Select({
label: 'Primary Select',
class: 'select-primary',
items: [
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2' },
{ value: 'option3', label: 'Option 3' }
],
value: primary,
onchange: (e) => primary(e.target.value)
}),
Select({
label: 'Secondary Select',
class: 'select-secondary',
items: [
{ value: 'option1', label: 'Option 1' },
{ value: 'option2', label: 'Option 2' }
],
value: secondary,
onchange: (e) => secondary(e.target.value)
}),
Select({
label: 'Ghost Select',
class: 'select-ghost',
items: [
{ value: '', label: 'Select an option' },
{ value: 'opt1', label: 'Option 1' },
{ value: 'opt2', label: 'Option 2' }
],
value: accent,
onchange: (e) => accent(e.target.value)
})
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initSelectExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initSelectExamples);
});
}
})();
</script>
```

View File

@@ -8,10 +8,22 @@ Stack component for layering multiple elements on top of each other, creating de
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `Array<VNode> \| VNode` | `-` | Elements to stack (first is bottom, last is top) |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `Array<VNode> \| VNode` | `-` | Elements to stack (first is bottom, last is top) |
## Styling
Stack supports all **daisyUI Stack classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `stack` | Base stack container |
| Direction | `stack-top`, `stack-bottom`, `stack-start`, `stack-end` | Stack alignment |
| Width | `w-*` (Tailwind) | Custom width for the stack |
> For further details, check the [daisyUI Stack Documentation](https://daisyui.com/components/stack) Full reference for CSS classes.
## Live Examples
@@ -77,19 +89,13 @@ $mount(CardsDemo, '#demo-cards');
const AvatarsDemo = () => {
return Stack({ class: 'w-32' }, [
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-neutral text-neutral-content rounded-full w-16' }, [
Span({}, 'JD')
])
Div({ class: 'bg-neutral text-neutral-content rounded-full w-16 h-16 flex items-center justify-center' }, 'JD')
]),
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-primary text-primary-content rounded-full w-16' }, [
Span({}, 'JS')
])
Div({ class: 'bg-primary text-primary-content rounded-full w-16 h-16 flex items-center justify-center' }, 'JS')
]),
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-secondary text-secondary-content rounded-full w-16' }, [
Span({}, 'BC')
])
Div({ class: 'bg-secondary text-secondary-content rounded-full w-16 h-16 flex items-center justify-center' }, 'BC')
])
]);
};
@@ -144,7 +150,7 @@ const GalleryDemo = () => {
...photos.map((photo, idx) =>
Div({
class: `${photo.color} rounded-lg shadow-lg transition-all`,
style: `transform: translate(${idx * 4}px, ${idx * 4}px); width: 100%; height: 100%;`
style: `transform: translate(${idx * 4}px, ${idx * 4}px);`
}, [
Div({ class: 'p-4 text-white font-bold' }, photo.label)
])
@@ -172,8 +178,8 @@ const InteractiveDemo = () => {
return Div({ class: 'flex flex-col gap-6 items-center' }, [
Stack({ class: 'w-56' }, colors.map((color, idx) =>
Div({
class: `bg-${color} text-${color}-content rounded-lg p-4 shadow-lg transition-all cursor-pointer ${idx === active() ? 'scale-105 z-10' : ''}`,
style: `transform: translate(${idx * 8}px, ${idx * 8}px);`,
class: () => `bg-${color} text-${color}-content rounded-lg p-4 shadow-lg transition-all cursor-pointer ${idx === active() ? 'scale-105 z-10' : ''}`,
style: () => `transform: translate(${idx * 8}px, ${idx * 8}px);`,
onclick: () => active(idx)
}, [
Div({ class: 'font-bold' }, labels[idx]),
@@ -184,7 +190,7 @@ const InteractiveDemo = () => {
Span({ class: 'font-bold' }, () => `Active: ${labels[active()]}`),
Div({ class: 'flex gap-2 mt-2' }, colors.map((_, idx) =>
Button({
class: `btn btn-xs ${idx === active() ? 'btn-primary' : 'btn-ghost'}`,
class: () => `btn btn-xs ${idx === active() ? 'btn-primary' : 'btn-ghost'}`,
onclick: () => active(idx)
}, `${idx + 1}`)
))
@@ -223,10 +229,10 @@ const NotificationsDemo = () => {
};
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Stack({ class: 'w-80' }, notifications().map((notif, idx) =>
Stack({ class: 'w-80' }, () => notifications().map((notif, idx) =>
Div({
class: `${typeClasses[notif.type]} rounded-lg p-3 shadow-lg transition-all cursor-pointer`,
style: `transform: translate(${idx * 4}px, ${idx * 4}px);`,
class: () => `${typeClasses[notif.type]} rounded-lg p-3 shadow-lg transition-all cursor-pointer`,
style: () => `transform: translate(${idx * 4}px, ${idx * 4}px);`,
onclick: () => removeNotification(notif.id)
}, [
Div({ class: 'flex justify-between items-center' }, [
@@ -235,7 +241,7 @@ const NotificationsDemo = () => {
])
])
)),
notifications().length === 0
() => notifications().length === 0
? Div({ class: 'alert alert-soft' }, 'No notifications')
: Button({
class: 'btn btn-sm btn-ghost mt-2',
@@ -285,235 +291,4 @@ const VariantsDemo = () => {
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initStackExamples = () => {
// 1. Basic Stack
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
return Stack({ class: 'w-40' }, [
Div({ class: 'bg-primary text-primary-content rounded-lg p-4 shadow-lg' }, 'Layer 1'),
Div({ class: 'bg-secondary text-secondary-content rounded-lg p-4 shadow-lg' }, 'Layer 2'),
Div({ class: 'bg-accent text-accent-content rounded-lg p-4 shadow-lg' }, 'Layer 3')
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. Card Stack
const cardsTarget = document.querySelector('#demo-cards');
if (cardsTarget && !cardsTarget.hasChildNodes()) {
const CardsDemo = () => {
return Stack({ class: 'w-64' }, [
Div({ class: 'card bg-base-100 shadow-xl border border-base-300' }, [
Div({ class: 'card-body p-4' }, [
Span({ class: 'text-sm opacity-70' }, 'Back Card'),
Span({ class: 'font-bold' }, 'Additional info')
])
]),
Div({ class: 'card bg-primary text-primary-content shadow-xl' }, [
Div({ class: 'card-body p-4' }, [
Span({ class: 'text-sm' }, 'Front Card'),
Span({ class: 'font-bold text-lg' }, 'Main Content')
])
])
]);
};
$mount(CardsDemo, cardsTarget);
}
// 3. Avatar Stack
const avatarsTarget = document.querySelector('#demo-avatars');
if (avatarsTarget && !avatarsTarget.hasChildNodes()) {
const AvatarsDemo = () => {
return Stack({ class: 'w-32' }, [
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-neutral text-neutral-content rounded-full w-16' }, [
Span({}, 'JD')
])
]),
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-primary text-primary-content rounded-full w-16' }, [
Span({}, 'JS')
])
]),
Div({ class: 'avatar placeholder' }, [
Div({ class: 'bg-secondary text-secondary-content rounded-full w-16' }, [
Span({}, 'BC')
])
])
]);
};
$mount(AvatarsDemo, avatarsTarget);
}
// 4. Image Stack
const imagesTarget = document.querySelector('#demo-images');
if (imagesTarget && !imagesTarget.hasChildNodes()) {
const ImagesDemo = () => {
return Stack({ class: 'w-48' }, [
Div({ class: 'w-full h-32 bg-gradient-to-r from-primary to-secondary rounded-lg shadow-lg' }, [
Div({ class: 'p-2 text-white text-sm' }, 'Background Image')
]),
Div({ class: 'w-full h-32 bg-gradient-to-r from-secondary to-accent rounded-lg shadow-lg translate-x-2 translate-y-2' }, [
Div({ class: 'p-2 text-white text-sm' }, 'Middle Layer')
]),
Div({ class: 'w-full h-32 bg-gradient-to-r from-accent to-primary rounded-lg shadow-lg translate-x-4 translate-y-4 flex items-center justify-center' }, [
Span({ class: 'text-white font-bold' }, 'Top Layer')
])
]);
};
$mount(ImagesDemo, imagesTarget);
}
// 5. Photo Gallery Stack
const galleryTarget = document.querySelector('#demo-gallery');
if (galleryTarget && !galleryTarget.hasChildNodes()) {
const GalleryDemo = () => {
const photos = [
{ color: 'bg-primary', label: 'Photo 1' },
{ color: 'bg-secondary', label: 'Photo 2' },
{ color: 'bg-accent', label: 'Photo 3' },
{ color: 'bg-info', label: 'Photo 4' }
];
return Stack({ class: 'w-48 cursor-pointer hover:scale-105 transition-transform' }, [
...photos.map((photo, idx) =>
Div({
class: `${photo.color} rounded-lg shadow-lg transition-all`,
style: `transform: translate(${idx * 4}px, ${idx * 4}px); width: 100%; height: 100%;`
}, [
Div({ class: 'p-4 text-white font-bold' }, photo.label)
])
)
]);
};
$mount(GalleryDemo, galleryTarget);
}
// 6. Interactive Stack
const interactiveTarget = document.querySelector('#demo-interactive');
if (interactiveTarget && !interactiveTarget.hasChildNodes()) {
const InteractiveDemo = () => {
const active = $(0);
const colors = ['primary', 'secondary', 'accent', 'info', 'success'];
const labels = ['Home', 'Profile', 'Settings', 'Messages', 'Notifications'];
return Div({ class: 'flex flex-col gap-6 items-center' }, [
Stack({ class: 'w-56' }, colors.map((color, idx) =>
Div({
class: `bg-${color} text-${color}-content rounded-lg p-4 shadow-lg transition-all cursor-pointer ${idx === active() ? 'scale-105 z-10' : ''}`,
style: `transform: translate(${idx * 8}px, ${idx * 8}px);`,
onclick: () => active(idx)
}, [
Div({ class: 'font-bold' }, labels[idx]),
Div({ class: 'text-sm opacity-80' }, `Layer ${idx + 1}`)
])
)),
Div({ class: 'mt-4 text-center' }, [
Span({ class: 'font-bold' }, () => `Active: ${labels[active()]}`),
Div({ class: 'flex gap-2 mt-2' }, colors.map((_, idx) =>
Button({
class: `btn btn-xs ${idx === active() ? 'btn-primary' : 'btn-ghost'}`,
onclick: () => active(idx)
}, `${idx + 1}`)
))
])
]);
};
$mount(InteractiveDemo, interactiveTarget);
}
// 7. Notification Stack
const notificationsTarget = document.querySelector('#demo-notifications');
if (notificationsTarget && !notificationsTarget.hasChildNodes()) {
const NotificationsDemo = () => {
const notifications = $([
{ id: 1, message: 'New message from John', type: 'info' },
{ id: 2, message: 'Your order has shipped', type: 'success' },
{ id: 3, message: 'Meeting in 10 minutes', type: 'warning' }
]);
const removeNotification = (id) => {
notifications(notifications().filter(n => n.id !== id));
};
const typeClasses = {
info: 'bg-info text-info-content',
success: 'bg-success text-success-content',
warning: 'bg-warning text-warning-content',
error: 'bg-error text-error-content'
};
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Stack({ class: 'w-80' }, notifications().map((notif, idx) =>
Div({
class: `${typeClasses[notif.type]} rounded-lg p-3 shadow-lg transition-all cursor-pointer`,
style: `transform: translate(${idx * 4}px, ${idx * 4}px);`,
onclick: () => removeNotification(notif.id)
}, [
Div({ class: 'flex justify-between items-center' }, [
Span({ class: 'text-sm' }, notif.message),
Span({ class: 'text-xs opacity-70 cursor-pointer hover:opacity-100' }, '✕')
])
])
)),
notifications().length === 0
? Div({ class: 'alert alert-soft' }, 'No notifications')
: Button({
class: 'btn btn-sm btn-ghost mt-2',
onclick: () => notifications([])
}, 'Clear All')
]);
};
$mount(NotificationsDemo, notificationsTarget);
}
// 8. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Div({ class: 'text-center' }, [
Div({ class: 'text-sm mb-2' }, 'Small Stack'),
Stack({ class: 'w-24' }, [
Div({ class: 'bg-primary rounded p-2 text-xs' }, '1'),
Div({ class: 'bg-secondary rounded p-2 text-xs' }, '2'),
Div({ class: 'bg-accent rounded p-2 text-xs' }, '3')
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm mb-2' }, 'Medium Stack'),
Stack({ class: 'w-32' }, [
Div({ class: 'bg-primary rounded p-3' }, 'A'),
Div({ class: 'bg-secondary rounded p-3' }, 'B'),
Div({ class: 'bg-accent rounded p-3' }, 'C')
])
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-sm mb-2' }, 'Large Stack'),
Stack({ class: 'w-40' }, [
Div({ class: 'bg-primary rounded p-4' }, 'X'),
Div({ class: 'bg-secondary rounded p-4' }, 'Y'),
Div({ class: 'bg-accent rounded p-4' }, 'Z')
])
])
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initStackExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initStackExamples);
});
}
})();
</script>
```

View File

@@ -8,13 +8,25 @@ Statistic card component for displaying metrics, counts, and key performance ind
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
| `label` | `string \| VNode \| Signal` | `-` | Statistic label/title |
| `value` | `string \| number \| Signal` | `-` | Main statistic value |
| `desc` | `string \| VNode \| Signal` | `-` | Description or trend text |
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed in the figure area |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `label` | `string \| VNode \| Signal` | `-` | Statistic label/title |
| `value` | `string \| number \| Signal` | `-` | Main statistic value |
| `desc` | `string \| VNode \| Signal` | `-` | Description or trend text |
| `icon` | `string \| VNode \| Signal` | `-` | Icon displayed in the figure area |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
## Styling
Stat supports all **daisyUI Stat classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `stat` | Base stat container |
| Sections | `stat-figure`, `stat-title`, `stat-value`, `stat-desc` | Stat sub-components |
| Variants | `stat-compact` | Compact spacing variant |
> For further details, check the [daisyUI Stat Documentation](https://daisyui.com/components/stat) Full reference for CSS classes.
## Live Examples
@@ -66,19 +78,19 @@ const IconsDemo = () => {
label: 'Active Users',
value: '1,234',
desc: 'Currently online',
icon: Icons.iconShow
icon: Span({ class: 'text-2xl' }, '👥')
}),
Stat({
label: 'New Orders',
value: '89',
desc: 'Today',
icon: Icons.iconSuccess
icon: Span({ class: 'text-2xl' }, '📦')
}),
Stat({
label: 'Pending Tasks',
value: '23',
desc: 'Need attention',
icon: Icons.iconWarning
icon: Span({ class: 'text-2xl' }, '⏳')
})
]);
};
@@ -104,13 +116,13 @@ const ReactiveDemo = () => {
label: 'Counter',
value: () => count(),
desc: 'Click the button to increase',
icon: Icons.iconInfo
icon: Span({ class: 'text-2xl' }, '🔢')
}),
Stat({
label: 'Squared',
value: () => Math.pow(count(), 2),
desc: 'Square of counter',
icon: Icons.iconSuccess
icon: Span({ class: 'text-2xl' }, '📐')
})
]),
Div({ class: 'flex gap-2 justify-center' }, [
@@ -144,19 +156,19 @@ const TrendsDemo = () => {
label: 'Weekly Sales',
value: '$12,345',
desc: Div({ class: 'text-success' }, '↗︎ 15% increase'),
icon: Icons.iconSuccess
icon: Span({ class: 'text-2xl' }, '📈')
}),
Stat({
label: 'Bounce Rate',
value: '42%',
desc: Div({ class: 'text-error' }, '↘︎ 3% from last week'),
icon: Icons.iconError
icon: Span({ class: 'text-2xl' }, '📉')
}),
Stat({
label: 'Avg. Session',
value: '4m 32s',
desc: Div({ class: 'text-warning' }, '↗︎ 12 seconds'),
icon: Icons.iconWarning
icon: Span({ class: 'text-2xl' }, '⏱️')
})
]);
};
@@ -237,25 +249,25 @@ const DashboardDemo = () => {
label: 'Total Users',
value: () => stats().users.toLocaleString(),
desc: 'Registered users',
icon: Icons.iconShow
icon: Span({ class: 'text-2xl' }, '👥')
}),
Stat({
label: 'Revenue',
value: () => `$${stats().revenue.toLocaleString()}`,
desc: 'This month',
icon: Icons.iconSuccess
icon: Span({ class: 'text-2xl' }, '💰')
}),
Stat({
label: 'Orders',
value: () => stats().orders.toLocaleString(),
desc: 'Completed',
icon: Icons.iconInfo
icon: Span({ class: 'text-2xl' }, '📦')
}),
Stat({
label: 'Satisfaction',
value: () => `${stats().satisfaction}%`,
desc: stats().satisfaction > 90 ? 'Excellent!' : 'Good',
icon: Icons.iconWarning
icon: Span({ class: 'text-2xl' }, '😊')
})
]),
Div({ class: 'flex justify-center' }, [
@@ -285,28 +297,28 @@ const VariantsDemo = () => {
label: 'Primary Stat',
value: '1,234',
desc: 'With description',
icon: Icons.iconInfo,
icon: Span({ class: 'text-2xl' }, '⭐'),
class: 'bg-primary/10 text-primary'
}),
Stat({
label: 'Success Stat',
value: '89%',
desc: 'Success rate',
icon: Icons.iconSuccess,
icon: Span({ class: 'text-2xl' }, '✅'),
class: 'bg-success/10 text-success'
}),
Stat({
label: 'Warning Stat',
value: '23',
desc: 'Pending items',
icon: Icons.iconWarning,
icon: Span({ class: 'text-2xl' }, '⚠️'),
class: 'bg-warning/10 text-warning'
}),
Stat({
label: 'Error Stat',
value: '5',
desc: 'Failed attempts',
icon: Icons.iconError,
icon: Span({ class: 'text-2xl' }, '❌'),
class: 'bg-error/10 text-error'
})
]);
@@ -349,296 +361,4 @@ const CompactDemo = () => {
]);
};
$mount(CompactDemo, '#demo-compact');
```
<script>
(function() {
const initStatExamples = () => {
// 1. Basic Stat
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
return Div({ class: 'grid grid-cols-1 md:grid-cols-3 gap-4' }, [
Stat({
label: 'Total Users',
value: '2,345',
desc: '↗︎ 120 new users this month'
}),
Stat({
label: 'Revenue',
value: '$45,678',
desc: '↘︎ 5% decrease from last month'
}),
Stat({
label: 'Conversion Rate',
value: '3.45%',
desc: '↗︎ 0.5% increase'
})
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. With Icons
const iconsTarget = document.querySelector('#demo-icons');
if (iconsTarget && !iconsTarget.hasChildNodes()) {
const IconsDemo = () => {
return Div({ class: 'grid grid-cols-1 md:grid-cols-3 gap-4' }, [
Stat({
label: 'Active Users',
value: '1,234',
desc: 'Currently online',
icon: Icons.iconShow
}),
Stat({
label: 'New Orders',
value: '89',
desc: 'Today',
icon: Icons.iconSuccess
}),
Stat({
label: 'Pending Tasks',
value: '23',
desc: 'Need attention',
icon: Icons.iconWarning
})
]);
};
$mount(IconsDemo, iconsTarget);
}
// 3. Reactive Values
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const count = $(0);
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'grid grid-cols-1 md:grid-cols-2 gap-4' }, [
Stat({
label: 'Counter',
value: () => count(),
desc: 'Click the button to increase',
icon: Icons.iconInfo
}),
Stat({
label: 'Squared',
value: () => Math.pow(count(), 2),
desc: 'Square of counter',
icon: Icons.iconSuccess
})
]),
Div({ class: 'flex gap-2 justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: () => count(count() + 1)
}, 'Increment'),
Button({
class: 'btn btn-ghost',
onclick: () => count(0)
}, 'Reset')
])
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 4. With Trend Indicators
const trendsTarget = document.querySelector('#demo-trends');
if (trendsTarget && !trendsTarget.hasChildNodes()) {
const TrendsDemo = () => {
return Div({ class: 'grid grid-cols-1 md:grid-cols-3 gap-4' }, [
Stat({
label: 'Weekly Sales',
value: '$12,345',
desc: Div({ class: 'text-success' }, '↗︎ 15% increase'),
icon: Icons.iconSuccess
}),
Stat({
label: 'Bounce Rate',
value: '42%',
desc: Div({ class: 'text-error' }, '↘︎ 3% from last week'),
icon: Icons.iconError
}),
Stat({
label: 'Avg. Session',
value: '4m 32s',
desc: Div({ class: 'text-warning' }, '↗︎ 12 seconds'),
icon: Icons.iconWarning
})
]);
};
$mount(TrendsDemo, trendsTarget);
}
// 5. Multiple Stats in Row
const multipleTarget = document.querySelector('#demo-multiple');
if (multipleTarget && !multipleTarget.hasChildNodes()) {
const MultipleDemo = () => {
return Div({ class: 'grid grid-cols-1 md:grid-cols-4 gap-4' }, [
Stat({
label: 'Posts',
value: '1,234',
desc: 'Total content',
icon: Span({ class: 'text-2xl' }, '📝')
}),
Stat({
label: 'Comments',
value: '8,901',
desc: 'Engagement',
icon: Span({ class: 'text-2xl' }, '💬')
}),
Stat({
label: 'Likes',
value: '12,345',
desc: 'Reactions',
icon: Span({ class: 'text-2xl' }, '❤️')
}),
Stat({
label: 'Shares',
value: '456',
desc: 'Viral reach',
icon: Span({ class: 'text-2xl' }, '🔄')
})
]);
};
$mount(MultipleDemo, multipleTarget);
}
// 6. Dashboard Example
const dashboardTarget = document.querySelector('#demo-dashboard');
if (dashboardTarget && !dashboardTarget.hasChildNodes()) {
const DashboardDemo = () => {
const stats = $({
users: 1245,
revenue: 89342,
orders: 342,
satisfaction: 94
});
const updateStats = () => {
stats({
users: stats().users + Math.floor(Math.random() * 50),
revenue: stats().revenue + Math.floor(Math.random() * 1000),
orders: stats().orders + Math.floor(Math.random() * 20),
satisfaction: Math.min(100, stats().satisfaction + Math.floor(Math.random() * 5) - 2)
});
};
return Div({ class: 'flex flex-col gap-6' }, [
Div({ class: 'grid grid-cols-1 md:grid-cols-4 gap-4' }, [
Stat({
label: 'Total Users',
value: () => stats().users.toLocaleString(),
desc: 'Registered users',
icon: Icons.iconShow
}),
Stat({
label: 'Revenue',
value: () => `$${stats().revenue.toLocaleString()}`,
desc: 'This month',
icon: Icons.iconSuccess
}),
Stat({
label: 'Orders',
value: () => stats().orders.toLocaleString(),
desc: 'Completed',
icon: Icons.iconInfo
}),
Stat({
label: 'Satisfaction',
value: () => `${stats().satisfaction}%`,
desc: stats().satisfaction > 90 ? 'Excellent!' : 'Good',
icon: Icons.iconWarning
})
]),
Div({ class: 'flex justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: updateStats
}, 'Refresh Data')
])
]);
};
$mount(DashboardDemo, dashboardTarget);
}
// 7. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
return Div({ class: 'grid grid-cols-1 md:grid-cols-2 gap-4' }, [
Stat({
label: 'Primary Stat',
value: '1,234',
desc: 'With description',
icon: Icons.iconInfo,
class: 'bg-primary/10 text-primary'
}),
Stat({
label: 'Success Stat',
value: '89%',
desc: 'Success rate',
icon: Icons.iconSuccess,
class: 'bg-success/10 text-success'
}),
Stat({
label: 'Warning Stat',
value: '23',
desc: 'Pending items',
icon: Icons.iconWarning,
class: 'bg-warning/10 text-warning'
}),
Stat({
label: 'Error Stat',
value: '5',
desc: 'Failed attempts',
icon: Icons.iconError,
class: 'bg-error/10 text-error'
})
]);
};
$mount(VariantsDemo, variantsTarget);
}
// 8. Compact Stats
const compactTarget = document.querySelector('#demo-compact');
if (compactTarget && !compactTarget.hasChildNodes()) {
const CompactDemo = () => {
return Div({ class: 'flex flex-wrap gap-4' }, [
Stat({
label: 'Views',
value: '12.3K',
class: 'stat-compact'
}),
Stat({
label: 'Likes',
value: '2,456',
class: 'stat-compact'
}),
Stat({
label: 'Comments',
value: '345',
class: 'stat-compact'
}),
Stat({
label: 'Shares',
value: '89',
class: 'stat-compact'
})
]);
};
$mount(CompactDemo, compactTarget);
}
};
initStatExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initStatExamples);
});
}
})();
</script>
```

View File

@@ -8,13 +8,25 @@ Toggle component that swaps between two states (on/off) with customizable icons
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
| `value` | `boolean \| Signal<boolean>` | `false` | Swap state (true = on, false = off) |
| `on` | `string \| VNode` | `-` | Content to show when state is on |
| `off` | `string \| VNode` | `-` | Content to show when state is off |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `onclick` | `function` | `-` | Click event handler |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `value` | `boolean \| Signal<boolean>` | `false` | Swap state (true = on, false = off) |
| `on` | `string \| VNode \| Signal` | `-` | Content to show when state is on |
| `off` | `string \| VNode \| Signal` | `-` | Content to show when state is off |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `onclick` | `function` | `-` | Click event handler |
## Styling
Swap supports all **daisyUI Swap classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `swap` | Base swap container |
| Size | `swap-xs`, `swap-sm`, `swap-md`, `swap-lg` | Swap scale |
| Effect | `swap-rotate`, `swap-flip` | Animation effect on toggle |
> For further details, check the [daisyUI Swap Documentation](https://daisyui.com/components/swap) Full reference for CSS classes.
## Live Examples
@@ -56,8 +68,8 @@ const IconsDemo = () => {
return Swap({
value: isOn,
on: Icons.iconShow,
off: Icons.iconHide,
on: "👁️",
off: "👁️‍🗨️",
onclick: () => isOn(!isOn())
});
};
@@ -126,8 +138,8 @@ const ReactiveDemo = () => {
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Swap({
value: isOn,
on: Icons.iconShow,
off: Icons.iconHide,
on: "👁️",
off: "👁️‍🗨️",
onclick: () => isOn(!isOn())
}),
Div({ class: 'text-center' }, () =>
@@ -231,8 +243,8 @@ const VariantsDemo = () => {
Div({ class: 'text-xs mb-2' }, 'Check'),
Swap({
value: $(true),
on: Icons.iconSuccess,
off: Icons.iconError
on: "✅",
off: "❌"
})
])
]);
@@ -264,8 +276,8 @@ const TodoDemo = () => {
Span({ class: todo.completed() ? 'line-through opacity-50' : '' }, todo.text),
Swap({
value: todo.completed,
on: Icons.iconSuccess,
off: Icons.iconClose,
on: "✅",
off: "⬜",
onclick: () => todo.completed(!todo.completed())
})
])
@@ -277,224 +289,4 @@ const TodoDemo = () => {
]);
};
$mount(TodoDemo, '#demo-todo');
```
<script>
(function() {
const initSwapExamples = () => {
// 1. Basic Swap
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const isOn = $(false);
return Swap({
value: isOn,
on: "🌟 ON",
off: "💫 OFF",
onclick: () => isOn(!isOn())
});
};
$mount(BasicDemo, basicTarget);
}
// 2. Icon Swap
const iconsTarget = document.querySelector('#demo-icons');
if (iconsTarget && !iconsTarget.hasChildNodes()) {
const IconsDemo = () => {
const isOn = $(false);
return Swap({
value: isOn,
on: Icons.iconShow,
off: Icons.iconHide,
onclick: () => isOn(!isOn())
});
};
$mount(IconsDemo, iconsTarget);
}
// 3. Emoji Swap
const emojiTarget = document.querySelector('#demo-emoji');
if (emojiTarget && !emojiTarget.hasChildNodes()) {
const EmojiDemo = () => {
const isOn = $(false);
return Swap({
value: isOn,
on: "❤️",
off: "🖤",
onclick: () => isOn(!isOn())
});
};
$mount(EmojiDemo, emojiTarget);
}
// 4. Custom Content Swap
const customTarget = document.querySelector('#demo-custom');
if (customTarget && !customTarget.hasChildNodes()) {
const CustomDemo = () => {
const isOn = $(false);
return Swap({
value: isOn,
on: Div({ class: "badge badge-success gap-1" }, ["✅", " Active"]),
off: Div({ class: "badge badge-ghost gap-1" }, ["⭕", " Inactive"]),
onclick: () => isOn(!isOn())
});
};
$mount(CustomDemo, customTarget);
}
// 5. Reactive State
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const isOn = $(false);
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Swap({
value: isOn,
on: Icons.iconShow,
off: Icons.iconHide,
onclick: () => isOn(!isOn())
}),
Div({ class: 'text-center' }, () =>
isOn()
? Div({ class: 'alert alert-success' }, 'Content is visible')
: Div({ class: 'alert alert-soft' }, 'Content is hidden')
)
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 6. Toggle Mode Swap
const modeTarget = document.querySelector('#demo-mode');
if (modeTarget && !modeTarget.hasChildNodes()) {
const ModeDemo = () => {
const darkMode = $(false);
const notifications = $(true);
const sound = $(false);
return Div({ class: 'flex flex-col gap-4 w-full' }, [
Div({ class: 'flex justify-between items-center' }, [
Span({}, 'Dark mode'),
Swap({
value: darkMode,
on: "🌙",
off: "☀️",
onclick: () => darkMode(!darkMode())
})
]),
Div({ class: 'flex justify-between items-center' }, [
Span({}, 'Notifications'),
Swap({
value: notifications,
on: "🔔",
off: "🔕",
onclick: () => notifications(!notifications())
})
]),
Div({ class: 'flex justify-between items-center' }, [
Span({}, 'Sound effects'),
Swap({
value: sound,
on: "🔊",
off: "🔇",
onclick: () => sound(!sound())
})
]),
Div({ class: 'mt-2 p-3 rounded-lg', style: () => darkMode() ? 'background: #1f2937; color: white' : 'background: #f3f4f6' }, [
Div({ class: 'text-sm' }, () => `Mode: ${darkMode() ? 'Dark' : 'Light'} | Notifications: ${notifications() ? 'On' : 'Off'} | Sound: ${sound() ? 'On' : 'Off'}`)
])
]);
};
$mount(ModeDemo, modeTarget);
}
// 7. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center items-center' }, [
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Volume'),
Swap({
value: $(false),
on: "🔊",
off: "🔇"
})
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Like'),
Swap({
value: $(true),
on: "❤️",
off: "🤍"
})
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Star'),
Swap({
value: $(false),
on: "⭐",
off: "☆"
})
]),
Div({ class: 'text-center' }, [
Div({ class: 'text-xs mb-2' }, 'Check'),
Swap({
value: $(true),
on: Icons.iconSuccess,
off: Icons.iconError
})
])
]);
};
$mount(VariantsDemo, variantsTarget);
}
// 8. Simple Todo Toggle
const todoTarget = document.querySelector('#demo-todo');
if (todoTarget && !todoTarget.hasChildNodes()) {
const TodoDemo = () => {
const todos = [
{ id: 1, text: 'Complete documentation', completed: $(true) },
{ id: 2, text: 'Review pull requests', completed: $(false) },
{ id: 3, text: 'Deploy to production', completed: $(false) }
];
return Div({ class: 'flex flex-col gap-3' }, [
Div({ class: 'text-sm font-bold mb-2' }, 'Todo list'),
...todos.map(todo =>
Div({ class: 'flex items-center justify-between p-2 bg-base-200 rounded-lg' }, [
Span({ class: todo.completed() ? 'line-through opacity-50' : '' }, todo.text),
Swap({
value: todo.completed,
on: Icons.iconSuccess,
off: Icons.iconClose,
onclick: () => todo.completed(!todo.completed())
})
])
),
Div({ class: 'mt-2 text-sm opacity-70' }, () => {
const completed = todos.filter(t => t.completed()).length;
return `${completed} of ${todos.length} tasks completed`;
})
]);
};
$mount(TodoDemo, todoTarget);
}
};
initSwapExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initSwapExamples);
});
}
})();
</script>
```

View File

@@ -8,15 +8,38 @@ Data table component with sorting, pagination, zebra stripes, pin rows, and cust
## Props
| Prop | Type | Default | Description |
| :----------- | :-------------------------------------- | :--------------- | :----------------------------------------------- |
| `items` | `Array \| Signal<Array>` | `[]` | Data array to display |
| `columns` | `Array<{label: string, key?: string, render?: function, class?: string, footer?: string}>` | `[]` | Column definitions |
| `keyFn` | `function` | `(item, idx) => idx` | Unique key function for rows |
| `zebra` | `boolean \| Signal<boolean>` | `false` | Enable zebra striping |
| `pinRows` | `boolean \| Signal<boolean>` | `false` | Pin header rows on scroll |
| `empty` | `string \| VNode` | `'No data'` | Content to show when no data |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `items` | `Array \| Signal<Array>` | `[]` | Data array to display |
| `columns` | `Array<Column>` | `[]` | Column definitions |
| `keyFn` | `function` | `(item, idx) => idx` | Unique key function for rows |
| `zebra` | `boolean \| Signal<boolean>` | `false` | Enable zebra striping |
| `pinRows` | `boolean \| Signal<boolean>` | `false` | Pin header rows on scroll |
| `empty` | `string \| VNode` | `'No data'` | Content to show when no data |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
### Column Structure
| Property | Type | Description |
| :--- | :--- | :--- |
| `label` | `string` | Column header text |
| `key` | `string` | Property key to display from item |
| `render` | `function(item, index)` | Custom render function for cell content |
| `class` | `string` | Additional CSS classes for column cells |
| `footer` | `string \| VNode` | Footer content for the column |
## Styling
Table supports all **daisyUI Table classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `table` | Base table styling |
| Variant | `table-zebra` | Zebra striping |
| Size | `table-xs`, `table-sm`, `table-md`, `table-lg`, `table-xl` | Table scale |
| Feature | `table-pin-rows`, `table-pin-cols` | Pin headers/columns |
> For further details, check the [daisyUI Table Documentation](https://daisyui.com/components/table) Full reference for CSS classes.
## Live Examples
@@ -194,8 +217,8 @@ const EmptyDemo = () => {
{ label: 'ID', key: 'id' },
{ label: 'Name', key: 'name' }
],
empty: Div({ class: 'flex flex-col items-center gap-2' }, [
Icons.iconInfo,
empty: Div({ class: 'flex flex-col items-center gap-2 p-4' }, [
Span({ class: 'text-2xl' }, '📭'),
Span({}, 'No records found')
])
});
@@ -386,331 +409,4 @@ const VariantsDemo = () => {
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initTableExamples = () => {
// 1. Basic Table
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const users = [
{ id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'Editor' }
];
return Table({
items: users,
columns: [
{ label: 'ID', key: 'id' },
{ label: 'Name', key: 'name' },
{ label: 'Email', key: 'email' },
{ label: 'Role', key: 'role' }
]
});
};
$mount(BasicDemo, basicTarget);
}
// 2. With Zebra Stripes
const zebraTarget = document.querySelector('#demo-zebra');
if (zebraTarget && !zebraTarget.hasChildNodes()) {
const ZebraDemo = () => {
const products = [
{ id: 1, name: 'Laptop', price: '$999', stock: 15 },
{ id: 2, name: 'Mouse', price: '$29', stock: 42 },
{ id: 3, name: 'Keyboard', price: '$79', stock: 28 },
{ id: 4, name: 'Monitor', price: '$299', stock: 12 }
];
return Table({
items: products,
columns: [
{ label: 'Product', key: 'name' },
{ label: 'Price', key: 'price' },
{ label: 'Stock', key: 'stock' }
],
zebra: true
});
};
$mount(ZebraDemo, zebraTarget);
}
// 3. With Custom Cell Rendering
const customTarget = document.querySelector('#demo-custom');
if (customTarget && !customTarget.hasChildNodes()) {
const CustomDemo = () => {
const orders = [
{ id: 101, customer: 'Alice', amount: 250, status: 'completed' },
{ id: 102, customer: 'Bob', amount: 89, status: 'pending' },
{ id: 103, customer: 'Charlie', amount: 450, status: 'shipped' }
];
return Table({
items: orders,
columns: [
{ label: 'Order ID', key: 'id' },
{ label: 'Customer', key: 'customer' },
{
label: 'Amount',
key: 'amount',
render: (item) => `$${item.amount}`
},
{
label: 'Status',
key: 'status',
render: (item) => {
const statusClass = {
completed: 'badge badge-success',
pending: 'badge badge-warning',
shipped: 'badge badge-info'
};
return Span({ class: statusClass[item.status] }, item.status);
}
}
],
zebra: true
});
};
$mount(CustomDemo, customTarget);
}
// 4. With Footers
const footerTarget = document.querySelector('#demo-footer');
if (footerTarget && !footerTarget.hasChildNodes()) {
const FooterDemo = () => {
const sales = [
{ month: 'January', revenue: 12500, expenses: 8900 },
{ month: 'February', revenue: 14200, expenses: 9200 },
{ month: 'March', revenue: 16800, expenses: 10100 }
];
const totalRevenue = sales.reduce((sum, item) => sum + item.revenue, 0);
const totalExpenses = sales.reduce((sum, item) => sum + item.expenses, 0);
return Table({
items: sales,
columns: [
{ label: 'Month', key: 'month' },
{
label: 'Revenue',
key: 'revenue',
render: (item) => `$${item.revenue.toLocaleString()}`,
footer: `Total: $${totalRevenue.toLocaleString()}`
},
{
label: 'Expenses',
key: 'expenses',
render: (item) => `$${item.expenses.toLocaleString()}`,
footer: `Total: $${totalExpenses.toLocaleString()}`
},
{
label: 'Profit',
render: (item) => `$${(item.revenue - item.expenses).toLocaleString()}`,
footer: `$${(totalRevenue - totalExpenses).toLocaleString()}`
}
],
zebra: true
});
};
$mount(FooterDemo, footerTarget);
}
// 5. Empty State
const emptyTarget = document.querySelector('#demo-empty');
if (emptyTarget && !emptyTarget.hasChildNodes()) {
const EmptyDemo = () => {
const emptyList = [];
return Table({
items: emptyList,
columns: [
{ label: 'ID', key: 'id' },
{ label: 'Name', key: 'name' }
],
empty: Div({ class: 'flex flex-col items-center gap-2' }, [
Icons.iconInfo,
Span({}, 'No records found')
])
});
};
$mount(EmptyDemo, emptyTarget);
}
// 6. Reactive Data
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const filter = $('all');
const tasks = $([
{ id: 1, title: 'Complete documentation', completed: true },
{ id: 2, title: 'Review pull requests', completed: false },
{ id: 3, title: 'Deploy to production', completed: false },
{ id: 4, title: 'Update dependencies', completed: true }
]);
const filteredTasks = () => {
if (filter() === 'completed') {
return tasks().filter(t => t.completed);
} else if (filter() === 'pending') {
return tasks().filter(t => !t.completed);
}
return tasks();
};
const addTask = () => {
const newId = Math.max(...tasks().map(t => t.id), 0) + 1;
tasks([...tasks(), { id: newId, title: `Task ${newId}`, completed: false }]);
};
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex gap-2' }, [
Button({
class: 'btn btn-sm',
onclick: () => filter('all')
}, 'All'),
Button({
class: 'btn btn-sm',
onclick: () => filter('completed')
}, 'Completed'),
Button({
class: 'btn btn-sm',
onclick: () => filter('pending')
}, 'Pending'),
Button({
class: 'btn btn-sm btn-primary',
onclick: addTask
}, 'Add Task')
]),
Table({
items: filteredTasks,
columns: [
{ label: 'ID', key: 'id' },
{ label: 'Title', key: 'title' },
{
label: 'Status',
render: (item) => item.completed
? Span({ class: 'badge badge-success' }, '✓ Done')
: Span({ class: 'badge badge-warning' }, '○ Pending')
}
],
zebra: true
})
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 7. With Actions
const actionsTarget = document.querySelector('#demo-actions');
if (actionsTarget && !actionsTarget.hasChildNodes()) {
const ActionsDemo = () => {
const users = $([
{ id: 1, name: 'John Doe', email: 'john@example.com', active: true },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', active: false },
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', active: true }
]);
const deleteUser = (id) => {
users(users().filter(u => u.id !== id));
Toast('User deleted', 'alert-info', 2000);
};
const toggleActive = (id) => {
users(users().map(u =>
u.id === id ? { ...u, active: !u.active } : u
));
};
return Table({
items: users,
columns: [
{ label: 'ID', key: 'id' },
{ label: 'Name', key: 'name' },
{ label: 'Email', key: 'email' },
{
label: 'Status',
render: (item) => item.active
? Span({ class: 'badge badge-success' }, 'Active')
: Span({ class: 'badge badge-ghost' }, 'Inactive')
},
{
label: 'Actions',
render: (item) => Div({ class: 'flex gap-1' }, [
Button({
class: 'btn btn-xs btn-ghost',
onclick: () => toggleActive(item.id)
}, item.active ? 'Deactivate' : 'Activate'),
Button({
class: 'btn btn-xs btn-error',
onclick: () => deleteUser(item.id)
}, 'Delete')
])
}
],
zebra: true
});
};
$mount(ActionsDemo, actionsTarget);
}
// 8. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
const data = [
{ id: 1, name: 'Item 1', value: 100 },
{ id: 2, name: 'Item 2', value: 200 },
{ id: 3, name: 'Item 3', value: 300 }
];
return Div({ class: 'flex flex-col gap-6' }, [
Div({ class: 'text-sm font-bold' }, 'Default Table'),
Table({
items: data,
columns: [
{ label: 'ID', key: 'id' },
{ label: 'Name', key: 'name' },
{ label: 'Value', key: 'value' }
]
}),
Div({ class: 'text-sm font-bold mt-4' }, 'Zebra Stripes'),
Table({
items: data,
columns: [
{ label: 'ID', key: 'id' },
{ label: 'Name', key: 'name' },
{ label: 'Value', key: 'value' }
],
zebra: true
}),
Div({ class: 'text-sm font-bold mt-4' }, 'Compact Table'),
Table({
items: data,
columns: [
{ label: 'ID', key: 'id' },
{ label: 'Name', key: 'name' },
{ label: 'Value', key: 'value' }
],
class: 'table-compact'
})
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initTableExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initTableExamples);
});
}
})();
</script>
```

View File

@@ -8,21 +8,46 @@ Tabs component for organizing content into separate panels with tab navigation.
## Props
| Prop | Type | Default | Description |
| :----------- | :-------------------------------------- | :---------- | :----------------------------------------------- |
| `items` | `Array<TabItem> \| Signal<Array>` | `[]` | Array of tab items |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `items` | `Array<TabItem> \| Signal<Array>` | `[]` | Array of tab items |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
### TabItem Structure
| Property | Type | Description |
| :---------- | :--------------------------- | :----------------------------------------------- |
| `label` | `string \| VNode` | Tab button label |
| `content` | `VNode \| function` | Content to display when tab is active |
| `active` | `boolean \| Signal<boolean>` | Whether this tab is active (only one per group) |
| `disabled` | `boolean` | Whether tab is disabled |
| `tip` | `string` | Tooltip text for the tab |
| `onclick` | `function` | Click handler (optional, overrides default) |
| Property | Type | Description |
| :--- | :--- | :--- |
| `label` | `string \| VNode` | Tab button label |
| `content` | `VNode \| function` | Content to display when tab is active |
| `active` | `boolean \| Signal<boolean>` | Whether this tab is active (only one per group) |
| `disabled` | `boolean` | Whether tab is disabled |
| `tip` | `string` | Tooltip text for the tab |
| `onclick` | `function` | Click handler (optional, overrides default) |
## Styling
Tabs supports all **daisyUI Tabs classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `tabs` | Base tabs container |
| Variant | `tabs-box`, `tabs-lifted` | Tab style variants |
| Size | `tabs-xs`, `tabs-sm`, `tabs-md`, `tabs-lg` | Tab scale |
| State | `tab-active`, `tab-disabled` | Tab states |
> For further details, check the [daisyUI Tabs Documentation](https://daisyui.com/components/tabs) Full reference for CSS classes.
### Example
```javascript
Tabs({
items: [
{ label: "Tab 1", content: "Content 1", active: true },
{ label: "Tab 2", content: "Content 2" }
],
class: "tabs-box"
});
```
## Live Examples
@@ -344,334 +369,41 @@ $mount(FormTabs, '#demo-form');
```javascript
const VariantsDemo = () => {
const items = [
{ label: 'Tab 1', content: 'Content 1' },
{ label: 'Tab 2', content: 'Content 2' },
{ label: 'Tab 3', content: 'Content 3' }
].map((tab, idx) => ({
...tab,
active: () => idx === 0,
content: Div({ class: 'p-4' }, tab.content)
}));
const active1 = $('tab1');
const active2 = $('tab1');
const active3 = $('tab1');
const createItems = (active) => [
{
label: 'Tab 1',
active: () => active() === 'tab1',
onclick: () => active('tab1'),
content: Div({ class: 'p-4' }, 'Content 1')
},
{
label: 'Tab 2',
active: () => active() === 'tab2',
onclick: () => active('tab2'),
content: Div({ class: 'p-4' }, 'Content 2')
},
{
label: 'Tab 3',
active: () => active() === 'tab3',
onclick: () => active('tab3'),
content: Div({ class: 'p-4' }, 'Content 3')
}
];
return Div({ class: 'flex flex-col gap-6' }, [
Div({ class: 'text-sm font-bold' }, 'Default Tabs'),
Tabs({ items }),
Tabs({ items: createItems(active1) }),
Div({ class: 'text-sm font-bold mt-4' }, 'Boxed Tabs'),
Tabs({ items, class: 'tabs-box' }),
Tabs({ items: createItems(active2), class: 'tabs-box' }),
Div({ class: 'text-sm font-bold mt-4' }, 'Lifted Tabs'),
Tabs({ items, class: 'tabs-lifted' })
Tabs({ items: createItems(active3), class: 'tabs-lifted' })
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initTabsExamples = () => {
// 1. Basic Tabs
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const activeTab = $('tab1');
return Tabs({
items: [
{
label: 'Tab 1',
active: () => activeTab() === 'tab1',
onclick: () => activeTab('tab1'),
content: Div({ class: 'p-4' }, 'Content for Tab 1')
},
{
label: 'Tab 2',
active: () => activeTab() === 'tab2',
onclick: () => activeTab('tab2'),
content: Div({ class: 'p-4' }, 'Content for Tab 2')
},
{
label: 'Tab 3',
active: () => activeTab() === 'tab3',
onclick: () => activeTab('tab3'),
content: Div({ class: 'p-4' }, 'Content for Tab 3')
}
]
});
};
$mount(BasicDemo, basicTarget);
}
// 2. With Icons
const iconsTarget = document.querySelector('#demo-icons');
if (iconsTarget && !iconsTarget.hasChildNodes()) {
const IconsDemo = () => {
const activeTab = $('home');
return Tabs({
items: [
{
label: Span({ class: 'flex items-center gap-2' }, ['🏠', 'Home']),
active: () => activeTab() === 'home',
onclick: () => activeTab('home'),
content: Div({ class: 'p-4' }, 'Welcome to the Home tab!')
},
{
label: Span({ class: 'flex items-center gap-2' }, ['⭐', 'Favorites']),
active: () => activeTab() === 'favorites',
onclick: () => activeTab('favorites'),
content: Div({ class: 'p-4' }, 'Your favorite items appear here.')
},
{
label: Span({ class: 'flex items-center gap-2' }, ['⚙️', 'Settings']),
active: () => activeTab() === 'settings',
onclick: () => activeTab('settings'),
content: Div({ class: 'p-4' }, 'Configure your preferences.')
}
]
});
};
$mount(IconsDemo, iconsTarget);
}
// 3. With Tooltips
const tooltipsTarget = document.querySelector('#demo-tooltips');
if (tooltipsTarget && !tooltipsTarget.hasChildNodes()) {
const TooltipsDemo = () => {
const activeTab = $('profile');
return Tabs({
items: [
{
label: 'Profile',
tip: 'View your profile information',
active: () => activeTab() === 'profile',
onclick: () => activeTab('profile'),
content: Div({ class: 'p-4' }, 'Profile information here.')
},
{
label: 'Settings',
tip: 'Adjust your preferences',
active: () => activeTab() === 'settings',
onclick: () => activeTab('settings'),
content: Div({ class: 'p-4' }, 'Settings configuration.')
},
{
label: 'Notifications',
tip: 'Manage notifications',
active: () => activeTab() === 'notifications',
onclick: () => activeTab('notifications'),
content: Div({ class: 'p-4' }, 'Notification settings.')
}
]
});
};
$mount(TooltipsDemo, tooltipsTarget);
}
// 4. Disabled Tab
const disabledTarget = document.querySelector('#demo-disabled');
if (disabledTarget && !disabledTarget.hasChildNodes()) {
const DisabledDemo = () => {
const activeTab = $('basic');
return Tabs({
items: [
{
label: 'Basic',
active: () => activeTab() === 'basic',
onclick: () => activeTab('basic'),
content: Div({ class: 'p-4' }, 'Basic features available.')
},
{
label: 'Premium',
disabled: true,
tip: 'Upgrade to access',
content: Div({ class: 'p-4' }, 'Premium content (locked)')
},
{
label: 'Pro',
disabled: true,
tip: 'Coming soon',
content: Div({ class: 'p-4' }, 'Pro features (coming soon)')
}
]
});
};
$mount(DisabledDemo, disabledTarget);
}
// 5. Reactive Content
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const activeTab = $('counter');
const count = $(0);
return Tabs({
items: [
{
label: 'Counter',
active: () => activeTab() === 'counter',
onclick: () => activeTab('counter'),
content: Div({ class: 'p-4 text-center' }, [
Div({ class: 'text-4xl font-bold mb-4' }, () => count()),
Button({
class: 'btn btn-primary',
onclick: () => count(count() + 1)
}, 'Increment')
])
},
{
label: 'Timer',
active: () => activeTab() === 'timer',
onclick: () => activeTab('timer'),
content: Div({ class: 'p-4' }, () => `Current time: ${new Date().toLocaleTimeString()}`)
},
{
label: 'Status',
active: () => activeTab() === 'status',
onclick: () => activeTab('status'),
content: Div({ class: 'p-4' }, () => `Counter value: ${count()}, Last updated: ${new Date().toLocaleTimeString()}`)
}
]
});
};
$mount(ReactiveDemo, reactiveTarget);
}
// 6. Form Tabs
const formTarget = document.querySelector('#demo-form');
if (formTarget && !formTarget.hasChildNodes()) {
const FormTabs = () => {
const activeTab = $('personal');
const formData = $({
name: '',
email: '',
address: '',
city: '',
notifications: true,
newsletter: false
});
const updateField = (field, value) => {
formData({ ...formData(), [field]: value });
};
const handleSubmit = () => {
Toast('Form submitted!', 'alert-success', 2000);
console.log(formData());
};
return Div({ class: 'flex flex-col gap-4' }, [
Tabs({
items: [
{
label: 'Personal Info',
active: () => activeTab() === 'personal',
onclick: () => activeTab('personal'),
content: Div({ class: 'p-4 space-y-4' }, [
Input({
label: 'Name',
value: () => formData().name,
placeholder: 'Enter your name',
oninput: (e) => updateField('name', e.target.value)
}),
Input({
label: 'Email',
type: 'email',
value: () => formData().email,
placeholder: 'email@example.com',
oninput: (e) => updateField('email', e.target.value)
})
])
},
{
label: 'Address',
active: () => activeTab() === 'address',
onclick: () => activeTab('address'),
content: Div({ class: 'p-4 space-y-4' }, [
Input({
label: 'Address',
value: () => formData().address,
placeholder: 'Street address',
oninput: (e) => updateField('address', e.target.value)
}),
Input({
label: 'City',
value: () => formData().city,
placeholder: 'City',
oninput: (e) => updateField('city', e.target.value)
})
])
},
{
label: 'Preferences',
active: () => activeTab() === 'prefs',
onclick: () => activeTab('prefs'),
content: Div({ class: 'p-4 space-y-4' }, [
Checkbox({
label: 'Email notifications',
value: () => formData().notifications,
onclick: () => updateField('notifications', !formData().notifications)
}),
Checkbox({
label: 'Newsletter subscription',
value: () => formData().newsletter,
onclick: () => updateField('newsletter', !formData().newsletter)
})
])
}
]
}),
Div({ class: 'flex justify-end mt-4' }, [
Button({
class: 'btn btn-primary',
onclick: handleSubmit
}, 'Submit')
])
]);
};
$mount(FormTabs, formTarget);
}
// 7. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
const items = [
{ label: 'Tab 1', content: 'Content 1' },
{ label: 'Tab 2', content: 'Content 2' },
{ label: 'Tab 3', content: 'Content 3' }
].map((tab, idx) => ({
...tab,
active: () => idx === 0,
content: Div({ class: 'p-4' }, tab.content)
}));
return Div({ class: 'flex flex-col gap-6' }, [
Div({ class: 'text-sm font-bold' }, 'Default Tabs'),
Tabs({ items }),
Div({ class: 'text-sm font-bold mt-4' }, 'Boxed Tabs'),
Tabs({ items, class: 'tabs-box' }),
Div({ class: 'text-sm font-bold mt-4' }, 'Lifted Tabs'),
Tabs({ items, class: 'tabs-lifted' })
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initTabsExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initTabsExamples);
});
}
})();
</script>
```

View File

@@ -8,22 +8,34 @@ Timeline component for displaying chronological events, steps, or progress with
## Props
| Prop | Type | Default | Description |
| :----------- | :-------------------------------------- | :--------------- | :----------------------------------------------- |
| `items` | `Array<TimelineItem> \| Signal` | `[]` | Timeline items to display |
| `vertical` | `boolean \| Signal<boolean>` | `true` | Vertical or horizontal orientation |
| `compact` | `boolean \| Signal<boolean>` | `false` | Compact mode with less padding |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `items` | `Array<TimelineItem> \| Signal` | `[]` | Timeline items to display |
| `vertical` | `boolean \| Signal<boolean>` | `true` | Vertical or horizontal orientation |
| `compact` | `boolean \| Signal<boolean>` | `false` | Compact mode with less padding |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
### TimelineItem Structure
| Property | Type | Description |
| :---------- | :--------------------------- | :----------------------------------------------- |
| `title` | `string \| VNode \| Signal` | Event title or main text |
| `detail` | `string \| VNode \| Signal` | Additional details or description |
| `icon` | `string \| VNode` | Custom icon (overrides type) |
| `type` | `string` | Type: 'success', 'warning', 'error', 'info' |
| `completed` | `boolean` | Whether event is completed (affects connector) |
| Property | Type | Description |
| :--- | :--- | :--- |
| `title` | `string \| VNode \| Signal` | Event title or main text |
| `detail` | `string \| VNode \| Signal` | Additional details or description |
| `icon` | `string \| VNode` | Custom icon (overrides type) |
| `type` | `string` | Type: `'success'`, `'warning'`, `'error'`, `'info'` |
| `completed` | `boolean` | Whether event is completed (affects connector) |
## Styling
Timeline supports all **daisyUI Timeline classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Base | `timeline`, `timeline-vertical`, `timeline-horizontal` | Timeline orientation |
| Size | `timeline-compact` | Compact spacing variant |
| Color | `bg-primary`, `bg-success`, `bg-warning`, `bg-error`, `bg-info` | Icon background colors |
> For further details, check the [daisyUI Timeline Documentation](https://daisyui.com/components/timeline) Full reference for CSS classes.
## Live Examples
@@ -117,10 +129,10 @@ $mount(CompactDemo, '#demo-compact');
```javascript
const IconsDemo = () => {
const milestones = [
{ title: 'Kickoff', detail: 'Project kickoff meeting', icon: Icons.iconInfo, completed: true },
{ title: 'MVP', detail: 'Minimum viable product', icon: Icons.iconSuccess, completed: true },
{ title: 'Beta', detail: 'Beta release', icon: Icons.iconWarning, completed: false },
{ title: 'Launch', detail: 'Public launch', icon: Icons.iconShow, completed: false }
{ title: 'Kickoff', detail: 'Project kickoff meeting', icon: '🚀', completed: true },
{ title: 'MVP', detail: 'Minimum viable product', icon: '💡', completed: true },
{ title: 'Beta', detail: 'Beta release', icon: '⚙️', completed: false },
{ title: 'Launch', detail: 'Public launch', icon: '🎉', completed: false }
];
return Timeline({ items: milestones });
@@ -139,12 +151,12 @@ $mount(IconsDemo, '#demo-icons');
```javascript
const ReactiveDemo = () => {
const currentStep = $(2);
const currentStep = $(0);
const steps = [
{ title: 'Order Placed', detail: 'Your order has been confirmed', type: 'success', completed: true },
{ title: 'Processing', detail: 'Payment verified, preparing shipment', type: 'success', completed: currentStep() > 1 },
{ title: 'Shipped', detail: 'Package on the way', type: 'warning', completed: currentStep() > 2 },
{ title: 'Delivered', detail: 'Arriving soon', type: 'info', completed: currentStep() > 3 }
{ title: 'Order Placed', detail: 'Your order has been confirmed', type: 'success' },
{ title: 'Processing', detail: 'Payment verified, preparing shipment', type: 'success' },
{ title: 'Shipped', detail: 'Package on the way', type: 'warning' },
{ title: 'Delivered', detail: 'Arriving soon', type: 'info' }
];
const items = () => steps.map((step, idx) => ({
@@ -174,7 +186,8 @@ const ReactiveDemo = () => {
}, 'Next Step'),
Button({
class: 'btn btn-ghost btn-sm',
onclick: reset
onclick: reset,
disabled: () => currentStep() === 0
}, 'Reset')
])
]);
@@ -193,29 +206,29 @@ $mount(ReactiveDemo, '#demo-reactive');
```javascript
const OrderDemo = () => {
const status = $('shipped');
const status = $('pending'); // ← empezar en 'pending'
const statusMap = {
pending: { title: 'Order Pending', detail: 'Awaiting confirmation', completed: false, type: 'warning' },
confirmed: { title: 'Order Confirmed', detail: 'Payment received', completed: false, type: 'info' },
processing: { title: 'Processing', detail: 'Preparing your order', completed: false, type: 'info' },
shipped: { title: 'Shipped', detail: 'Package in transit', completed: false, type: 'info' },
delivered: { title: 'Delivered', detail: 'Order completed', completed: false, type: 'success' }
pending: { title: 'Order Pending', detail: 'Awaiting confirmation', type: 'warning' },
confirmed: { title: 'Order Confirmed', detail: 'Payment received', type: 'info' },
processing: { title: 'Processing', detail: 'Preparing your order', type: 'info' },
shipped: { title: 'Shipped', detail: 'Package in transit', type: 'info' },
delivered: { title: 'Delivered', detail: 'Order completed', type: 'success' }
};
const statusOrder = ['pending', 'confirmed', 'processing', 'shipped', 'delivered'];
const currentIndex = statusOrder.indexOf(status());
const currentIndex = () => statusOrder.indexOf(status());
const items = statusOrder.map((key, idx) => ({
const items = () => statusOrder.map((key, idx) => ({
...statusMap[key],
completed: idx < currentIndex
completed: idx < currentIndex()
}));
return Div({ class: 'flex flex-col gap-4' }, [
Timeline({ items, compact: true }),
Div({ class: 'flex gap-2 justify-center flex-wrap mt-4' }, statusOrder.map(s =>
Button({
class: `btn btn-xs ${status() === s ? 'btn-primary' : 'btn-ghost'}`,
class: () => `btn btn-xs ${status() === s ? 'btn-primary' : 'btn-ghost'}`,
onclick: () => status(s)
}, statusMap[s].title)
))
@@ -236,42 +249,12 @@ $mount(OrderDemo, '#demo-order');
```javascript
const HistoryDemo = () => {
const milestones = [
{
title: '2015 - Founded',
detail: 'Company started with 3 employees in a small office',
type: 'success',
completed: true
},
{
title: '2017 - First Product',
detail: 'Launched our first software product to market',
type: 'success',
completed: true
},
{
title: '2019 - Series A',
detail: 'Raised $5M in funding, expanded team to 50',
type: 'success',
completed: true
},
{
title: '2022 - Global Expansion',
detail: 'Opened offices in Europe and Asia',
type: 'info',
completed: true
},
{
title: '2024 - AI Integration',
detail: 'Launched AI-powered features',
type: 'warning',
completed: false
},
{
title: '2026 - Future Goals',
detail: 'Aiming for market leadership',
type: 'info',
completed: false
}
{ title: '2015 - Founded', detail: 'Company started with 3 employees', type: 'success', completed: true },
{ title: '2017 - First Product', detail: 'Launched first software product', type: 'success', completed: true },
{ title: '2019 - Series A', detail: 'Raised $5M, expanded to 50 employees', type: 'success', completed: true },
{ title: '2022 - Global Expansion', detail: 'Opened offices in Europe and Asia', type: 'info', completed: true },
{ title: '2024 - AI Integration', detail: 'Launched AI-powered features', type: 'warning', completed: false },
{ title: '2026 - Future Goals', detail: 'Aiming for market leadership', type: 'info', completed: false }
];
return Timeline({ items: milestones });
@@ -308,246 +291,4 @@ const VariantsDemo = () => {
]);
};
$mount(VariantsDemo, '#demo-variants');
```
<script>
(function() {
const initTimelineExamples = () => {
// 1. Basic Timeline
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
const events = [
{ title: 'Project Started', detail: 'Initial planning and setup', type: 'info', completed: true },
{ title: 'Design Phase', detail: 'UI/UX design completed', type: 'success', completed: true },
{ title: 'Development', detail: 'Core features implemented', type: 'warning', completed: false },
{ title: 'Testing', detail: 'Quality assurance', type: 'info', completed: false },
{ title: 'Launch', detail: 'Production deployment', type: 'success', completed: false }
];
return Timeline({ items: events });
};
$mount(BasicDemo, basicTarget);
}
// 2. Horizontal Timeline
const horizontalTarget = document.querySelector('#demo-horizontal');
if (horizontalTarget && !horizontalTarget.hasChildNodes()) {
const HorizontalDemo = () => {
const steps = [
{ title: 'Step 1', detail: 'Requirements', type: 'success', completed: true },
{ title: 'Step 2', detail: 'Design', type: 'success', completed: true },
{ title: 'Step 3', detail: 'Development', type: 'warning', completed: false },
{ title: 'Step 4', detail: 'Testing', type: 'info', completed: false },
{ title: 'Step 5', detail: 'Deploy', type: 'info', completed: false }
];
return Timeline({
items: steps,
vertical: false,
class: 'min-w-[600px]'
});
};
$mount(HorizontalDemo, horizontalTarget);
}
// 3. Compact Timeline
const compactTarget = document.querySelector('#demo-compact');
if (compactTarget && !compactTarget.hasChildNodes()) {
const CompactDemo = () => {
const activities = [
{ title: 'User login', detail: '10:30 AM', type: 'success', completed: true },
{ title: 'Viewed dashboard', detail: '10:32 AM', type: 'info', completed: true },
{ title: 'Updated profile', detail: '10:45 AM', type: 'success', completed: true },
{ title: 'Made purchase', detail: '11:00 AM', type: 'warning', completed: false }
];
return Timeline({
items: activities,
compact: true
});
};
$mount(CompactDemo, compactTarget);
}
// 4. Custom Icons
const iconsTarget = document.querySelector('#demo-icons');
if (iconsTarget && !iconsTarget.hasChildNodes()) {
const IconsDemo = () => {
const milestones = [
{ title: 'Kickoff', detail: 'Project kickoff meeting', icon: Icons.iconInfo, completed: true },
{ title: 'MVP', detail: 'Minimum viable product', icon: Icons.iconSuccess, completed: true },
{ title: 'Beta', detail: 'Beta release', icon: Icons.iconWarning, completed: false },
{ title: 'Launch', detail: 'Public launch', icon: Icons.iconShow, completed: false }
];
return Timeline({ items: milestones });
};
$mount(IconsDemo, iconsTarget);
}
// 5. Reactive Timeline
const reactiveTarget = document.querySelector('#demo-reactive');
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
const ReactiveDemo = () => {
const currentStep = $(2);
const steps = [
{ title: 'Order Placed', detail: 'Your order has been confirmed', type: 'success', completed: true },
{ title: 'Processing', detail: 'Payment verified, preparing shipment', type: 'success', completed: currentStep() > 1 },
{ title: 'Shipped', detail: 'Package on the way', type: 'warning', completed: currentStep() > 2 },
{ title: 'Delivered', detail: 'Arriving soon', type: 'info', completed: currentStep() > 3 }
];
const items = () => steps.map((step, idx) => ({
...step,
completed: idx < currentStep()
}));
const nextStep = () => {
if (currentStep() < steps.length) {
currentStep(currentStep() + 1);
Toast(`Step ${currentStep()}: ${steps[currentStep() - 1].title}`, 'alert-info', 1500);
}
};
const reset = () => {
currentStep(0);
Toast('Order tracking reset', 'alert-warning', 1500);
};
return Div({ class: 'flex flex-col gap-4' }, [
Timeline({ items: items }),
Div({ class: 'flex gap-2 justify-center mt-4' }, [
Button({
class: 'btn btn-primary btn-sm',
onclick: nextStep,
disabled: () => currentStep() >= steps.length
}, 'Next Step'),
Button({
class: 'btn btn-ghost btn-sm',
onclick: reset
}, 'Reset')
])
]);
};
$mount(ReactiveDemo, reactiveTarget);
}
// 6. Order Status Tracker
const orderTarget = document.querySelector('#demo-order');
if (orderTarget && !orderTarget.hasChildNodes()) {
const OrderDemo = () => {
const status = $('shipped');
const statusMap = {
pending: { title: 'Order Pending', detail: 'Awaiting confirmation', completed: false, type: 'warning' },
confirmed: { title: 'Order Confirmed', detail: 'Payment received', completed: false, type: 'info' },
processing: { title: 'Processing', detail: 'Preparing your order', completed: false, type: 'info' },
shipped: { title: 'Shipped', detail: 'Package in transit', completed: false, type: 'info' },
delivered: { title: 'Delivered', detail: 'Order completed', completed: false, type: 'success' }
};
const statusOrder = ['pending', 'confirmed', 'processing', 'shipped', 'delivered'];
const currentIndex = statusOrder.indexOf(status());
const items = statusOrder.map((key, idx) => ({
...statusMap[key],
completed: idx < currentIndex
}));
return Div({ class: 'flex flex-col gap-4' }, [
Timeline({ items, compact: true }),
Div({ class: 'flex gap-2 justify-center flex-wrap mt-4' }, statusOrder.map(s =>
Button({
class: `btn btn-xs ${status() === s ? 'btn-primary' : 'btn-ghost'}`,
onclick: () => status(s)
}, statusMap[s].title)
))
]);
};
$mount(OrderDemo, orderTarget);
}
// 7. Company History
const historyTarget = document.querySelector('#demo-history');
if (historyTarget && !historyTarget.hasChildNodes()) {
const HistoryDemo = () => {
const milestones = [
{
title: '2015 - Founded',
detail: 'Company started with 3 employees in a small office',
type: 'success',
completed: true
},
{
title: '2017 - First Product',
detail: 'Launched our first software product to market',
type: 'success',
completed: true
},
{
title: '2019 - Series A',
detail: 'Raised $5M in funding, expanded team to 50',
type: 'success',
completed: true
},
{
title: '2022 - Global Expansion',
detail: 'Opened offices in Europe and Asia',
type: 'info',
completed: true
},
{
title: '2024 - AI Integration',
detail: 'Launched AI-powered features',
type: 'warning',
completed: false
},
{
title: '2026 - Future Goals',
detail: 'Aiming for market leadership',
type: 'info',
completed: false
}
];
return Timeline({ items: milestones });
};
$mount(HistoryDemo, historyTarget);
}
// 8. All Variants
const variantsTarget = document.querySelector('#demo-variants');
if (variantsTarget && !variantsTarget.hasChildNodes()) {
const VariantsDemo = () => {
const sampleItems = [
{ title: 'Event 1', detail: 'Description here', type: 'success', completed: true },
{ title: 'Event 2', detail: 'Description here', type: 'warning', completed: false },
{ title: 'Event 3', detail: 'Description here', type: 'info', completed: false }
];
return Div({ class: 'flex flex-col gap-8' }, [
Div({ class: 'text-sm font-bold' }, 'Vertical Timeline'),
Timeline({ items: sampleItems }),
Div({ class: 'text-sm font-bold mt-4' }, 'Horizontal Timeline'),
Timeline({ items: sampleItems, vertical: false, class: 'min-w-[500px]' }),
Div({ class: 'text-sm font-bold mt-4' }, 'Compact Timeline'),
Timeline({ items: sampleItems, compact: true })
]);
};
$mount(VariantsDemo, variantsTarget);
}
};
initTimelineExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initTimelineExamples);
});
}
})();
</script>
```

View File

@@ -6,11 +6,11 @@ Toast notification utility for displaying temporary messages that automatically
`Toast(message, type = 'alert-info', duration = 3500)`
| Param | Type | Default | Description |
| :--------- | :--------------------------- | :--------------- | :----------------------------------------------- |
| `message` | `string \| VNode` | `-` | Message content to display |
| `type` | `string` | `'alert-info'` | Alert type: 'alert-info', 'alert-success', 'alert-warning', 'alert-error' |
| `duration` | `number` | `3500` | Auto-dismiss duration in milliseconds |
| Param | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `message` | `string \| VNode` | `-` | Message content to display |
| `type` | `string` | `'alert-info'` | Alert type: `'alert-info'`, `'alert-success'`, `'alert-warning'`, `'alert-error'` |
| `duration` | `number` | `3500` | Auto-dismiss duration in milliseconds |
## Live Examples
@@ -340,291 +340,4 @@ const MultipleDemo = () => {
]);
};
$mount(MultipleDemo, '#demo-multiple');
```
<script>
(function() {
const initToastExamples = () => {
// 1. Basic Toast
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
return Div({ class: 'flex flex-wrap gap-2 justify-center' }, [
Button({
class: 'btn btn-info',
onclick: () => Toast('This is an info message', 'alert-info', 3000)
}, 'Info Toast'),
Button({
class: 'btn btn-success',
onclick: () => Toast('Operation successful!', 'alert-success', 3000)
}, 'Success Toast'),
Button({
class: 'btn btn-warning',
onclick: () => Toast('Please check your input', 'alert-warning', 3000)
}, 'Warning Toast'),
Button({
class: 'btn btn-error',
onclick: () => Toast('An error occurred', 'alert-error', 3000)
}, 'Error Toast')
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. Different Durations
const durationTarget = document.querySelector('#demo-duration');
if (durationTarget && !durationTarget.hasChildNodes()) {
const DurationDemo = () => {
return Div({ class: 'flex flex-wrap gap-2 justify-center' }, [
Button({
class: 'btn btn-sm',
onclick: () => Toast('Short (1s)', 'alert-info', 1000)
}, '1 Second'),
Button({
class: 'btn btn-sm',
onclick: () => Toast('Normal (3s)', 'alert-success', 3000)
}, '3 Seconds'),
Button({
class: 'btn btn-sm',
onclick: () => Toast('Long (5s)', 'alert-warning', 5000)
}, '5 Seconds'),
Button({
class: 'btn btn-sm',
onclick: () => Toast('Very Long (8s)', 'alert-error', 8000)
}, '8 Seconds')
]);
};
$mount(DurationDemo, durationTarget);
}
// 3. Interactive Toast
const interactiveTarget = document.querySelector('#demo-interactive');
if (interactiveTarget && !interactiveTarget.hasChildNodes()) {
const InteractiveDemo = () => {
const count = $(0);
const showRandomToast = () => {
const types = ['alert-info', 'alert-success', 'alert-warning', 'alert-error'];
const messages = [
'You clicked the button!',
'Action completed successfully',
'Processing your request...',
'Something interesting happened'
];
const randomType = types[Math.floor(Math.random() * types.length)];
const randomMessage = messages[Math.floor(Math.random() * messages.length)];
count(count() + 1);
Toast(`${randomMessage} (${count()})`, randomType, 2000);
};
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Button({
class: 'btn btn-primary',
onclick: showRandomToast
}, 'Show Random Toast'),
Div({ class: 'text-sm opacity-70' }, () => `Toasts shown: ${count()}`)
]);
};
$mount(InteractiveDemo, interactiveTarget);
}
// 4. Form Validation Toast
const formTarget = document.querySelector('#demo-form');
if (formTarget && !formTarget.hasChildNodes()) {
const FormToastDemo = () => {
const email = $('');
const password = $('');
const handleSubmit = () => {
if (!email()) {
Toast('Please enter your email', 'alert-warning', 3000);
return;
}
if (!email().includes('@')) {
Toast('Please enter a valid email address', 'alert-error', 3000);
return;
}
if (!password()) {
Toast('Please enter your password', 'alert-warning', 3000);
return;
}
if (password().length < 6) {
Toast('Password must be at least 6 characters', 'alert-error', 3000);
return;
}
Toast('Login successful! Redirecting...', 'alert-success', 2000);
};
return Div({ class: 'flex flex-col gap-4 max-w-md mx-auto' }, [
Div({ class: 'text-lg font-bold text-center' }, 'Login Form'),
Input({
label: 'Email',
type: 'email',
value: email,
placeholder: 'user@example.com',
oninput: (e) => email(e.target.value)
}),
Input({
label: 'Password',
type: 'password',
value: password,
placeholder: 'Enter password',
oninput: (e) => password(e.target.value)
}),
Button({
class: 'btn btn-primary',
onclick: handleSubmit
}, 'Login')
]);
};
$mount(FormToastDemo, formTarget);
}
// 5. Success Feedback
const feedbackTarget = document.querySelector('#demo-feedback');
if (feedbackTarget && !feedbackTarget.hasChildNodes()) {
const FeedbackDemo = () => {
const items = $([
{ id: 1, name: 'Item 1', saved: false },
{ id: 2, name: 'Item 2', saved: false },
{ id: 3, name: 'Item 3', saved: false }
]);
const saveItem = (id) => {
items(items().map(item =>
item.id === id ? { ...item, saved: true } : item
));
Toast(`Item ${id} saved successfully!`, 'alert-success', 2000);
};
const saveAll = () => {
items(items().map(item => ({ ...item, saved: true })));
Toast('All items saved!', 'alert-success', 2000);
};
return Div({ class: 'flex flex-col gap-4' }, [
Div({ class: 'flex justify-between items-center' }, [
Span({ class: 'font-bold' }, 'Items to Save'),
Button({
class: 'btn btn-sm btn-primary',
onclick: saveAll
}, 'Save All')
]),
Div({ class: 'flex flex-col gap-2' }, items().map(item =>
Div({ class: 'flex justify-between items-center p-3 bg-base-200 rounded-lg' }, [
Span({}, item.name),
item.saved
? Span({ class: 'badge badge-success' }, '✓ Saved')
: Button({
class: 'btn btn-xs btn-primary',
onclick: () => saveItem(item.id)
}, 'Save')
])
))
]);
};
$mount(FeedbackDemo, feedbackTarget);
}
// 6. Error Handling
const errorTarget = document.querySelector('#demo-error');
if (errorTarget && !errorTarget.hasChildNodes()) {
const ErrorDemo = () => {
const simulateApiCall = () => {
const success = Math.random() > 0.3;
if (success) {
Toast('Data loaded successfully!', 'alert-success', 2000);
} else {
Toast('Failed to load data. Please try again.', 'alert-error', 3000);
}
};
const simulateNetworkError = () => {
Toast('Network error: Unable to connect to server', 'alert-error', 4000);
};
const simulateTimeout = () => {
Toast('Request timeout (5s). Please check your connection.', 'alert-warning', 4000);
};
return Div({ class: 'flex flex-wrap gap-3 justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: simulateApiCall
}, 'Simulate API Call'),
Button({
class: 'btn btn-error',
onclick: simulateNetworkError
}, 'Network Error'),
Button({
class: 'btn btn-warning',
onclick: simulateTimeout
}, 'Timeout')
]);
};
$mount(ErrorDemo, errorTarget);
}
// 7. Custom Messages
const customTarget = document.querySelector('#demo-custom');
if (customTarget && !customTarget.hasChildNodes()) {
const CustomDemo = () => {
const showCustomToast = (type, message) => {
Toast(message, type, 3000);
};
return Div({ class: 'flex flex-wrap gap-2 justify-center' }, [
Button({
class: 'btn btn-info',
onclick: () => showCustomToast('alert-info', '📧 New email received from john@example.com')
}, 'Email'),
Button({
class: 'btn btn-success',
onclick: () => showCustomToast('alert-success', '💰 Payment of $49.99 completed')
}, 'Payment'),
Button({
class: 'btn btn-warning',
onclick: () => showCustomToast('alert-warning', '⚠️ Your session will expire in 5 minutes')
}, 'Session Warning'),
Button({
class: 'btn btn-error',
onclick: () => showCustomToast('alert-error', '🔒 Failed login attempt detected')
}, 'Security Alert')
]);
};
$mount(CustomDemo, customTarget);
}
// 8. Multiple Toasts
const multipleTarget = document.querySelector('#demo-multiple');
if (multipleTarget && !multipleTarget.hasChildNodes()) {
const MultipleDemo = () => {
const showMultipleToasts = () => {
Toast('First message', 'alert-info', 3000);
setTimeout(() => Toast('Second message', 'alert-success', 3000), 500);
setTimeout(() => Toast('Third message', 'alert-warning', 3000), 1000);
setTimeout(() => Toast('Fourth message', 'alert-error', 3000), 1500);
};
return Div({ class: 'flex justify-center' }, [
Button({
class: 'btn btn-primary',
onclick: showMultipleToasts
}, 'Show Multiple Toasts')
]);
};
$mount(MultipleDemo, multipleTarget);
}
};
initToastExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initToastExamples);
});
}
})();
</script>
```

View File

@@ -8,11 +8,30 @@ Tooltip component for displaying helpful hints and additional information on hov
## Props
| Prop | Type | Default | Description |
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
| `tip` | `string \| VNode \| Signal` | `-` | Tooltip content to display on hover |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `VNode` | `-` | Element to attach the tooltip to |
| Prop | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `tip` | `string \| VNode \| Signal` | `-` | Tooltip content to display on hover |
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
| `children` | `VNode` | `-` | Element to attach the tooltip to |
## Styling
Tooltip supports all **daisyUI Tooltip classes**:
| Category | Keywords | Description |
| :--- | :--- | :--- |
| Position | `tooltip-top` (default), `tooltip-bottom`, `tooltip-left`, `tooltip-right` | Tooltip position |
| Color | `tooltip-primary`, `tooltip-secondary`, `tooltip-accent`, `tooltip-info`, `tooltip-success`, `tooltip-warning`, `tooltip-error` | Tooltip color variants |
> For further details, check the [daisyUI Tooltip Documentation](https://daisyui.com/components/tooltip) Full reference for CSS classes.
### Example
```javascript
Tooltip({ tip: "This is a tooltip", class: "tooltip-primary" }, [
Button({ class: "btn" }, "Hover me")
]);
```
## Live Examples
@@ -35,7 +54,7 @@ const BasicDemo = () => {
Span({ class: 'text-sm cursor-help border-b border-dashed' }, 'Help text')
]),
Tooltip({ tip: 'Icons can also have tooltips' }, [
Icons.iconInfo
Span({ class: 'text-2xl' }, '')
])
]);
};
@@ -154,7 +173,6 @@ $mount(FormDemo, '#demo-form');
```javascript
const InteractiveDemo = () => {
const showTip = $(false);
const tooltipText = $('Hover over the button!');
const updateTooltip = (text) => {
@@ -211,7 +229,7 @@ const RichDemo = () => {
Tooltip({
tip: Div({ class: 'text-left p-1' }, [
Div({ class: 'font-bold flex items-center gap-1' }, [
Icons.iconWarning,
Span({}, '⚠️'),
Span({}, 'System Status')
]),
Div({ class: 'text-xs' }, 'All systems operational'),
@@ -303,252 +321,4 @@ const AllPositionsDemo = () => {
]);
};
$mount(AllPositionsDemo, '#demo-all-positions');
```
<script>
(function() {
const initTooltipExamples = () => {
// 1. Basic Tooltip
const basicTarget = document.querySelector('#demo-basic');
if (basicTarget && !basicTarget.hasChildNodes()) {
const BasicDemo = () => {
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
Tooltip({ tip: 'This is a tooltip' }, [
Button({ class: 'btn btn-primary' }, 'Hover me')
]),
Tooltip({ tip: 'Tooltips can be placed on any element' }, [
Span({ class: 'text-sm cursor-help border-b border-dashed' }, 'Help text')
]),
Tooltip({ tip: 'Icons can also have tooltips' }, [
Icons.iconInfo
])
]);
};
$mount(BasicDemo, basicTarget);
}
// 2. Tooltip Positions
const positionsTarget = document.querySelector('#demo-positions');
if (positionsTarget && !positionsTarget.hasChildNodes()) {
const PositionsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Tooltip({ tip: 'Top tooltip', class: 'tooltip-top' }, [
Button({ class: 'btn btn-sm' }, 'Top')
]),
Tooltip({ tip: 'Bottom tooltip', class: 'tooltip-bottom' }, [
Button({ class: 'btn btn-sm' }, 'Bottom')
]),
Tooltip({ tip: 'Left tooltip', class: 'tooltip-left' }, [
Button({ class: 'btn btn-sm' }, 'Left')
]),
Tooltip({ tip: 'Right tooltip', class: 'tooltip-right' }, [
Button({ class: 'btn btn-sm' }, 'Right')
])
]);
};
$mount(PositionsDemo, positionsTarget);
}
// 3. Tooltip with Icons
const iconsTarget = document.querySelector('#demo-icons');
if (iconsTarget && !iconsTarget.hasChildNodes()) {
const IconsDemo = () => {
return Div({ class: 'flex flex-wrap gap-8 justify-center' }, [
Tooltip({ tip: 'Save document' }, [
Button({ class: 'btn btn-ghost btn-circle' }, '💾')
]),
Tooltip({ tip: 'Edit item' }, [
Button({ class: 'btn btn-ghost btn-circle' }, '✏️')
]),
Tooltip({ tip: 'Delete permanently' }, [
Button({ class: 'btn btn-ghost btn-circle text-error' }, '🗑️')
]),
Tooltip({ tip: 'Settings' }, [
Button({ class: 'btn btn-ghost btn-circle' }, '⚙️')
])
]);
};
$mount(IconsDemo, iconsTarget);
}
// 4. Form Field Tooltips
const formTarget = document.querySelector('#demo-form');
if (formTarget && !formTarget.hasChildNodes()) {
const FormDemo = () => {
const username = $('');
const email = $('');
return Div({ class: 'flex flex-col gap-4 max-w-md mx-auto' }, [
Div({ class: 'flex items-center gap-2' }, [
Input({
label: 'Username',
value: username,
placeholder: 'Choose a username',
oninput: (e) => username(e.target.value)
}),
Tooltip({ tip: 'Must be at least 3 characters, letters and numbers only' }, [
Span({ class: 'cursor-help text-info' }, '?')
])
]),
Div({ class: 'flex items-center gap-2' }, [
Input({
label: 'Email',
type: 'email',
value: email,
placeholder: 'Enter your email',
oninput: (e) => email(e.target.value)
}),
Tooltip({ tip: 'We\'ll never share your email with anyone' }, [
Span({ class: 'cursor-help text-info' }, '?')
])
])
]);
};
$mount(FormDemo, formTarget);
}
// 5. Interactive Tooltip
const interactiveTarget = document.querySelector('#demo-interactive');
if (interactiveTarget && !interactiveTarget.hasChildNodes()) {
const InteractiveDemo = () => {
const showTip = $(false);
const tooltipText = $('Hover over the button!');
const updateTooltip = (text) => {
tooltipText(text);
setTimeout(() => {
tooltipText('Hover over the button!');
}, 2000);
};
return Div({ class: 'flex flex-col gap-4 items-center' }, [
Tooltip({ tip: () => tooltipText() }, [
Button({
class: 'btn btn-primary btn-lg',
onclick: () => Toast('Button clicked!', 'alert-info', 2000)
}, 'Interactive Button')
]),
Div({ class: 'flex gap-2 flex-wrap justify-center mt-4' }, [
Button({
class: 'btn btn-xs',
onclick: () => updateTooltip('You clicked the button!')
}, 'Change Tooltip'),
Button({
class: 'btn btn-xs',
onclick: () => updateTooltip('Try hovering now!')
}, 'Change Again')
])
]);
};
$mount(InteractiveDemo, interactiveTarget);
}
// 6. Rich Tooltip Content
const richTarget = document.querySelector('#demo-rich');
if (richTarget && !richTarget.hasChildNodes()) {
const RichDemo = () => {
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
Tooltip({
tip: Div({ class: 'text-left p-1' }, [
Div({ class: 'font-bold' }, 'User Info'),
Div({ class: 'text-xs' }, 'John Doe'),
Div({ class: 'text-xs' }, 'john@example.com'),
Div({ class: 'badge badge-xs badge-primary mt-1' }, 'Admin')
])
}, [
Button({ class: 'btn btn-outline' }, 'User Profile')
]),
Tooltip({
tip: Div({ class: 'text-left p-1' }, [
Div({ class: 'font-bold flex items-center gap-1' }, [
Icons.iconWarning,
Span({}, 'System Status')
]),
Div({ class: 'text-xs' }, 'All systems operational'),
Div({ class: 'text-xs text-success' }, '✓ 99.9% uptime')
])
}, [
Button({ class: 'btn btn-outline' }, 'System Status')
])
]);
};
$mount(RichDemo, richTarget);
}
// 7. Color Variants
const colorsTarget = document.querySelector('#demo-colors');
if (colorsTarget && !colorsTarget.hasChildNodes()) {
const ColorsDemo = () => {
return Div({ class: 'flex flex-wrap gap-4 justify-center' }, [
Tooltip({ tip: 'Primary tooltip', class: 'tooltip-primary' }, [
Button({ class: 'btn btn-primary btn-sm' }, 'Primary')
]),
Tooltip({ tip: 'Secondary tooltip', class: 'tooltip-secondary' }, [
Button({ class: 'btn btn-secondary btn-sm' }, 'Secondary')
]),
Tooltip({ tip: 'Accent tooltip', class: 'tooltip-accent' }, [
Button({ class: 'btn btn-accent btn-sm' }, 'Accent')
]),
Tooltip({ tip: 'Info tooltip', class: 'tooltip-info' }, [
Button({ class: 'btn btn-info btn-sm' }, 'Info')
]),
Tooltip({ tip: 'Success tooltip', class: 'tooltip-success' }, [
Button({ class: 'btn btn-success btn-sm' }, 'Success')
]),
Tooltip({ tip: 'Warning tooltip', class: 'tooltip-warning' }, [
Button({ class: 'btn btn-warning btn-sm' }, 'Warning')
]),
Tooltip({ tip: 'Error tooltip', class: 'tooltip-error' }, [
Button({ class: 'btn btn-error btn-sm' }, 'Error')
])
]);
};
$mount(ColorsDemo, colorsTarget);
}
// 8. All Tooltip Positions
const allPositionsTarget = document.querySelector('#demo-all-positions');
if (allPositionsTarget && !allPositionsTarget.hasChildNodes()) {
const AllPositionsDemo = () => {
return Div({ class: 'grid grid-cols-3 gap-4 justify-items-center' }, [
Div({ class: 'col-start-2' }, [
Tooltip({ tip: 'Top tooltip', class: 'tooltip-top' }, [
Button({ class: 'btn btn-sm w-24' }, 'Top')
])
]),
Div({ class: 'col-start-1 row-start-2' }, [
Tooltip({ tip: 'Left tooltip', class: 'tooltip-left' }, [
Button({ class: 'btn btn-sm w-24' }, 'Left')
])
]),
Div({ class: 'col-start-2 row-start-2' }, [
Tooltip({ tip: 'Center tooltip', class: 'tooltip' }, [
Button({ class: 'btn btn-sm w-24' }, 'Center')
])
]),
Div({ class: 'col-start-3 row-start-2' }, [
Tooltip({ tip: 'Right tooltip', class: 'tooltip-right' }, [
Button({ class: 'btn btn-sm w-24' }, 'Right')
])
]),
Div({ class: 'col-start-2 row-start-3' }, [
Tooltip({ tip: 'Bottom tooltip', class: 'tooltip-bottom' }, [
Button({ class: 'btn btn-sm w-24' }, 'Bottom')
])
])
]);
};
$mount(AllPositionsDemo, allPositionsTarget);
}
};
initTooltipExamples();
if (window.$docsify) {
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
hook.doneEach(initTooltipExamples);
});
}
})();
</script>
```