Refactor data table components for improved layout and functionality
- Made the `title` prop optional in `DataTableCard`, allowing for more flexible usage without a title. - Updated the rendering logic in `DataTableCard` to conditionally display the title and description. - Enhanced the styling of table headers across various pages for a more consistent appearance, removing unnecessary backdrop blur effects. - Replaced button elements with native button tags in `SortableTh` for better accessibility and semantic HTML.
This commit is contained in:
@@ -10,7 +10,8 @@
|
||||
toolbar,
|
||||
children
|
||||
}: {
|
||||
title: string;
|
||||
/** Без title — как в shadcn dashboard-01: сразу toolbar и таблица */
|
||||
title?: string;
|
||||
description?: string;
|
||||
toolbar?: Snippet;
|
||||
children: Snippet;
|
||||
@@ -19,12 +20,14 @@
|
||||
</script>
|
||||
|
||||
<Card.Root class={cn('overflow-hidden rounded-lg border border-border shadow-sm', className)}>
|
||||
<div class="border-b border-border bg-muted/40 px-4 py-3">
|
||||
<Card.Title class="text-base font-semibold">{title}</Card.Title>
|
||||
{#if description}
|
||||
<Card.Description class="text-xs sm:text-sm">{description}</Card.Description>
|
||||
{/if}
|
||||
</div>
|
||||
{#if title}
|
||||
<div class="border-b border-border bg-muted/40 px-4 py-3">
|
||||
<Card.Title class="text-base font-semibold">{title}</Card.Title>
|
||||
{#if description}
|
||||
<Card.Description class="text-xs sm:text-sm">{description}</Card.Description>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{#if toolbar}
|
||||
{@render toolbar()}
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import * as Table from '$lib/components/ui/table/index.js';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import ArrowUpDownIcon from '@lucide/svelte/icons/arrow-up-down';
|
||||
import ArrowUpIcon from '@lucide/svelte/icons/arrow-up';
|
||||
@@ -25,23 +24,31 @@
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<Table.Head class={cn(numeric && 'text-right', className)}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
<Table.Head
|
||||
class={cn(
|
||||
'px-3 py-2 text-left align-middle text-sm font-medium text-foreground',
|
||||
numeric && 'text-right',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class={cn(
|
||||
'-ml-2 h-8 gap-1 px-2 font-medium text-muted-foreground hover:text-foreground',
|
||||
'inline-flex max-w-full items-center gap-1.5 rounded-md',
|
||||
'text-foreground/90 transition-colors',
|
||||
'hover:bg-accent/60 hover:text-foreground',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/45',
|
||||
numeric && 'ms-auto'
|
||||
)}
|
||||
onclick={onToggle}
|
||||
>
|
||||
{@render children()}
|
||||
{#if active && dir === 'asc'}
|
||||
<ArrowUpIcon class="size-3.5 opacity-70" aria-hidden="true" />
|
||||
<ArrowUpIcon class="size-3 shrink-0 opacity-70" aria-hidden="true" />
|
||||
{:else if active && dir === 'desc'}
|
||||
<ArrowDownIcon class="size-3.5 opacity-70" aria-hidden="true" />
|
||||
<ArrowDownIcon class="size-3 shrink-0 opacity-70" aria-hidden="true" />
|
||||
{:else}
|
||||
<ArrowUpDownIcon class="size-3.5 opacity-50" aria-hidden="true" />
|
||||
<ArrowUpDownIcon class="size-3 shrink-0 opacity-40" aria-hidden="true" />
|
||||
{/if}
|
||||
</Button>
|
||||
</button>
|
||||
</Table.Head>
|
||||
|
||||
@@ -666,8 +666,8 @@
|
||||
{/snippet}
|
||||
<ScrollArea class="max-h-[min(70vh,520px)] w-full" orientation="both">
|
||||
<Table.Root>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted/95 backdrop-blur-sm">
|
||||
<Table.Row>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted">
|
||||
<Table.Row class="border-b border-border/80 hover:bg-transparent">
|
||||
<SortableTh
|
||||
active={ipSortKey === 'ip'}
|
||||
dir={ipSortDir}
|
||||
@@ -819,8 +819,8 @@
|
||||
{/snippet}
|
||||
<ScrollArea class="max-h-[min(70vh,480px)] w-full" orientation="both">
|
||||
<Table.Root>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted/95 backdrop-blur-sm">
|
||||
<Table.Row>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted">
|
||||
<Table.Row class="border-b border-border/80 hover:bg-transparent">
|
||||
<SortableTh
|
||||
active={nodeSortKey === 'alias'}
|
||||
dir={nodeSortDir}
|
||||
@@ -953,8 +953,8 @@
|
||||
{/snippet}
|
||||
<ScrollArea class="max-h-[min(60vh,400px)] w-full" orientation="vertical">
|
||||
<Table.Root>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted/95 backdrop-blur-sm">
|
||||
<Table.Row>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted">
|
||||
<Table.Row class="border-b border-border/80 hover:bg-transparent">
|
||||
<SortableTh
|
||||
active={topSortKey === 'user'}
|
||||
dir={topSortDir}
|
||||
|
||||
@@ -378,8 +378,8 @@
|
||||
{/snippet}
|
||||
<ScrollArea class="max-h-[min(75vh,640px)] w-full" orientation="both">
|
||||
<Table.Root>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted/95 backdrop-blur-sm">
|
||||
<Table.Row>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted">
|
||||
<Table.Row class="border-b border-border/80 hover:bg-transparent">
|
||||
<SortableTh
|
||||
active={incSortKey === 'sev'}
|
||||
dir={incSortDir}
|
||||
|
||||
@@ -610,8 +610,8 @@
|
||||
{/snippet}
|
||||
<ScrollArea class="max-h-[min(75vh,560px)] w-full" orientation="both">
|
||||
<Table.Root>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted/95 backdrop-blur-sm">
|
||||
<Table.Row>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted">
|
||||
<Table.Row class="border-b border-border/80 hover:bg-transparent">
|
||||
<SortableTh
|
||||
active={ipSortKey === 'user'}
|
||||
dir={ipSortDir}
|
||||
|
||||
@@ -404,8 +404,8 @@
|
||||
{/snippet}
|
||||
<ScrollArea class="max-h-[min(70vh,520px)] w-full" orientation="both">
|
||||
<Table.Root>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted/95 backdrop-blur-sm">
|
||||
<Table.Row>
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted">
|
||||
<Table.Row class="border-b border-border/80 hover:bg-transparent">
|
||||
<SortableTh
|
||||
active={liveSortKey === 'sev'}
|
||||
dir={liveSortDir}
|
||||
|
||||
@@ -389,10 +389,7 @@
|
||||
</Card.Root>
|
||||
{/snippet}
|
||||
{#snippet children()}
|
||||
<DataTableCard
|
||||
title="Список пользователей"
|
||||
description="Слияние по имени между серверами; клик по строке — карточка пользователя"
|
||||
>
|
||||
<DataTableCard>
|
||||
{#snippet toolbar()}
|
||||
<div class="border-b border-border">
|
||||
<div
|
||||
@@ -467,8 +464,8 @@
|
||||
{/snippet}
|
||||
<ScrollArea class="max-h-[min(70vh,560px)] w-full" orientation="both">
|
||||
<Table.Root>
|
||||
<Table.Header class="sticky top-0 z-10 border-b border-border bg-muted/95 backdrop-blur-sm">
|
||||
<Table.Row class="border-0 hover:bg-transparent">
|
||||
<Table.Header class="sticky top-0 z-10 bg-muted">
|
||||
<Table.Row class="border-b border-border/80 hover:bg-transparent">
|
||||
<Table.Head class="w-10 px-2">
|
||||
{#if pageUsernames.length > 0}
|
||||
<Checkbox
|
||||
@@ -487,7 +484,7 @@
|
||||
>Имя</SortableTh>
|
||||
<Table.Head
|
||||
class={cn(
|
||||
'text-muted-foreground text-xs font-medium uppercase tracking-wide',
|
||||
'px-3 py-2 text-left align-middle text-sm font-medium text-foreground',
|
||||
!showColLinks && 'hidden',
|
||||
showColLinks && 'hidden sm:table-cell'
|
||||
)}
|
||||
@@ -515,7 +512,9 @@
|
||||
showColExpires && 'hidden sm:table-cell'
|
||||
)}
|
||||
>Истекает</SortableTh>
|
||||
<Table.Head class="w-12 px-2 text-right" aria-hidden="true"></Table.Head>
|
||||
<Table.Head class="w-12 px-2 text-right">
|
||||
<span class="sr-only">Действия</span>
|
||||
</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
|
||||
Reference in New Issue
Block a user