componente · @solu30/ui-kit/feedback
ConfirmDialog
Diálogo de confirmación ya armado, con título, descripción y dos botones, en variante neutra o de peligro.
214 sitios de import en 7 de 15 verticales — un archivo que la importa cuenta una vez, monte una instancia o veinte.
- TheCharterPanel
- carlosmartinpavon
- cmp-packages
- dietaly
- gestorsalon
- luigi
- raquetea
El componente, montado
Cargando el espécimen…
export function ConfirmDialogMatrix() {
const [activeScenario, setActiveScenario] = useState<ConfirmScenarioKey | null>(null)
const [loading, setLoading] = useState(false)
const focus = useFocusReturnCheck()
const close = () => {
setActiveScenario(null)
focus.notifyClosed()
}
const scenario = activeScenario ? CONFIRM_SCENARIOS[activeScenario] : null
return (
<Stage label="ConfirmDialog · 3 variantes, una instancia" tall wide>
<div className="flex flex-col items-center gap-3">
<Row>
{CONFIRM_ORDER.map((key) => (
<TriggerButton
key={key}
tone={key === 'danger' ? 'danger' : 'default'}
onClick={(e) => focus.openFrom(e, () => setActiveScenario(key))}
>
{CONFIRM_SCENARIOS[key].trigger}
</TriggerButton>
))}
</Row>
<FocusReturnNote result={focus.result} />
</div>
{/* La misma razón que en DialogCompositionMatrix y SheetCompositionMatrix:
`ConfirmDialog` sale por un portal y su panel NO se dibuja adentro del
escenario. 217 sitios de import y lo único que la ficha mostraba sin
hacer clic eran tres botones — o sea el disparador, no la pieza. La
réplica deja ver la forma del panel de una; el componente real sigue
al lado y sigue abriendo de verdad. Rotulada, como manda el contrato
de `ReplicaContenida`: una réplica que se lee como la pieza real es
peor que no tenerla. */}
<ReplicaContenida nota="réplica del panel danger, no el portal">
<div className="flex flex-col gap-3 p-4">
<p className="text-sm font-semibold text-content">{CONFIRM_SCENARIOS.danger.title}</p>
<p className="text-xs text-content/70">{CONFIRM_SCENARIOS.danger.description}</p>
<div className="flex justify-end gap-2">
<span className="rounded-md border border-content/10 bg-container px-3 py-1.5 text-xs text-content">
Cancelar
</span>
<span className="rounded-md border border-transparent bg-red/80 px-3 py-1.5 text-xs font-medium text-white">
{CONFIRM_SCENARIOS.danger.confirmLabel}
</span>
</div>
</div>
</ReplicaContenida>
<ConfirmDialog
isOpen={activeScenario !== null}
onCancel={close}
onConfirm={() => {
setLoading(true)
setTimeout(() => {
setLoading(false)
close()
}, 700)
}}
variant={activeScenario ?? 'default'}
title={scenario?.title ?? ''}
description={scenario?.description}
confirmLabel={scenario?.confirmLabel}
cancelLabel="Cancelar"
loading={loading}
/>
</Stage>
)
}De specimens/feedback.tsx · ConfirmDialogMatrix: el espécimen del Muestrario, no un ejemplo oficial del paquete. Muestra cómo se usa la pieza de verdad, montada contra los tipos instalados —eso es lo que vale— pero no es documentación del kit.
Cómo se usa
import { ConfirmDialog } from '@solu30/ui-kit/feedback'Props
| Prop | Requerida | Tipo | Qué hace |
|---|---|---|---|
| isOpen | sí | boolean | — |
| onConfirm | sí | () => void | — |
| onCancel | sí | () => void | — |
| title | sí | string | — |
| description | opcional | ReactNode | — |
| variant | opcional | "default" | "danger" | "warning" | — |
| confirmLabel | opcional | string | — |
| cancelLabel | opcional | string | — |
| loading | opcional | boolean | — |
| icon | opcional | ReactNode | — |
| manageHistory | opcional | boolean | Si este diálogo maneja su propia entrada de `window.history` (pushState al abrir, "atrás" lo cierra). Default `true` — sin cambios de comportamiento para consumidores existentes. Poné `false` cuando `isOpen`/`onCancel` YA están sincronizados con la URL de la app (Next.js App Router: `?param=` + `router.push`/`searchParams`), o cuando el diálogo se abre ANIDADO dentro de un overlay URL-driven — en ambos casos el pushState propio del kit mete una entrada espuria en el historial y "atrás" necesita un press de más para cerrar lo de afuera (ADR-262). @default true |
Ejemplo del paquete
// Ya trae los dos botones: no se le pasan children.
<ConfirmDialog
isOpen={porBorrar !== null}
variant="danger"
title="¿Borrar la reserva?"
description="No se puede deshacer."
confirmLabel="Borrar"
loading={borrando}
onConfirm={borrar}
onCancel={() => setPorBorrar(null)}
/>Del @example del JSDoc de @solu30/ui-kit, sin editar: son 65 de los 541 símbolos. La sangría y los renglones en blanco son los del paquete.