Some checks failed
Deploy Application / deploy (push) Has been cancelled
1050 lines
26 KiB
Markdown
1050 lines
26 KiB
Markdown
# LiveComponents UI Integration Guide
|
|
|
|
**Complete Guide to UI Features: Tooltips, Loading States, Dialogs, and Notifications**
|
|
|
|
This guide covers the integrated UI features available in LiveComponents, including tooltips, skeleton loading, dialogs, modals, notifications, and loading states.
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Tooltip System](#tooltip-system)
|
|
2. [Loading States & Skeleton Loading](#loading-states--skeleton-loading)
|
|
3. [UI Helper System](#ui-helper-system)
|
|
4. [Event-Based UI Integration](#event-based-ui-integration)
|
|
5. [Notification Component](#notification-component)
|
|
6. [Dialog & Modal Integration](#dialog--modal-integration)
|
|
7. [Best Practices](#best-practices)
|
|
|
|
---
|
|
|
|
## Tooltip System
|
|
|
|
### Overview
|
|
|
|
The Tooltip System provides automatic tooltip initialization and management for LiveComponent elements. Tooltips are automatically initialized when components are mounted and cleaned up when components are destroyed.
|
|
|
|
**Features**:
|
|
- Automatic initialization for `data-tooltip` attributes
|
|
- Accessibility support (ARIA attributes)
|
|
- Smart positioning (viewport-aware)
|
|
- Validation error tooltips
|
|
- Automatic cleanup
|
|
|
|
### Basic Usage
|
|
|
|
```html
|
|
<!-- Simple tooltip -->
|
|
<button
|
|
data-lc-action="save"
|
|
data-tooltip="Save your changes"
|
|
>
|
|
Save
|
|
</button>
|
|
|
|
<!-- Tooltip with validation error -->
|
|
<input
|
|
type="email"
|
|
data-lc-action="validateEmail"
|
|
data-tooltip="Enter a valid email address"
|
|
data-tooltip-error="Invalid email format"
|
|
/>
|
|
```
|
|
|
|
### Server-Side Validation Tooltips
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\Attributes\Action;
|
|
use App\Framework\LiveComponents\ValueObjects\LiveComponentError;
|
|
|
|
final class UserForm extends LiveComponent
|
|
{
|
|
#[Action]
|
|
public function validateEmail(string $email): void
|
|
{
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
|
// Error will automatically show tooltip if element has data-tooltip-error
|
|
throw LiveComponentError::validation(
|
|
'Invalid email format',
|
|
['field' => 'email'],
|
|
$this->id->toString(),
|
|
'validateEmail'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Custom Tooltip Configuration
|
|
|
|
```javascript
|
|
// Configure tooltip behavior globally
|
|
import { tooltipManager } from './modules/livecomponent/TooltipManager.js';
|
|
|
|
// Adjust delays
|
|
tooltipManager.tooltipDelay = 500; // Show after 500ms
|
|
tooltipManager.hideDelay = 200; // Hide after 200ms
|
|
```
|
|
|
|
### Tooltip Events
|
|
|
|
```javascript
|
|
// Listen for tooltip events
|
|
window.addEventListener('livecomponent:tooltip-shown', (e) => {
|
|
const { element, tooltipText } = e.detail;
|
|
console.log(`Tooltip shown: ${tooltipText}`);
|
|
});
|
|
|
|
window.addEventListener('livecomponent:tooltip-hidden', (e) => {
|
|
const { element } = e.detail;
|
|
console.log('Tooltip hidden');
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Loading States & Skeleton Loading
|
|
|
|
### Overview
|
|
|
|
The Loading State System provides configurable loading indicators during LiveComponent actions, including skeleton loaders, spinners, and progress indicators.
|
|
|
|
**Features**:
|
|
- Fragment-specific loading
|
|
- Configurable loading types (skeleton, spinner, progress, none)
|
|
- Smooth transitions
|
|
- Optimistic UI integration (no loading for optimistic actions)
|
|
- Per-component configuration
|
|
|
|
### Basic Usage
|
|
|
|
```html
|
|
<!-- Component with skeleton loading -->
|
|
<div
|
|
data-live-component="product-list"
|
|
data-loading-type="skeleton"
|
|
data-loading-fragments="product-list"
|
|
>
|
|
<div data-lc-fragment="product-list">
|
|
<!-- Skeleton template will be shown during loading -->
|
|
<div class="skeleton-item">
|
|
<div class="skeleton-image"></div>
|
|
<div class="skeleton-text"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
### Server-Side Loading Configuration
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\Attributes\Action;
|
|
use App\Framework\LiveComponents\Attributes\Loading;
|
|
|
|
final class ProductList extends LiveComponent
|
|
{
|
|
#[Action]
|
|
#[Loading(type: 'skeleton', fragments: ['product-list'], showDelay: 150)]
|
|
public function loadProducts(string $category): void
|
|
{
|
|
$this->products = $this->productService->getByCategory($category);
|
|
}
|
|
|
|
#[Action]
|
|
#[Loading(type: 'spinner', showDelay: 0)] // Show immediately
|
|
public function quickAction(): void
|
|
{
|
|
// Fast action with immediate spinner
|
|
}
|
|
}
|
|
```
|
|
|
|
### Loading Types
|
|
|
|
#### 1. Skeleton Loading
|
|
|
|
```html
|
|
<!-- Skeleton template in component -->
|
|
<div data-lc-fragment="content">
|
|
<!-- Default content -->
|
|
<div class="product-card">
|
|
<img src="{product.image}" />
|
|
<h3>{product.name}</h3>
|
|
</div>
|
|
|
|
<!-- Skeleton template (shown during loading) -->
|
|
<template data-skeleton-template>
|
|
<div class="product-card skeleton">
|
|
<div class="skeleton-image"></div>
|
|
<div class="skeleton-text"></div>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
```
|
|
|
|
#### 2. Spinner Loading
|
|
|
|
```html
|
|
<!-- Automatic spinner overlay -->
|
|
<div
|
|
data-live-component="component-id"
|
|
data-loading-type="spinner"
|
|
>
|
|
<!-- Spinner automatically appears during actions -->
|
|
</div>
|
|
```
|
|
|
|
#### 3. Progress Loading
|
|
|
|
```html
|
|
<!-- Progress bar for long-running actions -->
|
|
<div
|
|
data-live-component="upload-component"
|
|
data-loading-type="progress"
|
|
>
|
|
<div class="progress-bar" data-progress="0"></div>
|
|
</div>
|
|
```
|
|
|
|
### Custom Loading Configuration
|
|
|
|
```javascript
|
|
// Configure loading behavior per component
|
|
const component = LiveComponentManager.getInstance().getComponent('component-id');
|
|
|
|
component.setLoadingConfig({
|
|
type: 'skeleton',
|
|
fragments: ['content', 'sidebar'],
|
|
showDelay: 150,
|
|
hideDelay: 100
|
|
});
|
|
```
|
|
|
|
### Loading Events
|
|
|
|
```javascript
|
|
// Listen for loading state changes
|
|
window.addEventListener('livecomponent:loading-started', (e) => {
|
|
const { componentId, type, fragments } = e.detail;
|
|
console.log(`Loading started: ${componentId} (${type})`);
|
|
});
|
|
|
|
window.addEventListener('livecomponent:loading-finished', (e) => {
|
|
const { componentId, duration } = e.detail;
|
|
console.log(`Loading finished: ${componentId} (${duration}ms)`);
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## UI Helper System
|
|
|
|
### Overview
|
|
|
|
The UI Helper System provides a standardized way for LiveComponents to interact with common UI elements like dialogs, modals, and notifications.
|
|
|
|
**Features**:
|
|
- Unified API for UI components
|
|
- Integration with UIManager
|
|
- Promise-based API
|
|
- Automatic cleanup
|
|
- Component-scoped UI elements
|
|
|
|
### Dialog & Modal Helpers
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\Attributes\Action;
|
|
|
|
final class UserManagement extends LiveComponent
|
|
{
|
|
#[Action]
|
|
public function showDeleteConfirm(int $userId): void
|
|
{
|
|
// Show confirmation dialog via UI Helper
|
|
$this->uiHelper->showDialog(
|
|
title: 'Delete User',
|
|
message: 'Are you sure you want to delete this user?',
|
|
buttons: [
|
|
['label' => 'Cancel', 'action' => 'cancel'],
|
|
['label' => 'Delete', 'action' => 'confirm', 'variant' => 'danger']
|
|
]
|
|
)->then(function($action) use ($userId) {
|
|
if ($action === 'confirm') {
|
|
$this->deleteUser($userId);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
### JavaScript API
|
|
|
|
```javascript
|
|
import { LiveComponentUIHelper } from './modules/livecomponent/LiveComponentUIHelper.js';
|
|
|
|
const uiHelper = new LiveComponentUIHelper(liveComponentManager);
|
|
|
|
// Show modal
|
|
const action = await uiHelper.showModal('component-id', {
|
|
title: 'Confirm Action',
|
|
content: '<p>Are you sure?</p>',
|
|
size: 'medium',
|
|
buttons: [
|
|
{ label: 'Cancel', action: 'cancel' },
|
|
{ label: 'Confirm', action: 'confirm', variant: 'primary' }
|
|
]
|
|
});
|
|
|
|
if (action === 'confirm') {
|
|
// Handle confirmation
|
|
}
|
|
|
|
// Show alert
|
|
await uiHelper.showAlert('component-id', {
|
|
title: 'Success',
|
|
message: 'Operation completed successfully',
|
|
type: 'success'
|
|
});
|
|
|
|
// Show confirm dialog
|
|
const confirmed = await uiHelper.showConfirm('component-id', {
|
|
title: 'Delete Item',
|
|
message: 'This action cannot be undone.',
|
|
confirmText: 'Delete',
|
|
cancelText: 'Cancel'
|
|
});
|
|
```
|
|
|
|
### Notification Helpers
|
|
|
|
```javascript
|
|
// Show notification
|
|
uiHelper.showNotification('component-id', 'Operation successful', 'success');
|
|
|
|
// Show error notification with retry
|
|
uiHelper.showErrorNotification(
|
|
'component-id',
|
|
'Upload failed. Please try again.',
|
|
'error',
|
|
true, // Can retry
|
|
() => {
|
|
// Retry logic
|
|
retryUpload();
|
|
}
|
|
);
|
|
|
|
// Hide notification
|
|
uiHelper.hideNotification('component-id');
|
|
```
|
|
|
|
---
|
|
|
|
## Event-Based UI Integration
|
|
|
|
### Overview
|
|
|
|
The Event-Based UI Integration system allows LiveComponents to trigger UI components (Toasts, Modals) by dispatching events from PHP actions. The JavaScript `UIEventHandler` automatically listens to these events and displays the appropriate UI components.
|
|
|
|
**Features**:
|
|
- Automatic UI component display from PHP events
|
|
- Type-safe event payloads
|
|
- Toast queue management
|
|
- Modal stack management
|
|
- Zero JavaScript code required in components
|
|
|
|
### Architecture
|
|
|
|
- **PHP Side**: Components dispatch events via `ComponentEventDispatcher`
|
|
- **JavaScript Side**: `UIEventHandler` listens to events and displays UI components
|
|
- **Integration**: Events are automatically dispatched after action execution
|
|
|
|
### Basic Usage with UIHelper
|
|
|
|
The `UIHelper` class provides convenient methods for dispatching UI events:
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Application\LiveComponents\Product;
|
|
|
|
use App\Framework\LiveComponents\Attributes\Action;
|
|
use App\Framework\LiveComponents\Attributes\LiveComponent;
|
|
use App\Framework\LiveComponents\ComponentEventDispatcher;
|
|
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
|
|
use App\Framework\LiveComponents\UI\UIHelper;
|
|
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
|
|
|
#[LiveComponent('product-form')]
|
|
final readonly class ProductFormComponent implements LiveComponentContract
|
|
{
|
|
public function __construct(
|
|
public ComponentId $id,
|
|
public ProductFormState $state
|
|
) {
|
|
}
|
|
|
|
#[Action]
|
|
public function save(?ComponentEventDispatcher $events = null): ProductFormState
|
|
{
|
|
// ... save logic ...
|
|
|
|
// Show success toast
|
|
(new UIHelper($events))->successToast('Product saved successfully!');
|
|
|
|
return $this->state->withSaved();
|
|
}
|
|
|
|
#[Action]
|
|
public function delete(int $productId, ?ComponentEventDispatcher $events = null): ProductFormState
|
|
{
|
|
// Show confirmation dialog with action
|
|
(new UIHelper($events))->confirmDelete(
|
|
$this->id,
|
|
"product #{$productId}",
|
|
'doDelete',
|
|
['id' => $productId]
|
|
);
|
|
|
|
return $this->state;
|
|
}
|
|
|
|
#[Action]
|
|
public function doDelete(int $id): ProductFormState
|
|
{
|
|
// This action is automatically called when user confirms
|
|
// ... delete logic ...
|
|
return $this->state->withoutProduct($id);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Toast Events
|
|
|
|
#### Show Toast
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\UI\UIHelper;
|
|
|
|
#[Action]
|
|
public function save(?ComponentEventDispatcher $events = null): State
|
|
{
|
|
// Basic success toast
|
|
(new UIHelper($events))->successToast('Saved successfully!');
|
|
|
|
// Toast with custom duration
|
|
(new UIHelper($events))->infoToast('File uploaded', 7000);
|
|
|
|
// Using fluent interface for multiple toasts
|
|
(new UIHelper($events))
|
|
->infoToast('Processing...')
|
|
->successToast('Done!');
|
|
|
|
return $this->state;
|
|
}
|
|
```
|
|
|
|
**Toast Types**:
|
|
- `info` - Blue, informational message
|
|
- `success` - Green, success message
|
|
- `warning` - Orange, warning message
|
|
- `error` - Red, error message
|
|
|
|
**Positions**:
|
|
- `top-right` (default)
|
|
- `top-left`
|
|
- `bottom-right`
|
|
- `bottom-left`
|
|
|
|
#### Hide Toast
|
|
|
|
```php
|
|
#[Action]
|
|
public function dismissNotification(?ComponentEventDispatcher $events = null): State
|
|
{
|
|
$this->hideToast($events, 'global');
|
|
return $this->state;
|
|
}
|
|
```
|
|
|
|
### Modal Events
|
|
|
|
#### Show Modal
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\UI\UIHelper;
|
|
use App\Framework\LiveComponents\Events\UI\Options\ModalOptions;
|
|
use App\Framework\LiveComponents\Events\UI\Enums\ModalSize;
|
|
|
|
#[Action]
|
|
public function showEditForm(int $userId, ?ComponentEventDispatcher $events = null): State
|
|
{
|
|
$formHtml = $this->renderEditForm($userId);
|
|
|
|
(new UIHelper($events))->modal(
|
|
$this->id,
|
|
'Edit User',
|
|
$formHtml,
|
|
ModalOptions::create()
|
|
->withSize(ModalSize::Large)
|
|
->withButtons([
|
|
['text' => 'Save', 'class' => 'btn-primary', 'action' => 'save'],
|
|
['text' => 'Cancel', 'class' => 'btn-secondary', 'action' => 'close']
|
|
])
|
|
);
|
|
|
|
return $this->state;
|
|
}
|
|
```
|
|
|
|
#### Show Confirmation Dialog
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\UI\UIHelper;
|
|
|
|
#[Action]
|
|
public function requestDelete(int $id, ?ComponentEventDispatcher $events = null): State
|
|
{
|
|
(new UIHelper($events))->confirmDelete(
|
|
$this->id,
|
|
"item #{$id}",
|
|
'doDelete',
|
|
['id' => $id]
|
|
);
|
|
|
|
return $this->state;
|
|
}
|
|
```
|
|
|
|
**Note**: Confirmation dialogs return a result via the `modal:confirm:result` event. You can listen to this event in JavaScript to handle the confirmation:
|
|
|
|
```javascript
|
|
document.addEventListener('modal:confirm:result', (e) => {
|
|
const { componentId, confirmed } = e.detail;
|
|
if (confirmed) {
|
|
// User confirmed - execute action
|
|
LiveComponentManager.getInstance()
|
|
.executeAction(componentId, 'confirmDelete', { id: 123 });
|
|
}
|
|
});
|
|
```
|
|
|
|
#### Show Alert Dialog
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\UI\UIHelper;
|
|
|
|
#[Action]
|
|
public function showError(?ComponentEventDispatcher $events = null): State
|
|
{
|
|
(new UIHelper($events))->alertError(
|
|
$this->id,
|
|
'Error',
|
|
'An error occurred while processing your request.'
|
|
);
|
|
|
|
return $this->state;
|
|
}
|
|
```
|
|
|
|
#### Close Modal
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\UI\UIHelper;
|
|
|
|
#[Action]
|
|
public function closeModal(?ComponentEventDispatcher $events = null): State
|
|
{
|
|
(new UIHelper($events))->closeModal($this->id);
|
|
return $this->state;
|
|
}
|
|
```
|
|
|
|
### Manual Event Dispatching
|
|
|
|
If you prefer to dispatch events manually (without UIHelper):
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\ComponentEventDispatcher;
|
|
use App\Framework\LiveComponents\Events\UI\ToastShowEvent;
|
|
|
|
#[Action]
|
|
public function save(?ComponentEventDispatcher $events = null): State
|
|
{
|
|
// ... save logic ...
|
|
|
|
if ($events !== null) {
|
|
// Option 1: Using Event classes (recommended)
|
|
$events->dispatchEvent(ToastShowEvent::success('Saved successfully!'));
|
|
|
|
// Option 2: Direct instantiation
|
|
$events->dispatchEvent(new ToastShowEvent(
|
|
message: 'Saved successfully!',
|
|
type: 'success',
|
|
duration: 5000
|
|
));
|
|
}
|
|
|
|
return $this->state;
|
|
}
|
|
```
|
|
|
|
### JavaScript Event Handling
|
|
|
|
The `UIEventHandler` automatically listens to these events and displays UI components. You can also listen to events manually:
|
|
|
|
```javascript
|
|
// Listen to toast events
|
|
document.addEventListener('toast:show', (e) => {
|
|
const { message, type, duration, position } = e.detail;
|
|
console.log(`Toast: ${message} (${type})`);
|
|
});
|
|
|
|
// Listen to modal events
|
|
document.addEventListener('modal:show', (e) => {
|
|
const { componentId, title, content } = e.detail;
|
|
console.log(`Modal shown: ${title}`);
|
|
});
|
|
|
|
// Listen to confirmation results
|
|
document.addEventListener('modal:confirm:result', (e) => {
|
|
const { componentId, confirmed } = e.detail;
|
|
if (confirmed) {
|
|
// Handle confirmation
|
|
}
|
|
});
|
|
```
|
|
|
|
### Toast Queue Management
|
|
|
|
The `ToastQueue` automatically manages multiple toasts:
|
|
- Maximum 5 toasts per position (configurable)
|
|
- Automatic stacking with spacing
|
|
- Oldest toast removed when limit reached
|
|
- Auto-dismiss with queue management
|
|
|
|
### Modal Stack Management
|
|
|
|
The `ModalManager` handles modal stacking using native `<dialog>` element:
|
|
- **Native `<dialog>` element**: Uses `showModal()` for true modal behavior
|
|
- **Automatic backdrop**: Native `::backdrop` pseudo-element
|
|
- **Automatic focus management**: Browser handles focus trapping
|
|
- **Automatic ESC handling**: Native `cancel` event
|
|
- **Z-index management**: Manual stacking for nested modals
|
|
- **Stack tracking**: Manages multiple open modals
|
|
|
|
#### Native `<dialog>` Benefits
|
|
|
|
The `<dialog>` element provides:
|
|
- **True modal behavior**: Blocks background interaction via `showModal()`
|
|
- **Native backdrop**: Automatic overlay with `::backdrop` pseudo-element
|
|
- **Automatic focus trapping**: Browser handles focus management
|
|
- **Automatic ESC handling**: Native `cancel` event
|
|
- **Better accessibility**: Native ARIA attributes and semantics
|
|
- **Better performance**: Native browser implementation
|
|
- **Wide browser support**: Chrome 37+, Firefox 98+, Safari 15.4+
|
|
|
|
The system uses native `<dialog>` features for optimal modal behavior and accessibility.
|
|
|
|
### Best Practices
|
|
|
|
1. **Use UIHelper**: Prefer the UIHelper class over manual event dispatching for type safety and convenience
|
|
2. **Component IDs**: Always provide component IDs for modals to enable proper management
|
|
3. **Toast Duration**: Use appropriate durations (5s for success, longer for errors)
|
|
4. **Modal Sizes**: Choose appropriate sizes (small for alerts, large for forms)
|
|
5. **Error Handling**: Always show user-friendly error messages
|
|
|
|
```php
|
|
// Good: Clear, user-friendly toast
|
|
(new UIHelper($events))->successToast('Product saved successfully!');
|
|
|
|
// Bad: Technical error message
|
|
(new UIHelper($events))->errorToast('SQL Error: INSERT failed');
|
|
```
|
|
|
|
---
|
|
|
|
## Notification Component
|
|
|
|
### Overview
|
|
|
|
The NotificationComponent is a full-featured LiveComponent for displaying toast notifications with support for different types, positions, durations, and action buttons.
|
|
|
|
**Features**:
|
|
- Type-safe state management
|
|
- Multiple notification types (info, success, warning, error)
|
|
- Configurable positions (top-right, top-left, bottom-right, bottom-left)
|
|
- Auto-dismiss with duration
|
|
- Action buttons
|
|
- Icon support
|
|
|
|
### Basic Usage
|
|
|
|
```php
|
|
use App\Application\LiveComponents\Notification\NotificationComponent;
|
|
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
|
|
|
final class ProductController
|
|
{
|
|
public function create(): ViewResult
|
|
{
|
|
$notification = NotificationComponent::mount(
|
|
ComponentId::generate('notification'),
|
|
message: 'Product created successfully',
|
|
type: 'success',
|
|
duration: 5000
|
|
);
|
|
|
|
return new ViewResult('product/index', [
|
|
'notification' => $notification
|
|
]);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Template Integration
|
|
|
|
```html
|
|
<!-- Include notification component in layout -->
|
|
{notification}
|
|
|
|
<!-- Or use in component template -->
|
|
<div data-live-component="{notification.id}">
|
|
<!-- Notification will render here -->
|
|
</div>
|
|
```
|
|
|
|
### Server-Side Actions
|
|
|
|
```php
|
|
final class NotificationExample extends LiveComponent
|
|
{
|
|
#[Action]
|
|
public function showSuccess(): NotificationState
|
|
{
|
|
return NotificationState::empty()
|
|
->withMessage('Operation successful!', 'success')
|
|
->show();
|
|
}
|
|
|
|
#[Action]
|
|
public function showError(string $message): NotificationState
|
|
{
|
|
return NotificationState::empty()
|
|
->withMessage($message, 'error')
|
|
->show();
|
|
}
|
|
|
|
#[Action]
|
|
public function showWithAction(): NotificationState
|
|
{
|
|
return new NotificationState(
|
|
message: 'File uploaded successfully',
|
|
type: 'success',
|
|
isVisible: true,
|
|
actionText: 'View',
|
|
actionUrl: '/files'
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Client-Side API
|
|
|
|
```javascript
|
|
// Show notification via LiveComponent action
|
|
liveComponentManager.executeAction('notification-id', 'showNotification', {
|
|
message: 'Operation successful',
|
|
type: 'success',
|
|
duration: 5000
|
|
});
|
|
|
|
// Hide notification
|
|
liveComponentManager.executeAction('notification-id', 'hide');
|
|
```
|
|
|
|
### Notification Types
|
|
|
|
```php
|
|
// Info notification
|
|
$notification = NotificationState::empty()
|
|
->withMessage('New update available', 'info')
|
|
->show();
|
|
|
|
// Success notification
|
|
$notification = NotificationState::empty()
|
|
->withMessage('Changes saved', 'success')
|
|
->show();
|
|
|
|
// Warning notification
|
|
$notification = NotificationState::empty()
|
|
->withMessage('Low disk space', 'warning')
|
|
->show();
|
|
|
|
// Error notification
|
|
$notification = NotificationState::empty()
|
|
->withMessage('Upload failed', 'error')
|
|
->show();
|
|
```
|
|
|
|
### Notification Positions
|
|
|
|
```php
|
|
$notification = new NotificationState(
|
|
message: 'Notification message',
|
|
type: 'info',
|
|
position: 'top-right', // or 'top-left', 'bottom-right', 'bottom-left'
|
|
isVisible: true
|
|
);
|
|
```
|
|
|
|
### Notification with Action Button
|
|
|
|
```php
|
|
$notification = new NotificationState(
|
|
message: 'File ready for download',
|
|
type: 'success',
|
|
isVisible: true,
|
|
actionText: 'Download',
|
|
actionUrl: '/download/file.pdf'
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Dialog & Modal Integration
|
|
|
|
### Overview
|
|
|
|
LiveComponents integrate seamlessly with the UIManager for dialogs and modals, providing a consistent API across the application.
|
|
|
|
### Basic Modal Usage
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\Attributes\Action;
|
|
|
|
final class UserSettings extends LiveComponent
|
|
{
|
|
#[Action]
|
|
public function showEditModal(int $userId): void
|
|
{
|
|
// Modal will be shown via UI Helper
|
|
$this->uiHelper->showModal(
|
|
title: 'Edit User',
|
|
content: $this->renderEditForm($userId),
|
|
size: 'large',
|
|
buttons: [
|
|
['label' => 'Save', 'action' => 'save', 'variant' => 'primary'],
|
|
['label' => 'Cancel', 'action' => 'cancel']
|
|
]
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Modal with LiveComponent Content
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\UI\UIHelper;
|
|
use App\Framework\LiveComponents\Events\UI\Options\ModalOptions;
|
|
use App\Framework\LiveComponents\Events\UI\Enums\ModalSize;
|
|
|
|
#[Action]
|
|
public function showUserModal(int $userId, ?ComponentEventDispatcher $events = null): void
|
|
{
|
|
$userComponent = UserDetailsComponent::mount(
|
|
ComponentId::generate('user-details'),
|
|
userId: $userId
|
|
);
|
|
|
|
(new UIHelper($events))->modal(
|
|
$this->id,
|
|
'User Details',
|
|
$userComponent->render(),
|
|
ModalOptions::create()->withSize(ModalSize::Medium)
|
|
);
|
|
}
|
|
```
|
|
|
|
### Modal Events
|
|
|
|
```javascript
|
|
// Listen for modal events
|
|
window.addEventListener('livecomponent:modal-opened', (e) => {
|
|
const { componentId, modalInstance } = e.detail;
|
|
console.log(`Modal opened for component: ${componentId}`);
|
|
});
|
|
|
|
window.addEventListener('livecomponent:modal-closed', (e) => {
|
|
const { componentId, action } = e.detail;
|
|
console.log(`Modal closed with action: ${action}`);
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### 1. Tooltip Usage
|
|
|
|
- **Do**: Use tooltips for helpful context and validation errors
|
|
- **Don't**: Overuse tooltips - they can be distracting
|
|
- **Accessibility**: Always ensure tooltips are keyboard-accessible
|
|
|
|
```html
|
|
<!-- Good: Helpful tooltip -->
|
|
<button data-tooltip="Save your changes (Ctrl+S)">
|
|
Save
|
|
</button>
|
|
|
|
<!-- Bad: Obvious tooltip -->
|
|
<button data-tooltip="Click to save">
|
|
Save
|
|
</button>
|
|
```
|
|
|
|
### 2. Loading States
|
|
|
|
- **Do**: Use skeleton loading for content-heavy updates
|
|
- **Do**: Use spinners for quick actions (< 500ms)
|
|
- **Don't**: Show loading for optimistic UI updates
|
|
- **Do**: Configure appropriate delays to prevent flickering
|
|
|
|
```php
|
|
// Good: Appropriate loading type
|
|
#[Loading(type: 'skeleton', fragments: ['product-list'])]
|
|
public function loadProducts(): void { }
|
|
|
|
// Good: Quick action with spinner
|
|
#[Loading(type: 'spinner', showDelay: 0)]
|
|
public function toggleFavorite(): void { }
|
|
```
|
|
|
|
### 3. Notifications
|
|
|
|
- **Do**: Use notifications for important feedback
|
|
- **Don't**: Overuse notifications - they can be annoying
|
|
- **Do**: Set appropriate durations (5s for success, longer for errors)
|
|
- **Do**: Provide action buttons for actionable notifications
|
|
|
|
```php
|
|
// Good: Clear, actionable notification
|
|
$notification = new NotificationState(
|
|
message: 'File uploaded successfully',
|
|
type: 'success',
|
|
duration: 5000,
|
|
actionText: 'View',
|
|
actionUrl: '/files'
|
|
);
|
|
|
|
// Bad: Too many notifications
|
|
// Don't show a notification for every minor action
|
|
```
|
|
|
|
### 4. Modals & Dialogs
|
|
|
|
- **Do**: Use modals for important confirmations
|
|
- **Don't**: Overuse modals - they interrupt user flow
|
|
- **Do**: Provide clear action buttons
|
|
- **Do**: Support keyboard navigation (Escape to close)
|
|
|
|
```php
|
|
// Good: Clear confirmation dialog
|
|
$this->uiHelper->showConfirm(
|
|
title: 'Delete Item',
|
|
message: 'This action cannot be undone.',
|
|
confirmText: 'Delete',
|
|
cancelText: 'Cancel'
|
|
);
|
|
```
|
|
|
|
### 5. Error Handling
|
|
|
|
- **Do**: Use ErrorBoundary for automatic error handling
|
|
- **Do**: Show user-friendly error messages
|
|
- **Do**: Provide retry options for recoverable errors
|
|
- **Don't**: Show technical error details to users
|
|
|
|
```php
|
|
// Good: User-friendly error
|
|
throw LiveComponentError::validation(
|
|
'Please enter a valid email address',
|
|
['field' => 'email'],
|
|
$this->id->toString()
|
|
);
|
|
|
|
// Bad: Technical error
|
|
throw new \Exception('Invalid email format: ' . $email);
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
### Global Configuration
|
|
|
|
```javascript
|
|
import { sharedConfig } from './modules/livecomponent/SharedConfig.js';
|
|
|
|
// Configure default values
|
|
sharedConfig.defaultDebounce = 300;
|
|
sharedConfig.defaultCacheTTL = 5000;
|
|
sharedConfig.defaultLoadingShowDelay = 150;
|
|
sharedConfig.defaultLoadingType = 'skeleton';
|
|
sharedConfig.defaultNotificationDuration = 5000;
|
|
sharedConfig.defaultNotificationPosition = 'top-right';
|
|
sharedConfig.defaultModalAnimation = 'fade';
|
|
```
|
|
|
|
### Per-Component Configuration
|
|
|
|
```html
|
|
<!-- Component-level configuration -->
|
|
<div
|
|
data-live-component="component-id"
|
|
data-loading-type="skeleton"
|
|
data-loading-show-delay="200"
|
|
data-notification-position="bottom-right"
|
|
>
|
|
<!-- Component content -->
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Tooltips Not Showing
|
|
|
|
1. Check that `data-tooltip` attribute is present
|
|
2. Verify TooltipManager is initialized
|
|
3. Check browser console for errors
|
|
4. Ensure element is visible and not hidden
|
|
|
|
### Loading States Not Working
|
|
|
|
1. Verify `data-loading-type` attribute is set
|
|
2. Check that fragments match between HTML and PHP
|
|
3. Ensure LoadingStateManager is initialized
|
|
4. Check for JavaScript errors in console
|
|
|
|
### Notifications Not Displaying
|
|
|
|
1. Verify NotificationComponent is mounted
|
|
2. Check that component ID matches
|
|
3. Ensure state is properly serialized
|
|
4. Check browser console for errors
|
|
|
|
### Modals Not Opening
|
|
|
|
1. Verify UIManager is initialized
|
|
2. Check that modal content is valid HTML
|
|
3. Ensure no JavaScript errors are blocking execution
|
|
4. Check z-index conflicts
|
|
|
|
---
|
|
|
|
**Next**: [API Reference](api-reference.md) →
|
|
|