Saltar al contenido
ssntl/ui

Input group

Un formulario de un solo campo con el botón adentro, con esquinas concéntricas al campo.

Instalación

$ pnpm dlx shadcn@latest add @ssntl/input-group

Uso

tsx
import { InputGroup, InputGroupButton, InputGroupInput } from "@/components/ui/input-group"

<InputGroup>
  <InputGroupInput aria-label="Buscar" placeholder="Órdenes, clientes…" />
  <InputGroupButton aria-label="Buscar"><Search /></InputGroupButton>
</InputGroup>
  • Esquinas anidadas: el campo es una píldora con 4px de padding alrededor de un botón píldora de 48px, y ambas curvas comparten centro.
  • Solo para formularios de un campo: búsqueda, newsletter, un prompt.
  • El botón solo de ícono necesita aria-label; el campo también, si no hay etiqueta visible.

Código

components/ui/input-group.tsx
import * as React from "react"

import { cn } from "@/lib/utils"
import { Button } from "@/registry/ssntl/ui/button"

// A single-field form (search, newsletter, prompt) with the submit button set
// inside the field. Nested corners: a pill with 4px padding around a 48px pill
// button, so both curves share one center (outer = inner + padding).
function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
  return (
    <div
      data-slot="input-group"
      className={cn(
        "flex w-full min-w-0 items-center gap-1 rounded-full border border-input bg-card p-1 pl-5",
        "transition-[border-color] duration-(--duration-fast) ease-out hover:border-muted-foreground",
        "focus-within:border-foreground has-[input:focus-visible]:outline-2 has-[input:focus-visible]:outline-offset-2 has-[input:focus-visible]:outline-ring",
        "has-aria-invalid:border-destructive",
        className
      )}
      {...props}
    />
  )
}

function InputGroupInput({
  className,
  ...props
}: React.ComponentProps<"input">) {
  return (
    <input
      data-slot="input-group-input"
      className={cn(
        "h-12 min-w-0 flex-1 bg-transparent text-body text-foreground outline-none placeholder:text-muted-foreground",
        className
      )}
      {...props}
    />
  )
}

function InputGroupButton({
  size = "icon",
  className,
  ...props
}: React.ComponentProps<typeof Button>) {
  return (
    <Button
      data-slot="input-group-button"
      size={size}
      className={cn("shrink-0", className)}
      {...props}
    />
  )
}

export { InputGroup, InputGroupButton, InputGroupInput }