mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-25 20:32:26 +01:00
feat(table): add header icon support and enhance cell rendering options
This commit is contained in:
33
src/lib/client/ui/navigation/tabs/Tabs.svelte
Normal file
33
src/lib/client/ui/navigation/tabs/Tabs.svelte
Normal file
@@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
import type { ComponentType } from 'svelte';
|
||||
|
||||
interface Tab {
|
||||
label: string;
|
||||
href: string;
|
||||
active?: boolean;
|
||||
icon?: ComponentType;
|
||||
}
|
||||
|
||||
export let tabs: Tab[] = [];
|
||||
</script>
|
||||
|
||||
<div class="border-b border-neutral-200 dark:border-neutral-800">
|
||||
<nav class="-mb-px flex gap-2" aria-label="Tabs">
|
||||
{#each tabs as tab (tab.href)}
|
||||
<a
|
||||
href={tab.href}
|
||||
class="flex items-center gap-2 border-b-2 px-4 py-3 text-sm font-medium transition-colors {tab.active
|
||||
? 'border-blue-600 text-blue-600 dark:border-blue-500 dark:text-blue-500'
|
||||
: 'border-transparent text-neutral-600 hover:border-neutral-300 hover:text-neutral-900 dark:text-neutral-400 dark:hover:border-neutral-700 dark:hover:text-neutral-50'}"
|
||||
>
|
||||
{#if tab.icon}
|
||||
<svelte:component this={tab.icon} size={16} />
|
||||
{/if}
|
||||
{tab.label}
|
||||
</a>
|
||||
{/each}
|
||||
|
||||
<!-- Actions slot for custom action tabs (like Add Instance) -->
|
||||
<slot name="actions" />
|
||||
</nav>
|
||||
</div>
|
||||
@@ -9,6 +9,8 @@
|
||||
key: string;
|
||||
/** Header text to display */
|
||||
header: string;
|
||||
/** Optional icon component to display before header text */
|
||||
headerIcon?: ComponentType;
|
||||
/** Optional width class (e.g., 'w-32', 'w-1/4') */
|
||||
width?: string;
|
||||
/** Text alignment */
|
||||
@@ -16,7 +18,7 @@
|
||||
/** Whether column is sortable */
|
||||
sortable?: boolean;
|
||||
/** Custom cell renderer - receives the full row object */
|
||||
cell?: (row: T) => string | ComponentType;
|
||||
cell?: (row: T) => string | ComponentType | { html: string };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,6 +29,7 @@
|
||||
export let hoverable: boolean = true;
|
||||
export let compact: boolean = false;
|
||||
export let emptyMessage: string = 'No data available';
|
||||
export let onRowClick: ((row: T) => void) | undefined = undefined;
|
||||
|
||||
/**
|
||||
* Get cell value by key path (supports nested properties like 'user.name')
|
||||
@@ -61,7 +64,12 @@
|
||||
<th
|
||||
class={`${compact ? 'px-4 py-2' : 'px-6 py-3'} text-xs font-medium uppercase tracking-wider text-neutral-700 dark:text-neutral-300 ${getAlignClass(column.align)} ${column.width || ''}`}
|
||||
>
|
||||
{column.header}
|
||||
<div class="flex items-center gap-1.5 {column.align === 'center' ? 'justify-center' : column.align === 'right' ? 'justify-end' : ''}">
|
||||
{#if column.headerIcon}
|
||||
<svelte:component this={column.headerIcon} size={14} />
|
||||
{/if}
|
||||
{column.header}
|
||||
</div>
|
||||
</th>
|
||||
{/each}
|
||||
<!-- Actions column slot -->
|
||||
@@ -89,9 +97,10 @@
|
||||
{:else}
|
||||
{#each data as row, rowIndex}
|
||||
<tr
|
||||
class={hoverable
|
||||
class="{hoverable
|
||||
? 'transition-colors hover:bg-neutral-50 dark:hover:bg-neutral-900'
|
||||
: ''}
|
||||
: ''} {onRowClick ? 'cursor-pointer' : ''}"
|
||||
on:click={() => onRowClick && onRowClick(row)}
|
||||
>
|
||||
{#each columns as column}
|
||||
<td
|
||||
@@ -101,6 +110,8 @@
|
||||
{@const rendered = column.cell(row)}
|
||||
{#if typeof rendered === 'string'}
|
||||
{rendered}
|
||||
{:else if typeof rendered === 'object' && 'html' in rendered}
|
||||
{@html rendered.html}
|
||||
{:else}
|
||||
<svelte:component this={rendered} {row} />
|
||||
{/if}
|
||||
@@ -124,3 +135,29 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
td :global(ul) {
|
||||
list-style-type: disc;
|
||||
padding-left: 1.5rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
td :global(ol) {
|
||||
list-style-type: decimal;
|
||||
padding-left: 1.5rem;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
td :global(li) {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
|
||||
td :global(p) {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
td :global(strong) {
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user