Skip to content

Instantly share code, notes, and snippets.

@svex99
Created April 20, 2024 08:23
Show Gist options
  • Save svex99/8f194d51702a75e73af397bdc4d2e92b to your computer and use it in GitHub Desktop.
Save svex99/8f194d51702a75e73af397bdc4d2e92b to your computer and use it in GitHub Desktop.
Svelte data-table with generics and back-end pagination (shadcn-svelte)
<script context="module" lang="ts">
export type Column<T> = {
accessor: keyof T;
header: string;
format?(item: T): string;
cell?: (item: T) => any;
};
</script>
<script lang="ts" generics="T extends { id: string }">
import { page } from '$app/stores';
import { Render } from 'svelte-render';
import * as Table from '$lib/components/ui/table';
import { Button } from '$lib/components/ui/button';
import { getOffsetPaginationQueryParams } from '$lib/utils/pagination';
export let columns: Column<T>[] = [];
export let data: T[] = []; // Data will contain one item more than page size to indicate that there are more items.
export let caption: string = '';
$: pagination = getOffsetPaginationQueryParams($page.url);
$: tableData = data.length > pagination.size ? data.slice(0, data.length - 1) : data;
</script>
<div>
<Table.Root>
{#if caption}
<Table.Caption>{caption}</Table.Caption>
{/if}
<Table.Header>
<Table.Row>
{#each columns as column}
<Table.Head>{column.header}</Table.Head>
{/each}
</Table.Row>
</Table.Header>
{#if tableData.length}
<Table.Body>
{#each tableData as item (item.id)}
<Table.Row>
{#each columns as column}
<Table.Cell>
{#if column.cell}
<Render of={column.cell(item)} />
{:else if column.format}
{column.format(item)}
{:else}
{item[column.accessor]}
{/if}
</Table.Cell>
{/each}
</Table.Row>
{/each}
</Table.Body>
{:else}
<td colspan={columns.length} class="py-5 text-center text-muted-foreground">
There is no data to display.
</td>
{/if}
</Table.Root>
{#if tableData.length}
<div>
<p class="text-xs text-muted-foreground">Showing {tableData.length} item(s)</p>
<div class="w-full flex items-center justify-center space-x-8">
<Button
variant="outline"
size="sm"
class="w-[6rem] {pagination.page > 1 ? 'visible' : 'invisible'}"
href="{$page.url.pathname}?page={pagination.page - 1}&size={pagination.size}"
>
Previous
</Button>
<p class="text-muted-foreground text-sm">{pagination.page}</p>
<Button
variant="outline"
size="sm"
class="w-[6rem] {data.length > pagination.size ? 'visible' : 'invisible'}"
href="{$page.url.pathname}?page={pagination.page + 1}&size={pagination.size}"
>
Next
</Button>
</div>
</div>
{/if}
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment