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 1000
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.state.processedData 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.state.processedData.length * datagrid.state.pagination.page -
					datagrid.state.pagination.perPage}
				:
				{datagrid.state.processedData.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>
In the coming days, the grid will officially launch its version 1.0. I recommend waiting until the release before incorporating it into your projects, as we've completely rewritten and simplified the internal logic, making it more efficient. Version 1.0 will also introduce exciting new features such as row grouping and aggregation. If you have any questions or feedback, feel free to reach out! If you find this project valuable, please consider giving it a star on GitHub. Thank you for your support! ❤️