From a05216b992ed7fd089f2dd8b0c3a376a47fe162b Mon Sep 17 00:00:00 2001 From: natxocc Date: Wed, 1 Apr 2026 00:01:35 +0200 Subject: [PATCH] Docs --- docs/components/alert.md | 521 ++++++++++++++++++++++++++++++++ docs/components/badge.md | 4 +- docs/components/indicator.md | 556 +++++++++++++++++++++++++++++++++++ docs/index.html | 5 +- 4 files changed, 1082 insertions(+), 4 deletions(-) create mode 100644 docs/components/alert.md diff --git a/docs/components/alert.md b/docs/components/alert.md new file mode 100644 index 0000000..ea2dded --- /dev/null +++ b/docs/components/alert.md @@ -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` | `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 + +
+
+

Live Demo

+
+
+
+ +```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 + +
+
+

Live Demo

+
+
+
+ +```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 + +
+
+

Live Demo

+
+
+
+ +```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 + +
+
+

Live Demo

+
+
+
+ +```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 + +
+
+

Live Demo

+
+
+
+ +```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 + +
+
+

Live Demo

+
+
+
+ +```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 + +
+
+

Live Demo

+
+
+
+ +```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 + +
+
+

Live Demo

+
+
+
+ +```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'); +``` + + diff --git a/docs/components/badge.md b/docs/components/badge.md index 3de2637..2738e50 100644 --- a/docs/components/badge.md +++ b/docs/components/badge.md @@ -382,11 +382,11 @@ $mount(InlineDemo, '#demo-inline'); const IconsDemo = () => { return Div({ class: 'flex flex-wrap gap-2' }, [ Badge({ class: 'gap-1' }, [ - Icons.iconSuccess, + Img({src: Icons.iconSuccess}), Span({}, 'Success') ]), Badge({ class: 'gap-1 badge-warning' }, [ - Icons.iconWarning, + Img({src: Icons.iconWarning}), Span({}, 'Warning') ]), Badge({ class: 'gap-1 badge-error' }, [ diff --git a/docs/components/indicator.md b/docs/components/indicator.md index e69de29..6fc95fd 100644 --- a/docs/components/indicator.md +++ b/docs/components/indicator.md @@ -0,0 +1,556 @@ +# Indicator + +Indicator component for adding badges, status markers, or notifications to elements. Perfect for showing counts, online status, or alerts. + +## Tag + +`Indicator` + +## 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 | + +## Live Examples + +### Basic Indicator + +
+
+

Live Demo

+
+
+
+ +```javascript +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, '#demo-basic'); +``` + +### Online Status Indicator + +
+
+

Live Demo

+
+
+
+ +```javascript +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, '#demo-status'); +``` + +### Reactive Counter + +
+
+

Live Demo

+
+
+
+ +```javascript +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, '#demo-reactive'); +``` + +### Shopping Cart + +
+
+

Live Demo

+
+
+
+ +```javascript +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, '#demo-cart'); +``` + +### Email Inbox + +
+
+

Live Demo

+
+
+
+ +```javascript +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, '#demo-inbox'); +``` + +### Custom Position + +
+
+

Live Demo

+
+
+
+ +```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 + +
+
+

Live Demo

+
+
+
+ +```javascript +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, '#demo-variants'); +``` + + diff --git a/docs/index.html b/docs/index.html index 196d9f9..c7775bd 100644 --- a/docs/index.html +++ b/docs/index.html @@ -39,8 +39,9 @@ }; - - + + +