Input group
Cuando el valor tiene prefijo, sufijo o acción pegada (URL, copiar, buscar).
Campos compuestos del dashboard: PrefixedInput para la pantalla pública, SearchInput para buscar por idea y CopyField para el código corto de acceso. SuffixInput es una variante disponible sin uso actual en la app.
Instalación
shadcn
npx shadcn@latest add https://design.moodinary.com/r/input-group.jsonimport
import { PrefixedInput, SearchInput, CopyField, SuffixInput } from "@moodinary/ui/components/input-group";Ejemplos
Prefijo, búsqueda, copiar y unidad
El prefijo es fijo. CopyField muestra un valor de solo lectura; tu callback decide cómo copiarlo y cuándo confirmar el resultado.
/kiosk/
Código ilustrativo, no es un acceso real.
%
Código
default.tsx
"use client";
import { Field, FieldHint, FieldLabel } from "@moodinary/ui/components/field";
import {
CopyField,
PrefixedInput,
SearchInput,
SuffixInput,
} from "@moodinary/ui/components/input-group";
import { useCallback, useState } from "react";
function copyHint(copyError: boolean, copied: boolean): string {
if (copyError) {
return "No se pudo copiar. Seleccioná el código y copialo a mano.";
}
return copied
? "Link de ejemplo copiado."
: "Código ilustrativo, no es un acceso real.";
}
export default function InputGroupDefault() {
const [copied, setCopied] = useState(false);
const [copyError, setCopyError] = useState(false);
const copy = useCallback(async () => {
setCopyError(false);
try {
await navigator.clipboard.writeText("https://send.moodinary.com/ABC123");
setCopied(true);
setTimeout(() => setCopied(false), 1500);
} catch {
setCopyError(true);
}
}, []);
return (
<div className="grid w-full max-w-xl gap-4 sm:grid-cols-2">
<Field>
<FieldLabel htmlFor="screen-link">Link de la pantalla</FieldLabel>
<PrefixedInput
id="screen-link"
inputClassName="font-mono text-[13px]"
prefix="/kiosk/"
readOnly
value="equipo-demo?token=ejemplo"
/>
</Field>
<Field>
<FieldLabel htmlFor="search">Buscar por idea</FieldLabel>
<SearchInput
id="search"
placeholder="Buscá por idea, no solo por palabra"
/>
</Field>
<Field>
<FieldLabel htmlFor="send-code">Código</FieldLabel>
<CopyField
aria-label="send.moodinary.com/ABC123"
copied={copied}
copyLabel={copied ? "Link copiado" : "Copiar link"}
id="send-code"
onCopy={copy}
prefix="send.moodinary.com/"
value="ABC123"
/>
<FieldHint aria-live="polite">{copyHint(copyError, copied)}</FieldHint>
</Field>
<Field>
<FieldLabel htmlFor="suffix-demo">Unidad (demostración)</FieldLabel>
<SuffixInput
defaultValue={50}
id="suffix-demo"
max={100}
min={0}
suffix="%"
type="number"
/>
</Field>
</div>
);
}Props
PrefixedInput
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| prefix | React.ReactNode | - | Texto fijo antes del valor editable. |
| inputClassName | string | - | Clases del input; className se aplica al grupo. |
| ...props | Omit<React.ComponentProps<"input">, "prefix"> | - | Atributos nativos como id, value, disabled y onChange. |
SearchInput
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| shortcut | string | - | Atajo visible. No registra un atajo de teclado por sí solo. |
| submitLabel | string | - | Agrega un botón submit para búsquedas dentro de un form. |
| ...props | React.ComponentProps<"input"> | - | Atributos del campo de búsqueda. |
CopyField
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| value | string | - | Contenido de solo lectura. |
| onCopy | () => void | - | Acción del botón. La escritura en el portapapeles queda a cargo del caller. |
| copied | boolean | - | Reemplaza el ícono de copiar por una confirmación. |
| copyLabel | string | "Copiar" | Nombre accesible del botón. |
| mono | boolean | true | Usa tipografía monoespaciada. |
| prefix | string | - | Prefijo apagado, fuera del valor copiable. |
SuffixInput
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| suffix | React.ReactNode | - | Unidad fija a la derecha. |
| ...props | React.ComponentProps<"input"> | - | Atributos nativos, por ejemplo type, min y step. |
InputGroup / InputGroupAddon
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
| align | "start" | "end" | "start" | En InputGroupAddon define el borde del prefijo o sufijo. |
| children | React.ReactNode | - | Composición con InputGroupInput, InputGroupIcon y InputGroupButton. |
Cuándo sí y cuándo no
Hacé
- Elegí la variante de propósito antes de componer a mano.
- Mantené las unidades fuera del valor editable.
- Confirmá la copia solo después de que el portapapeles responda.
Evitá
- No incluyas secretos reales en los ejemplos.
- No muestres un atajo que no funciona.
- No uses un prefijo largo que deje sin espacio al valor en móvil.
Accesibilidad
- Asociá el label al id del input, no al contenedor.
- Tab recorre el campo y la acción; Enter envía si el botón es submit.
- El botón de copiar necesita un nombre accesible concreto.