fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
@@ -11,9 +11,10 @@ This guide covers the integrated UI features available in LiveComponents, includ
|
||||
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)
|
||||
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)
|
||||
|
||||
---
|
||||
|
||||
@@ -338,6 +339,332 @@ 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
|
||||
@@ -519,18 +846,23 @@ final class UserSettings extends LiveComponent
|
||||
### 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): void
|
||||
public function showUserModal(int $userId, ?ComponentEventDispatcher $events = null): void
|
||||
{
|
||||
$userComponent = UserDetailsComponent::mount(
|
||||
ComponentId::generate('user-details'),
|
||||
userId: $userId
|
||||
);
|
||||
|
||||
$this->uiHelper->showModal(
|
||||
title: 'User Details',
|
||||
content: $userComponent->render(),
|
||||
size: 'medium'
|
||||
(new UIHelper($events))->modal(
|
||||
$this->id,
|
||||
'User Details',
|
||||
$userComponent->render(),
|
||||
ModalOptions::create()->withSize(ModalSize::Medium)
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user