Advanced example

Inventory

Product name
Price
Quantity
Category
Expiration date
Location
Manufacturer
Actions
Sprouts - Onion
261.51
972
furniture
7/14/2023
Apt 1819
Feedspan
Beer - True North Lager
191.32
38
furniture
7/17/2023
Suite 11
Brainlounge
Milk - Condensed
826.1
714
furniture
6/9/2022
Apt 767
Vimbo
Brandy Cherry - Mcguinness
555.07
212
electronics
2/26/2022
Suite 47
Brainlounge
Olive - Spread Tapenade
506.45
509
furniture
3/5/2022
Room 1868
Gabtune
Tea - Black Currant
750.65
848
clothing
11/1/2022
Room 1846
Skaboo
Coconut Milk - Unsweetened
658.56
149
electronics
8/5/2022
PO Box 75154
Jaloo
Nut - Walnut, Pieces
763.42
147
furniture
9/7/2023
PO Box 39137
Kimia
Apple - Northern Spy
81.35
950
clothing
12/12/2023
Room 1284
Linkbridge
Orange - Canned, Mandarin
939.59
200
furniture
7/28/2022
18th Floor
Feedbug
Veal - Brisket, Provimi,bnls
353.49
226
electronics
6/17/2023
Suite 20
Buzzdog
Wine - Ice Wine
867.85
618
clothing
5/10/2022
10th Floor
Edgepulse
Table Cloth 54x72 White
703.86
770
furniture
12/9/2023
Suite 25
Jabberstorm
Wine - Cotes Du Rhone Parallele
967.56
435
clothing
8/25/2022
Suite 73
Voolia
Wine - Duboeuf Beaujolais
247.42
105
clothing
7/23/2022
Apt 572
Dabtype
Carrots - Jumbo
880.11
350
furniture
3/21/2022
Room 1965
Linktype
Wine - Touraine Azay - Le - Rideau
949.68
290
electronics
12/30/2023
Apt 110
Midel
Schnappes Peppermint - Walker
879.93
69
clothing
12/19/2023
Room 139
Oyope
Wine - Champagne Brut Veuve
496.79
201
clothing
2/21/2023
Room 1571
Youspan
Appetizer - Smoked Salmon / Dill
274.6
856
electronics
1/1/2022
Suite 97
Jabberstorm
Showing 0 : 20 of 1
Page 1

Features Implemented

  • Sorting
  • Filtering
  • Context menu
  • Custom styling
  • Custom Footer
  • Custom Pagination
  • Column freezing
  • Column reordering
  • Column hiding
  • Column resizing
  • Column alignment
  • Row expanding
  • Row selection
  • Sticky header
  • Sticky parent row header
  • Copying data
  • Exporting data
  • Status indicator
  • Scroll to top
  • Sticky header
  • Sticky rows
  • Custom cells
  • Options menu
import type { BaseColumn } from "$lib/datagrid/types";
import type { InventoryDataRow } from "$lib/data/inventory";

export const columns = [
    {
        id: 'checkbox',
        title: 'Row selection',
        width: '50px',
        pinned: {
            position: 'left'
        },
        visible: true,
        resizable: false,
        sortable: false,
        exportable: false,
        selectable: false,
        moveable: false
    },

    {
        id: 'expand',
        title: 'Row expand',
        width: '50px',
        pinnable: true,
        pinned: {
            position: 'left'
        },
        visible: true,
        resizable: false,
        sortable: false,
        exportable: false,
        selectable: false,
        moveable: false
    },
    {
        id: 'product.name',
        title: 'Product name',
        sortable: true,
        grow: true,
        filterType: 'string',
        filterValue: '',
        pinnable: true,
        pinned: {
            position: 'left'
        }
    },
    {
        id: 'price',
        title: 'Price',
        sortable: true,
        filterType: 'range',
        filterValue: [-99999999999, 9999999999],
        align: 'end'
    },
    {
        id: 'quantity',
        title: 'Quantity',
        sortable: true,
        filterType: 'number',
        filterValue: '',
        align: 'end',

    },
    {
        id: 'category',
        title: 'Category',
        width: '130px',
        filterType: 'select',
        filterValue: '',
        options: [
            { label: 'Everything', value: '' },
            { label: 'Furniture', value: 'furniture' },
            { label: 'Clothing', value: 'clothing' },
            { label: 'Electronics', value: 'electronics' }
        ],
    },
    {
        id: 'expiration_date',
        title: 'Expiration date',
        width: '120px',
    },
    {
        id: 'location',
        title: 'Location',
        width: '200px',
    },
    {
        id: 'manufacturer',
        title: 'Manufacturer',
        width: '200px',
    },
    {
        id: 'actions',
        title: 'Actions',
        width: '110px',
        visible: true,
        resizable: false,
        sortable: false,
        exportable: false,
        selectable: false,
        hideable: false,
        pinned: {
            position: 'right'
        },
        align: 'start'
    }
] satisfies BaseColumn<InventoryDataRow>[]
<script lang="ts">
	import { setContext } from 'svelte';
	import { columns } from './columns.svelte';
	import { inventoryData as data } from '$lib/data/inventory';
	import MaterialSymbolsDeleteOutline from '~icons/material-symbols/delete-outline';
	import { TzezarDatagrid } from '$lib/datagrid/tzezar-datagrid.svelte';
	import * as Datagrid from '$lib/datagrid';
	import Button from '$lib/components/ui/button/button.svelte';
	import { removeRow } from '$lib/datagrid/fns/remove-row';
	import { cn } from '$lib/utils';
	import { toast } from 'svelte-sonner';
	import EditForm from './edit-form.svelte';
	import ExpandedRowContent from './expanded-row-content.svelte';
	import * as ContextMenu from '$lib/components/ui/context-menu';
	import { getNestedValue } from '$lib/datagrid/fns/get-nested-value';
	import CellWithContextMenu from './cell-with-context-menu.svelte';
	import CustomPagination from './custom-pagination.svelte';

	let datagrid = setContext(
		`datagrid`,
		new TzezarDatagrid({
			title: 'Inventory',
			data,
			columns,
			options: {
				pagination: { display: false },
				rows: { striped: true },
				footer: { display: true },
				topbar: {
					display: true,
					displayCopyDataMenu: true,
					displayExportDataMenu: true,
					displayFullscreenToggle: true,
					displayHeadFilterToggle: true,
					settingsMenu: {
						display: true
					}
				}
			}
		})
	);
