Ir al contenido
Secciones
Idioma, actualmente Español
Sistema de diseño Rastrillo

Opciones y acciones

Los controles con los que se elige entre opciones, y las filas que cierran un formulario. Cada uno va dentro de un rst-box con relleno, porque esa es la regla que estos parciales dan por hecha.

Cada ejemplo funciona de verdad, pero sus enlaces no llevan a ninguna parte. HTML muestra el marcado para copiar.

field-select

La envoltura de field alrededor de un select nativo. A partir de diez opciones, el select activa el combo con filtro de select.js; lo que el formulario envía sigue siendo el select nativo.

Tres opciones: un select nativo

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_few">Rol</label>
  <select rst-input id="sel_few" name="sel_few">
    <option value="admin" selected>Administrador</option>
    <option value="editor">Editor</option>
    <option value="member">Miembro</option>
  </select>
</div>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "field-select" dict
    "ID" "sel_few"
    "Name" "sel_few"
    "Label" "Rol"
    "Options" (list
        (dict "Label" "Administrador" "Selected" true "Value" "admin")
        (dict "Label" "Editor" "Value" "editor")
        (dict "Label" "Miembro" "Value" "member"))}}

Doce opciones: el combo con filtro

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_many">Ciudad</label>
  <select rst-input id="sel_many" name="sel_many" data-rst-select data-rst-select-filter="Escribe para filtrar" data-rst-select-results="{n} resultados" data-rst-select-result-one="1 resultado" data-rst-select-no-matches="No hay coincidencias" aria-describedby="sel_many-help">
    <option value="dublin" selected>Dublín</option>
    <option value="lisbon">Lisboa</option>
    <option value="madrid">Madrid</option>
    <option value="berlin">Berlín</option>
    <option value="rome">Roma</option>
    <option value="athens">Atenas</option>
    <option value="helsinki">Helsinki</option>
    <option value="reykjavík">Reikiavik</option>
    <option value="warsaw">Varsovia</option>
    <option value="zagreb">Zagreb</option>
    <option value="tallinn">Tallin</option>
    <option value="valletta">La Valeta</option>
  </select>
  <p rst-field-help id="sel_many-help">Empieza a escribir para filtrar.</p>
</div>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "field-select" dict
    "ID" "sel_many"
    "Name" "sel_many"
    "Label" "Ciudad"
    "Options" (list
        (dict "Label" "Dublín" "Selected" true "Value" "dublin")
        (dict "Label" "Lisboa" "Value" "lisbon")
        (dict "Label" "Madrid" "Value" "madrid")
        (dict "Label" "Berlín" "Value" "berlin")
        (dict "Label" "Roma" "Value" "rome")
        (dict "Label" "Atenas" "Value" "athens")
        (dict "Label" "Helsinki" "Value" "helsinki")
        (dict "Label" "Reikiavik" "Value" "reykjavík")
        (dict "Label" "Varsovia" "Value" "warsaw")
        (dict "Label" "Zagreb" "Value" "zagreb")
        (dict "Label" "Tallin" "Value" "tallinn")
        (dict "Label" "La Valeta" "Value" "valletta"))
    "Help" "Empieza a escribir para filtrar."}}

A partir de diez opciones, el parcial activa select.js. Por debajo de diez, no: buscar entre un puñado de elementos es mobiliario, no ayuda.

Doce opciones, Plain: la mejora rechazada

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_plain">Ciudad</label>
  <select rst-input id="sel_plain" name="sel_plain">
    <option value="dublin" selected>Dublín</option>
    <option value="lisbon">Lisboa</option>
    <option value="madrid">Madrid</option>
    <option value="berlin">Berlín</option>
    <option value="rome">Roma</option>
    <option value="athens">Atenas</option>
    <option value="helsinki">Helsinki</option>
    <option value="reykjavík">Reikiavik</option>
    <option value="warsaw">Varsovia</option>
    <option value="zagreb">Zagreb</option>
    <option value="tallinn">Tallin</option>
    <option value="valletta">La Valeta</option>
  </select>
</div>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "field-select" dict
    "ID" "sel_plain"
    "Name" "sel_plain"
    "Label" "Ciudad"
    "Options" (list
        (dict "Label" "Dublín" "Selected" true "Value" "dublin")
        (dict "Label" "Lisboa" "Value" "lisbon")
        (dict "Label" "Madrid" "Value" "madrid")
        (dict "Label" "Berlín" "Value" "berlin")
        (dict "Label" "Roma" "Value" "rome")
        (dict "Label" "Atenas" "Value" "athens")
        (dict "Label" "Helsinki" "Value" "helsinki")
        (dict "Label" "Reikiavik" "Value" "reykjavík")
        (dict "Label" "Varsovia" "Value" "warsaw")
        (dict "Label" "Zagreb" "Value" "zagreb")
        (dict "Label" "Tallin" "Value" "tallinn")
        (dict "Label" "La Valeta" "Value" "valletta"))
    "Plain" true}}

