#5094 "Status and layout macros, and form classes". - formatting.STATES: one state -> (tone, label) map for up, down, stale, firing, pending, acknowledged, no_data, ok, warn, crit, resolved and inactive; status_info() reads it (an unmapped state is drawn dim under its own name). required() lets a macro refuse a missing argument. - _macros.html: status_badge and status_dot (a dot always carries its label, sr-only unless shown), stat_tile, empty_state, page_header (actions via a call block) and icon_button, which raises without a label. - steward.css: tone classes (badge-/dot-/stat- ok, warn, crit, info, dim), .sr-only, .page-header, .stat-tile, .empty-state, .btn-icon and the form classes .field, .field-label, .field-hint and .input. Info text is a 40% mix (--blue): the house --fs-info-fg falls to 4.18:1 on its tint over a hovered row, 40% holds 5.09:1. - First users: the Alerts list, history and maintenance pages (headers, state badges, empty states) and the alert forms' hints; the inline inp/lbl style sets on the monitor and host forms become .input / .field-label. - tests/core/test_macros.py renders each macro through the app environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
87 lines
4.1 KiB
HTML
87 lines
4.1 KiB
HTML
{# Shared template macros. Import with: {% from "_macros.html" import crumbs %} #}
|
||
|
||
{# Breadcrumb trail. `items` is a list of (label, href) pairs, ordered root→current.
|
||
Intermediate items with a truthy href render as links; the last item (or any
|
||
item with an empty href) renders as plain current text. #}
|
||
{% macro crumbs(items) -%}
|
||
<nav class="breadcrumb" aria-label="Breadcrumb">
|
||
{%- for label, href in items -%}
|
||
{%- if loop.last -%}
|
||
<span class="breadcrumb-cur" aria-current="page">{{ label }}</span>
|
||
{%- elif href -%}
|
||
<a href="{{ href }}">{{ label }}</a><span class="breadcrumb-sep">›</span>
|
||
{%- else -%}
|
||
<span>{{ label }}</span><span class="breadcrumb-sep">›</span>
|
||
{%- endif -%}
|
||
{%- endfor -%}
|
||
</nav>
|
||
{%- endmacro %}
|
||
|
||
{# A state as a labelled badge: up, down, stale, firing, pending, acknowledged,
|
||
no_data, ok, warn, crit, resolved, inactive (formatting.STATES). `state` may
|
||
be a string or an enum; `label` replaces the standard wording. #}
|
||
{% macro status_badge(state, label=none, title=none) -%}
|
||
{%- set tone, text = status_info(state) -%}
|
||
<span class="badge badge-{{ tone }}"{% if title %} title="{{ title }}"{% endif %}>{{ label or text }}</span>
|
||
{%- endmacro %}
|
||
|
||
{# A state as a coloured dot. The dot is never the only signal: its label is
|
||
read out to a screen reader, and shown beside it when `show_label`. #}
|
||
{% macro status_dot(state, label=none, show_label=false) -%}
|
||
{%- set tone, text = status_info(state) -%}
|
||
<span class="dot dot-{{ tone }}" aria-hidden="true" title="{{ label or text }}"></span>
|
||
{%- if show_label %}<span class="dot-label">{{ label or text }}</span>{% else %}<span class="sr-only">{{ label or text }}</span>{% endif %}
|
||
{%- endmacro %}
|
||
|
||
{# Threshold colouring: `threshold_style(value, kind)` (an inline style) and
|
||
`status_class(value, kind)` ('ok' / 'warn' / 'crit' / 'unknown') are jinja
|
||
globals reading the cutoffs from Settings → Thresholds. #}
|
||
|
||
{# One headline number. `state` (a STATES name, or a status_class result)
|
||
colours the value; `href` makes the whole tile a link. #}
|
||
{% macro stat_tile(label, value, state=none, hint=none, href=none) -%}
|
||
{%- set tone = status_info(state)[0] if state else none -%}
|
||
{%- set body -%}
|
||
<span class="stat-tile-label">{{ label }}</span>
|
||
<span class="stat-tile-value">{{ value }}</span>
|
||
{%- if hint %}<span class="stat-tile-hint">{{ hint }}</span>{% endif %}
|
||
{%- endset -%}
|
||
{%- if href -%}
|
||
<a href="{{ href }}" class="stat-tile{% if tone %} stat-{{ tone }}{% endif %}">{{ body }}</a>
|
||
{%- else -%}
|
||
<div class="stat-tile{% if tone %} stat-{{ tone }}{% endif %}">{{ body }}</div>
|
||
{%- endif -%}
|
||
{%- endmacro %}
|
||
|
||
{# What a list shows when it has nothing in it, with an optional next step. #}
|
||
{% macro empty_state(message, action_label=none, action_href=none) -%}
|
||
<div class="empty-state">
|
||
<p>{{ message }}</p>
|
||
{%- if action_label and action_href %}<a class="btn btn-ghost btn-sm" href="{{ action_href }}">{{ action_label }}</a>{% endif %}
|
||
</div>
|
||
{%- endmacro %}
|
||
|
||
{# The page's <h1>, with its actions to the right:
|
||
{% call page_header("Alerts") %}<a class="btn" href="…">New rule</a>{% endcall %} #}
|
||
{% macro page_header(title, subtitle=none) -%}
|
||
<div class="page-header">
|
||
<div>
|
||
<h1 class="page-title">{{ title }}</h1>
|
||
{%- if subtitle %}<p class="page-subtitle">{{ subtitle }}</p>{% endif %}
|
||
</div>
|
||
{%- if caller is defined %}<div class="page-actions">{{ caller() }}</div>{% endif %}
|
||
</div>
|
||
{%- endmacro %}
|
||
|
||
{# A button that shows only an icon. The label is required: it is the button's
|
||
accessible name and its tooltip, and rendering one without it raises. #}
|
||
{% macro icon_button(icon, label, href=none, cls="btn-ghost") -%}
|
||
{%- set name = required(label, "icon_button label") -%}
|
||
{%- if href -%}
|
||
<a class="btn btn-sm btn-icon {{ cls }}" href="{{ href }}" aria-label="{{ name }}" title="{{ name }}">{{ icon }}</a>
|
||
{%- else -%}
|
||
<button type="{{ kwargs.get('type', 'button') }}" class="btn btn-sm btn-icon {{ cls }}" aria-label="{{ name }}" title="{{ name }}"
|
||
{%- if kwargs.get('confirm') %} data-confirm="{{ kwargs.confirm }}"{% endif %}>{{ icon }}</button>
|
||
{%- endif -%}
|
||
{%- endmacro %}
|