Working to expand and improve pager behavior.

This commit is contained in:
Yaro Kasear 2025-08-28 09:56:15 -05:00
parent 0c2a9847cb
commit bdec7202df
3 changed files with 76 additions and 12 deletions

View file

@ -1,4 +1,5 @@
{% macro options(items, value_attr="id", label_path="name", getp=None) -%} {% macro options(items, value_attr="id", label_path="name", getp=None) -%}
{%- for obj in items -%} {%- for obj in items -%}
<option value="{{ getp(obj, value_attr) }}">{{ getp(obj, label_path) }}</option> <option value="{{ getp(obj, value_attr) }}">{{ getp(obj, label_path) }}</option>
{%- endfor -%} {%- endfor -%}
@ -32,14 +33,56 @@
{%- endmacro %} {%- endmacro %}
{% macro pager(model, page, pages, per_page, sort, filters, fields_csv) -%} {% macro pager(model, page, pages, per_page, sort, filters, fields_csv) -%}
<nav id="pager" hx-swap-oob="true"> {#
<a hx-get="/ui/{{ model }}/frag/rows?page={{ page-1 }}&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}" <nav id="pager">
hx-target="#rows" rel="prev" hx-swap="innerHTML">Prev</a> {% if page > 1 %}
<button type="button"
hx-get="/ui/{{ model }}/frag/rows?page=1&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}"
hx-target="#rows" hx-swap="innerHTML">First</button>
<button type="button"
hx-get="/ui/{{ model }}/frag/rows?page={{ page-1 }}&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}"
hx-target="#rows" rel="prev" hx-swap="innerHTML">Prev</button>
{% endif %}
<span>Page {{ page }} / {{ pages }}</span> <span>Page {{ page }} / {{ pages }}</span>
<a hx-get="/ui/{{ model }}/frag/rows?page={{ page+1 }}&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}" {% if page < pages %} <button type="button"
hx-target="#rows" rel="next" hx-swap="innerHTML">Next</a> hx-get="/ui/{{ model }}/frag/rows?page={{ page+1 }}&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}"
hx-target="#rows" rel="next" hx-swap="innerHTML">Next</button>
<button type="button"
hx-get="/ui/{{ model }}/frag/rows?page={{ pages }}&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}"
hx-target="#rows" hx-swap="innerHTML">Last</button>
{% endif %}
</nav>
#}
<nav id="pager">
<ul class="pagination justify-content-center bg-white">
<li class="page-item {%- if page <= 1 %} disabled{% endif %}">
<a class="page-link"
hx-get="/ui/{{ model }}/frag/rows?page={{ page - 1 }}&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}"
hx-target="#rows" hx-swap="innerHTML" {%- if page <= 1 %} hx-disable{% endif %}>Prev</a>
</li>
<li class="page-item {%- if page <= 1 %} disabled{% endif %}">
<a class="page-link"
hx-get="/ui/{{ model }}/frag/rows?page=1&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}"
hx-target="#rows" hx-swap="innerHTML" {%- if page <= 1 %} hx-disable{% endif %}>1</a>
</li>
{% if pages > 1 %}
<li class="page-item"><a class="page-link">...</a></li>
<li class="page-item"><a class="page-link">...</a></li>
<li class="page-item {%- if page >= pages %} disabled{% endif %}">
<a class="page-link"
hx-get="/ui/{{ model }}/frag/rows?page={{ pages }}&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}"
hx-target="#rows" hx-swap="innerHTML" {%- if page >= pages %} hx-disable{% endif %}>{{ pages }}</a>
</li>
{% endif %}
<li class="page-item {%- if page >= pages %} disabled{% endif %}">
<a class="page-link"
hx-get="/ui/{{ model }}/frag/rows?page={{ page + 1 }}&per_page={{ per_page }}{% if sort %}&sort={{ sort }}{% endif %}{% if fields_csv %}&fields_csv={{ fields_csv|urlencode }}{% endif %}{% for k,v in filters.items() %}&{{k}}={{ v|urlencode }}{% endfor %}"
hx-target="#rows" hx-swap="innerHTML" {%- if page >= pages %} hx-disable{% endif %}>Next</a>
</li>
</ul>
<span>Page {{ page }} / {{ pages }}</span>
</nav> </nav>
{%- endmacro %} {%- endmacro %}

View file

@ -106,14 +106,31 @@ def make_fragments_blueprint(db_session_factory, registry: Dict[str, Any], *, na
expand = _collect_expand_from_paths(fields) expand = _collect_expand_from_paths(fields)
spec = QuerySpec(filters=filters, order_by=[sort] if sort else [], page=page, per_page=per_page, expand=expand) spec = QuerySpec(filters=filters, order_by=[sort] if sort else [], page=page, per_page=per_page, expand=expand)
s = session(); svc = CrudService(s, default_eager_policy) s = session(); svc = CrudService(s, default_eager_policy)
rows, total = svc.list(Model, spec) rows, _ = svc.list(Model, spec)
html = render_template("crudkit/rows.html", items=rows, fields=fields, getp=_getp)
return html
@bp.get("/<model>/frag/pager")
def pager(model):
Model = registry.get(model) or abort(404)
page = request.args.get("page", type=int) or 1
per_page = request.args.get("per_page", type=int) or 20
filters = _parse_filters(request.args)
sort = request.args.get("sort")
fields_csv = request.args.get("fields_csv") or "id,name"
fields = _paths_from_csv(fields_csv)
expand = _collect_expand_from_paths(fields)
spec = QuerySpec(filters=filters, order_by=[sort] if sort else [], page=page, per_page=per_page, expand=expand)
s = session(); svc = CrudService(s, default_eager_policy)
_, total = svc.list(Model, spec)
pages = max(1, ceil(total / per_page)) pages = max(1, ceil(total / per_page))
rows_html = render_template("crudkit/rows.html", items=rows, fields=fields, getp=_getp) html = render_template("crudkit/pager.html", model=model, page=page, pages=pages,
pager_html = render_template("crudkit/pager.html", model=model, page=page, pages=pages,
per_page=per_page, sort=sort, filters=filters, fields_csv=fields_csv) per_page=per_page, sort=sort, filters=filters, fields_csv=fields_csv)
return html
return rows_html + pager_html
@bp.get("/<model>/frag/form") @bp.get("/<model>/frag/form")
def form(model): def form(model):

View file

@ -76,12 +76,16 @@ refresh_url=none, model=none) %}
</tr> </tr>
</thead> </thead>
<tbody id="rows" <tbody id="rows"
hx-get="/ui/{{ model }}/frag/rows?page={{ page }}&{{ per_page }}&fields_csv={{ fields|join(',') }}" hx-get="/ui/{{ model }}/frag/rows?page={{ page }}&per_page{{ per_page }}&fields_csv={{ fields|join(',') }}"
hx-trigger="load" hx-trigger="load"
hx-target="#rows" hx-target="#rows"
hx-swap="innerHTML"> hx-swap="innerHTML">
</tbody> </tbody>
</table> </table>
<div id="pager"></div> <div id="pager"
hx-get="/ui/{{ model }}/frag/pager?page={{ page }}&per_page={{ per_page }}&fields_csv={{ fields|join(',') }}"
hx-trigger="load, htmx:afterSwap from:#rows"
hx-target="#pager"
hx-swap="outerHTML"></div>
</div> </div>
{% endmacro %} {% endmacro %}