feature/homogenizacion-estilos-catalogos-fijos-y-generales

This commit is contained in:
2026-04-13 11:16:54 -06:00
parent aa8891145c
commit a0a6b52fc1
83 changed files with 4293 additions and 2044 deletions

View File

@@ -0,0 +1,152 @@
<script lang="ts" generics="TData, TValue">
import { onMount } from 'svelte';
import { type ColumnDef, getCoreRowModel } from '@tanstack/table-core';
import { createSvelteTable, FlexRender } from '$lib/components/ui/data-table/index.js';
import * as Table from '$lib/components/ui/table/index.js';
type InfiniteDataTableProps<TData, TValue> = {
columns: ColumnDef<TData, TValue>[];
data: TData[];
loading: boolean;
hasMore: boolean;
loadMore: () => void;
emptyMessage?: string;
};
let {
data,
columns,
loading,
hasMore,
loadMore,
emptyMessage = 'No hay resultados.'
}: InfiniteDataTableProps<TData, TValue> = $props();
const table = createSvelteTable({
get data() {
return data;
},
columns,
getCoreRowModel: getCoreRowModel()
});
let scrollContainer = $state<HTMLDivElement>();
let loadingTrigger = $state<HTMLDivElement>();
onMount(() => {
const observer = new IntersectionObserver(
(entries) => {
const [entry] = entries;
if (entry.isIntersecting && hasMore && !loading) {
loadMore();
}
},
{
root: scrollContainer,
threshold: 0.1
}
);
if (loadingTrigger) {
observer.observe(loadingTrigger);
}
return () => {
observer.disconnect();
};
});
</script>
<div class="flex h-full w-full flex-col overflow-hidden">
<div
class="catalog-table-scroll min-h-[320px] max-h-[calc(100svh-280px)]"
bind:this={scrollContainer}
>
<Table.Root>
<Table.Header class="catalog-table-header">
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
{@const headerList = headerGroup.headers}
{@const lastHeaderColId = headerList[headerList.length - 1]?.column.id}
<Table.Row>
{#each headerList as header (header.id)}
{@const colId = header.column.id}
<Table.Head
class={[
'catalog-table-head-cell',
colId === lastHeaderColId &&
'catalog-table-sticky-right z-30'
]
.filter(Boolean)
.join(' ')}
>
{#if !header.isPlaceholder}
<FlexRender
content={header.column.columnDef.header}
context={header.getContext()}
/>
{/if}
</Table.Head>
{/each}
</Table.Row>
{/each}
</Table.Header>
<Table.Body>
{#if table.getRowModel().rows.length}
{#each table.getRowModel().rows as row (row.id)}
{@const visibleCells = row.getVisibleCells()}
{@const lastCellColId = visibleCells[visibleCells.length - 1]?.column.id}
<Table.Row
data-state={row.getIsSelected() && 'selected'}
class="catalog-table-row"
>
{#each visibleCells as cell (cell.id)}
{@const colId = cell.column.id}
<Table.Cell
class={[
'whitespace-nowrap',
colId === lastCellColId &&
'catalog-table-sticky-right z-10'
]
.filter(Boolean)
.join(' ')}
>
<FlexRender
content={cell.column.columnDef.cell}
context={cell.getContext()}
/>
</Table.Cell>
{/each}
</Table.Row>
{/each}
{:else}
<Table.Row>
<Table.Cell colspan={columns.length} class="h-24 text-center text-sm text-muted-foreground">
{emptyMessage}
</Table.Cell>
</Table.Row>
{/if}
{#if hasMore}
<Table.Row>
<Table.Cell colspan={columns.length} class="h-24 text-center p-0">
<div bind:this={loadingTrigger} class="flex h-full w-full items-center justify-center">
{#if loading}
<div class="flex items-center justify-center gap-3 rounded-full border bg-muted/30 px-6 py-2 shadow-sm">
<div class="h-5 w-5 animate-spin rounded-full border-2 border-primary border-t-transparent text-primary"></div>
<span class="text-sm font-medium text-foreground">Cargando más registros...</span>
</div>
{:else}
<div class="flex items-center gap-2 text-sm text-muted-foreground">
<span class="h-px w-8 bg-border"></span>
<span>Desplázate para cargar más</span>
<span class="h-px w-8 bg-border"></span>
</div>
{/if}
</div>
</Table.Cell>
</Table.Row>
{/if}
</Table.Body>
</Table.Root>
</div>
</div>