Skip to content

Commit

Permalink
Employees data table
Browse files Browse the repository at this point in the history
  • Loading branch information
gskorokhod committed Jul 21, 2024
1 parent f8f167c commit 57795a5
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 89 deletions.
128 changes: 128 additions & 0 deletions src/lib/components/elements/data-tables/core/data-table.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<script lang="ts">
// noinspection ES6UnusedImports
import * as Table from "$lib/components/ui/table";
import {
type ReadOrWritable,
Column,
createTable, Subscribe, Render
} from "svelte-headless-table";
import {
addPagination,
addSortBy,
addTableFilter,
type AnyPlugins,
type PaginationConfig, type SortByConfig, type SortKey
} from "svelte-headless-table/plugins";
import { Button } from "$lib/components/ui/button";
import { ArrowDown, ArrowUp, ArrowUpDown } from "lucide-svelte";
import { cn } from "$lib/utils.ts";
let data: ReadOrWritable<any[]>;
let columns: Column<any, AnyPlugins>[];
let paginationConfig: PaginationConfig | undefined = undefined;
let sortByConfig: SortByConfig | undefined = undefined;
let className: string = "";
let table = createTable(data, {
page: addPagination(paginationConfig), sort: addSortBy(sortByConfig), filter: addTableFilter({
fn: ({ filterValue, value }) => {
console.log("Filtering: ", `\n\tvalue = \'${value}\'\n\tfilterValue = \'${filterValue}\'`);
return value.toLowerCase().includes(filterValue.toLowerCase());
}
})
});
const {
visibleColumns,
headerRows,
pageRows,
tableAttrs,
tableBodyAttrs,
pluginStates
} = table.createViewModel(columns);
const { pageIndex, hasPreviousPage, hasNextPage } = pluginStates.page;
const { filterValue } = pluginStates.filter;
const { sortKeys } = pluginStates.sort;
export { data, columns, filterValue, sortKeys, className as class };
</script>

