← Components/Modals

ConfirmModal

confirmmodal-1779378817385.tsx
'use client'
import { useState, useEffect } from 'react'

interface ConfirmModalProps {
  variant?: 'danger' | 'warning' | 'info'
}

export default function ConfirmModal({ variant = 'danger' }: ConfirmModalProps) {
  const [open, setOpen] = useState(false)
  const [currentVariant, setCurrentVariant] = useState(variant)
  const [confirmed, setConfirmed] = useState(false)

  useEffect(() => {
    if (!open) return
    const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') setOpen(false) }
    document.addEventListener('keydown', handler)
    return () => document.removeEventListener('keydown', handler)
  }, [open])

  const VARIANTS = {
    danger: { icon: 'šŸ—‘', color: '#ef4444', border: 'rgba(239,68,68,0.2)', bg: 'rgba(239,68,68,0.1)', title: 'Delete component?', body: 'This action cannot be undone. The component and all its usage data will be permanently removed from our servers.', confirm: 'Delete permanently' },
    warning: { icon: 'āš ļø', color: '#f59e0b', border: 'rgba(245,158,11,0.2)', bg: 'rgba(245,158,11,0.1)', title: 'Unpublish component?', body: 'The component will be hidden from the catalogue and MCP searches until you publish it again.', confirm: 'Yes, unpublish' },
    info: { icon: 'šŸ“‹', color: '#6366f1', border: 'rgba(99,102,241,0.2)', bg: 'rgba(99,102,241,0.1)', title: 'Copy to collection?', body: 'This component will be added to your Pro collection and synced across all your projects.', confirm: 'Add to collection' },
  }

  const v = VARIANTS[currentVariant]

  return (
    <div style={{ background: '#0A0A0A', padding: 40, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', minHeight: 300, gap: 16 }}>
      <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', justifyContent: 'center' }}>
        {(Object.keys(VARIANTS) as typeof currentVariant[]).map(k => (
          <button key={k} onClick={() => { setCurrentVariant(k); setOpen(true); setConfirmed(false) }}
            style={{ background: VARIANTS[k].bg, border: `1px solid ${VARIANTS[k].border}`, color: VARIANTS[k].color, borderRadius: 8, padding: '8px 16px', cursor: 'pointer', fontSize: 13, fontWeight: 600 }}>
            {k.charAt(0).toUpperCase() + k.slice(1)}
          </button>
        ))}
      </div>

      {confirmed && (
        <div style={{ color: '#22c55e', fontSize: 13, animation: 'fadeIn 0.3s' }}>āœ“ Action confirmed!</div>
      )}

      {open && (
        <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.7)', backdropFilter: 'blur(8px)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 9999 }}
          onClick={e => { if (e.target === e.currentTarget) setOpen(false) }}>
          <div style={{ background: '#111', border: '1px solid rgba(255,255,255,0.1)', borderRadius: 16, padding: 32, width: 400, boxShadow: '0 24px 64px rgba(0,0,0,0.6)', animation: 'popIn 0.2s ease' }}>
            <div style={{ width: 52, height: 52, borderRadius: '50%', background: v.bg, border: `1px solid ${v.border}`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 24, marginBottom: 20 }}>{v.icon}</div>
            <div style={{ color: '#F5F5F0', fontSize: 18, fontWeight: 700, marginBottom: 12 }}>{v.title}</div>
            <div style={{ color: 'rgba(255,255,255,0.5)', fontSize: 14, lineHeight: 1.6, marginBottom: 28 }}>{v.body}</div>
            <div style={{ display: 'flex', gap: 10 }}>
              <button onClick={() => setOpen(false)} style={{ flex: 1, background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.1)', color: '#F5F5F0', borderRadius: 8, padding: '11px', cursor: 'pointer', fontSize: 14, fontWeight: 600 }}>Cancel</button>
              <button onClick={() => { setOpen(false); setConfirmed(true) }} style={{ flex: 1, background: v.color, color: '#fff', border: 'none', borderRadius: 8, padding: '11px', cursor: 'pointer', fontSize: 14, fontWeight: 700 }}>{v.confirm}</button>
            </div>
          </div>
        </div>
      )}
      <style>{`@keyframes popIn { from { opacity:0; transform:scale(0.92) } to { opacity:1; transform:scale(1) } } @keyframes fadeIn { from { opacity:0 } to { opacity:1 } }`}</style>
    </div>
  )
}

Component info

CategoryModals
Frameworkreact
TierFREE
Views0
Copies0

About

Animated confirmation modal with danger/warning variants, keyboard support, and backdrop blur