Docs
This commit is contained in:
521
docs/components/alert.md
Normal file
521
docs/components/alert.md
Normal file
@@ -0,0 +1,521 @@
|
||||
# Alert
|
||||
|
||||
Alert component for displaying contextual messages, notifications, and feedback with different severity levels. Supports soft and solid variants.
|
||||
|
||||
## Tag
|
||||
|
||||
`Alert`
|
||||
|
||||
## Props
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
| :----------- | :--------------------------- | :---------- | :----------------------------------------------- |
|
||||
| `type` | `string` | `'info'` | Alert type: 'info', 'success', 'warning', 'error' |
|
||||
| `soft` | `boolean \| Signal<boolean>` | `true` | Use soft variant (subtle background) |
|
||||
| `actions` | `VNode \| function` | `-` | Optional action buttons or content |
|
||||
| `message` | `string \| VNode \| Signal` | `-` | Alert message content |
|
||||
| `class` | `string` | `''` | Additional CSS classes (DaisyUI + Tailwind) |
|
||||
| `children` | `string \| VNode` | `-` | Alert content (alternative to `message`) |
|
||||
|
||||
## Live Examples
|
||||
|
||||
### Basic Alerts
|
||||
|
||||
<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-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const BasicDemo = () => {
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
Alert({ type: 'info', message: 'This is an informational message.' }),
|
||||
Alert({ type: 'success', message: 'Operation completed successfully!' }),
|
||||
Alert({ type: 'warning', message: 'Please review your input before proceeding.' }),
|
||||
Alert({ type: 'error', message: 'An error occurred while processing your request.' })
|
||||
]);
|
||||
};
|
||||
$mount(BasicDemo, '#demo-basic');
|
||||
```
|
||||
|
||||
### Soft vs Solid 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-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const VariantsDemo = () => {
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
Alert({ type: 'info', soft: true, message: 'Soft info alert (default)' }),
|
||||
Alert({ type: 'info', soft: false, message: 'Solid info alert' }),
|
||||
Alert({ type: 'success', soft: true, message: 'Soft success alert' }),
|
||||
Alert({ type: 'success', soft: false, message: 'Solid success alert' })
|
||||
]);
|
||||
};
|
||||
$mount(VariantsDemo, '#demo-variants');
|
||||
```
|
||||
|
||||
### With Actions
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-actions" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const ActionsDemo = () => {
|
||||
const showUndo = $(false);
|
||||
const deletedItem = $(null);
|
||||
|
||||
const deleteItem = (item) => {
|
||||
deletedItem(item);
|
||||
showUndo(true);
|
||||
setTimeout(() => {
|
||||
if (showUndo()) {
|
||||
showUndo(false);
|
||||
Toast('Item permanently deleted', 'alert-info', 2000);
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
const undoDelete = () => {
|
||||
showUndo(false);
|
||||
Toast(`Restored: ${deletedItem()}`, 'alert-success', 2000);
|
||||
};
|
||||
|
||||
return Div({ class: 'flex flex-col gap-4' }, [
|
||||
Div({ class: 'flex gap-2' }, [
|
||||
Button({ class: 'btn btn-sm', onclick: () => deleteItem('Document A') }, 'Delete Document A'),
|
||||
Button({ class: 'btn btn-sm', onclick: () => deleteItem('Document B') }, 'Delete Document B')
|
||||
]),
|
||||
() => showUndo() ? Alert({
|
||||
type: 'warning',
|
||||
soft: true,
|
||||
message: `Deleted: ${deletedItem()}`,
|
||||
actions: Button({
|
||||
class: 'btn btn-sm btn-primary',
|
||||
onclick: undoDelete
|
||||
}, 'Undo')
|
||||
}) : null
|
||||
]);
|
||||
};
|
||||
$mount(ActionsDemo, '#demo-actions');
|
||||
```
|
||||
|
||||
### Dismissible Alert
|
||||
|
||||
<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-dismissible" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const DismissibleDemo = () => {
|
||||
const visible = $(true);
|
||||
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
() => visible() ? Alert({
|
||||
type: 'info',
|
||||
message: 'This alert can be dismissed. Click the X button to close.',
|
||||
actions: Button({
|
||||
class: 'btn btn-xs btn-circle btn-ghost',
|
||||
onclick: () => visible(false)
|
||||
}, '✕')
|
||||
}) : null,
|
||||
() => !visible() ? Button({
|
||||
class: 'btn btn-sm btn-ghost',
|
||||
onclick: () => visible(true)
|
||||
}, 'Show Alert') : null
|
||||
]);
|
||||
};
|
||||
$mount(DismissibleDemo, '#demo-dismissible');
|
||||
```
|
||||
|
||||
### Reactive Alert
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-reactive" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const ReactiveDemo = () => {
|
||||
const email = $('');
|
||||
const error = $('');
|
||||
|
||||
const validateEmail = (value) => {
|
||||
email(value);
|
||||
if (value && !value.includes('@')) {
|
||||
error('Please enter a valid email address');
|
||||
} else {
|
||||
error('');
|
||||
}
|
||||
};
|
||||
|
||||
return Div({ class: 'flex flex-col gap-4' }, [
|
||||
Input({
|
||||
label: 'Email Address',
|
||||
placeholder: 'Enter your email',
|
||||
value: email,
|
||||
oninput: (e) => validateEmail(e.target.value)
|
||||
}),
|
||||
() => error() ? Alert({ type: 'error', message: error() }) : null,
|
||||
() => email() && !error() ? Alert({
|
||||
type: 'success',
|
||||
message: `Valid email: ${email()}`
|
||||
}) : null
|
||||
]);
|
||||
};
|
||||
$mount(ReactiveDemo, '#demo-reactive');
|
||||
```
|
||||
|
||||
### Form Validation
|
||||
|
||||
<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-form" class="bg-base-100 p-6 rounded-xl border border-base-300"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const FormDemo = () => {
|
||||
const name = $('');
|
||||
const email = $('');
|
||||
const submitted = $(false);
|
||||
const errors = $({ name: '', email: '' });
|
||||
|
||||
const validate = () => {
|
||||
const newErrors = {
|
||||
name: name().trim() ? '' : 'Name is required',
|
||||
email: email().trim() ? (email().includes('@') ? '' : 'Invalid email') : 'Email is required'
|
||||
};
|
||||
errors(newErrors);
|
||||
return !newErrors.name && !newErrors.email;
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (validate()) {
|
||||
submitted(true);
|
||||
setTimeout(() => submitted(false), 3000);
|
||||
Toast('Form submitted successfully!', 'alert-success', 2000);
|
||||
}
|
||||
};
|
||||
|
||||
return Div({ class: 'flex flex-col gap-4' }, [
|
||||
Div({ class: 'text-lg font-bold' }, 'Contact Form'),
|
||||
Input({
|
||||
label: 'Name',
|
||||
value: name,
|
||||
error: () => errors().name,
|
||||
oninput: (e) => {
|
||||
name(e.target.value);
|
||||
validate();
|
||||
}
|
||||
}),
|
||||
Input({
|
||||
label: 'Email',
|
||||
value: email,
|
||||
error: () => errors().email,
|
||||
oninput: (e) => {
|
||||
email(e.target.value);
|
||||
validate();
|
||||
}
|
||||
}),
|
||||
Button({ class: 'btn btn-primary', onclick: handleSubmit }, 'Submit'),
|
||||
() => submitted() ? Alert({
|
||||
type: 'success',
|
||||
message: 'Thank you! We will contact you soon.'
|
||||
}) : null,
|
||||
() => (errors().name || errors().email) ? Alert({
|
||||
type: 'error',
|
||||
message: 'Please fix the errors above before submitting.'
|
||||
}) : null
|
||||
]);
|
||||
};
|
||||
$mount(FormDemo, '#demo-form');
|
||||
```
|
||||
|
||||
### Icon Alerts
|
||||
|
||||
<div class="card bg-base-200 border border-base-300 shadow-sm my-6">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title text-sm uppercase opacity-50 mb-4">Live Demo</h3>
|
||||
<div id="demo-icons" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const IconsDemo = () => {
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
Alert({ type: 'info', message: 'Information alert with custom icon' }),
|
||||
Alert({ type: 'success', message: 'Success alert with custom icon' }),
|
||||
Alert({ type: 'warning', message: 'Warning alert with custom icon' }),
|
||||
Alert({ type: 'error', message: 'Error alert with custom icon' })
|
||||
]);
|
||||
};
|
||||
$mount(IconsDemo, '#demo-icons');
|
||||
```
|
||||
|
||||
### All Types
|
||||
|
||||
<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-all" class="bg-base-100 p-6 rounded-xl border border-base-300 flex flex-col gap-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
```javascript
|
||||
const AllTypesDemo = () => {
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
Alert({ type: 'info', message: 'ℹ️ This is an info alert' }),
|
||||
Alert({ type: 'success', message: '✅ This is a success alert' }),
|
||||
Alert({ type: 'warning', message: '⚠️ This is a warning alert' }),
|
||||
Alert({ type: 'error', message: '❌ This is an error alert' })
|
||||
]);
|
||||
};
|
||||
$mount(AllTypesDemo, '#demo-all');
|
||||
```
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const initAlertExamples = () => {
|
||||
|
||||
// 1. Basic Alerts
|
||||
const basicTarget = document.querySelector('#demo-basic');
|
||||
if (basicTarget && !basicTarget.hasChildNodes()) {
|
||||
const BasicDemo = () => {
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
Alert({ type: 'info', message: 'This is an informational message.' }),
|
||||
Alert({ type: 'success', message: 'Operation completed successfully!' }),
|
||||
Alert({ type: 'warning', message: 'Please review your input before proceeding.' }),
|
||||
Alert({ type: 'error', message: 'An error occurred while processing your request.' })
|
||||
]);
|
||||
};
|
||||
$mount(BasicDemo, basicTarget);
|
||||
}
|
||||
|
||||
// 2. Soft vs Solid Variants
|
||||
const variantsTarget = document.querySelector('#demo-variants');
|
||||
if (variantsTarget && !variantsTarget.hasChildNodes()) {
|
||||
const VariantsDemo = () => {
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
Alert({ type: 'info', soft: true, message: 'Soft info alert (default)' }),
|
||||
Alert({ type: 'info', soft: false, message: 'Solid info alert' }),
|
||||
Alert({ type: 'success', soft: true, message: 'Soft success alert' }),
|
||||
Alert({ type: 'success', soft: false, message: 'Solid success alert' })
|
||||
]);
|
||||
};
|
||||
$mount(VariantsDemo, variantsTarget);
|
||||
}
|
||||
|
||||
// 3. With Actions
|
||||
const actionsTarget = document.querySelector('#demo-actions');
|
||||
if (actionsTarget && !actionsTarget.hasChildNodes()) {
|
||||
const ActionsDemo = () => {
|
||||
const showUndo = $(false);
|
||||
const deletedItem = $(null);
|
||||
|
||||
const deleteItem = (item) => {
|
||||
deletedItem(item);
|
||||
showUndo(true);
|
||||
setTimeout(() => {
|
||||
if (showUndo()) {
|
||||
showUndo(false);
|
||||
Toast('Item permanently deleted', 'alert-info', 2000);
|
||||
}
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
const undoDelete = () => {
|
||||
showUndo(false);
|
||||
Toast(`Restored: ${deletedItem()}`, 'alert-success', 2000);
|
||||
};
|
||||
|
||||
return Div({ class: 'flex flex-col gap-4' }, [
|
||||
Div({ class: 'flex gap-2' }, [
|
||||
Button({ class: 'btn btn-sm', onclick: () => deleteItem('Document A') }, 'Delete Document A'),
|
||||
Button({ class: 'btn btn-sm', onclick: () => deleteItem('Document B') }, 'Delete Document B')
|
||||
]),
|
||||
() => showUndo() ? Alert({
|
||||
type: 'warning',
|
||||
soft: true,
|
||||
message: `Deleted: ${deletedItem()}`,
|
||||
actions: Button({
|
||||
class: 'btn btn-sm btn-primary',
|
||||
onclick: undoDelete
|
||||
}, 'Undo')
|
||||
}) : null
|
||||
]);
|
||||
};
|
||||
$mount(ActionsDemo, actionsTarget);
|
||||
}
|
||||
|
||||
// 4. Dismissible Alert
|
||||
const dismissibleTarget = document.querySelector('#demo-dismissible');
|
||||
if (dismissibleTarget && !dismissibleTarget.hasChildNodes()) {
|
||||
const DismissibleDemo = () => {
|
||||
const visible = $(true);
|
||||
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
() => visible() ? Alert({
|
||||
type: 'info',
|
||||
message: 'This alert can be dismissed. Click the X button to close.',
|
||||
actions: Button({
|
||||
class: 'btn btn-xs btn-circle btn-ghost',
|
||||
onclick: () => visible(false)
|
||||
}, '✕')
|
||||
}) : null,
|
||||
() => !visible() ? Button({
|
||||
class: 'btn btn-sm btn-ghost',
|
||||
onclick: () => visible(true)
|
||||
}, 'Show Alert') : null
|
||||
]);
|
||||
};
|
||||
$mount(DismissibleDemo, dismissibleTarget);
|
||||
}
|
||||
|
||||
// 5. Reactive Alert
|
||||
const reactiveTarget = document.querySelector('#demo-reactive');
|
||||
if (reactiveTarget && !reactiveTarget.hasChildNodes()) {
|
||||
const ReactiveDemo = () => {
|
||||
const email = $('');
|
||||
const error = $('');
|
||||
|
||||
const validateEmail = (value) => {
|
||||
email(value);
|
||||
if (value && !value.includes('@')) {
|
||||
error('Please enter a valid email address');
|
||||
} else {
|
||||
error('');
|
||||
}
|
||||
};
|
||||
|
||||
return Div({ class: 'flex flex-col gap-4' }, [
|
||||
Input({
|
||||
label: 'Email Address',
|
||||
placeholder: 'Enter your email',
|
||||
value: email,
|
||||
oninput: (e) => validateEmail(e.target.value)
|
||||
}),
|
||||
() => error() ? Alert({ type: 'error', message: error() }) : null,
|
||||
() => email() && !error() ? Alert({
|
||||
type: 'success',
|
||||
message: `Valid email: ${email()}`
|
||||
}) : null
|
||||
]);
|
||||
};
|
||||
$mount(ReactiveDemo, reactiveTarget);
|
||||
}
|
||||
|
||||
// 6. Form Validation
|
||||
const formTarget = document.querySelector('#demo-form');
|
||||
if (formTarget && !formTarget.hasChildNodes()) {
|
||||
const FormDemo = () => {
|
||||
const name = $('');
|
||||
const email = $('');
|
||||
const submitted = $(false);
|
||||
const errors = $({ name: '', email: '' });
|
||||
|
||||
const validate = () => {
|
||||
const newErrors = {
|
||||
name: name().trim() ? '' : 'Name is required',
|
||||
email: email().trim() ? (email().includes('@') ? '' : 'Invalid email') : 'Email is required'
|
||||
};
|
||||
errors(newErrors);
|
||||
return !newErrors.name && !newErrors.email;
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (validate()) {
|
||||
submitted(true);
|
||||
setTimeout(() => submitted(false), 3000);
|
||||
Toast('Form submitted successfully!', 'alert-success', 2000);
|
||||
}
|
||||
};
|
||||
|
||||
return Div({ class: 'flex flex-col gap-4' }, [
|
||||
Div({ class: 'text-lg font-bold' }, 'Contact Form'),
|
||||
Input({
|
||||
label: 'Name',
|
||||
value: name,
|
||||
error: () => errors().name,
|
||||
oninput: (e) => {
|
||||
name(e.target.value);
|
||||
validate();
|
||||
}
|
||||
}),
|
||||
Input({
|
||||
label: 'Email',
|
||||
value: email,
|
||||
error: () => errors().email,
|
||||
oninput: (e) => {
|
||||
email(e.target.value);
|
||||
validate();
|
||||
}
|
||||
}),
|
||||
Button({ class: 'btn btn-primary', onclick: handleSubmit }, 'Submit'),
|
||||
() => submitted() ? Alert({
|
||||
type: 'success',
|
||||
message: 'Thank you! We will contact you soon.'
|
||||
}) : null,
|
||||
() => (errors().name || errors().email) ? Alert({
|
||||
type: 'error',
|
||||
message: 'Please fix the errors above before submitting.'
|
||||
}) : null
|
||||
]);
|
||||
};
|
||||
$mount(FormDemo, formTarget);
|
||||
}
|
||||
|
||||
// 7. Icon Alerts
|
||||
const iconsTarget = document.querySelector('#demo-icons');
|
||||
if (iconsTarget && !iconsTarget.hasChildNodes()) {
|
||||
const IconsDemo = () => {
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
Alert({ type: 'info', message: 'Information alert with custom icon' }),
|
||||
Alert({ type: 'success', message: 'Success alert with custom icon' }),
|
||||
Alert({ type: 'warning', message: 'Warning alert with custom icon' }),
|
||||
Alert({ type: 'error', message: 'Error alert with custom icon' })
|
||||
]);
|
||||
};
|
||||
$mount(IconsDemo, iconsTarget);
|
||||
}
|
||||
|
||||
// 8. All Types
|
||||
const allTarget = document.querySelector('#demo-all');
|
||||
if (allTarget && !allTarget.hasChildNodes()) {
|
||||
const AllTypesDemo = () => {
|
||||
return Div({ class: 'flex flex-col gap-3' }, [
|
||||
Alert({ type: 'info', message: 'ℹ️ This is an info alert' }),
|
||||
Alert({ type: 'success', message: '✅ This is a success alert' }),
|
||||
Alert({ type: 'warning', message: '⚠️ This is a warning alert' }),
|
||||
Alert({ type: 'error', message: '❌ This is an error alert' })
|
||||
]);
|
||||
};
|
||||
$mount(AllTypesDemo, allTarget);
|
||||
}
|
||||
};
|
||||
|
||||
initAlertExamples();
|
||||
|
||||
if (window.$docsify) {
|
||||
window.$docsify.plugins = [].concat(window.$docsify.plugins || [], (hook) => {
|
||||
hook.doneEach(initAlertExamples);
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
Reference in New Issue
Block a user