generateur_v3/frontend/src/components/exos/ExoList.svelte

215 lines
4.5 KiB
Svelte

<script lang="ts">
import { getExos, getTags } from "../../requests/exo.request";
import { writable, type Writable } from "svelte/store";
import type { Exercice, Tag } from "../../types/exo.type";
import TagViewer from "./TagViewer.svelte";
import TagSelector from "../forms/TagSelector.svelte";
import { getContext } from "svelte";
import FaTimes from "svelte-icons/fa/FaTimes.svelte";
import type { ExoSelect } from "src/types/room.type";
export let selectedExos: Writable<ExoSelect[]>;
const exos = writable<{ hasMore: Boolean; data: Exercice[]; page: number }>({
hasMore: true,
data: [],
page: 1
});
const tags = writable<Tag[]>([]);
let search = "";
let selected: Tag[] = [];
const { isAuth } = getContext<{ isAuth: Writable<boolean> }>("auth");
const { close } = getContext<{ close: () => void }>("modal");
$:filter = $isAuth ? "user" : "public";
$: {
getExos(filter, { search, size: 20, tags: [...selected.map((t) => t.id_code)] }).then((r) => {
exos.set({
hasMore: r.totalPage > 1,
data: r.items,
page: 1
});
});
}
$: getTags().then((r) => {
tags.set(r);
});
const fetchNextPage = () => {
if ($exos.hasMore) {
getExos(filter, {
search,
size: 20,
tags: [...selected.map((t) => t.id_code)],
page: $exos.page + 1
}).then((r) => {
exos.update((o) => {
return {
hasMore: r.totalPage > r.page,
data: [...o.data, ...r.items],
page: r.page
};
});
});
}
};
let more: HTMLDivElement;
let updateText: number | null = null;
</script>
<div
class="exolist"
bind:this={more}
on:scroll={() => {
if (
more != undefined &&
$exos.hasMore &&
more.offsetHeight + more.scrollTop >= more.scrollHeight
) {
fetchNextPage();
}
}}
>
<div class="close" on:click={() => {
close()
}}>
<FaTimes />
</div>
<div class="head">
<input
type="text"
name="search"
placeholder="Rechercher..."
class="input"
on:input={(e) => {
if (updateText != null) {
clearTimeout(updateText);
}
updateText = window.setTimeout(() => {
search = e.currentTarget.value;
}, 500);
}}
/>
{#if !!$isAuth}
<div class="auth-head">
<TagSelector options={$tags} bind:selected placeholder="Selectionner..." />
<select class="input" bind:value={filter}>
<option value="user">Vos exercices</option>
<option value="public">Tous les exercices</option>
</select>
</div>
{/if}
</div>
<div class="exos">
{#if $exos.data.length == 0}
<p class="empty">Aucun exercice trouvé</p>
{/if}
{#each $exos.data as e (e.id_code)}
<div
on:click={() => {
if ($selectedExos.map((s) => s.exercice_id).includes(e.id_code)) {
selectedExos.update((n) => {
return [...n.filter((s) => s.exercice_id != e.id_code)];
});
} else {
selectedExos.update((n) => {
return [...n, { quantity: 10, exercice_id: e.id_code, name: e.name }];
});
}
}}
class:selected={$selectedExos.map((s) => s.exercice_id).includes(e.id_code)}
on:keydown={() => {}}
>
<p>{e.name}</p>
<TagViewer tags={e.tags} id={e.id_code} />
</div>
{/each}
{#if $exos.hasMore}
<p class="more"><span class="spinner" /></p>
{/if}
</div>
</div>
<style lang="scss">
@import '../../mixins';
.auth-head {
display: flex;
select {
width: max-content;
padding: 10px 5px;
gap: 5px;
}
}
.more {
display: flex;
justify-content: center;
}
.exolist {
overflow: scroll;
background-color: $background;
padding: 20px;
height: 100%;
min-width: 600px;
@include down(800){
min-width: 0;
width: 100%;
}
}
.selected {
color: grey;
}
.head {
display: flex;
flex-direction: column;
gap: 10px;
}
.exos {
> div {
padding: 16px 10px;
cursor: pointer;
transition: 0.3s;
display: flex;
align-items: center;
gap: 20px;
&:hover {
background-color: darken($color: $background, $amount: 3);
}
}
}
.close {
position: absolute;
top: 7px;
right: 7px;
cursor: pointer;
width: 15px;
height: 15px;
transition: .2s;
&:hover {
transform: scale(1.1);
}
}
.empty {
font-style: italic;
width: 100%;
text-align: center;
margin: 20px 0;
}
</style>