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

View File

@ -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>

View File

@ -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>

View File

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