Перейти к содержимому
Разделы
Язык, сейчас Русский
Дизайн-система Rastrillo

Выбор и действия

Элементы управления, которыми выбирают из вариантов, и строки, которыми форма завершается. Все они лежат в rst-box с отступами, потому что именно это предполагают эти партиалы.

Каждый пример работает, но его ссылки никуда не ведут. HTML показывает разметку для копирования.

field-select

Обёртка field вокруг родного select. Больше десяти вариантов — и select включает комбобокс с фильтром из select.js; отправляет форма всё равно родной select.

Три варианта: обычный select

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_few">Роль</label>
  <select rst-input id="sel_few" name="sel_few">
    <option value="admin" selected>Администратор</option>
    <option value="editor">Редактор</option>
    <option value="member">Участник</option>
  </select>
</div>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "field-select" dict
    "ID" "sel_few"
    "Name" "sel_few"
    "Label" "Роль"
    "Options" (list
        (dict "Label" "Администратор" "Selected" true "Value" "admin")
        (dict "Label" "Редактор" "Value" "editor")
        (dict "Label" "Участник" "Value" "member"))}}

Двенадцать вариантов: комбобокс с фильтром

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_many">Город</label>
  <select rst-input id="sel_many" name="sel_many" data-rst-select data-rst-select-filter="Введите текст для фильтра" data-rst-select-results="Результатов: {n}" data-rst-select-result-one="1 результат" data-rst-select-no-matches="Совпадений нет" aria-describedby="sel_many-help">
    <option value="dublin" selected>Дублин</option>
    <option value="lisbon">Лиссабон</option>
    <option value="madrid">Мадрид</option>
    <option value="berlin">Берлин</option>
    <option value="rome">Рим</option>
    <option value="athens">Афины</option>
    <option value="helsinki">Хельсинки</option>
    <option value="reykjavík">Рейкьявик</option>
    <option value="warsaw">Варшава</option>
    <option value="zagreb">Загреб</option>
    <option value="tallinn">Таллин</option>
    <option value="valletta">Валлетта</option>
  </select>
  <p rst-field-help id="sel_many-help">Начните вводить, чтобы отфильтровать.</p>
</div>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "field-select" dict
    "ID" "sel_many"
    "Name" "sel_many"
    "Label" "Город"
    "Options" (list
        (dict "Label" "Дублин" "Selected" true "Value" "dublin")
        (dict "Label" "Лиссабон" "Value" "lisbon")
        (dict "Label" "Мадрид" "Value" "madrid")
        (dict "Label" "Берлин" "Value" "berlin")
        (dict "Label" "Рим" "Value" "rome")
        (dict "Label" "Афины" "Value" "athens")
        (dict "Label" "Хельсинки" "Value" "helsinki")
        (dict "Label" "Рейкьявик" "Value" "reykjavík")
        (dict "Label" "Варшава" "Value" "warsaw")
        (dict "Label" "Загреб" "Value" "zagreb")
        (dict "Label" "Таллин" "Value" "tallinn")
        (dict "Label" "Валлетта" "Value" "valletta"))
    "Help" "Начните вводить, чтобы отфильтровать."}}

Больше десяти вариантов — и партиал включает select.js. Меньше десяти — нет: поиск по горстке пунктов это мебель, а не помощь.

Двенадцать вариантов, Plain: от улучшения отказались

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_plain">Город</label>
  <select rst-input id="sel_plain" name="sel_plain">
    <option value="dublin" selected>Дублин</option>
    <option value="lisbon">Лиссабон</option>
    <option value="madrid">Мадрид</option>
    <option value="berlin">Берлин</option>
    <option value="rome">Рим</option>
    <option value="athens">Афины</option>
    <option value="helsinki">Хельсинки</option>
    <option value="reykjavík">Рейкьявик</option>
    <option value="warsaw">Варшава</option>
    <option value="zagreb">Загреб</option>
    <option value="tallinn">Таллин</option>
    <option value="valletta">Валлетта</option>
  </select>
</div>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "field-select" dict
    "ID" "sel_plain"
    "Name" "sel_plain"
    "Label" "Город"
    "Options" (list
        (dict "Label" "Дублин" "Selected" true "Value" "dublin")
        (dict "Label" "Лиссабон" "Value" "lisbon")
        (dict "Label" "Мадрид" "Value" "madrid")
        (dict "Label" "Берлин" "Value" "berlin")
        (dict "Label" "Рим" "Value" "rome")
        (dict "Label" "Афины" "Value" "athens")
        (dict "Label" "Хельсинки" "Value" "helsinki")
        (dict "Label" "Рейкьявик" "Value" "reykjavík")
        (dict "Label" "Варшава" "Value" "warsaw")
        (dict "Label" "Загреб" "Value" "zagreb")
        (dict "Label" "Таллин" "Value" "tallinn")
        (dict "Label" "Валлетта" "Value" "valletta"))
    "Plain" true}}

