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 { .ib {
display: inline-block; display: inline-block;
padding: 1px; padding: 1px;

View File

@ -102,20 +102,10 @@ main {
border-radius: 10px; 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, .photos,
.albums { .albums {
margin: 20px;
min-height: 50px; // To contain the aria-busy loading wheel, even if empty
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View File

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

View File

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