Obligatorio, con un error

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_error">Rol</label>
  <select rst-input id="sel_error" name="sel_error" required aria-invalid="true" aria-describedby="sel_error-error">
    <option value="admin" selected>Administrador</option>
    <option value="editor">Editor</option>
    <option value="member">Miembro</option>
  </select>
  <p rst-field-error id="sel_error-error" role="alert">Elige un rol para esta persona.</p>
</div>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "field-select" dict
    "ID" "sel_error"
    "Name" "sel_error"
    "Label" "Rol"
    "Options" (list
        (dict "Label" "Administrador" "Selected" true "Value" "admin")
        (dict "Label" "Editor" "Value" "editor")
        (dict "Label" "Miembro" "Value" "member"))
    "Required" true
    "Error" "Elige un rol para esta persona."}}

Escrito a mano, con optgroups

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_grouped">Región</label>
  <select rst-input id="sel_grouped" name="sel_grouped" data-rst-select data-rst-select-filter="Escribe para filtrar" data-rst-select-results="{n} resultados" data-rst-select-result-one="1 resultado" data-rst-select-no-matches="No hay coincidencias">
    <optgroup label="Europa">
      <option value="dublin" selected>Dublín</option>
      <option value="lisbon">Lisboa</option>
      <option value="warsaw">Varsovia</option>
    </optgroup>
    <optgroup label="América">
      <option value="montreal">Montreal</option>
      <option value="lima">Lima</option>
      <option value="austin">Austin</option>
    </optgroup>
    <optgroup label="Asia">
      <option value="osaka">Osaka</option>
      <option value="hanoi">Hanói</option>
      <option value="dhaka">Daca</option>
    </optgroup>
    <optgroup label="África">
      <option value="accra">Acra</option>
      <option value="nairobi">Nairobi</option>
      <option value="tunis">Túnez</option>
    </optgroup>
  </select>
</div>

Aquí Options es una lista plana, así que las opciones agrupadas son marcado que escribe la aplicación. select.js dibuja los grupos en vez de descartarlos; un select escrito a mano también puede rechazar la mejora del todo con data-rst-select="false".

field-check

El único mecanismo de conmutación: una casilla de verificación real con aspecto de interruptor, sin perder teclado ni lector de pantalla.

Encendido

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<label rst-switch>
  <input type="checkbox" name="notify_on" checked>
  <span rst-switch-track aria-hidden="true"></span>
  Avisarme por correo de las respuestas
</label>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "field-check" dict
    "Name" "notify_on"
    "Label" "Avisarme por correo de las respuestas"
    "Checked" true}}

Apagado

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<label rst-switch>
  <input type="checkbox" name="notify_off">
  <span rst-switch-track aria-hidden="true"></span>
  Avisarme por correo de las menciones
</label>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "field-check" dict
    "Name" "notify_off"
    "Label" "Avisarme por correo de las menciones"}}

Desactivado

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<label rst-switch>
  <input type="checkbox" name="notify_disabled" disabled>
  <span rst-switch-track aria-hidden="true"></span>
  Enviarme un resumen semanal por correo
</label>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "field-check" dict
    "Name" "notify_disabled"
    "Label" "Enviarme un resumen semanal por correo"
    "Disabled" true}}

choice-field

Tarjetas de opción: el grupo de radios o casillas que preferimos. Title se mantiene corto; Desc explica.

Elige una

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<fieldset rst-choice>
  <legend>Plan</legend>
  <div rst-choice-cards>
    <label>
      <input type="radio" name="plan" value="free">
      <span>
        <span rst-choice-title>Gratis</span>
        <span rst-choice-desc>Un proyecto, soporte de la comunidad.</span>
      </span>
    </label>
    <label>
      <input type="radio" name="plan" value="pro" checked>
      <span>
        <span rst-choice-title>Pro</span>
        <span rst-choice-desc>Proyectos ilimitados y un buzón de soporte.</span>
      </span>
    </label>
  </div>