Обязательное, с ошибкой

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_error">Роль</label>
  <select rst-input id="sel_error" name="sel_error" required aria-invalid="true" aria-describedby="sel_error-error">
    <option value="admin" selected>Администратор</option>
    <option value="editor">Редактор</option>
    <option value="member">Участник</option>
  </select>
  <p rst-field-error id="sel_error-error" role="alert">Выберите роль для этого человека.</p>
</div>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "field-select" dict
    "ID" "sel_error"
    "Name" "sel_error"
    "Label" "Роль"
    "Options" (list
        (dict "Label" "Администратор" "Selected" true "Value" "admin")
        (dict "Label" "Редактор" "Value" "editor")
        (dict "Label" "Участник" "Value" "member"))
    "Required" true
    "Error" "Выберите роль для этого человека."}}

Написан вручную, с optgroup

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<div rst-field>
  <label rst-field-label for="sel_grouped">Регион</label>
  <select rst-input id="sel_grouped" name="sel_grouped" data-rst-select data-rst-select-filter="Введите текст для фильтра" data-rst-select-results="Результатов: {n}" data-rst-select-result-one="1 результат" data-rst-select-no-matches="Совпадений нет">
    <optgroup label="Европа">
      <option value="dublin" selected>Дублин</option>
      <option value="lisbon">Лиссабон</option>
      <option value="warsaw">Варшава</option>
    </optgroup>
    <optgroup label="Америка">
      <option value="montreal">Монреаль</option>
      <option value="lima">Лима</option>
      <option value="austin">Остин</option>
    </optgroup>
    <optgroup label="Азия">
      <option value="osaka">Осака</option>
      <option value="hanoi">Ханой</option>
      <option value="dhaka">Дакка</option>
    </optgroup>
    <optgroup label="Африка">
      <option value="accra">Аккра</option>
      <option value="nairobi">Найроби</option>
      <option value="tunis">Тунис</option>
    </optgroup>
  </select>
</div>

Options здесь — плоский список, поэтому сгруппированные варианты пишет разметкой само приложение. select.js отрисовывает группы, а не выбрасывает их; написанный вручную select может и вовсе отказаться от улучшения через data-rst-select="false".

field-check

Единственный механизм переключения: настоящий флажок с видом тумблера, клавиатура и экранный диктор не страдают.

Включено

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<label rst-switch>
  <input type="checkbox" name="notify_on" checked>
  <span rst-switch-track aria-hidden="true"></span>
  Сообщать мне об ответах по почте
</label>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "field-check" dict
    "Name" "notify_on"
    "Label" "Сообщать мне об ответах по почте"
    "Checked" true}}

Выключено

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<label rst-switch>
  <input type="checkbox" name="notify_off">
  <span rst-switch-track aria-hidden="true"></span>
  Сообщать мне об упоминаниях по почте
</label>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "field-check" dict
    "Name" "notify_off"
    "Label" "Сообщать мне об упоминаниях по почте"}}

Отключено

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<label rst-switch>
  <input type="checkbox" name="notify_disabled" disabled>
  <span rst-switch-track aria-hidden="true"></span>
  Присылать еженедельную сводку
</label>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "field-check" dict
    "Name" "notify_disabled"
    "Label" "Присылать еженедельную сводку"
    "Disabled" true}}

choice-field

Карточки вариантов: предпочтительная группа радиокнопок или флажков. Title остаётся коротким, объясняет Desc.

Выберите одно

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<fieldset rst-choice>
  <legend>Тариф</legend>
  <div rst-choice-cards>
    <label>
      <input type="radio" name="plan" value="free">
      <span>
        <span rst-choice-title>Бесплатно</span>
        <span rst-choice-desc>Один проект, поддержка сообщества.</span>
      </span>
    </label>
    <label>
      <input type="radio" name="plan" value="pro" checked>
      <span>
        <span rst-choice-title>Про</span>
        <span rst-choice-desc>Неограниченное число проектов и ящик поддержки.</span>
      </span>
    </label>
  </div>
