Confirmation Modal Example
A reusable confirmation modal component that can be triggered from any component via events.
Overview
This example demonstrates:
- Cross-component communication via events
- Reusable utility components
- Locked properties for security
- Event dispatching and listening
Component Structure
ConfirmationModal Component
app/Diffyne/Utils/ConfirmationModal.php:
php
<?php
namespace App\Diffyne\Utils;
use Diffyne\Attributes\Invokable;
use Diffyne\Attributes\Locked;
use Diffyne\Attributes\On;
use Diffyne\Component;
use Illuminate\View\View;
class ConfirmationModal extends Component
{
public bool $isOpen = false;
#[Locked]
public string $title = 'Confirm Action';
#[Locked]
public string $message = 'Are you sure you want to proceed?';
#[Locked]
public string $confirmText = 'Confirm';
#[Locked]
public string $cancelText = 'Cancel';
#[Locked]
public string $eventName = '';
#[Locked]
public array $eventData = [];
/**
* Open the modal with custom content.
*/
#[On('open-confirmation-modal')]
#[Invokable]
public function open(array $data): void
{
$this->isOpen = true;
$this->title = $data['title'] ?? 'Confirm Action';
$this->message = $data['message'] ?? 'Are you sure you want to proceed?';
$this->confirmText = $data['confirmText'] ?? 'Confirm';
$this->cancelText = $data['cancelText'] ?? 'Cancel';
$this->eventName = $data['eventName'] ?? '';
$this->eventData = $data['eventData'] ?? [];
}
/**
* Close the modal.
*/
#[Invokable]
public function close(): void
{
$this->isOpen = false;
$this->eventName = '';
$this->title = 'Confirm Action';
$this->message = 'Are you sure you want to proceed?';
$this->confirmText = 'Confirm';
$this->cancelText = 'Cancel';
$this->eventData = [];
}
/**
* Confirm action and dispatch the event.
*/
#[Invokable]
public function confirm(): void
{
if ($this->eventName) {
$this->dispatch($this->eventName, $this->eventData);
}
$this->close();
}
public function render(): View
{
return view('diffyne.utils.confirmation-modal');
}
}resources/views/diffyne/utils/confirmation-modal.blade.php:
blade
@if($isOpen)
<div
class="fixed inset-0 z-50 overflow-y-auto"
aria-labelledby="modal-title"
role="dialog"
aria-modal="true">
<!-- Background overlay -->
<div
class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
diff:click="close">
</div>
<!-- Modal panel -->
<div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<div class="relative transform overflow-hidden rounded-lg bg-white text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg">
<div class="bg-white px-4 pb-4 pt-5 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div class="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<svg class="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
</svg>
</div>
<div class="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
<h3 class="text-base font-semibold leading-6 text-gray-900" id="modal-title">
{{ $title }}
</h3>
<div class="mt-2">
<p class="text-sm text-gray-500">
{{ $message }}
</p>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
<button
type="button"
diff:click="confirm"
class="inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:ml-3 sm:w-auto">
{{ $confirmText }}
</button>
<button
type="button"
diff:click="close"
class="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto">
{{ $cancelText }}
</button>
</div>
</div>
</div>
</div>
@endifUsage Example
PostList Component
app/Diffyne/PostList.php:
php
<?php
namespace App\Diffyne;
use App\Models\Post;
use Diffyne\Attributes\Invokable;
use Diffyne\Attributes\Locked;
use Diffyne\Attributes\On;
use Diffyne\Component;
use Illuminate\View\View;
class PostList extends Component
{
#[Locked]
public array $posts = [];
public function mount(): void
{
$this->loadPosts();
}
private function loadPosts(): void
{
$this->posts = Post::all()->toArray();
}
/**
* Open confirmation modal before deleting.
*/
#[Invokable]
public function confirmDelete(int $id): void
{
$post = Post::find($id);
if ($post) {
$this->dispatch('open-confirmation-modal', [
'title' => 'Delete Post',
'message' => "Are you sure you want to delete \"{$post->title}\"? This action cannot be undone.",
'confirmText' => 'Delete',
'cancelText' => 'Cancel',
'eventName' => 'delete-post-confirmed',
'eventData' => ['id' => $id],
]);
}
}
/**
* Handle confirmed deletion.
*/
#[On('delete-post-confirmed')]
public function deletePost(array $data): void
{
$id = $data['id'] ?? null;
if (!$id) {
return;
}
$post = Post::find($id);
if ($post) {
$postTitle = $post->title;
$post->delete();
$this->loadPosts();
// Dispatch browser event for notifications
$this->dispatchBrowserEvent('post-deleted', [
'title' => $postTitle,
'message' => 'Post deleted successfully!',
'type' => 'success',
]);
}
}
public function render(): View
{
return view('diffyne.post-list');
}
}resources/views/diffyne/post-list.blade.php:
blade
<div>
<h1>Posts</h1>
@foreach($posts as $post)
<div class="post-item">
<h2>{{ $post['title'] }}</h2>
<p>{{ $post['content'] }}</p>
<button
diff:click="confirmDelete({{ $post['id'] }})"
class="text-red-600 hover:text-red-800">
Delete
</button>
</div>
@endforeach
</div>Layout
resources/views/layouts/app.blade.php:
blade
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
@diffyneStyles
</head>
<body>
<main>
@diffyne($component, $params ?? [])
</main>
{{-- Include confirmation modal globally --}}
@diffyne('Utils/ConfirmationModal')
@diffyneScripts
</body>
</html>How It Works
- User clicks delete button →
confirmDelete()is called - Component dispatches event →
open-confirmation-modalwith modal configuration - ConfirmationModal listens → Opens with custom title, message, etc.
- User confirms →
confirm()dispatches the configured event (delete-post-confirmed) - PostList listens →
deletePost()handles the actual deletion - Modal closes → User sees updated list
Benefits
- Reusable: One modal component for all confirmations
- Decoupled: Components don't need to know about modal implementation
- Flexible: Customize title, message, buttons per use case
- Secure: Locked properties prevent tampering
Advanced Usage
Custom Styling
php
$this->dispatch('open-confirmation-modal', [
'title' => 'Delete Post',
'message' => 'Are you sure?',
'confirmText' => 'Yes, Delete',
'cancelText' => 'Cancel',
'eventName' => 'delete-post-confirmed',
'eventData' => ['id' => $id],
'confirmClass' => 'bg-red-600', // Custom styling
'cancelClass' => 'bg-gray-200',
]);Multiple Confirmation Types
php
// Delete confirmation
$this->dispatch('open-confirmation-modal', [
'title' => 'Delete',
'message' => 'This cannot be undone.',
'confirmText' => 'Delete',
'eventName' => 'delete-confirmed',
]);
// Publish confirmation
$this->dispatch('open-confirmation-modal', [
'title' => 'Publish Post',
'message' => 'This will make the post visible to everyone.',
'confirmText' => 'Publish',
'eventName' => 'publish-confirmed',
]);Next Steps
- Component Events - Learn about event system
- Attributes - Learn about #[On] and #[Locked]
- Security - Security best practices