</fieldset>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "choice-field" dict
    "Legend" "Plan"
    "Name" "plan"
    "Options" (list
        (dict "Desc" "Un proyecto, soporte de la comunidad." "Title" "Gratis" "Value" "free")
        (dict "Checked" true "Desc" "Proyectos ilimitados y un buzón de soporte." "Title" "Pro" "Value" "pro"))}}

Elige las que quieras

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<fieldset rst-choice>
  <legend>Enviarme</legend>
  <div rst-choice-cards>
    <label>
      <input type="checkbox" name="digest" value="replies" checked>
      <span>
        <span rst-choice-title>Respuestas</span>
        <span rst-choice-desc>En el momento.</span>
      </span>
    </label>
    <label>
      <input type="checkbox" name="digest" value="weekly">
      <span>
        <span rst-choice-title>Resumen semanal</span>
        <span rst-choice-desc>Los lunes por la mañana.</span>
      </span>
    </label>
  </div>
</fieldset>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "choice-field" dict
    "Legend" "Enviarme"
    "Name" "digest"
    "Type" "checkbox"
    "Options" (list
        (dict "Checked" true "Desc" "En el momento." "Title" "Respuestas" "Value" "replies")
        (dict "Desc" "Los lunes por la mañana." "Title" "Resumen semanal" "Value" "weekly"))}}

form-foot

La fila final de un formulario: un envío principal y un enlace opcional para cancelar. Cancelar es un enlace, porque salir de un formulario es navegar.

Guardar y cancelar

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit">Guardar entrada</button>
  <a rst-btn="lg" href="/posts">Volver a las entradas</a>
</div>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "form-foot" dict
    "Submit" "Guardar entrada"
    "CancelHref" "/posts"
    "CancelLabel" "Volver a las entradas"}}

Solo enviar

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit">Enviar invitación</button>
</div>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "form-foot" dict "Submit" "Enviar invitación"}}

En reposo: el botón antes de que ocurra nada

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit">Publicar</button>
  <a rst-btn="lg" href="/posts">Volver a las entradas</a>
</div>

Pon esto dentro de <section rst-box><form rst-form>.

{{template "form-foot" dict
    "Submit" "Publicar"
    "CancelHref" "/posts"
    "CancelLabel" "Volver a las entradas"}}

Un botón que CAMBIA algo recibe un estado de carga; un botón que solo revela algo —una divulgación, un desplegable, una pestaña— no. rastrillo.js aplica esa regla a todos los botones de envío de todos los formularios, sin nada que activar.

Trabajando: lo que rastrillo.js escribe al salir el formulario

Vista previa

Pon esto dentro de <section rst-box><form rst-form>.

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit" aria-busy="true" disabled>
    <span rst-spin aria-hidden="true"></span>
    Publicar
  </button>
  <a rst-btn="lg" href="/posts">Volver a las entradas</a>
</div>

Solo el botón que se pulsó: los demás botones de envío del formulario conservan su nombre y su valor, y el formulario queda protegido frente a un segundo envío. data-busy="false" desactiva la regla, en el formulario o en un botón; data-busy-label sustituye el texto. Sin scripts no ocurre nada de esto y el formulario se envía igual que siempre, así que la idempotencia sigue siendo cosa del servidor.

confirm-form

La fila de acciones de la página de confirmación. Las acciones destructivas tienen su propia ruta en vez de un diálogo, así que no hay nada que programar.

Destructivo

Vista previa

Pon esto dentro de <section rst-box>.

<form rst-form-actions method="post" action="/posts/1/delete">
  <input type="hidden" name="csrf" value="tok-123">
  <input type="hidden" name="id" value="1">
  <a rst-btn="ghost lg" href="/posts/1">Cancelar</a>
  <button type="submit" rst-btn="danger lg">Eliminar esta entrada</button>
</form>

Pon esto dentro de <section rst-box>.

{{template "confirm-form" dict
    "Action" "/posts/1/delete"
    "Label" "Eliminar esta entrada"
    "Danger" true
    "Hidden" (list (list "csrf" "tok-123") (list "id" "1"))
    "CancelHref" "/posts/1"}}

Caso normal, con la redacción de cancelar por defecto

Vista previa

Pon esto dentro de <section rst-box>.

<form rst-form-actions method="post" action="/posts/1/publish">
  <a rst-btn="ghost lg" href="/posts/1">Cancelar</a>
  <button type="submit" rst-btn="primary lg">Publicar</button>
</form>

Pon esto dentro de <section rst-box>.

{{template "confirm-form" dict
    "Action" "/posts/1/publish"
    "Label" "Publicar"
    "CancelHref" "/posts/1"}}

Sin CancelLabel, así que el enlace de cancelar se redacta desde el catálogo.