mirror of
https://github.com/bartvdbraak/omnidash.git
synced 2025-06-29 04:39:12 +00:00
chore: move structure to root
This commit is contained in:
parent
3b27d3841b
commit
eed9c4161f
213 changed files with 1 additions and 38 deletions
|
@ -0,0 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||
import type { HTMLButtonAttributes } from 'svelte/elements';
|
||||
import type { Writable } from 'svelte/store';
|
||||
|
||||
type $$Props = HTMLButtonAttributes & {
|
||||
checked: Writable<boolean>;
|
||||
};
|
||||
export let checked: Writable<boolean>;
|
||||
</script>
|
||||
|
||||
<Checkbox bind:checked={$checked} {...$$restProps} />
|
|
@ -0,0 +1,58 @@
|
|||
<script lang="ts">
|
||||
import { ArrowDown, ArrowUp, CaretSort } from 'radix-icons-svelte';
|
||||
import { cn } from '$lib/utils';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
|
||||
let className: string | undefined | null = undefined;
|
||||
export { className as class };
|
||||
export let props: {
|
||||
select: never;
|
||||
sort: {
|
||||
order: 'desc' | 'asc' | undefined;
|
||||
toggle: (event: Event) => void;
|
||||
clear: () => void;
|
||||
disabled: boolean;
|
||||
};
|
||||
filter: never;
|
||||
};
|
||||
|
||||
function handleAscSort(e: Event) {
|
||||
if (props.sort.order === 'asc') {
|
||||
return;
|
||||
}
|
||||
props.sort.toggle(e);
|
||||
}
|
||||
|
||||
function handleDescSort(e: Event) {
|
||||
if (props.sort.order === 'desc') {
|
||||
return;
|
||||
}
|
||||
props.sort.toggle(e);
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !props.sort.disabled}
|
||||
<div class={cn('flex items-center', className)}>
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild let:builder>
|
||||
<Button variant="ghost" builders={[builder]} class="-ml-3 h-8 data-[state=open]:bg-accent">
|
||||
<slot />
|
||||
{#if props.sort.order === 'desc'}
|
||||
<ArrowDown class="ml-2 h-4 w-4" />
|
||||
{:else if props.sort.order === 'asc'}
|
||||
<ArrowUp class="ml-2 h-4 w-4" />
|
||||
{:else}
|
||||
<CaretSort class="ml-2 h-4 w-4" />
|
||||
{/if}
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="start">
|
||||
<DropdownMenu.Item on:click={handleAscSort}>Asc</DropdownMenu.Item>
|
||||
<DropdownMenu.Item on:click={handleDescSort}>Desc</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
{:else}
|
||||
<slot />
|
||||
{/if}
|
|
@ -0,0 +1,96 @@
|
|||
<script lang="ts">
|
||||
import { PlusCircled, Check } from 'radix-icons-svelte';
|
||||
import * as Command from '$lib/components/ui/command';
|
||||
import * as Popover from '$lib/components/ui/popover';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { cn } from '$lib/utils';
|
||||
import Separator from '$lib/components/ui/separator/separator.svelte';
|
||||
import Badge from '$lib/components/ui/badge/badge.svelte';
|
||||
import type { statuses } from '../(data)/data';
|
||||
|
||||
export let filterValues: string[] = [];
|
||||
export let title: string;
|
||||
export let options = [] as typeof statuses;
|
||||
|
||||
let open = false;
|
||||
|
||||
const handleSelect = (currentValue: string) => {
|
||||
if (Array.isArray(filterValues) && filterValues.includes(currentValue)) {
|
||||
filterValues = filterValues.filter((v) => v !== currentValue);
|
||||
} else {
|
||||
filterValues = [...(Array.isArray(filterValues) ? filterValues : []), currentValue];
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<Popover.Root bind:open>
|
||||
<Popover.Trigger asChild let:builder>
|
||||
<Button builders={[builder]} variant="outline" size="sm" class="h-8 border-dashed">
|
||||
<PlusCircled class="mr-2 h-4 w-4" />
|
||||
{title}
|
||||
|
||||
{#if filterValues.length > 0}
|
||||
<Separator orientation="vertical" class="mx-2 h-4" />
|
||||
<Badge variant="secondary" class="rounded-sm px-1 font-normal lg:hidden">
|
||||
{filterValues.length}
|
||||
</Badge>
|
||||
<div class="hidden space-x-1 lg:flex">
|
||||
{#if filterValues.length > 2}
|
||||
<Badge variant="secondary" class="rounded-sm px-1 font-normal">
|
||||
{filterValues.length} Selected
|
||||
</Badge>
|
||||
{:else}
|
||||
{#each filterValues as option}
|
||||
<Badge variant="secondary" class="rounded-sm px-1 font-normal">
|
||||
{option}
|
||||
</Badge>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</Button>
|
||||
</Popover.Trigger>
|
||||
<Popover.Content class="w-[200px] p-0" align="start" side="bottom">
|
||||
<Command.Root>
|
||||
<Command.Input placeholder={title} />
|
||||
<Command.List>
|
||||
<Command.Empty>No results found.</Command.Empty>
|
||||
<Command.Group>
|
||||
{#each options as option}
|
||||
<Command.Item
|
||||
value={option.value}
|
||||
onSelect={(currentValue) => {
|
||||
handleSelect(currentValue);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={cn(
|
||||
'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
|
||||
filterValues.includes(option.value)
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: 'opacity-50 [&_svg]:invisible'
|
||||
)}
|
||||
>
|
||||
<Check className={cn('h-4 w-4')} />
|
||||
</div>
|
||||
<span>
|
||||
{option.label}
|
||||
</span>
|
||||
</Command.Item>
|
||||
{/each}
|
||||
</Command.Group>
|
||||
{#if filterValues.length > 0}
|
||||
<Command.Separator />
|
||||
<Command.Item
|
||||
class="justify-center text-center"
|
||||
onSelect={() => {
|
||||
filterValues = [];
|
||||
}}
|
||||
>
|
||||
Clear filters
|
||||
</Command.Item>
|
||||
{/if}
|
||||
</Command.List>
|
||||
</Command.Root>
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
|
@ -0,0 +1,86 @@
|
|||
<script lang="ts">
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { ChevronRight, ChevronLeft, DoubleArrowRight, DoubleArrowLeft } from 'radix-icons-svelte';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import type { Ticket } from '../(data)/schemas';
|
||||
import type { AnyPlugins } from 'svelte-headless-table/plugins';
|
||||
import type { TableViewModel } from 'svelte-headless-table';
|
||||
import { defaultPageSize } from '.';
|
||||
|
||||
export let tableModel: TableViewModel<Ticket, AnyPlugins>;
|
||||
|
||||
const { pageRows, pluginStates, rows } = tableModel;
|
||||
|
||||
const { hasNextPage, hasPreviousPage, pageIndex, pageCount, pageSize } = pluginStates.page;
|
||||
|
||||
const { selectedDataIds } = pluginStates.select;
|
||||
</script>
|
||||
|
||||
<div class="flex items-center justify-between px-2">
|
||||
<div class="flex-1 text-sm text-muted-foreground">
|
||||
{Object.keys($selectedDataIds).length} of{' '}
|
||||
{$rows.length} row(s) selected.
|
||||
</div>
|
||||
<div class="flex items-center space-x-6 lg:space-x-8">
|
||||
<div class="flex items-center space-x-2">
|
||||
<p class="text-sm font-medium">Rows per page</p>
|
||||
<Select.Root
|
||||
onSelectedChange={(selected) => pageSize.set(Number(selected?.value))}
|
||||
selected={{ value: defaultPageSize, label: defaultPageSize.toString() }}
|
||||
>
|
||||
<Select.Trigger class="w-[180px]">
|
||||
<Select.Value placeholder="Select page size" />
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="10">10</Select.Item>
|
||||
<Select.Item value="20">20</Select.Item>
|
||||
<Select.Item value="30">30</Select.Item>
|
||||
<Select.Item value="40">40</Select.Item>
|
||||
<Select.Item value="50">50</Select.Item>
|
||||
<Select.Item value="100">100</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="flex w-[100px] items-center justify-center text-sm font-medium">
|
||||
Page {$pageIndex + 1} of {$pageCount}
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
class="hidden h-8 w-8 p-0 lg:flex"
|
||||
on:click={() => ($pageIndex = 0)}
|
||||
disabled={!$hasPreviousPage}
|
||||
>
|
||||
<span class="sr-only">Go to first page</span>
|
||||
<DoubleArrowLeft size={15} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
class="h-8 w-8 p-0"
|
||||
on:click={() => ($pageIndex = $pageIndex - 1)}
|
||||
disabled={!$hasPreviousPage}
|
||||
>
|
||||
<span class="sr-only">Go to previous page</span>
|
||||
<ChevronLeft size={15} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
class="h-8 w-8 p-0"
|
||||
on:click={() => ($pageIndex = $pageIndex + 1)}
|
||||
disabled={!$hasNextPage}
|
||||
>
|
||||
<span class="sr-only">Go to next page</span>
|
||||
<ChevronRight size={15} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
class="hidden h-8 w-8 p-0 lg:flex"
|
||||
on:click={() => ($pageIndex = Math.ceil($rows.length / $pageRows.length) - 1)}
|
||||
disabled={!$hasNextPage}
|
||||
>
|
||||
<span class="sr-only">Go to last page</span>
|
||||
<DoubleArrowRight size={15} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts">
|
||||
import { priorities } from '../(data)/data';
|
||||
export let value: string;
|
||||
const priority = priorities.find((priority) => priority.value === value);
|
||||
const Icon = priority?.icon;
|
||||
</script>
|
||||
|
||||
{#if priority}
|
||||
<div class="flex items-center">
|
||||
{#if Icon}
|
||||
<Icon class="mr-2 h-4 w-4 text-muted-foreground" />
|
||||
{/if}
|
||||
<span>{priority.label}</span>
|
||||
</div>
|
||||
{/if}
|
|
@ -0,0 +1,46 @@
|
|||
<script lang="ts">
|
||||
import { DotsHorizontal } from 'radix-icons-svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import { labels } from '../(data)/data';
|
||||
import { ticketSchema, type Ticket } from '../(data)/schemas';
|
||||
|
||||
export let row: Ticket;
|
||||
const ticket = ticketSchema.parse(row);
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild let:builder>
|
||||
<Button
|
||||
variant="ghost"
|
||||
builders={[builder]}
|
||||
class="flex h-8 w-8 p-0 data-[state=open]:bg-muted"
|
||||
>
|
||||
<DotsHorizontal class="h-4 w-4" />
|
||||
<span class="sr-only">Open menu</span>
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content class="w-[160px]" align="end">
|
||||
<DropdownMenu.Item>Edit</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>Make a copy</DropdownMenu.Item>
|
||||
<DropdownMenu.Item>Favorite</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Sub>
|
||||
<DropdownMenu.SubTrigger>Labels</DropdownMenu.SubTrigger>
|
||||
<DropdownMenu.SubContent>
|
||||
<DropdownMenu.RadioGroup value={ticket.label}>
|
||||
{#each labels as label}
|
||||
<DropdownMenu.RadioItem value={label.value}>
|
||||
{label.label}
|
||||
</DropdownMenu.RadioItem>
|
||||
{/each}
|
||||
</DropdownMenu.RadioGroup>
|
||||
</DropdownMenu.SubContent>
|
||||
</DropdownMenu.Sub>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item>
|
||||
Delete
|
||||
<DropdownMenu.Shortcut>⌘⌫</DropdownMenu.Shortcut>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
|
@ -0,0 +1,16 @@
|
|||
<script lang="ts">
|
||||
import { statuses } from '../(data)/data';
|
||||
|
||||
export let value: string;
|
||||
const status = statuses.find((status) => status.value === value);
|
||||
const Icon = status?.icon;
|
||||
</script>
|
||||
|
||||
{#if status}
|
||||
<div class="flex w-[100px] items-center">
|
||||
{#if Icon}
|
||||
<Icon class="mr-2 h-4 w-4 text-muted-foreground" />
|
||||
{/if}
|
||||
<span>{status.label}</span>
|
||||
</div>
|
||||
{/if}
|
|
@ -0,0 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { Badge } from '$lib/components/ui/badge';
|
||||
import { labels } from '../(data)/data';
|
||||
|
||||
export let value: string;
|
||||
export let labelValue: string;
|
||||
const label = labels.find((label) => label.value === labelValue);
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-2">
|
||||
{#if label}
|
||||
<Badge variant="outline">{label.label}</Badge>
|
||||
{/if}
|
||||
<span class="max-w-[500px] truncate font-medium">
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
|
@ -0,0 +1,69 @@
|
|||
<script lang="ts">
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { DataTableFacetedFilter, DataTableViewOptions } from '.';
|
||||
import type { Ticket } from '../(data)/schemas';
|
||||
import Button from '$lib/components/ui/button/button.svelte';
|
||||
import { Cross2 } from 'radix-icons-svelte';
|
||||
import { statuses, priorities } from '../(data)/data';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import type { TableViewModel } from 'svelte-headless-table';
|
||||
import type { AnyPlugins } from 'svelte-headless-table/plugins';
|
||||
|
||||
export let tableModel: TableViewModel<Ticket, AnyPlugins>;
|
||||
|
||||
const { pluginStates } = tableModel;
|
||||
const {
|
||||
filterValue
|
||||
}: {
|
||||
filterValue: Writable<string>;
|
||||
} = pluginStates.filter;
|
||||
|
||||
const {
|
||||
filterValues
|
||||
}: {
|
||||
filterValues: Writable<{
|
||||
status: string[];
|
||||
priority: string[];
|
||||
}>;
|
||||
} = pluginStates.colFilter;
|
||||
|
||||
$: showReset = Object.values({ ...$filterValues, $filterValue }).some((v) => v.length > 0);
|
||||
</script>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-1 items-center space-x-2">
|
||||
<Input
|
||||
placeholder="Filter tickets..."
|
||||
class="h-8 w-[150px] lg:w-[250px]"
|
||||
type="search"
|
||||
bind:value={$filterValue}
|
||||
/>
|
||||
|
||||
<DataTableFacetedFilter
|
||||
bind:filterValues={$filterValues.status}
|
||||
title="Status"
|
||||
options={statuses}
|
||||
/>
|
||||
<DataTableFacetedFilter
|
||||
bind:filterValues={$filterValues.priority}
|
||||
title="Priority"
|
||||
options={priorities}
|
||||
/>
|
||||
{#if showReset}
|
||||
<Button
|
||||
on:click={() => {
|
||||
$filterValue = '';
|
||||
$filterValues.status = [];
|
||||
$filterValues.priority = [];
|
||||
}}
|
||||
variant="ghost"
|
||||
class="h-8 px-2 lg:px-3"
|
||||
>
|
||||
Reset
|
||||
<Cross2 class="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<DataTableViewOptions {tableModel} />
|
||||
</div>
|
|
@ -0,0 +1,42 @@
|
|||
<script lang="ts">
|
||||
import { MixerHorizontal } from 'radix-icons-svelte';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||
import type { Ticket } from '../(data)/schemas';
|
||||
import type { TableViewModel } from 'svelte-headless-table';
|
||||
import type { AnyPlugins } from 'svelte-headless-table/plugins';
|
||||
|
||||
export let tableModel: TableViewModel<Ticket, AnyPlugins>;
|
||||
const { pluginStates, flatColumns } = tableModel;
|
||||
const { hiddenColumnIds } = pluginStates.hide;
|
||||
|
||||
const ids = flatColumns.map((col: { id: string }) => col.id);
|
||||
|
||||
let hideForId = Object.fromEntries(ids.map((id: string) => [id, true]));
|
||||
|
||||
$: $hiddenColumnIds = Object.entries(hideForId)
|
||||
.filter(([, hide]) => !hide)
|
||||
.map(([id]) => id);
|
||||
|
||||
const hidableCols = ['title', 'status', 'priority'];
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger asChild let:builder>
|
||||
<Button variant="outline" size="sm" class="ml-auto hidden h-8 lg:flex" builders={[builder]}>
|
||||
<MixerHorizontal class="mr-2 h-4 w-4" />
|
||||
View
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
<DropdownMenu.Label>Toggle columns</DropdownMenu.Label>
|
||||
<DropdownMenu.Separator />
|
||||
{#each flatColumns as col}
|
||||
{#if hidableCols.includes(col.id)}
|
||||
<DropdownMenu.CheckboxItem bind:checked={hideForId[col.id]}>
|
||||
{col.header}
|
||||
</DropdownMenu.CheckboxItem>
|
||||
{/if}
|
||||
{/each}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
216
src/routes/(dashboard)/dashboard/(components)/data-table.svelte
Normal file
216
src/routes/(dashboard)/dashboard/(components)/data-table.svelte
Normal file
|
@ -0,0 +1,216 @@
|
|||
<script lang="ts">
|
||||
import { get, readable } from 'svelte/store';
|
||||
import { Render, Subscribe, createRender, createTable } from 'svelte-headless-table';
|
||||
import * as Table from '$lib/components/ui/table';
|
||||
import {
|
||||
addColumnFilters,
|
||||
addHiddenColumns,
|
||||
addPagination,
|
||||
addSelectedRows,
|
||||
addSortBy,
|
||||
addTableFilter
|
||||
} from 'svelte-headless-table/plugins';
|
||||
import {
|
||||
DataTableCheckbox,
|
||||
DataTableTitleCell,
|
||||
DataTableStatusCell,
|
||||
DataTableRowActions,
|
||||
DataTablePriorityCell,
|
||||
DataTableColumnHeader,
|
||||
DataTableToolbar,
|
||||
DataTablePagination,
|
||||
defaultPageSize
|
||||
} from '.';
|
||||
|
||||
import type { Ticket } from '../(data)/schemas';
|
||||
|
||||
export let data: Ticket[];
|
||||
|
||||
const table = createTable(readable(data), {
|
||||
select: addSelectedRows(),
|
||||
sort: addSortBy({
|
||||
toggleOrder: ['asc', 'desc']
|
||||
}),
|
||||
page: addPagination({ initialPageSize: defaultPageSize }),
|
||||
filter: addTableFilter({
|
||||
fn: ({ filterValue, value }) => {
|
||||
return value.toLowerCase().includes(filterValue.toLowerCase());
|
||||
}
|
||||
}),
|
||||
colFilter: addColumnFilters(),
|
||||
hide: addHiddenColumns()
|
||||
});
|
||||
|
||||
const columns = table.createColumns([
|
||||
table.display({
|
||||
id: 'select',
|
||||
header: (_, { pluginStates }) => {
|
||||
const { allPageRowsSelected } = pluginStates.select;
|
||||
return createRender(DataTableCheckbox, {
|
||||
checked: allPageRowsSelected,
|
||||
'aria-label': 'Select all'
|
||||
});
|
||||
},
|
||||
cell: ({ row }, { pluginStates }) => {
|
||||
const { getRowState } = pluginStates.select;
|
||||
const { isSelected } = getRowState(row);
|
||||
return createRender(DataTableCheckbox, {
|
||||
checked: isSelected,
|
||||
'aria-label': 'Select row',
|
||||
class: 'translate-y-[2px]'
|
||||
});
|
||||
},
|
||||
plugins: {
|
||||
sort: {
|
||||
disable: true
|
||||
}
|
||||
}
|
||||
}),
|
||||
table.column({
|
||||
accessor: 'id',
|
||||
header: () => {
|
||||
return 'Ticket ID';
|
||||
},
|
||||
id: 'ticket',
|
||||
plugins: {
|
||||
sort: {
|
||||
disable: true
|
||||
}
|
||||
}
|
||||
}),
|
||||
table.column({
|
||||
accessor: 'title',
|
||||
header: 'Title',
|
||||
id: 'title',
|
||||
cell: ({ value, row }) => {
|
||||
if (row.isData()) {
|
||||
return createRender(DataTableTitleCell, {
|
||||
value,
|
||||
labelValue: row.original.label
|
||||
});
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}),
|
||||
table.column({
|
||||
accessor: 'status',
|
||||
header: 'Status',
|
||||
id: 'status',
|
||||
cell: ({ value }) => {
|
||||
return createRender(DataTableStatusCell, {
|
||||
value
|
||||
});
|
||||
},
|
||||
plugins: {
|
||||
colFilter: {
|
||||
fn: ({ filterValue, value }) => {
|
||||
if (filterValue.length === 0) return true;
|
||||
if (!Array.isArray(filterValue) || typeof value !== 'string') return true;
|
||||
return filterValue.some((filter) => {
|
||||
return value.includes(filter);
|
||||
});
|
||||
},
|
||||
initialFilterValue: [],
|
||||
render: ({ filterValue }) => {
|
||||
return get(filterValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
table.column({
|
||||
accessor: 'priority',
|
||||
id: 'priority',
|
||||
header: 'Priority',
|
||||
cell: ({ value }) => {
|
||||
return createRender(DataTablePriorityCell, {
|
||||
value
|
||||
});
|
||||
},
|
||||
plugins: {
|
||||
colFilter: {
|
||||
fn: ({ filterValue, value }) => {
|
||||
if (filterValue.length === 0) return true;
|
||||
if (!Array.isArray(filterValue) || typeof value !== 'string') return true;
|
||||
|
||||
return filterValue.some((filter) => {
|
||||
return value.includes(filter);
|
||||
});
|
||||
},
|
||||
initialFilterValue: [],
|
||||
render: ({ filterValue }) => {
|
||||
return get(filterValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
table.display({
|
||||
id: 'actions',
|
||||
header: () => {
|
||||
return '';
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
if (row.isData() && row.original) {
|
||||
return createRender(DataTableRowActions, {
|
||||
row: row.original
|
||||
});
|
||||
}
|
||||
return '';
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
||||
const tableModel = table.createViewModel(columns);
|
||||
|
||||
const { headerRows, pageRows, tableAttrs, tableBodyAttrs } = tableModel;
|
||||
</script>
|
||||
|
||||
<div class="space-y-4">
|
||||
<DataTableToolbar {tableModel} />
|
||||
<div class="rounded-md border">
|
||||
<Table.Root {...$tableAttrs}>
|
||||
<Table.Header>
|
||||
{#each $headerRows as headerRow}
|
||||
<Subscribe rowAttrs={headerRow.attrs()}>
|
||||
<Table.Row>
|
||||
{#each headerRow.cells as cell (cell.id)}
|
||||
<Subscribe attrs={cell.attrs()} let:attrs props={cell.props()} let:props>
|
||||
<Table.Head {...attrs}>
|
||||
{#if cell.id !== 'select' && cell.id !== 'actions'}
|
||||
<DataTableColumnHeader {props}
|
||||
><Render of={cell.render()} /></DataTableColumnHeader
|
||||
>
|
||||
{:else}
|
||||
<Render of={cell.render()} />
|
||||
{/if}
|
||||
</Table.Head>
|
||||
</Subscribe>
|
||||
{/each}
|
||||
</Table.Row>
|
||||
</Subscribe>
|
||||
{/each}
|
||||
</Table.Header>
|
||||
<Table.Body {...$tableBodyAttrs}>
|
||||
{#each $pageRows as row (row.id)}
|
||||
<Subscribe rowAttrs={row.attrs()} let:rowAttrs>
|
||||
<Table.Row {...rowAttrs}>
|
||||
{#each row.cells as cell (cell.id)}
|
||||
<Subscribe attrs={cell.attrs()} let:attrs>
|
||||
<Table.Cell {...attrs}>
|
||||
{#if cell.id === 'ticket'}
|
||||
<div class="w-[80px]">
|
||||
<Render of={cell.render()} />
|
||||
</div>
|
||||
{:else}
|
||||
<Render of={cell.render()} />
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
</Subscribe>
|
||||
{/each}
|
||||
</Table.Row>
|
||||
</Subscribe>
|
||||
{/each}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</div>
|
||||
<DataTablePagination {tableModel} />
|
||||
</div>
|
12
src/routes/(dashboard)/dashboard/(components)/index.ts
Normal file
12
src/routes/(dashboard)/dashboard/(components)/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
export { default as DataTableCheckbox } from './data-table-checkbox.svelte';
|
||||
export { default as DataTableTitleCell } from './data-table-title-cell.svelte';
|
||||
export { default as DataTableStatusCell } from './data-table-status-cell.svelte';
|
||||
export { default as DataTableRowActions } from './data-table-row-actions.svelte';
|
||||
export { default as DataTablePriorityCell } from './data-table-priority-cell.svelte';
|
||||
export { default as DataTableColumnHeader } from './data-table-column-header.svelte';
|
||||
export { default as DataTableToolbar } from './data-table-toolbar.svelte';
|
||||
export { default as DataTablePagination } from './data-table-pagination.svelte';
|
||||
export { default as DataTableViewOptions } from './data-table-view-options.svelte';
|
||||
export { default as DataTableFacetedFilter } from './data-table-faceted-filter.svelte';
|
||||
|
||||
export const defaultPageSize = 15;
|
Loading…
Add table
Add a link
Reference in a new issue