HextaUI

useInvalidShake

Sacude un control de formulario cuando un intento de envío lo encuentra inválido, y nunca mientras alguien aún está escribiendo.

Submit without choosing.
"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { useInvalidShake } from "@/hooks/use-invalid-shake"

export function UseInvalidShakeDemo() {
  const ref = React.useRef<HTMLSelectElement>(null)
  const [sent, setSent] = React.useState<string>()
  useInvalidShake(ref)

  return (
    <form
      className="flex w-full max-w-xs flex-col gap-3"
      onSubmit={(event) => {
        event.preventDefault()
        setSent(String(new FormData(event.currentTarget).get("plan")))
      }}
    >
      <label className="flex flex-col gap-2 text-sm font-medium">
        Plan
        <select
          ref={ref}
          name="plan"
          required
          defaultValue=""
          className="h-9 rounded-md bg-muted px-2.5 text-sm font-normal outline-none focus-visible:ring-3 focus-visible:ring-focus-ring focus-visible:outline-hidden data-shake:motion-safe:animate-button-shake pointer-coarse:text-touch [&:user-invalid]:ring-2 [&:user-invalid]:ring-destructive/40"
        >
          <option value="" disabled>
            Choose a plan
          </option>
          <option value="hobby">Hobby</option>
          <option value="pro">Pro</option>
        </select>
      </label>
      <Button type="submit" size="sm">
        Continue
      </Button>
      <output className="text-sm text-muted-foreground">
        {sent ? `plan=${sent}` : "Submit without choosing."}
      </output>
    </form>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/use-invalid-shake.json

Añade el hook y todo lo que necesita a tu proyecto.

import { useInvalidShake } from "@/hooks/use-invalid-shake"
const ref = React.useRef<HTMLSelectElement>(null)
useInvalidShake(ref)

<form>
  <select
    ref={ref}
    required
    className="data-shake:motion-safe:animate-button-shake"
  >
    …
  </select>
  <button type="submit">Continue</button>
</form>

El hook solo define un atributo data-shake. El movimiento viene del animate-button-shake del tema, que se adjunta con data-shake:motion-safe:animate-button-shake. Input, Textarea, Native select e Input group de HextaUI ya incluyen esa clase y llaman al hook. Úsalo directamente para tus propios controles.

Una vibración responde a una pregunta: ¿por qué no funcionó mi envío? Por eso el hook solo vibra justo después de que alguien intente enviar, y nunca mientras sigue rellenando el formulario.

EventoDescripción
Submit attemptHacer clic en un botón de envío, pulsar Enter en un input del formulario, o el evento submit del formulario.
Invalid within 600msEl control dispara invalid, o tiene aria-invalid="true", data-invalid o :user-invalid. Eso cubre la validación nativa, los fields de Base UI y los errores del servidor definidos justo después de enviar.
Shakedata-shake se define durante 400ms. Vibra una vez por intento, lleguen cuantas señales lleguen.
  • Dentro de un InputGroup, vibra todo el grupo, no solo el control interior.
  • Con movimiento reducido, no se define nada. Acompaña siempre la vibración con un mensaje de error visible, ya que la vibración es una señal, no el mensaje.
  • El hook necesita que el control pertenezca a un <form>. Sin uno, no hace nada.
  • Desactívalo por control con el argumento enabled, o con shake={false} en los inputs de HextaUI.
  • Reiniciar el atributo reinicia la animación, así que un segundo intento fallido vibra de nuevo aunque el primero siga en marcha.
  • La validación nativa enfoca el primer control no válido al enviar. La vibración solo añade movimiento encima y nunca mueve el foco.
PropTipoPredeterminado
refEl control de formulario que se va a observar.
RefObject<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null>–
enabledSi debe vibrar.
booleantrue
AtributoDescripción
data-shakePresente durante 400ms tras un intento de envío fallido, en el control o en su InputGroup.

Input, Textarea, NativeSelect e InputGroup.