laterite-admin 0.9.13

Laterite CMF admin surface: the Axum router, session middleware, and login shell
Documentation
{# A list of rows. Each cell's control is rendered by its own field type, so a
   switch in a row behaves as a switch and a date as a date.

   Two layouts. Inline puts every field of every row on the page at once, which
   suits one narrow column. List collapses a row to the line that names it and
   opens one at a time, which is what a row of several fields needs: four
   columns side by side is a table nobody can read. `details` carries the open
   state, so the collapse works before any script runs. The move and duplicate
   controls need the script and start hidden. #}
{% macro tools() %}
<span class="lat-repeater__tools">
  {% if data.reorder %}
  <button class="lat-btn lat-btn--ghost lat-btn--sm lat-btn--icon lat-repeater__up" type="button" aria-label="{{ data.text.up }}" title="{{ data.text.up }}" hidden></button>
  <button class="lat-btn lat-btn--ghost lat-btn--sm lat-btn--icon lat-repeater__down" type="button" aria-label="{{ data.text.down }}" title="{{ data.text.down }}" hidden></button>
  {% endif %}
  {% if data.duplicate %}
  <button class="lat-btn lat-btn--ghost lat-btn--sm lat-btn--icon lat-repeater__duplicate" type="button" aria-label="{{ data.text.duplicate }}" title="{{ data.text.duplicate }}" hidden></button>
  {% endif %}
  <button class="lat-btn lat-btn--ghost lat-btn--sm lat-repeater__remove" type="button">{{ data.text.remove }}</button>
</span>
{% endmacro %}
{% macro cells(row) %}
{% for cell in row %}
<div class="lat-field">
  <span class="lat-field__label">{{ cell.label }}</span>
  {{ cell.control|safe }}
  {% if !cell.help.is_empty() %}<p class="lat-help">{{ cell.help }}</p>{% endif %}
</div>
{% endfor %}
{% endmacro %}
<div class="lat-repeater{% if data.list %} lat-repeater--list{% endif %}" data-lat-widget="repeater" data-lat-enter-scope="rows"{% if !data.reorder %} data-lat-reorder="off"{% endif %}{% if !data.duplicate %} data-lat-duplicate="off"{% endif %}>
  <div class="lat-repeater__rows{% if !data.rows.is_empty() %} lat-repeater__rows--filled{% endif %}">
    {% for row in data.rows %}
    {% if data.list %}
    <details class="lat-repeater__row">
      <summary class="lat-repeater__summary">
        <span class="lat-repeater__title">{% let text = data.summaries[loop.index0].clone() %}{% if text.is_empty() %}<span class="lat-repeater__untitled">{{ data.text.untitled }}</span>{% else %}{{ text }}{% endif %}</span>
        {% call tools() %}
      </summary>
      <div class="lat-repeater__fields">
        {% call cells(row) %}
      </div>
    </details>
    {% else %}
    <div class="lat-repeater__row">
      {% call cells(row) %}
      {% call tools() %}
    </div>
    {% endif %}
    {% endfor %}
  </div>
  {# Inert until cloned: a template's controls are not submitted and are not
     form-associated, so the placeholder index never reaches the server. A
     cloned list row is added open, since the operator just asked for it. #}
  <template class="lat-repeater__blank">
    {% if data.list %}
    <details class="lat-repeater__row" open>
      <summary class="lat-repeater__summary">
        <span class="lat-repeater__title"><span class="lat-repeater__untitled">{{ data.text.untitled }}</span></span>
        {% call tools() %}
      </summary>
      <div class="lat-repeater__fields">
        {% call cells(data.blank) %}
      </div>
    </details>
    {% else %}
    <div class="lat-repeater__row">
      {% call cells(data.blank) %}
      {% call tools() %}
    </div>
    {% endif %}
  </template>
  <button class="lat-btn lat-btn--secondary lat-btn--sm lat-repeater__add" type="button" data-lat-summary-index="{{ data.summary_index }}">+ {{ data.text.add }}</button>
</div>