113 lines
2.7 KiB
Svelte
113 lines
2.7 KiB
Svelte
<script lang="ts" generics="TData, TValue">
|
|
import { onMount } from 'svelte';
|
|
import type { ColumnDef } from '@tanstack/table-core';
|
|
import {
|
|
getCoreRowModel,
|
|
type TableOptions
|
|
} from '@tanstack/table-core';
|
|
import { createSvelteTable, FlexRender } from '$lib/components/ui/data-table/index.js';
|
|
import * as Table from '$lib/components/ui/table';
|
|
|
|
type Props = {
|
|
data: TData[];
|
|
columns: ColumnDef<TData, TValue>[];
|
|
loading?: boolean;
|
|
hasMore?: boolean;
|
|
loadMore?: () => void;
|
|
};
|
|
|
|
let { data, columns, loading = false, hasMore = false, loadMore }: Props = $props();
|
|
|
|
let scrollContainer: HTMLDivElement;
|
|
let observer: IntersectionObserver;
|
|
|
|
const options = $derived<TableOptions<TData>>({
|
|
get data() {
|
|
return data;
|
|
},
|
|
columns,
|
|
getCoreRowModel: getCoreRowModel()
|
|
});
|
|
|
|
const table = createSvelteTable(options);
|
|
|
|
onMount(() => {
|
|
if (!loadMore) return;
|
|
|
|
// Create intersection observer for infinite scroll
|
|
observer = new IntersectionObserver(
|
|
(entries) => {
|
|
const [entry] = entries;
|
|
if (entry.isIntersecting && hasMore && !loading && loadMore) {
|
|
loadMore();
|
|
}
|
|
},
|
|
{
|
|
root: scrollContainer,
|
|
threshold: 0.1
|
|
}
|
|
);
|
|
|
|
// Observe the last row
|
|
const lastRow = scrollContainer?.querySelector('tbody tr:last-child');
|
|
if (lastRow) {
|
|
observer.observe(lastRow);
|
|
}
|
|
|
|
return () => {
|
|
observer?.disconnect();
|
|
};
|
|
});
|
|
</script>
|
|
|
|
<div class="w-full">
|
|
<div class="rounded-md border max-h-[600px] overflow-y-auto" bind:this={scrollContainer}>
|
|
<Table.Root>
|
|
<Table.Header class="bg-background">
|
|
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
|
<Table.Row>
|
|
{#each headerGroup.headers as header (header.id)}
|
|
<Table.Head>
|
|
{#if !header.isPlaceholder}
|
|
<FlexRender
|
|
content={header.column.columnDef.header}
|
|
context={header.getContext()}
|
|
/>
|
|
{/if}
|
|
</Table.Head>
|
|
{/each}
|
|
</Table.Row>
|
|
{/each}
|
|
</Table.Header>
|
|
<Table.Body>
|
|
{#each table.getRowModel().rows as row (row.id)}
|
|
<Table.Row data-state={row.getIsSelected() && "selected"}>
|
|
{#each row.getVisibleCells() as cell (cell.id)}
|
|
<Table.Cell>
|
|
<FlexRender
|
|
content={cell.column.columnDef.cell}
|
|
context={cell.getContext()}
|
|
/>
|
|
</Table.Cell>
|
|
{/each}
|
|
</Table.Row>
|
|
{:else}
|
|
<Table.Row>
|
|
<Table.Cell colspan={columns.length} class="h-24 text-center">
|
|
No hay resultados.
|
|
</Table.Cell>
|
|
</Table.Row>
|
|
{/each}
|
|
|
|
{#if loading}
|
|
<Table.Row>
|
|
<Table.Cell colspan={columns.length} class="h-12 text-center text-muted-foreground">
|
|
Cargando...
|
|
</Table.Cell>
|
|
</Table.Row>
|
|
{/if}
|
|
</Table.Body>
|
|
</Table.Root>
|
|
</div>
|
|
</div>
|