</script>

<Datagrid.Datagrid>
	{#snippet topBar()}
		<Datagrid.TopBar />
	{/snippet}
	{#snippet head()}
		{#each datagrid.columns as column, i (column.id)}
			{#if column.id === 'checkbox'}
				<Datagrid.HeaderWithoutSpacing {column} title={column.title}>
					{#snippet custom()}
						<Datagrid.HeaderRowSelectionDropdown />
					{/snippet}
				</Datagrid.HeaderWithoutSpacing>
			{:else if column.id === 'expand'}
				<Datagrid.HeaderWithoutSpacing {column} title="" />
			{:else}
				<Datagrid.Header {column}>
					{#snippet filter()}
						<Datagrid.ColumnFilter {column} />
					{/snippet}
				</Datagrid.Header>
			{/if}
		{/each}
	{/snippet}
	{#snippet body()}
		{#each datagrid.internal.paginatedData as row, rowIndex}
			<Datagrid.Row {rowIndex}>
				{#each datagrid.columns as column, columnIndex}
					{#if column.id === 'checkbox'}
						<Datagrid.CellWithoutSpacing {row} {column} {columnIndex} {rowIndex}>
							{#snippet custom()}
								<Datagrid.CellRowSelectionCheckbox {row} />
							{/snippet}
						</Datagrid.CellWithoutSpacing>
					{:else if column.id === 'expand'}
						<Datagrid.CellWithoutSpacing {row} {column} {columnIndex} {rowIndex}>
							{#snippet custom()}
								<Datagrid.ExpandRowToggler rowId={row.id} />
							{/snippet}
						</Datagrid.CellWithoutSpacing>
					{:else if column.id === 'actions'}
						<Datagrid.Cell {row} {column} {columnIndex} {rowIndex}>
							{#snippet custom()}
								<div class={cn('flex flex-row gap-2')}>
									<Button
										size="sm"
										variant="destructive"
										onclick={() => {
											removeRow(row.id, datagrid);
											toast.success('Row removed');
										}}
									>
										<MaterialSymbolsDeleteOutline />
									</Button>
									<EditForm />
								</div>
							{/snippet}
						</Datagrid.Cell>
					{:else if column.id === 'quantity'}
						<Datagrid.Cell
							{row}
							{column}
							{columnIndex}
							{rowIndex}
							class={{
								cell: cn(row['quantity'] < 200 && 'text-red-500')
							}}
						/>
					{:else}
						<ContextMenu.Root>
							<ContextMenu.Trigger asChild let:builder>
								<CellWithContextMenu
									{builder}
									{columnIndex}
									{rowIndex}
									{column}
									{row}
									class={{ data: 'overflow-hidden text-ellipsis text-nowrap' }}
								/>
							</ContextMenu.Trigger>
							<ContextMenu.Content>
								<ContextMenu.Item>{getNestedValue(row, column.id)}</ContextMenu.Item>
							</ContextMenu.Content>
						</ContextMenu.Root>
					{/if}
				{/each}
			</Datagrid.Row>
			{#if Datagrid.isRowExpanded(datagrid, row.id)}
				<Datagrid.Row
					class={`border-b ${Datagrid.STAY_IN_PLACE} ${Datagrid.HIDE_BEHIND_PARENT_ROW}`}
					{rowIndex}
				>
					<ExpandedRowContent />
				</Datagrid.Row>
			{/if}
		{/each}
	{/snippet}
	{#snippet footer()}
		<div
			class="grid grid-cols-3 items-center p-2 pl-3"
			data-datagrid-footer-identifier={datagrid.identifier}
		>
			<span>
				Showing {datagrid.internal.paginatedData.length * datagrid.state.pagination.page -
					datagrid.state.pagination.perPage}
				:
				{datagrid.internal.paginatedData.length * datagrid.state.pagination.page}
				of
				{datagrid.state.pagination.count}
			</span>
			<div class="flex items-center justify-center">
				<CustomPagination />
			</div>
			<span class="flex justify-end">Page {datagrid.state.pagination.page}</span>
		</div>
	{/snippet}
</Datagrid.Datagrid>