Indicador "tira hacia abajo para actualizar" — patrón estándar de pantallas táctiles. El gesto solo se activa cuando la lista está al tope (scrollTop = 0). El JS dispara un CustomEvent("ptr:refresh", {detail:{done}}) en el host; el consumidor llama detail.done() al terminar.
Touch-first. En desktop usa mouse=true para activar fallback de mouse drag. Sin gesto real, los demos abajo lo simulan así.
Básico — pulsa y arrastra hacia abajo (mouse drag activo)
Tira para actualizar
Mensaje 1 · hace 2 minutos
Mensaje 2 · hace 5 minutos
Mensaje 3 · hace 1 hora
Mensaje 4 · hace 3 horas
Mensaje 5 · hace 1 día
Mensaje 6 · ayer
Mensaje 7 · ayer
Mensaje 8 · hace 2 días
Mensaje 9 · hace 3 días
Mensaje 10 · hace 1 semana
<div data-ptr-host data-ptr-mouse>
<div class="ptr" data-state="idle">
<div class="ptr-spinner" aria-hidden="true"></div>
<span class="ptr-label">
Tira para actualizar
</span>
</div>
<div data-ptr-scroller style="max-height:280px; overflow:auto; background:var(--bg-1); border:var(--border-w) solid var(--line); border-radius:var(--radius-md)">
<ul class="list" style="margin:0">
<li>Mensaje 1 · hace 2 minutos</li>
<li>Mensaje 2 · hace 5 minutos</li>
<li>Mensaje 3 · hace 1 hora</li>
<li>Mensaje 4 · hace 3 horas</li>
<li>Mensaje 5 · hace 1 día</li>
<li>Mensaje 6 · ayer</li>
<li>Mensaje 7 · ayer</li>
<li>Mensaje 8 · hace 2 días</li>
<li>Mensaje 9 · hace 3 días</li>
<li>Mensaje 10 · hace 1 semana</li>
</ul>
</div>
</div>
{% from "refresher.jinja" import refresher %}
{% call refresher(mouse=true) %}
<div data-ptr-scroller style="max-height:280px; overflow:auto; background:var(--bg-1)">
<ul class="list">
<li>Mensaje 1 · hace 2 minutos</li>
<li>Mensaje 2 · hace 5 minutos</li>
<li>Mensaje 3 · hace 1 hora</li>
<li>Mensaje 4 · hace 3 horas</li>
<li>Mensaje 5 · hace 1 día</li>
</ul>
</div>
{% endcall %}
{% from "refresher.jinja" import refresher %}
{% call refresher(mouse=true) %}
<div data-ptr-scroller style="max-height:280px; overflow:auto; background:var(--bg-1)">
<ul class="list">
<li>Mensaje 1 · hace 2 minutos</li>
<li>Mensaje 2 · hace 5 minutos</li>
<li>Mensaje 3 · hace 1 hora</li>
<li>Mensaje 4 · hace 3 horas</li>
<li>Mensaje 5 · hace 1 día</li>
</ul>
</div>
{% endcall %}
Install the Jinja addon and import the component:
pip install outfitkit
Then in your template:
{% from "<component>.jinja" import <component> %}
{{ <component>(...) }}