feat: reusable dynamic confirmation modal

This commit is contained in:
idevakk
2026-03-04 09:57:45 +05:30
parent 996ae20bbb
commit 41c1e7ad54
2 changed files with 120 additions and 3 deletions

View File

@@ -0,0 +1,103 @@
@props([
'name' => 'confirm-modal',
])
<div x-show="show"
x-data="{
show: false,
title: '',
message: '',
confirmLabel: 'Confirm',
type: 'danger',
action: null,
open(data) {
this.title = data.title || 'Are you sure?';
this.message = data.message || 'This action cannot be undone.';
this.confirmLabel = data.confirmLabel || 'Confirm';
this.type = data.type || 'danger';
this.action = data.action || null;
this.show = true;
},
confirm() {
if (this.action) {
this.action();
}
this.show = false;
}
}"
x-on:open-{{ $name }}.window="open($event.detail)"
class="fixed inset-0 z-[110] flex items-center justify-center p-4 lg:p-8"
style="display: none;"
x-cloak>
<!-- Backdrop -->
<div x-show="show"
x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="show = false"
class="absolute inset-0 bg-zinc-950/80 backdrop-blur-xl"></div>
<!-- Modal Card -->
<div x-show="show"
x-transition:enter="transition ease-out duration-500"
x-transition:enter-start="opacity-0 scale-95 translate-y-8"
x-transition:enter-end="opacity-100 scale-100 translate-y-0"
x-transition:leave="transition ease-in duration-300"
x-transition:leave-start="opacity-100 scale-100 translate-y-0"
x-transition:leave-end="opacity-0 scale-95 translate-y-8"
class="w-full max-w-[400px] bg-zinc-900 border border-white/10 rounded-[32px] p-8 relative overflow-hidden shadow-2xl">
<!-- Background Glow based on type -->
<div class="absolute top-0 left-1/2 -translate-x-1/2 w-64 h-64 rounded-full blur-[80px] -z-10 opacity-20"
:class="{
'bg-rose-500': type === 'danger',
'bg-blue-500': type === 'info',
'bg-amber-500': type === 'warning',
'bg-emerald-500': type === 'success'
}"></div>
<div class="text-center">
<!-- Icon -->
<div class="w-16 h-16 rounded-2xl flex items-center justify-center mx-auto mb-6 shadow-xl"
:class="{
'bg-rose-500/10 text-rose-500': type === 'danger',
'bg-blue-500/10 text-blue-500': type === 'info',
'bg-amber-500/10 text-amber-500': type === 'warning',
'bg-emerald-500/10 text-emerald-500': type === 'success'
}">
<!-- Danger Icon -->
<svg x-show="type === 'danger'" class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
<!-- Warning Icon -->
<svg x-show="type === 'warning'" class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>
<!-- Info Icon -->
<svg x-show="type === 'info'" class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
<!-- Success Icon -->
<svg x-show="type === 'success'" class="w-8 h-8" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>
</div>
<h3 class="text-2xl font-black text-white mb-2 tracking-tight uppercase italic" x-text="title"></h3>
<p class="text-xs font-bold text-zinc-500 uppercase tracking-widest leading-relaxed mb-8 px-4" x-text="message"></p>
<div class="grid grid-cols-2 gap-4">
<button @click="show = false"
class="py-4 rounded-2xl bg-white/5 border border-white/10 text-zinc-400 font-black text-[10px] uppercase tracking-[0.2em] hover:bg-white/10 hover:text-white transition-all">
Cancel
</button>
<button @click="confirm()"
class="py-4 rounded-2xl font-black text-[10px] uppercase tracking-[0.2em] shadow-xl transition-all"
:class="{
'bg-rose-600 text-white hover:bg-rose-500 shadow-rose-900/20': type === 'danger',
'bg-blue-600 text-white hover:bg-blue-500 shadow-blue-900/20': type === 'info',
'bg-amber-600 text-white hover:bg-amber-500 shadow-amber-900/20': type === 'warning',
'bg-emerald-600 text-white hover:bg-emerald-500 shadow-emerald-900/20': type === 'success'
}"
x-text="confirmLabel">
</button>
</div>
</div>
</div>
</div>