data-grid
Data grid without table borders
Data grid without table borders
Загрузка превью…
src/data-grid/c-data-grid-4.vue
<script setup lang="ts">
/**
* Порт `c-data-grid-4.tsx` ("Data grid without table borders"):
* `tableLayout = { headerBackground: false, rowBorder: false, rowRounded: true }`.
*
* Аватарки (`images.unsplash.com`) и флаги (`flagcdn.com`) — реальные
* сетевые запросы, недетерминированы в headless-стенде; заменены
* локальным 1x1 data-URI (см. docs/PORTING.md §5 "picsum.photos").
*/
import { h, ref } from "vue"
import { Badge } from "@/components/reui/badge"
import { DataGrid, DataGridContainer, DataGridScrollArea, DataGridTable, DataGridPagination } from "@/components/reui/data-grid"
import { Avatar, AvatarBadge, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { cn } from "@/lib/utils"
import {
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useVueTable,
} from "@tanstack/vue-table"
import type { ColumnDef, PaginationState, SortingState } from "@tanstack/vue-table"
const PIXEL =
"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="
interface IData {
id: string
name: string
availability: "online" | "away" | "busy" | "offline"
email: string
flag: string
location: string
status: "active" | "inactive"
}
const demoData: IData[] = [
{ id: "1", name: "Alex Johnson", availability: "online", email: "alex@apple.com", flag: "us", location: "United States", status: "active" },
{ id: "2", name: "Sarah Chen", availability: "away", email: "sarah@openai.com", flag: "gb", location: "United Kingdom", status: "inactive" },
{ id: "3", name: "Michael Rodriguez", availability: "busy", email: "michael@meta.com", flag: "ca", location: "Canada", status: "active" },
{ id: "4", name: "Emma Wilson", availability: "offline", email: "emma@tesla.com", flag: "au", location: "Australia", status: "inactive" },
{ id: "5", name: "David Kim", availability: "online", email: "david@sap.com", flag: "de", location: "Germany", status: "active" },
{ id: "6", name: "Aron Thompson", availability: "away", email: "aron@keenthemes.com", flag: "my", location: "Malaysia", status: "active" },
{ id: "7", name: "James Brown", availability: "busy", email: "james@bbva.es", flag: "es", location: "Spain", status: "inactive" },
]
function initials(name: string): string {
return name
.split(" ")
.map((n) => n[0])
.join("")
}
const statusColors: Record<IData["availability"], string> = {
online: "bg-green-500",
away: "bg-yellow-500",
busy: "bg-orange-500",
offline: "bg-gray-400",
}
const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 5 })
const sorting = ref<SortingState>([{ id: "name", desc: true }])
const columns: ColumnDef<IData>[] = [
{
accessorKey: "name",
id: "name",
header: "Name",
cell: (ctx) => {
const availability = ctx.row.original.availability
return h("div", { class: "flex items-center gap-3" }, [
h(Avatar, { class: "size-8" }, () => [
h(AvatarImage, { src: PIXEL, alt: ctx.row.original.name }),
h(AvatarFallback, () => initials(ctx.row.original.name)),
h(AvatarBadge, {
class: cn("size-1.5! p-0", statusColors[availability] || statusColors.offline),
}),
]),
h("div", { class: "space-y-px" }, [
h("div", { class: "text-foreground font-medium" }, ctx.row.original.name),
h("div", { class: "text-muted-foreground" }, ctx.row.original.email),
]),
])
},
size: 225,
enableSorting: true,
enableHiding: false,
},
{
accessorKey: "location",
header: "Location",
cell: (ctx) =>
h("div", { class: "flex items-center gap-1.5" }, [
h("img", {
src: PIXEL,
alt: ctx.row.original.flag,
class: "size-4 rounded-full object-cover",
}),
h("div", { class: "text-foreground font-medium" }, ctx.row.original.location),
]),
size: 160,
meta: {
headerClassName: "",
cellClassName: "text-start",
},
},
{
accessorKey: "status",
id: "status",
header: "Status",
cell: (ctx) => {
const status = ctx.row.original.status
return status === "active"
? h(Badge, { variant: "success-outline" }, () => "Approved")
: h(Badge, { variant: "warning-outline" }, () => "Pending")
},
size: 100,
},
]
const table = useVueTable({
get data() {
return demoData
},
columns,
get pageCount() {
return Math.ceil(demoData.length / pagination.value.pageSize)
},
getRowId: (row: IData) => row.id,
state: {
get pagination() {
return pagination.value
},
get sorting() {
return sorting.value
},
},
onPaginationChange: (updater) => {
pagination.value = typeof updater === "function" ? updater(pagination.value) : updater
},
onSortingChange: (updater) => {
sorting.value = typeof updater === "function" ? updater(sorting.value) : updater
},
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
})
</script>
<template>
<DataGrid
:table="table"
:record-count="demoData.length"
:table-layout="{ headerBackground: false, rowBorder: false, rowRounded: true }"
>
<div class="w-full space-y-2.5">
<DataGridContainer>
<DataGridScrollArea>
<DataGridTable />
</DataGridScrollArea>
</DataGridContainer>
<DataGridPagination />
</div>
</DataGrid>
</template>