{{-- Confirm Modal Component ----------------------- A branded confirmation dialog replacing cheap browser confirm(). Usage: Props: id - Unique identifier for this modal instance (required) title - Modal heading text (default: "Are you sure?") message - Body text explaining the action (default: "This action cannot be undone.") confirmText - Confirm button label (default: "Confirm") cancelText - Cancel button label (default: "Cancel") type - Visual style: danger | warning | info (default: "danger") icon - Icon name from x-icon component (optional, auto-selected by type) Events: Listens: confirm-modal { id, onConfirm?, onCancel?, title?, message? } Emits: confirmed-{id}, cancelled-{id} --}} @props([ 'id', 'title' => 'Are you sure?', 'message' => 'This action cannot be undone.', 'confirmText' => 'Confirm', 'cancelText' => 'Cancel', 'type' => 'danger', 'icon' => null, ]) @php $typeConfig = match($type) { 'danger' => [ 'iconBg' => 'bg-danger/10', 'iconColor' => 'text-danger', 'btnClass' => 'btn-danger', 'ringColor' => 'ring-danger/20', ], 'warning' => [ 'iconBg' => 'bg-warning/10', 'iconColor' => 'text-warning', 'btnClass' => 'inline-flex items-center justify-center gap-2 rounded-xl bg-warning px-5 py-2.5 text-sm font-semibold text-warning-content transition hover:bg-warning/90 disabled:opacity-60 disabled:cursor-not-allowed', 'ringColor' => 'ring-warning/20', ], 'info' => [ 'iconBg' => 'bg-info/10', 'iconColor' => 'text-info', 'btnClass' => 'inline-flex items-center justify-center gap-2 rounded-xl bg-info px-5 py-2.5 text-sm font-semibold text-white transition hover:bg-info/90 disabled:opacity-60 disabled:cursor-not-allowed', 'ringColor' => 'ring-info/20', ], default => [ 'iconBg' => 'bg-danger/10', 'iconColor' => 'text-danger', 'btnClass' => 'btn-danger', 'ringColor' => 'ring-danger/20', ], }; $defaultIcon = match($type) { 'danger' => 'trash', 'warning' => 'alert-triangle', 'info' => 'check-circle', default => 'alert-triangle', }; $resolvedIcon = $icon ?? $defaultIcon; @endphp
!el.disabled && el.offsetParent !== null); if (this._focusableEls.length) this._focusableEls[this._focusableEls.length - 1].focus(); }, _handleTab(e) { if (!this.open || !this._focusableEls.length) return; const first = this._focusableEls[0]; const last = this._focusableEls[this._focusableEls.length - 1]; if (e.shiftKey) { if (document.activeElement === first) { e.preventDefault(); last.focus(); } } else { if (document.activeElement === last) { e.preventDefault(); first.focus(); } } } }" x-on:confirm-modal.window="if ($event.detail.id === '{{ $id }}') show($event.detail)" x-on:keydown.escape.window="if (open) cancel()" x-on:keydown.enter.window="if (open && !processing) confirm()" x-on:keydown.tab.window="if (open) _handleTab($event)" x-cloak > {{-- Backdrop --}} {{-- Modal Panel --}}
{{-- Icon + Title --}}

{{-- Custom content slot --}} @if($slot->isNotEmpty())
{{ $slot }}
@endif {{-- Actions --}}