</fieldset>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "choice-field" dict
    "Legend" "Тариф"
    "Name" "plan"
    "Options" (list
        (dict "Desc" "Один проект, поддержка сообщества." "Title" "Бесплатно" "Value" "free")
        (dict "Checked" true "Desc" "Неограниченное число проектов и ящик поддержки." "Title" "Про" "Value" "pro"))}}

Выберите любые

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<fieldset rst-choice>
  <legend>Присылать мне</legend>
  <div rst-choice-cards>
    <label>
      <input type="checkbox" name="digest" value="replies" checked>
      <span>
        <span rst-choice-title>Ответы</span>
        <span rst-choice-desc>Сразу же.</span>
      </span>
    </label>
    <label>
      <input type="checkbox" name="digest" value="weekly">
      <span>
        <span rst-choice-title>Еженедельная сводка</span>
        <span rst-choice-desc>По понедельникам утром.</span>
      </span>
    </label>
  </div>
</fieldset>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "choice-field" dict
    "Legend" "Присылать мне"
    "Name" "digest"
    "Type" "checkbox"
    "Options" (list
        (dict "Checked" true "Desc" "Сразу же." "Title" "Ответы" "Value" "replies")
        (dict "Desc" "По понедельникам утром." "Title" "Еженедельная сводка" "Value" "weekly"))}}

form-foot

Завершающая строка формы: одна главная кнопка отправки и необязательная ссылка отмены. Отмена — ссылка, потому что уйти с формы значит перейти на другую страницу.

Сохранить и отменить

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit">Сохранить запись</button>
  <a rst-btn="lg" href="/posts">К записям</a>
</div>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "form-foot" dict
    "Submit" "Сохранить запись"
    "CancelHref" "/posts"
    "CancelLabel" "К записям"}}

Только отправка

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit">Отправить приглашение</button>
</div>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "form-foot" dict "Submit" "Отправить приглашение"}}

В покое: кнопка до того, как что-либо произошло

Предпросмотр

Поместите это внутрь <section rst-box><form rst-form>.

<div rst-form-foot>
  <button rst-btn="primary lg" type="submit">Опубликовать</button>
  <a rst-btn="lg" href="/posts">К записям</a>
</div>

Поместите это внутрь <section rst-box><form rst-form>.

{{template "form-foot" dict
    "Submit" "Опубликовать"
    "CancelHref" "/posts"
    "CancelLabel" "К записям"}}

Кнопка, которая ЧТО-ТО МЕНЯЕТ, получает состояние загрузки; кнопка, которая лишь что-то раскрывает — раскрывающийся блок, выпадающее меню, вкладка, — нет. rastrillo.js применяет это правило к каждой кнопке отправки в каждой форме, включать ничего не нужно.

В работе: то, что rastrillo.js пишет на выходе

Предпросмотр

Поместите это внутрь <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>
    Опубликовать
  </button>
  <a rst-btn="lg" href="/posts">К записям</a>
</div>

Только та кнопка, по которой нажали: все остальные кнопки отправки в форме сохраняют своё name и value, а сама форма защищена от повторной отправки. data-busy="false" отключает правило — на форме или на одной кнопке; data-busy-label заменяет текст. Без скриптов ничего этого не происходит и форма отправляется ровно так же, как всегда, так что идемпотентность по-прежнему забота сервера.

confirm-form

Строка действий на странице подтверждения. У необратимых действий свой маршрут, а не диалог, поэтому писать скрипт не для чего.

Необратимое действие

Предпросмотр

Поместите это внутрь <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">Отмена</a>
  <button type="submit" rst-btn="danger lg">Удалить эту запись</button>
</form>

Поместите это внутрь <section rst-box>.

{{template "confirm-form" dict
    "Action" "/posts/1/delete"
    "Label" "Удалить эту запись"
    "Danger" true
    "Hidden" (list (list "csrf" "tok-123") (list "id" "1"))
    "CancelHref" "/posts/1"}}

Обычный случай, текст отмены по умолчанию

Предпросмотр

Поместите это внутрь <section rst-box>.

<form rst-form-actions method="post" action="/posts/1/publish">
  <a rst-btn="ghost lg" href="/posts/1">Отмена</a>
  <button type="submit" rst-btn="primary lg">Опубликовать</button>
</form>

Поместите это внутрь <section rst-box>.

{{template "confirm-form" dict
    "Action" "/posts/1/publish"
    "Label" "Опубликовать"
    "CancelHref" "/posts/1"}}

CancelLabel не задан, поэтому текст ссылки отмены берётся из каталога.