Saltar al contenido principal

Settings Row

La fila que toda página de configuración repite, en una sola pieza. Label y descripción a la izquierda, el control a la derecha, y una sección que las apila y dibuja las líneas.

Notifications

How and when NachUI reaches you.

One email for every event on your account.

Beta

How often the alerts are bundled.

Shown on every email we send.

1'use client';
2
3import { useState } from 'react';
4import { Badge } from '@/components/ui/badge';
5import { Button } from '@/components/ui/button';
6import { SettingsRow } from '@/components/ui/settings-row';
7
8const FREQUENCIES = [
9 { value: 'instant', label: 'Instantly' },
10 { value: 'hourly', label: 'Hourly' },
11 { value: 'daily', label: 'Daily digest' },
12];
13
14export function Default() {
15 const [email, setEmail] = useState(true);
16 const [frequency, setFrequency] = useState('daily');
17 const [replyTo, setReplyTo] = useState('nacho@example.com');
18
19 return (
20 <div className="w-full max-w-xl">
21 <SettingsRow.Section
22 title="Notifications"
23 description="How and when NachUI reaches you."
24 footer={
25 <Button size="sm" type="button">
26 Save changes
27 </Button>
28 }
29 >
30 <SettingsRow label="Email alerts" description="One email for every event on your account.">
31 <SettingsRow.Switch checked={email} onCheckedChange={setEmail} />
32 </SettingsRow>
33 <SettingsRow
34 label="Frequency"
35 description="How often the alerts are bundled."
36 badge={<Badge variant="secondary">Beta</Badge>}
37 disabled={!email}
38 >
39 <SettingsRow.Select
40 options={FREQUENCIES}
41 value={frequency}
42 onValueChange={setFrequency}
43 />
44 </SettingsRow>
45 <SettingsRow label="Reply-to address" description="Shown on every email we send.">
46 <SettingsRow.Input
47 type="email"
48 value={replyTo}
49 onChange={(event) => setReplyTo(event.target.value)}
50 />
51 </SettingsRow>
52 </SettingsRow.Section>
53 </div>
54 );
55}

Instalación

pnpm dlx nachui add settings-row

Anatomía

1import { SettingsRow } from '@/components/ui/settings-row';
1<SettingsRow.Section
2 title="Notificaciones"
3 description="Cómo te avisamos."
4 footer={<Button size="sm">Guardar</Button>}
5>
6 <SettingsRow label="Alertas por email" description="Un email por cada evento.">
7 <SettingsRow.Switch checked={email} onCheckedChange={setEmail} />
8 </SettingsRow>
9 <SettingsRow label="Frecuencia" badge={<Badge variant="secondary">Beta</Badge>}>
10 <SettingsRow.Select options={frecuencias} value={frecuencia} onValueChange={setFrecuencia} />
11 </SettingsRow>
12 <SettingsRow label="Dirección de respuesta">
13 <SettingsRow.Input type="email" value={replyTo} onChange={onReplyTo} />
14 </SettingsRow>
15</SettingsRow.Section>
Una fila recibe un label, una descripción opcional y un control como hijo. Genera el id, apunta el Label ahí y le pasa la descripción al control como aria-describedby, así un lector de pantalla escucha lo mismo que ve alguien mirando. La fila se apila en el celular y va lado a lado desde sm para arriba.
Cualquier element sirve como hijo. Las partes incluidas son los mismos elements, ya con el tamaño de la fila y ya cableados a su label.

Composición

Un hybrid es composición visible. Abrí settings-row.tsx y estos son los elements que lo forman, en este orden:
SettingsRow
├── Label (+ Badge si lo pasás)
├── descripción (texto plano, aria-describedby)
└── control
├── SettingsRow.Switch → Switch
├── SettingsRow.Select → Select.Trigger + Select.Content + Select.Item
├── SettingsRow.Input → Input
├── SettingsRow.Action → Button
└── SettingsRow.Value → <code> + botón de copiar
SettingsRow.Section
├── header (título, descripción)
├── filas separadas por Separator
└── footer (slot)

Switch

SettingsRow.Switch es el element Switch con el id de la fila en su input. Hacer clic en el label lo alterna. checked y onCheckedChange funcionan como en el element, y disabled en la fila lo deshabilita.

Select

SettingsRow.Select recibe options como { value, label } y renderiza un Select chico a la derecha. Su trigger está etiquetado por la fila, así que el combobox se lee con el nombre de la fila. Nunca guarda el valor: pasale value y onValueChange, o defaultValue para dejarlo sin controlar.

Input

SettingsRow.Input es el element Input en tamaño chico, con un ancho tope para que una fila larga no lo estire. Todas las props del input pasan, salvo id, label y description, que son de la fila.

Action

