componente · @solu30/ui-kit/feedback
Dialog
Diálogo centrado que se arma por composición: disparador, contenido, encabezado, pie y cierre.
86 sitios de import en 7 de 15 verticales — un archivo que la importa cuenta una vez, monte una instancia o veinte.
- carlosmartinpavon
- cmp-packages
- dietaly
- elliot
- gestorsalon
- luigi
- raquetea
El componente, montado
Cargando el espécimen…
Éste es el conjunto completo. Abajo, los mismos casos por separado, cada uno con su código.
Cómo se usa
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@solu30/ui-kit/feedback'<Dialog open={abierto} onOpenChange={setAbierto}>
<DialogContent>
<DialogHeader>
<DialogTitle>Nueva combinación de sabores</DialogTitle>
<DialogDescription>Se agrega al catálogo de la sucursal.</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button onClick={() => setAbierto(false)}>Guardar</Button>
</DialogFooter>
</DialogContent>
</Dialog>Anatomía
Las partes que componen Dialog se importan del mismo subpath. Sueltas no significan nada: se montan siempre dentro de su padre.
Dialog
├── DialogClose
├── DialogContent
├── DialogDescription
├── DialogFooter
├── DialogHeader
├── DialogTitle
└── DialogTriggerProps
| Prop | Requerida | Tipo | Qué hace |
|---|---|---|---|
| open | opcional | boolean | — |
| onOpenChange | opcional | (open: boolean) => void | — |
| defaultOpen | opcional | boolean | — |
| modal | opcional | boolean | — |
| children | sí | ReactNode | — |
Formulario
Cuando el diálogo pide una acción del usuario antes de cerrarse —cargar o confirmar un dato— con header, cuerpo y footer con las dos acciones.
Cargando el espécimen…
export function DialogFormulario() {
const [flavorOpen, setFlavorOpen] = useState(false)
const flavorFocus = useFocusReturnCheck()
const closeFlavor = () => {
setFlavorOpen(false)
flavorFocus.notifyClosed()
}
return (
<>
<div className="flex flex-col items-center gap-1.5">
<TriggerButton onClick={(e) => flavorFocus.openFrom(e, () => setFlavorOpen(true))}>
Nueva combinación
</TriggerButton>
<FocusReturnNote result={flavorFocus.result} />
</div>
<Dialog open={flavorOpen} onOpenChange={(next) => (next ? setFlavorOpen(true) : closeFlavor())}>
<DialogContent>
<DialogHeader>
<DialogTitle>Nueva combinación de sabores</DialogTitle>
<DialogDescription>
Se agrega al catálogo de la sucursal Nueva Córdoba, disponible desde mañana.
</DialogDescription>
</DialogHeader>
<p className="px-6 py-2 text-sm text-content/70">
Dulce de leche granizado + chocolate amargo, con crocante de maní.
</p>
<DialogFooter>
<TriggerButton onClick={closeFlavor}>Cancelar</TriggerButton>
<TriggerButton onClick={closeFlavor}>Guardar</TriggerButton>
</DialogFooter>
</DialogContent>
</Dialog>
</>
)
}De specimens/feedback.tsx · DialogFormulario: 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.
Detalle
Cuando el diálogo sólo muestra información y se cierra con una sola acción — sin campos que completar.
Cargando el espécimen…
export function DialogDetalle() {
const [tripOpen, setTripOpen] = useState(false)
const tripFocus = useFocusReturnCheck()
const closeTrip = () => {
setTripOpen(false)
tripFocus.notifyClosed()
}
return (
<>
<div className="flex flex-col items-center gap-1.5">
<TriggerButton onClick={(e) => tripFocus.openFrom(e, () => setTripOpen(true))}>
Ver detalle del pasaje
</TriggerButton>
<FocusReturnNote result={tripFocus.result} />
</div>
<Dialog open={tripOpen} onOpenChange={(next) => (next ? setTripOpen(true) : closeTrip())}>
<DialogContent>
<DialogHeader>
<DialogTitle>Charter · sábado 14:00</DialogTitle>
<DialogDescription>Puerto Madero → Tigre, 6 pasajeros.</DialogDescription>
</DialogHeader>
<ul className="flex flex-col gap-1.5 px-6 py-2 text-sm text-content/70">
<li>Capitán asignado — confirma 2 h antes de zarpar</li>
<li>Embarcación: Azimut 42, cupo 8</li>
<li>Catering no incluido</li>
</ul>
<DialogFooter>
<TriggerButton onClick={closeTrip}>Cerrar</TriggerButton>
</DialogFooter>
</DialogContent>
</Dialog>
</>
)
}De specimens/feedback.tsx · DialogDetalle: 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.
Composición
Para ver las piezas sueltas —trigger, contenido, cierre— armadas a mano en vez de con el atajo, cuando la pantalla necesita controlar el disparador o el cierre por su cuenta.
Cargando el espécimen…
export function DialogCompositionMatrix({ abiertoAlMontar = false }: { abiertoAlMontar?: boolean }) {
const [open, setOpen] = useState(abiertoAlMontar)
return (
<Stage label="Dialog composable · Trigger + Content + Close, y el panel replicado adentro" tall wide>
<div className="flex flex-col items-center gap-3">
<div className="flex flex-col items-center gap-4 sm:flex-row sm:items-start sm:justify-center">
<Dialog open={open} onOpenChange={setOpen}>
{/* `asChild` clona el hijo en vez de envolverlo en otro botón: sin
esto quedan dos `<button>` anidados, que es HTML inválido. Es la
forma en que los consumidores lo usan de verdad —GestorSalon tiene
70 `TooltipTrigger asChild`— así que el espécimen la muestra. Que
el botón salga sin estilo era culpa de `TriggerButton`, que pisaba
su propio className con el spread; ver la nota allá arriba. */}
<DialogTrigger asChild>
<TriggerButton>Ver el detalle de la reserva</TriggerButton>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Reserva #482 · Salón Jardín</DialogTitle>
<DialogDescription>Sábado 22, de 21:00 a 04:00. 120 invitados confirmados.</DialogDescription>
</DialogHeader>
<ul className="flex flex-col gap-1.5 px-6 py-2 text-sm text-content/70">
<li>Seña acreditada — 30%, el 12 de agosto</li>
<li>Menú: cada invitado elige por el portal</li>
<li>Servicio de mozos incluido</li>
</ul>
<DialogFooter>
{/* `DialogClose` no necesita saber nada del estado de acá: cierra
por el contexto de Radix. Es la pieza que esta ficha muestra. */}
<DialogClose asChild>
<TriggerButton>Cerrar</TriggerButton>
</DialogClose>
<DialogClose asChild>
<TriggerButton tone="danger">Cancelar la reserva</TriggerButton>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
<ReplicaContenida>
<div className="flex flex-col gap-2 p-4">
<p className="text-sm font-medium text-content">Reserva #482 · Salón Jardín</p>
<p className="text-xs text-content/60">
Sábado 22, de 21:00 a 04:00. 120 invitados confirmados.
</p>
<ul className="flex flex-col gap-1 border-t border-content/10 pt-2 text-xs text-content/70">
<li>Seña acreditada — 30%, el 12 de agosto</li>
<li>Menú: cada invitado elige por el portal</li>
<li>Servicio de mozos incluido</li>
</ul>
<div className="flex justify-end gap-2 border-t border-content/10 pt-2">
<span className="rounded-md border border-content/10 bg-container px-3 py-1.5 text-xs text-content">
Cerrar
</span>
<span className="rounded-md border border-red/30 bg-red/10 px-3 py-1.5 text-xs text-red">
Cancelar la reserva
</span>
</div>
</div>
</ReplicaContenida>
</div>
<p className="max-w-[520px] text-center text-[11px] text-content/50">
El componente REAL es el de la izquierda y abre de verdad: su panel sale por un portal a
document.body y se dibuja sobre la página, no adentro de este recuadro. A la derecha, la
misma forma replicada acá para poder verla sin tapar la ficha. El botón es DialogTrigger;
los dos del pie del panel, DialogClose.
</p>
</div>
</Stage>
)
}De specimens/feedback.tsx · DialogCompositionMatrix: 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.
Ejemplo del paquete
// Sin `open` se maneja solo desde el trigger; con `open` + `onOpenChange` lo maneja el consumidor.
<Dialog open={abierto} onOpenChange={setAbierto}>
<DialogContent>
<DialogHeader>
<DialogTitle>Nueva reserva</DialogTitle>
<DialogDescription>Todos los campos son obligatorios.</DialogDescription>
</DialogHeader>
<FormularioReserva />
<DialogFooter>
<DialogClose asChild><Button variant="ghost">Cancelar</Button></DialogClose>
<Button onClick={guardar}>Guardar</Button>
</DialogFooter>
</DialogContent>
</Dialog>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.