improved feedback when loading ajax content

This commit is contained in:
thomas girod 2024-08-08 20:20:09 +02:00
parent 20c015c312
commit 7ea9a5ca2d
4 changed files with 56 additions and 23 deletions

View File

@ -93,6 +93,32 @@ a:not(.button) {
}
}
[aria-busy] {
--loading-size: 50px;
--loading-stroke: 5px;
--loading-duration: 1s;
position: relative;
}
[aria-busy]:after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: var(--loading-size);
height: var(--loading-size);
margin-top: calc(var(--loading-size) / 2 * -1);
margin-left: calc(var(--loading-size) / 2 * -1);
border: var(--loading-stroke) solid rgba(0, 0, 0, .15);
border-radius: 50%;
border-top-color: rgba(0, 0, 0, 0.5);
animation: rotate calc(var(--loading-duration)) linear infinite;
}
@keyframes rotate {
100% { transform: rotate(360deg); }
}
.ib {
display: inline-block;
padding: 1px;

View File

@ -102,20 +102,10 @@ main {
border-radius: 10px;
}
.paginator {
display: flex;
justify-content: center;
gap: 10px;
width: -moz-fit-content;
width: fit-content;
background-color: rgba(0,0,0,.1);
border-radius: 10px;
padding: 10px;
margin: 10px 0 10px auto;
}
.photos,
.albums {
margin: 20px;
min-height: 50px; // To contain the aria-busy loading wheel, even if empty
box-sizing: border-box;
display: flex;
flex-direction: row;

View File

@ -96,7 +96,7 @@
{% endif %}
</tr>
</thead>
<tbody id="dynamic_view_content">
<tbody id="dynamic_view_content" :aria-busy="loading">
<template x-for="uv in uvs.results" :key="uv.id">
<tr @click="window.location.href = `/pedagogy/uv/${uv.id}`" class="clickable">
<td><a :href="`/pedagogy/uv/${uv.id}`" x-text="uv.code"></a></td>
@ -140,6 +140,7 @@
document.addEventListener("alpine:init", () => {
Alpine.data("uv_search", () => ({
uvs: [],
loading: false,
page: parseInt(initialUrlParams.get("page")) || page_default,
page_size: parseInt(initialUrlParams.get("page_size")) || page_size_default,
search: initialUrlParams.get("search") || "",
@ -171,8 +172,10 @@
},
async fetch_data() {
this.loading = true;
const url = "{{ url("api:fetch_uvs") }}" + window.location.search;
this.uvs = await (await fetch(url)).json();
this.loading = false;
},
max_page() {

View File

@ -1,5 +1,4 @@
{% extends "core/base.jinja" %}
{% from "core/macros.jinja" import paginate %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('sas/album.scss') }}">
@ -62,7 +61,7 @@
<div x-data="pictures">
<h4>{% trans %}Pictures{% endtrans %}</h4>
<div class="photos">
<div class="photos" :aria-busy="loading">
<template x-for="picture in pictures.results">
<a :href="`/sas/picture/${picture.id}#pict`">
<div class="photo" :style="`background-image: url(${picture.thumb_url})`">
@ -81,13 +80,24 @@
</template>
</div>
<nav class="pagination" x-show="nb_pages() > 1">
<button @click="page--" :disabled="page <= 1">
{# Adding the prevent here is important, because otherwise,
clicking on the pagination buttons could submit the picture management form
and reload the page #}
<button
@click.prevent="page--"
:disabled="page <= 1"
@keyup.right.window="page = Math.min(nb_pages(), page + 1)"
>
<i class="fa fa-caret-left"></i>
</button>
<template x-for="i in nb_pages()">
<button x-text="i" @click="page = i":class="{active: page === i}"></button>
<button x-text="i" @click.prevent="page = i" :class="{active: page === i}"></button>
</template>
<button @click="page++" :disabled="page >= nb_pages()">
<button
@click.prevent="page++"
:disabled="page >= nb_pages()"
@keyup.left.window="page = Math.max(1, page - 1)"
>
<i class="fa fa-caret-right"></i>
</button>
</nav>
@ -95,9 +105,6 @@
{% if is_sas_admin %}
</form>
{% endif %}
{% if user.is_in_group(pk=settings.SITH_GROUP_SAS_ADMIN_ID) %}
<form class="add-files" id="upload_form" action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="inputs">
@ -122,18 +129,24 @@
Alpine.data("pictures", () => ({
pictures: {},
page: parseInt(initialUrlParams.get("page")) || 1,
loading: false,
async init() {
await this.fetch_pictures();
this.$watch("page", () => this.fetch_pictures());
this.$watch("page", () => {
update_query_string("page", this.page === 1 ? null : this.page);
this.fetch_pictures()
});
},
async fetch_pictures() {
update_query_string("page", this.page === 1 ? null : this.page);
this.loading=true;
const url = "{{ url("api:pictures") }}"
+"?album_id={{ album.id }}"
+`&page=${this.page}`
+"&page_size={{ settings.SITH_SAS_IMAGES_PER_PAGE }}";
this.pictures = await (await fetch(url)).json();
this.loading=false;
},
nb_pages() {
@ -141,6 +154,7 @@
}
}))
})
$("form#upload_form").submit(function (event) {
let formData = new FormData($(this)[0]);