Feat: add confirmation modal component and Livewire support
- Introduced `Confirmation` trait for reusable confirmation modal logic in Livewire components. - Added `ConfirmationModal` Livewire component for handling confirmation prompts with customizable title, subtitle, description, and styling. - Updated `captainhook.json` to increase `subjectLength` limit. - Integrated confirmation modal in the sidebar layout with `<livewire:confirmation-modal/>`.
This commit is contained in:
parent
2956d03001
commit
5eca6d3602
65
app/Concerns/Confirmation.php
Normal file
65
app/Concerns/Confirmation.php
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Concerns;
|
||||||
|
|
||||||
|
use Livewire\Attributes\On;
|
||||||
|
|
||||||
|
trait Confirmation
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Shows a confirmation modal before executing the given method.
|
||||||
|
*
|
||||||
|
* @param string $method Method name that should be executed if the user confirms.
|
||||||
|
* @param mixed|null $params Any parameters that should be passed to the method.
|
||||||
|
* @param string $title Title of the confirmation modal.
|
||||||
|
* @param string $subtitle Subtitle of the confirmation modal.
|
||||||
|
* @param string|null $description Description of the confirmation modal.
|
||||||
|
* @param string $descriptionClass Classes to apply to the description element. (!, !important to overide)
|
||||||
|
* @param string $acceptBtnClass Classes to apply to the accept button. (!, !important to overide)
|
||||||
|
* @param string $icon Icon to display in the confirmation modal.
|
||||||
|
*/
|
||||||
|
public function requireConfirmation(
|
||||||
|
string $method,
|
||||||
|
mixed $params = null,
|
||||||
|
string $title = 'Are you sure?',
|
||||||
|
string $subtitle = 'This action cannot be undone.',
|
||||||
|
?string $description = null,
|
||||||
|
string $descriptionClass = '',
|
||||||
|
string $acceptBtnClass = '',
|
||||||
|
string $icon = 'lucide.triangle-alert'
|
||||||
|
): void {
|
||||||
|
$this->dispatch(
|
||||||
|
'open-confirmation',
|
||||||
|
componentId: $this->getId(),
|
||||||
|
action: $method,
|
||||||
|
params: $params,
|
||||||
|
title: $title,
|
||||||
|
subtitle: $subtitle,
|
||||||
|
description: $description,
|
||||||
|
descriptionClass: $descriptionClass,
|
||||||
|
acceptBtnClass: $acceptBtnClass,
|
||||||
|
icon: $icon
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen globally for the acceptance, but only process if the ID matches.
|
||||||
|
*/
|
||||||
|
#[On('confirmation-accepted')]
|
||||||
|
public function onConfirmationAccepted(string $action, mixed $params, string $componentId): void
|
||||||
|
{
|
||||||
|
// Ignore the event if it wasn't requested by this specific component instance
|
||||||
|
if ($this->getId() !== $componentId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the method actually exists on the component
|
||||||
|
if (method_exists($this, $action)) {
|
||||||
|
|
||||||
|
// Execute the requested method, passing any provided parameters
|
||||||
|
$this->{$action}($params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@
|
|||||||
{
|
{
|
||||||
"action": "\\CaptainHook\\App\\Hook\\Message\\Action\\Beams",
|
"action": "\\CaptainHook\\App\\Hook\\Message\\Action\\Beams",
|
||||||
"options": {
|
"options": {
|
||||||
"subjectLength": 50,
|
"subjectLength": 100,
|
||||||
"bodyLineLength": 300
|
"bodyLineLength": 300
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
97
resources/views/components/⚡confirmation-modal.blade.php
Normal file
97
resources/views/components/⚡confirmation-modal.blade.php
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Livewire;
|
||||||
|
|
||||||
|
use Livewire\Component;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
|
use TailwindMerge\TailwindMerge;
|
||||||
|
|
||||||
|
new class extends Component {
|
||||||
|
public bool $open = false;
|
||||||
|
|
||||||
|
public string $title = 'Are you sure?';
|
||||||
|
public string $subtitle = 'This action cannot be undone.';
|
||||||
|
public ?string $description = null;
|
||||||
|
|
||||||
|
public string $icon = 'lucide.triangle-alert';
|
||||||
|
public string $descriptionClass = '';
|
||||||
|
public string $acceptBtnClass = '';
|
||||||
|
|
||||||
|
public string $action = '';
|
||||||
|
public mixed $params = null;
|
||||||
|
public string $componentId = '';
|
||||||
|
|
||||||
|
#[On('open-confirmation')]
|
||||||
|
public function openModal(
|
||||||
|
string $componentId,
|
||||||
|
string $action,
|
||||||
|
mixed $params = null,
|
||||||
|
string $title = 'Are you sure?',
|
||||||
|
string $subtitle = 'This action cannot be undone.',
|
||||||
|
?string $description = null,
|
||||||
|
string $descriptionClass = '',
|
||||||
|
string $acceptBtnClass = '',
|
||||||
|
string $icon = 'lucide.triangle-alert'
|
||||||
|
): void {
|
||||||
|
$this->action = $action;
|
||||||
|
$this->componentId = $componentId;
|
||||||
|
$this->title = $title;
|
||||||
|
$this->subtitle = $subtitle;
|
||||||
|
$this->description = $description;
|
||||||
|
$this->params = $params;
|
||||||
|
|
||||||
|
$this->descriptionClass = $descriptionClass;
|
||||||
|
$this->acceptBtnClass = $acceptBtnClass;
|
||||||
|
$this->icon = $icon;
|
||||||
|
|
||||||
|
$this->open = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function confirm(): void
|
||||||
|
{
|
||||||
|
$this->open = false;
|
||||||
|
|
||||||
|
$this->dispatch('confirmation-accepted',
|
||||||
|
action: $this->action,
|
||||||
|
params: $this->params,
|
||||||
|
componentId: $this->componentId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMergedAlertClass(): string
|
||||||
|
{
|
||||||
|
return TailwindMerge::factory()->make()->merge(
|
||||||
|
'alert-warning alert-soft text-red-600',
|
||||||
|
$this->descriptionClass
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMergedButtonClass(): string
|
||||||
|
{
|
||||||
|
return TailwindMerge::factory()->make()->merge(
|
||||||
|
'btn-warning',
|
||||||
|
$this->acceptBtnClass
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div>
|
||||||
|
<x-mary-modal wire:model="open" :title="$title" :subtitle="$subtitle" class="backdrop-blur" persistent>
|
||||||
|
@isset($description)
|
||||||
|
<x-mary-alert
|
||||||
|
class="{{ $this->getMergedAlertClass() }}"
|
||||||
|
:icon="$icon">
|
||||||
|
{{ $description }}
|
||||||
|
</x-mary-alert>
|
||||||
|
@endisset
|
||||||
|
<x-slot:actions>
|
||||||
|
<x-mary-button label="Cancel" @click="$wire.open = false"/>
|
||||||
|
|
||||||
|
<x-mary-button
|
||||||
|
label="Confirm"
|
||||||
|
class="{{ $this->getMergedButtonClass() }}"
|
||||||
|
wire:click="confirm"
|
||||||
|
spinner="confirm"/>
|
||||||
|
</x-slot:actions>
|
||||||
|
</x-mary-modal>
|
||||||
|
</div>
|
||||||
@ -15,6 +15,7 @@
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
<x-mary-toast/>
|
<x-mary-toast/>
|
||||||
|
<livewire:confirmation-modal/>
|
||||||
</div>
|
</div>
|
||||||
@livewireScripts
|
@livewireScripts
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user