Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
718 lines
17 KiB
Markdown
718 lines
17 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. [Notification Component](#notification-component)
|
|
5. [Dialog & Modal Integration](#dialog--modal-integration)
|
|
6. [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');
|
|
```
|
|
|
|
---
|
|
|
|
## 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
|
|
#[Action]
|
|
public function showUserModal(int $userId): void
|
|
{
|
|
$userComponent = UserDetailsComponent::mount(
|
|
ComponentId::generate('user-details'),
|
|
userId: $userId
|
|
);
|
|
|
|
$this->uiHelper->showModal(
|
|
title: 'User Details',
|
|
content: $userComponent->render(),
|
|
size: '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) →
|
|
|