add generic components for table sorting

This commit is contained in:
bytedream 2023-09-30 15:15:38 +02:00
parent 9ababd4847
commit 9be57c1004
3 changed files with 73 additions and 63 deletions
src
lib/components/Table
routes/admin/users

@ -0,0 +1,35 @@
<script lang="ts">
import { createEventDispatcher, getContext, onDestroy } from 'svelte';
import type { Writable } from 'svelte/store';
import { IconSolid } from 'svelte-heros-v2';
let id = crypto.randomUUID();
let asc = false;
let { ascHeader } = getContext('sortableTr') as { ascHeader: Writable<null | string> };
ascHeader.subscribe((v) => {
if (v !== id) asc = false;
});
let dispatch = createEventDispatcher();
onDestroy(() => {
if ($ascHeader === id) $ascHeader = null;
});
</script>
<th>
<button
class="flex flex-center"
on:click={() => {
dispatch('sort', { asc: (asc = !asc) });
$ascHeader = id;
}}
>
<span class="mr-1"><slot /></span>
<IconSolid
name={$ascHeader === id && asc ? 'chevron-up-solid' : 'chevron-down-solid'}
width="12"
height="12"
/>
</button>
</th>

@ -0,0 +1,12 @@
<script lang="ts">
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
setContext('sortableTr', {
ascHeader: writable(null)
});
</script>
<tr {...$$restProps}>
<slot />
</tr>

@ -1,6 +1,6 @@
<script lang="ts">
import type { PageData } from './$types';
import { IconOutline, IconSolid } from 'svelte-heros-v2';
import { IconOutline } from 'svelte-heros-v2';
import Input from '$lib/components/Input/Input.svelte';
import Select from '$lib/components/Input/Select.svelte';
import { env } from '$env/dynamic/public';
@ -8,41 +8,11 @@
import { buttonTriggeredRequest } from '$lib/components/utils';
import { browser } from '$app/environment';
import HeaderBar from './HeaderBar.svelte';
import SortableTr from '$lib/components/Table/SortableTr.svelte';
import SortableTh from '$lib/components/Table/SortableTh.svelte';
export let data: PageData;
let headers = [
{
name: 'Vorname',
key: 'firstname',
asc: false
},
{
name: 'Nachname',
key: 'lastname',
asc: false
},
{ name: 'Geburtstag', key: 'birthday', asc: false, sort: (a, b) => a.birthday - b.birthday },
{ name: 'Telefon', key: 'telephone', asc: false, sort: (a, b) => a.telephone - b.telephone },
{
name: 'Username',
key: 'username',
asc: false
},
{
name: 'Minecraft Edition',
key: 'playertype',
asc: false
},
{
name: 'Passwort',
key: 'password',
asc: false
},
{ name: 'UUID', key: 'uuid', asc: false }
];
let ascHeader: (typeof headers)[0] | null = null;
let currentPageUsers: (typeof User.prototype.dataValues)[] = [];
let currentPageUsersRequest: Promise<void> = new Promise((resolve) => resolve());
let usersPerPage = 50;
@ -70,12 +40,19 @@
});
}
async function sortUsers(key: string, reverse: boolean) {
const multiplyValue = reverse ? -1 : 1;
$: fetchPageUsers(userPage, userFilter);
currentPageUsers.sort((entryA, entryB) => {
const a = entryA[key];
const b = entryB[key];
let sortKey: string | null = null;
let sortAsc = false;
$: if (sortKey != null)
currentPageUsers = currentPageUsers.sort((entryA, entryB) => {
const multiplyValue = sortAsc ? -1 : 1;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const a = entryA[sortKey];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const b = entryB[sortKey];
switch (typeof a) {
case 'number':
@ -86,10 +63,6 @@
return (a - b) * multiplyValue;
}
});
currentPageUsers = currentPageUsers;
}
$: fetchPageUsers(userPage, userFilter);
async function updateUser(user: typeof User.prototype.dataValues) {
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
@ -126,29 +99,19 @@
<div class="h-full overflow-scroll" bind:this={userTableContainerElement}>
<table class="table table-auto">
<thead>
<tr class="[&>th]:bg-base-100 [&>th]:z-[1] [&>th]:sticky [&>th]:top-0">
<!-- prettier-ignore -->
<SortableTr class="[&>th]:bg-base-100 [&>th]:z-[1] [&>th]:sticky [&>th]:top-0">
<th />
{#each headers as header}
<th>
<button
class="flex items-center"
on:click={() => {
sortUsers(header.key, (ascHeader = ascHeader == header ? null : header));
}}
>
{header.name}
<span class="ml-1">
<IconSolid
name={ascHeader === header ? 'chevron-up-solid' : 'chevron-down-solid'}
width="12"
height="12"
/>
</span>
</button>
</th>
{/each}
<SortableTh on:sort={(e) => { sortKey = 'firstname'; sortAsc = e.detail.asc }}>Vorname</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'lastname'; sortAsc = e.detail.asc }}>Nachname</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'birthday'; sortAsc = e.detail.asc }}>Geburtstag</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'telephone'; sortAsc = e.detail.asc }}>Telefon</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'username'; sortAsc = e.detail.asc }}>Username</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'playertype'; sortAsc = e.detail.asc }}>Minecraft Edition</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'password'; sortAsc = e.detail.asc }}>Passwort</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'uuid'; sortAsc = e.detail.asc }}>UUID</SortableTh>
<th />
</tr>
</SortableTr>
</thead>
<tbody>
{#key currentPageUsersRequest}