Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ yarn-error.log*
# Misc
.DS_Store
*.pem


# Repomix
repomix-output.txt
47 changes: 47 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug server-side",
"type": "node-terminal",
"request": "launch",
"command": "bun dev",
"cwd": "${workspaceFolder}/apps/web"
},
{
"name": "Next.js: debug client-side",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000"
},
{
"name": "Next.js: debug client-side (Firefox)",
"type": "firefox",
"request": "launch",
"url": "http://localhost:3000",
"reAttach": true,
"pathMappings": [
{
"url": "webpack://_N_E",
"path": "${workspaceFolder}"
}
]
},
{
"name": "Next.js: debug full stack",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/next",
"cwd": "${workspaceFolder}/apps/web",
"runtimeArgs": ["--inspect"],
"skipFiles": ["<node_internals>/**"],
"serverReadyAction": {
"action": "debugWithEdge",
"killOnServerStop": true,
"pattern": "- Local:.+(https?://.+)",
"uriFormat": "%s",
"webRoot": "${workspaceFolder}"
}
}
]
}
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Changelog

## [Unreleased]

### Added

- **feat(data-table-filter)**: server-side filtering ([#53](https://github.com/kianbazza/ui/pull/53))
- **feat(data-table-filter)**: the great decoupling ([#47](https://github.com/kianbazza/ui/pull/47))

### Changed

- **feat(data-table-filter)**: use muted text color for operator display ([#55](https://github.com/kianbazza/ui/pull/55))
- **refactor(data-table-filter)**: registry item files re-organization ([#54](https://github.com/kianbazza/ui/pull/54))

### Fixed

- ...

### Other

- ...
184 changes: 184 additions & 0 deletions apps/web/app/demos/server/tst-query/_/columns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Checkbox } from '@/components/ui/checkbox'
import { cn } from '@/lib/utils'
import { createColumnHelper } from '@tanstack/react-table'
import { format } from 'date-fns'
import { CircleDashedIcon } from 'lucide-react'
import type { Issue } from './types'

export const LABEL_STYLES_MAP = {
red: 'bg-red-500 border-red-500',
orange: 'bg-orange-500 border-orange-500',
amber: 'bg-amber-500 border-amber-500',
yellow: 'bg-yellow-500 border-yellow-500',
lime: 'bg-lime-500 border-lime-500',
green: 'bg-green-500 border-green-500',
emerald: 'bg-emerald-500 border-emerald-500',
teal: 'bg-teal-500 border-teal-500',
cyan: 'bg-cyan-500 border-cyan-500',
sky: 'bg-sky-500 border-sky-500',
blue: 'bg-blue-500 border-blue-500',
indigo: 'bg-indigo-500 border-indigo-500',
violet: 'bg-violet-500 border-violet-500',
purple: 'bg-purple-500 border-purple-500',
fuchsia: 'bg-fuchsia-500 border-fuchsia-500',
pink: 'bg-pink-500 border-pink-500',
rose: 'bg-rose-500 border-rose-500',
neutral: 'bg-neutral-500 border-neutral-500',
} as const

export type TW_COLOR = keyof typeof LABEL_STYLES_MAP

const columnHelper = createColumnHelper<Issue>()

export const tstColumnDefs = [
columnHelper.display({
id: 'select',
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && 'indeterminate')
}
onCheckedChange={(value) => table.toggleAllRowsSelected(!!value)}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
enableColumnFilter: false,
}),
columnHelper.accessor((row) => row.status.id, {
id: 'status',
header: 'Status',
enableColumnFilter: true,
cell: ({ row }) => {
const { status } = row.original
const StatusIcon = status.icon

return (
<div className="flex items-center gap-2">
<StatusIcon className="size-4" />
<span>{status.name}</span>
</div>
)
},
}),
columnHelper.accessor((row) => row.title, {
id: 'title',
header: 'Title',
enableColumnFilter: true,
cell: ({ row }) => <div>{row.getValue('title')}</div>,
}),
columnHelper.accessor((row) => row.assignee?.id, {
id: 'assignee',
header: 'Assignee',
enableColumnFilter: true,
cell: ({ row }) => {
const user = row.original.assignee

if (!user) {
return <CircleDashedIcon className="size-5 text-border" />
}

const initials = user.name
.split(' ')
.map((x) => x[0])
.join('')
.toUpperCase()

return (
<Avatar className="size-5">
<AvatarImage src={user.picture} />
<AvatarFallback>{initials}</AvatarFallback>
</Avatar>
)
},
}),
columnHelper.accessor((row) => row.estimatedHours, {
id: 'estimatedHours',
header: 'Estimated Hours',
enableColumnFilter: true,
cell: ({ row }) => {
const estimatedHours = row.getValue<number>('estimatedHours')

if (!estimatedHours) {
return null
}

return (
<span>
<span className="tabular-nums tracking-tighter">
{estimatedHours}
</span>
<span className="text-muted-foreground ml-0.5">h</span>
</span>
)
},
}),
columnHelper.accessor((row) => row.startDate, {
id: 'startDate',
header: 'Start Date',
enableColumnFilter: true,
cell: ({ row }) => {
const startDate = row.getValue<Issue['startDate']>('startDate')

if (!startDate) {
return null
}

const formatted = format(startDate, 'MMM dd')

return <span>{formatted}</span>
},
}),
columnHelper.accessor((row) => row.endDate, {
id: 'endDate',
header: 'End Date',
cell: ({ row }) => {
const endDate = row.getValue<Issue['endDate']>('endDate')

if (!endDate) {
return null
}

const formatted = format(endDate, 'MMM dd')

return <span>{formatted}</span>
},
}),
columnHelper.accessor((row) => row.labels?.map((l) => l.id), {
id: 'labels',
header: 'Labels',
enableColumnFilter: true,
cell: ({ row }) => {
const labels = row.original.labels

if (!labels) return null

return (
<div className="flex gap-1">
{labels.map((l) => (
<div
key={l.id}
className={cn(
'flex items-center gap-1 py-1 px-2 rounded-full text-[11px] tracking-[-0.01em] shadow-xs',
LABEL_STYLES_MAP[l.color as TW_COLOR],
'border-none text-white font-medium',
)}
>
{l.name}
</div>
))}
</div>
)
},
}),
]
95 changes: 95 additions & 0 deletions apps/web/app/demos/server/tst-query/_/data-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { Button } from '@/components/ui/button'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table'
import { type Table as TanStackTable, flexRender } from '@tanstack/react-table'

export function DataTable({ table }: { table: TanStackTable<any> }) {
return (
<>
<div className="rounded-md border bg-white dark:bg-inherit">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
className="h-12"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={table.getAllColumns().length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground tabular-nums">
{table.getFilteredSelectedRowModel().rows.length} of{' '}
{table.getFilteredRowModel().rows.length} row(s) selected.{' '}
<span className="text-primary font-medium">
Total row count: {table.getCoreRowModel().rows.length}
</span>
</div>
<div className="space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</>
)
}
Loading