<div class={className}>
<div class="rounded-md border">
<Table.Root {...$tableAttrs}>
<Table.Header>
{#each $headerRows as headerRow (headerRow.id)}
<Subscribe rowAttrs={headerRow.attrs()} let:rowAttrs>
<Table.Row {...rowAttrs}>
{#each headerRow.cells as cell (cell.id)}
<Subscribe
attrs={cell.attrs()}
let:attrs
props={cell.props()}
let:props
>
<Table.Head
{...attrs}
class={cn("text-sm", "[&:has([role=checkbox])]:pl-3")}
>
{#if props.sort.disabled}
<Render of={cell.render()} />
{:else}
<Button variant="ghost" size="sm" on:click={props.sort.toggle}>
<Render of={cell.render()} />
{#if props.sort.order === "asc"}
<ArrowUp class={cn($sortKeys[0]?.id === cell.id && "text-foreground", "ml-2 h-4 w-4")} />
{:else if props.sort.order === "desc"}
<ArrowDown class={cn($sortKeys[0]?.id === cell.id && "text-foreground", "ml-2 h-4 w-4")} />
{:else}
<ArrowUpDown
class={cn(
$sortKeys[0]?.id === cell.id && "text-foreground",
"ml-2 h-4 w-4"
)}
/>
{/if}
</Button>
{/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 props={cell.props()} let:props>
<Table.Cell class="[&:has([role=checkbox])]:pl-3" {...attrs}>
<Render of={cell.render()} />
</Table.Cell>
</Subscribe>
{/each}
</Table.Row>
</Subscribe>
{/each}
</Table.Body>
</Table.Root>
</div>
<div class="flex items-center justify-end space-x-4 py-4">
<Button
variant="outline"
size="sm"
on:click={() => ($pageIndex = $pageIndex - 1)}
disabled={!$hasPreviousPage}>Previous
</Button
>
<Button
variant="outline"
size="sm"
disabled={!$hasNextPage}
on:click={() => ($pageIndex = $pageIndex + 1)}>Next
</Button
>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script lang="ts">
import { type Person, Skill } from "$lib/types/core.ts";
import { employees } from "$lib/stores.ts";
import { createRender, createTable, type ReadOrWritable } from "svelte-headless-table";
import PersonAvatar from "$lib/components/elements/person/person-avatar.svelte";
import SkillsList from "$lib/components/elements/skill/skills-list.svelte";
import ConstraintsList from "$lib/components/elements/constraint/constraints-list.svelte";
import DataTable from "$lib/components/elements/data-tables/core/data-table.svelte";
import { writable, type Writable } from "svelte/store";
import { createSortKeysStore, type WritableSortKeys } from "svelte-headless-table/plugins";
import type { Constraint } from "$lib/types/constraints.ts";
let data: ReadOrWritable<Person[]> = employees;
let filterValue: Writable<string> = writable("");
let sortKeys: WritableSortKeys = createSortKeysStore([]);
let className: string = "";
const table = createTable(data);
const columns = table.createColumns([
table.column({
id: "avatar",
accessor: (row: Person) => row,
header: "Avatar",
cell: (data) => createRender(PersonAvatar, { person: data.value }),
plugins: {
filter: {
disable: true
},
sort: {
disable: true
}
}
}),
table.column({
id: "name",
accessor: "name",
header: "Name"
}),
table.column({
id: "job_title",
accessor: "job_title",
header: "Job Title"
}),
table.column({
id: "skills",
accessor: (row: Person) => row.skills,
header: "Skills",
cell: (data) => createRender(SkillsList, { skills: data.value }),
plugins: {
filter: {
getFilterValue: (value: Skill[]) => value.map((skill) => skill.name).join(" ")
},
sort: {
getSortValue: (value: Skill[]) => value.map((skill) => skill.name).join(" ")
}
}
}),
table.column({
id: "constraints",
accessor: (row: Person) => row.constraints,
header: "Constraints",
cell: (data) => createRender(ConstraintsList, { constraints: data.value }),
plugins: {
filter: {
getFilterValue: (value: Constraint[]) => value.map((constraint) => constraint.type).join(" ")
},
sort: {
getSortValue: (value: Constraint[]) => value.map((constraint) => constraint.type).join(" ")
}
}
})
]);
export { data, filterValue, sortKeys, className as class };
</script>

<DataTable {data} {columns} bind:filterValue bind:sortKeys class={className} />
79 changes: 0 additions & 79 deletions src/lib/components/elements/data-tables/employees.svelte

This file was deleted.

32 changes: 32 additions & 0 deletions src/lib/components/elements/data-views/employees-data-view.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script lang="ts">
import TopBar from "$lib/components/elements/top-bar/top_bar.svelte";
import Search from "$lib/components/ui/search/search.svelte";
import EmployeesDataTable from "$lib/components/elements/data-tables/employees-data-table.svelte";
import type { ReadOrWritable } from "svelte-headless-table";
import type { Person } from "$lib/types/core.ts";
import { employees } from "$lib/stores.ts";
import { writable, type Writable } from "svelte/store";
import { createSortKeysStore, type WritableSortKeys } from "svelte-headless-table/plugins";
import { Button } from "$lib/components/ui/button";
let data: ReadOrWritable<Person[]> = employees;
let filterValue: Writable<string> = writable("");
let sortKeys: WritableSortKeys = createSortKeysStore([]);
export { data };
</script>

<TopBar sticky={true}>
<svelte:fragment slot="start">
<slot name="start" />
</svelte:fragment>

<svelte:fragment slot="middle">
<slot name="middle" />
</svelte:fragment>

<svelte:fragment slot="end">
<Search onInput={(s) => filterValue.set(s)} />
</svelte:fragment>
</TopBar>
<EmployeesDataTable {data} bind:filterValue bind:sortKeys class="w-full" />
10 changes: 5 additions & 5 deletions src/lib/components/ui/search/search.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<script lang="ts">
import { writable } from "svelte/store";
import { type Writable, writable } from "svelte/store";
import { onMount } from "svelte";
import { Button } from "$lib/components/ui/button";
import { SearchIcon } from "lucide-svelte";
import { Input } from "$lib/components/ui/input";
let searchInput = writable("");
let searchInput: Writable<string> = writable("");
let placeholder: string = "Search";
let debounceTimeout: number;
let focused: boolean = false;
Expand All @@ -16,10 +16,10 @@
let onSubmit: (value: string) => void = () => {
};
function handleInputChange(e: Event) {
function handleInputChange() {
clearTimeout(debounceTimeout);
debounceTimeout = setTimeout(() => {
onInput((e.target as HTMLInputElement).value);
onInput($searchInput);
}, debounceDelay);
}
Expand All @@ -32,7 +32,7 @@
return () => clearTimeout(debounceTimeout);
});
export { onInput, onSubmit, placeholder, debounceDelay };
export { searchInput, placeholder, debounceDelay, onInput, onSubmit };
</script>

<form class="relative h-10 rounded-md bg-white shadow overflow-clip" on:submit|preventDefault={handleSubmit}>
Expand Down
6 changes: 3 additions & 3 deletions src/lib/types/constraints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { Location, Person, Task } from "$lib/types/core.ts";
// ToDo: Discuss with Oz & add more constraints

export enum ConstraintType {
NoLocations,
NoTasks,
NoPeople
NoLocations = "NoLocations",
NoTasks = "NoTasks",
NoPeople = "NoPeople"
}

export type NoLocationsConstraint = {
Expand Down
4 changes: 2 additions & 2 deletions src/routes/employees/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script lang="ts">
import EmployeesTable from "$lib/components/elements/data-tables/employees.svelte";
import EmployeesDataView from "$lib/components/elements/data-views/employees-data-view.svelte";
</script>

<div class="bg-gray-50 w-full">
<main class="w-full h-dvh flex flex-col items-start justify-start overflow-y-scroll p-4">
<EmployeesTable />
<EmployeesDataView />
</main>
</div>

0 comments on commit 57795a5

Please sign in to comment.