{# The parts every page is built from (docs/UI.md, section 7). A page has no markup of its own for a button, a tag, a field, a card header, a pager or a filter bar. #} {% macro empty(text) %}
{{ text }}
{% endmacro %} {# An icon of the sprite, static/img/icons.svg (docs/UI.md 7), by its name in Bootstrap Icons. Without ``label`` a word or the control around it says what it is, so it is hidden from screen readers. With ``label`` it stands alone: read out, and shown as a tooltip. #} {% macro icon(name, label=None) %}{{ _svg(name, label) }}{% endmacro %} {% macro _svg(name, label=None) %}{% if label %}{{ label }}{% else %}{% endif %}{% endmacro %} {# The inside of a button or link with an icon: the word beside it, or with ``alone`` the icon by itself and the word as its tooltip. ``word`` brings the word back where no tooltip shows, on a touch screen or in a narrow window, for an icon that is not plain on its own. #} {% macro _face(name, label, alone=False, word=False) %}{% if alone %}{{ _svg(name) }}{% if word %}{{ label }}{% endif %}{% else %}{{ _svg(name) }} {{ label }}{% endif %}{% endmacro %} {# The attributes of a control that asks before it goes on: the question, the word of its button, red for what cannot be undone. #} {% macro _asks(confirm, label, danger) %}{% if confirm %} data-confirm="{{ confirm }}" data-confirm-label="{{ label }}"{% if danger %} data-confirm-danger{% endif %}{% endif %}{% endmacro %} {# A link that looks like a button, with an icon. ``alone``: the icon only, ``label`` its tooltip and ``name`` what a screen reader hears. #} {% macro link_button(url, label, icon, alone=False, word=False, cls="small", name=None) %}{{ _face(icon, label, alone, word) }}{% endmacro %} {# A button that copies the text of the element ``target`` (an id), e.g. a secret shown once. Needs app.js: without it, the text is selectable in one piece. #} {% macro copy_button(target, label="Copy") %}{% endmacro %} {# A time in a list: under a day ago as "3 minutes ago", older as date and time. The full stamp is always the tooltip. #} {% macro ago(value) %}{% if value %}{% else %}—{% endif %}{% endmacro %} {# The line under a detail page's heading: where the page sits. ``trail``: (label, url) pairs, the last one the page itself. #} {% macro breadcrumb(trail) %} {% endmacro %} {# A card with a heading. ``meta``: the small line beside it, ``tags``: markup at the right, ``tight``: the body without padding, for a table. #} {% macro card(title=None, meta=None, tags=None, tight=False, id=None) %}
{% if title %}

{{ title }}

{% if meta is not none %}{{ meta }}{% endif %}{% if tags %}{{ tags }}{% endif %}
{% endif %}
{{ caller() }}
{% endmacro %} {# Whether a webhook's last post went through. ``detail``: the error under the tag. #} {% macro webhook_state(error, detail=True) %}{% if error %}failing{% if detail %}{{ error }}{% endif %}{% else %}ok{% endif %}{% endmacro %} {# Rights as tags: groups first, then single operations. #} {% macro rights_tags(groups, operations) %}{% for group in groups %}{{ group }} {% endfor %}{% for operation in operations %}{{ operation }} {% endfor %}{% endmacro %} {# A secret the service shows once, e.g. a new token: ``title``, the value in one selectable piece, and the caller says what to do with it. #} {% macro secret_card(title, value, id="secret") %}

{{ title }}

shown this once

{{ caller() }}

{{ value }}{{ copy_button(id) }}
{% endmacro %} {# The first card of a detail page: what the record is, read-only. The caller gives ``fact`` lines. #} {% macro facts(title, tags=None) %}

{{ title }}

{% if tags %}{{ tags }}{% endif %}
{{ caller() }}
{% endmacro %} {% macro fact(label, value=None, cls="") %}
{{ label }}
{% if caller is defined %}{{ caller() }}{% else %}{{ value | or_missing }}{% endif %}{% endmacro %} {# A list that belongs to the record of a detail page, e.g. its tokens (docs/UI.md 4.2). It creates at its head and acts on its rows. ``add``: the word of the plus in its header, which opens the form ``add_form`` (an id, a ``row_form`` at the head of the table). #} {% macro related(title, count=None, meta=None, add=None, add_form=None, add_open=False) %}

{{ title }}

{% if count is not none %}{{ count }}{% endif %}{% if meta %}{{ meta }}{% endif %}{% if add %}{{ toggle(add_form, add, "plus-lg", alone=False, open=add_open) }}{% endif %}
{{ caller() }}
{% endmacro %} {# A button that opens and closes the form ``target`` (an id) at a row or at the head of a list. ``alone``: the icon by itself, ``label`` its tooltip and ``name`` what a screen reader hears. #} {% macro toggle(target, label, icon, alone=True, name=None, open=False) %}{% endmacro %} {# The form that unfolds under a row of a table, or at the head of the list: a row of its own across ``columns``. Without app.js it shows at once. ``open``: unfolded, e.g. when the list is still empty or a form came back refused. #} {% macro row_form(id, columns, open=False) %}{{ caller() }}{% endmacro %} {# A form in a dialog of its own, for a step that asks for more than a yes, e.g. removing a device with the password and a code. The caller gives the form, with ``dialog_buttons`` at its end. Opened by a ``dialog_button``. Without app.js it shows in the page. #} {% macro form_dialog(id, title, text) %}

{{ title }}

{{ text }}

{{ caller() }}
{% endmacro %} {% macro dialog_buttons(label, icon="trash", danger=True) %}
{% endmacro %} {# The button that opens the ``form_dialog`` ``target``: an icon alone on a row, or with its word in a batch bar. #} {% macro dialog_button(target, label, icon="trash", alone=True, name=None, danger=True) %}{% endmacro %} {# The tick box of a row for a batch: it belongs to the form ``batch`` (an id) above the table. ``all`` in the head ticks every row. #} {% macro tick(batch, name, value, label) %}{% endmacro %} {% macro tick_all(batch, name, label="Tick all") %}{% endmacro %} {# The hidden field every form that changes something carries. #} {% macro csrf_field() %}{% endmacro %} {# A form that is only a button, e.g. "Delete", with a question before it. ``icon``: an icon beside the word, or with ``alone`` in its place, the word then the tooltip and ``name`` what a screen reader hears, e.g. "Revoke the token laptop". A ``danger`` button asks with a red one. #} {% macro action(url, label, confirm=None, cls="small", fields={}, icon=None, alone=False, word=False, name=None) %}
{{ csrf_field() }} {% for key, value in fields.items() %}{% endfor %} {% if icon and alone %} {% elif icon %} {% else %}{% endif %}
{% endmacro %} {# A labelled input. #} {% macro field(name, label, value="", type="text", hint=None, required=False, cls="", attrs="") %}
{% if hint %}{{ hint }}{% endif %}
{% endmacro %} {# The filter bar above a list (filters.py): the search first, more filters folded, the active ones as chips under it. A GET form, so a filtered list has its URL. ``focus``: the cursor starts in the search, for a page that is a search. #} {% macro filter_bar(bar, problem=None, focus=False) %} {% if problem %}{% endif %} {% endmacro %} {# The links below a list: to the next page, and from a later page back to the first. ``pages``: (more, first), as ``page_links`` gives them. ``back`` and ``on`` name the links: by default for a list newest first. #} {% macro pager(pages, back="Newest", on="Older") %} {% set more, first = pages %} {% if more or first %}
{% if first %}{{ back }}{% endif %} {% if more %}{{ on }}{% endif %}
{% endif %} {% endmacro %} {# The options of a folder select: the tree indented by depth, without the folders in ``exclude``. #} {% macro folder_options(tree, exclude, selected=None) %} {% for folder, depth in tree %}{% if folder.id not in exclude %}{% endif %}{% endfor %} {% endmacro %} {# The tag beside an account whose user may read mail there and send it to any address: what an injected instruction needs to carry data out. #} {% macro anywhere_tag() %}reads and sends anywhere{% endmacro %} {# The stored credentials of an account, one per line. #} {% macro credentials(stored) %}{% for credential in stored %}{{ credential.field }}, changed {{ credential.updated_at | when }}{% if not loop.last %}
{% endif %}{% else %}—{% endfor %}{% endmacro %} {# The last card of a detail page, always: its one red button removes the record, after a question. ``text`` says what happens and what stays. #} {% macro danger_card(title, text, url, label, confirm) %}

{{ title }}

{{ text }} {{ action(url, label, confirm=confirm, cls="danger", icon="trash") }}
{% endmacro %} {# The tabs of a detail page, each a URL of its own (``?tab=``), drawn by the service: no script. ``items``: (key, label) pairs, ``active`` the key shown, ``base`` the page. #} {% macro tabs(items, active, base) %} {% endmacro %}