SettingsRow.Action es un Button, outline y chico por defecto. Usalo para "Administrar", "Rotar" o, con variant="destructive", para la única cosa de la página que no se puede deshacer.

Value

SettingsRow.Value muestra un valor de solo lectura en mono, el prefijo de una API key o el id de una cuenta. Pasale copy para sumar un botón que escribe el valor al portapapeles y muestra un check por un momento.

Danger zone

Keys and actions that are hard to undo.

Use it from the server only. Rotates on demand.

sk_live_4f2a…9c1e

The current key stops working right away.

Removes the workspace, its keys and every component synced to it.

1'use client';
2
3import { useState } from 'react';
4import { SettingsRow } from '@/components/ui/settings-row';
5
6export function Danger() {
7 const [deleted, setDeleted] = useState(false);
8
9 return (
10 <div className="w-full max-w-xl">
11 <SettingsRow.Section
12 title="Danger zone"
13 description="Keys and actions that are hard to undo."
14 className="border-destructive-border"
15 >
16 <SettingsRow label="API key" description="Use it from the server only. Rotates on demand.">
17 <SettingsRow.Value value="sk_live_4f2a…9c1e" copy />
18 </SettingsRow>
19 <SettingsRow label="Rotate key" description="The current key stops working right away.">
20 <SettingsRow.Action>Rotate</SettingsRow.Action>
21 </SettingsRow>
22 <SettingsRow
23 label="Delete account"
24 description="Removes the workspace, its keys and every component synced to it."
25 >
26 <SettingsRow.Action variant="destructive" onClick={() => setDeleted(true)}>
27 {deleted ? 'Scheduled' : 'Delete account'}
28 </SettingsRow.Action>
29 </SettingsRow>
30 </SettingsRow.Section>
31 </div>
32 );
33}

Section

SettingsRow.Section agrupa filas bajo un título en una tarjeta con borde. Pone un Separator entre cada par de filas y recibe un slot footer para un botón de guardar o una nota. Deshabilitar una fila la atenúa; deshabilitar toda la sección es pasarle disabled a cada fila que quieras apagar.

Referencia de API

SettingsRow

PropTipoDefaultDescripción
labelReactNode-El label de la fila, renderizado con Label
descriptionReactNode-Texto de ayuda bajo el label, enlazado al control
htmlForstring-Id del control; se genera si no lo pasás
badgeReactNode-Se renderiza junto al label, normalmente un Badge
disabledbooleanfalseAtenúa la fila y deshabilita el control incluido
childrenReactNode-El control, una parte incluida o cualquier element tuyo
classNamestring-Clases CSS adicionales

SettingsRow.Switch

PropTipoDefaultDescripción
checkedboolean-Estado controlado
defaultCheckedboolean-Estado inicial
onCheckedChange(checked: boolean) => void-Se llama en cada cambio
disabledbooleanel de la filaPisa el valor de la fila
classNamestring-Clases CSS adicionales

SettingsRow.Select

PropTipoDefaultDescripción
options{ value: string; label: string; disabled?: boolean }[]-Las entradas de la lista
valuestring-Valor controlado
defaultValuestring-Valor inicial sin controlar
onValueChange(value: string) => void-Se llama con el valor elegido
placeholderstring-Se muestra mientras no hay elección
namestring-Nombre del campo en el formulario
disabledbooleanel de la filaPisa el valor de la fila
classNamestring-Clases CSS adicionales

SettingsRow.Input

PropTipoDefaultDescripción
size'sm' | 'default' | 'lg''sm'Tamaño del input
disabledbooleanel de la filaPisa el valor de la fila
classNamestring-Clases CSS adicionales
...--Cualquier prop del input salvo id, label, description

SettingsRow.Action

PropTipoDefaultDescripción
variant'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link''outline'Variante del botón
size'default' | 'sm' | 'lg' | 'icon''sm'Tamaño del botón
disabledbooleanel de la filaPisa el valor de la fila
...--Cualquier prop de Button

SettingsRow.Value

PropTipoDefaultDescripción
valuestring-El texto que se muestra en mono
copybooleanfalseSuma el botón de copiar
copyLabelstring'Copy'Nombre accesible en reposo
copiedLabelstring'Copied'Nombre accesible durante 1.5s después de copiar
onCopied(value: string) => void-Se llama después de escribir al portapapeles
classNamestring-Clases CSS adicionales

SettingsRow.Section

PropTipoDefaultDescripción
titleReactNode-Encabezado de la tarjeta
descriptionReactNode-Texto bajo el encabezado
footerReactNode-Slot al pie, alineado a la derecha
childrenReactNode-Filas, con un Separator entre cada par
classNamestring-Clases CSS adicionales
¿Encontraste algo que mejorar?

¿Notaste un error, tipografía o detalle faltante en esta página? Ayúdanos a mejorar la documentación abriendo un issue en GitHub.

Crear un Issue