Skip to content

Commit b68a6c4

Browse files
committed
WIP
1 parent 7274134 commit b68a6c4

File tree

10 files changed

+355
-40
lines changed

10 files changed

+355
-40
lines changed

src/lib/ui/core/Table/DataTable/DataTable.svelte

Lines changed: 52 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
lang="ts"
33
generics="GItem extends Record<string, unknown>, GColumn extends BaseTableColumn<GItem>"
44
>
5-
import type { BaseTableColumn } from './types.js'
5+
import type { ComponentProps } from 'svelte'
66
77
import Table from '../Table.svelte'
88
import TableBody from '../TableBody.svelte'
@@ -11,16 +11,10 @@
1111
import TableHeader from '../TableHeader.svelte'
1212
import TableRow from '../TableRow.svelte'
1313
14-
type TProps = {
15-
items: GItem[]
16-
columns: GColumn[]
14+
import type { BaseTableColumn, TDataTableProps } from './types.js'
15+
import Pagination from '../Pagination/Pagination.svelte'
1716
18-
class?: string
19-
headerClass?: string
20-
bodyClass?: string
21-
headerRowClass?: string
22-
bodyRowClass?: string
23-
}
17+
type TProps = TDataTableProps<GItem, GColumn> & Partial<ComponentProps<typeof Pagination>>
2418
2519
const {
2620
items,
@@ -30,34 +24,58 @@
3024
bodyClass,
3125
headerRowClass,
3226
bodyRowClass,
27+
totalItems,
28+
page = 1,
29+
pageSize = items.length,
30+
rows,
31+
onPageChange,
3332
}: TProps = $props()
33+
34+
const hasMoreItems = $derived(items.length !== totalItems)
35+
const itemsCount = $derived(totalItems ?? items.length)
36+
const pageOffset = $derived((page - 1) * pageSize)
37+
const pageEndOffset = $derived(pageOffset + pageSize)
38+
const hasPages = $derived(pageSize !== itemsCount)
39+
40+
const pagedItems = $derived.by(() => {
41+
if (!hasPages) return items
42+
if (hasMoreItems) return items.slice(0, pageSize)
43+
44+
return items.slice(pageOffset, pageEndOffset)
45+
})
3446
</script>
3547
36-
<Table class={className}>
37-
<TableHeader class={headerClass}>
38-
<TableRow class={headerRowClass}>
39-
{#each columns as { title, Head, class: className }}
40-
{#if Head}
41-
<Head />
42-
{:else}
43-
<TableHead class={className}>{title}</TableHead>
44-
{/if}
45-
{/each}
46-
</TableRow>
47-
</TableHeader>
48-
<TableBody class={bodyClass}>
49-
{#each items as item, i}
50-
<TableRow class={bodyRowClass}>
51-
{#each columns as column}
52-
{#if column.Cell}
53-
<column.Cell {item} />
48+
<article class="flex w-full flex-col gap-4">
49+
<Table class={className}>
50+
<TableHeader class={headerClass}>
51+
<TableRow class={headerRowClass}>
52+
{#each columns as { title, Head, class: className }}
53+
{#if Head}
54+
<Head />
5455
{:else}
55-
<TableCell class={column.class}>
56-
{column.format(item, i)}
57-
</TableCell>
56+
<TableHead class={className}>{title}</TableHead>
5857
{/if}
5958
{/each}
6059
</TableRow>
61-
{/each}
62-
</TableBody>
63-
</Table>
60+
</TableHeader>
61+
<TableBody class={bodyClass}>
62+
{#each pagedItems as item, i}
63+
<TableRow class={bodyRowClass}>
64+
{#each columns as column}
65+
{#if column.Cell}
66+
<column.Cell {item} />
67+
{:else}
68+
<TableCell class={column.class}>
69+
{column.format(item, i)}
70+
</TableCell>
71+
{/if}
72+
{/each}
73+
</TableRow>
74+
{/each}
75+
</TableBody>
76+
</Table>
77+
78+
{#if hasPages}
79+
<Pagination {page} {pageSize} {rows} {onPageChange} totalItems={itemsCount} />
80+
{/if}
81+
</article>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<script
2+
lang="ts"
3+
generics="GItem extends Record<string, unknown>, GColumn extends BaseTableColumn<GItem>"
4+
>
5+
import type { BaseTableColumn, TDataTableProps } from './types.js'
6+
7+
import Table from '../Table.svelte'
8+
import TableBody from '../TableBody.svelte'
9+
import TableCell from '../TableCell.svelte'
10+
import TableHead from '../TableHead.svelte'
11+
import TableHeader from '../TableHeader.svelte'
12+
import TableRow from '../TableRow.svelte'
13+
import Pagination from '../Pagination/Pagination.svelte'
14+
import type { ComponentProps } from 'svelte'
15+
16+
// type TPaginationProps = ComponentProps<typeof Pagination>
17+
18+
type TProps = TDataTableProps<GItem, GColumn>
19+
20+
const {
21+
items,
22+
columns,
23+
class: className,
24+
headerClass,
25+
bodyClass,
26+
headerRowClass,
27+
bodyRowClass,
28+
}: TProps = $props()
29+
30+
// const hasMoreItems = $derived(!!totalItems && items.length !== totalItems)
31+
// const itemsCount = $derived(totalItems ?? items.length)
32+
// const pageOffset = $derived(page * pageSize)
33+
</script>
34+
35+
<!-- <article class="flex flex-col w-full gap-4"> -->
36+
<Table class={className}>
37+
<TableHeader class={headerClass}>
38+
<TableRow class={headerRowClass}>
39+
{#each columns as { title, Head, class: className }}
40+
{#if Head}
41+
<Head />
42+
{:else}
43+
<TableHead class={className}>{title}</TableHead>
44+
{/if}
45+
{/each}
46+
</TableRow>
47+
</TableHeader>
48+
<TableBody class={bodyClass}>
49+
{#each items as item, i}
50+
<TableRow class={bodyRowClass}>
51+
{#each columns as column}
52+
{#if column.Cell}
53+
<column.Cell {item} />
54+
{:else}
55+
<TableCell class={column.class}>
56+
{column.format(item, i)}
57+
</TableCell>
58+
{/if}
59+
{/each}
60+
</TableRow>
61+
{/each}
62+
</TableBody>
63+
</Table>
64+
65+
<!-- <Pagination {page} {pageSize} {...paginationProps} totalItems={itemsCount} /> -->
66+
<!-- </article> -->

src/lib/ui/core/Table/DataTable/types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,17 @@ export type BaseTableColumn<GItem extends Record<string, unknown>> = {
2323
Head: Component
2424
}
2525
)
26+
27+
export type TDataTableProps<
28+
GItem extends Record<string, unknown>,
29+
GColumn extends BaseTableColumn<GItem>,
30+
> = {
31+
items: GItem[]
32+
columns: GColumn[]
33+
34+
class?: string
35+
headerClass?: string
36+
bodyClass?: string
37+
headerRowClass?: string
38+
bodyRowClass?: string
39+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
<script lang="ts">
2+
import Button from '$ui/core/Button/Button.svelte'
3+
import Input from '$ui/core/Input/Input.svelte'
4+
import Popover from '$ui/core/Popover/Popover.svelte'
5+
import { cn } from '$ui/utils/index.js'
6+
import { noop } from '@melt-ui/svelte/internal/helpers'
7+
// import { noop } from 'rxjs'
8+
import type { ChangeEventHandler } from 'svelte/elements'
9+
10+
type TProps = {
11+
class?: string
12+
page?: number
13+
pageSize?: number
14+
rows?: number[]
15+
onPageChange?: (page: number, pageSize: number) => void
16+
totalItems: number
17+
}
18+
19+
const {
20+
class: className,
21+
page = 1,
22+
pageSize = 25,
23+
rows = [10, 25, 50],
24+
onPageChange = noop,
25+
totalItems,
26+
}: TProps = $props()
27+
28+
let isPageSizeOpened = $state(false)
29+
30+
$effect(() => {
31+
if (!process.env.IS_DEV_MODE) return
32+
33+
if (rows.length > 0 && !rows.includes(pageSize)) {
34+
console.error('[pageSize] value should be present in [rows] array or it should be empty')
35+
}
36+
})
37+
38+
const pagesAmount = $derived(Math.ceil(totalItems / pageSize))
39+
const maxPage = $derived(pagesAmount)
40+
41+
function adjustPageNumber(page: number) {
42+
if (!page || page < 1) return 1
43+
if (page > pagesAmount) return maxPage
44+
return page
45+
}
46+
47+
const onPageInput: ChangeEventHandler<HTMLInputElement> = ({ currentTarget }) => {
48+
const value = +currentTarget.value
49+
const page = adjustPageNumber(value)
50+
51+
onPageChange(page, pageSize)
52+
}
53+
54+
function onPageSizeChange(size: number) {
55+
isPageSizeOpened = false
56+
onPageChange(1, size)
57+
}
58+
59+
function onNextPage() {
60+
onPageChange(adjustPageNumber(page + 1), pageSize)
61+
}
62+
63+
function onPrevPage() {
64+
onPageChange(adjustPageNumber(page - 1), pageSize)
65+
}
66+
</script>
67+
68+
<section class={cn('flex items-center gap-4 whitespace-nowrap', className)}>
69+
{#if rows.length > 1}
70+
<Popover bind:isOpened={isPageSizeOpened}>
71+
{#snippet children({ props })}
72+
<Button
73+
{...props}
74+
class="sm:hidden"
75+
variant="border"
76+
size="md"
77+
icon="arrow-down"
78+
iconSize="8"
79+
iconHeight="5"
80+
iconOnRight
81+
>
82+
{pageSize} rows
83+
</Button>
84+
{/snippet}
85+
86+
{#snippet content()}
87+
<section class="flex flex-col">
88+
{#each rows as option}
89+
<Button
90+
class={cn(option === pageSize && 'text-green')}
91+
onclick={() => onPageSizeChange(option)}
92+
>
93+
{option} rows
94+
</Button>
95+
{/each}
96+
</section>
97+
{/snippet}
98+
</Popover>
99+
{/if}
100+
101+
<section class="flex items-center gap-2 text-waterloo">
102+
<span>Page</span>
103+
104+
<Input
105+
class="text-center"
106+
inputClass="[appearance:textfield]"
107+
type="number"
108+
min="1"
109+
max={maxPage}
110+
value={page}
111+
onchange={onPageInput}
112+
/>
113+
114+
<section>
115+
<span>of {pagesAmount}</span>
116+
<span class="ml-2 sm:hidden">({totalItems} rows total)</span>
117+
</section>
118+
</section>
119+
120+
<section class="ml-auto flex gap-2">
121+
<Button
122+
class="[&>svg]:rotate-180"
123+
variant="border"
124+
disabled={page <= 1}
125+
size="md"
126+
icon="arrow-right"
127+
iconSize="5"
128+
iconHeight="8"
129+
iconOnRight
130+
onclick={onPrevPage}
131+
>
132+
Prev
133+
</Button>
134+
135+
<Button
136+
variant="border"
137+
disabled={page >= maxPage}
138+
size="md"
139+
icon="arrow-right"
140+
iconSize="5"
141+
iconHeight="8"
142+
onclick={onNextPage}
143+
>
144+
Next
145+
</Button>
146+
</section>
147+
</section>
148+
149+
<!-- <style lang="scss">
150+
// input {
151+
// width: 45px;
152+
// appearance: textfield;
153+
// -moz-appearance: textfield;
154+
155+
// &::-webkit-outer-spin-button,
156+
// &::-webkit-inner-spin-button {
157+
// -webkit-appearance: none;
158+
// margin: 0;
159+
// }
160+
// }
161+
</style> -->
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script lang="ts">
2+
import { DataTable } from '$ui/core/Table/index.js'
3+
import { generateItems } from '../utils.js'
4+
import { columns } from './columns.js'
5+
6+
const items = generateItems(50)
7+
8+
let page = $state(1)
9+
let pageSize = $state(10)
10+
</script>
11+
12+
<main class="flex h-screen items-start justify-center px-10 py-5">
13+
<DataTable
14+
{items}
15+
{columns}
16+
{page}
17+
{pageSize}
18+
onPageChange={(p, size) => {
19+
page = p
20+
pageSize = size
21+
}}
22+
/>
23+
</main>

0 commit comments

Comments
 (0)