data-grid

Data grid with card container

Data grid with card container

Загрузка превью…

src/data-grid/c-data-grid-19.vue

<script setup lang="ts">
import { h, ref } from "vue"
import { Badge } from "@/components/reui/badge"
import { DataGrid, DataGridColumnHeader, DataGridContainer, DataGridPagination, DataGridScrollArea, DataGridTable } from "@/components/reui/data-grid"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { Card, CardAction, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
import { getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useVueTable } from "@tanstack/vue-table"
import type { ColumnDef, PaginationState, SortingState } from "@tanstack/vue-table"

interface IData {
  id: string
  name: string
  availability: "online" | "away" | "busy" | "offline"
  avatar: string
  status: "active" | "inactive"
  flag: string
  email: string
  company: string
  role: string
  joined: string
  location: string
  balance: number
}

const demoData: IData[] = [
  {
    id: "1",
    name: "Alex Johnson",
    availability: "online",
    avatar: "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "us",
    email: "alex@example.com",
    company: "Apple",
    role: "CEO",
    joined: "Jan, 2024",
    location: "United States",
    balance: 5143.03,
  },
  {
    id: "2",
    name: "Sarah Chen",
    availability: "away",
    avatar: "https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "gb",
    email: "sarah@example.com",
    company: "OpenAI",
    role: "CTO",
    joined: "Mar, 2023",
    location: "United Kingdom",
    balance: 4321.87,
  },
  {
    id: "3",
    name: "Michael Rodriguez",
    availability: "busy",
    avatar: "https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "ca",
    email: "michael@example.com",
    company: "Meta",
    role: "Designer",
    joined: "Jun, 2022",
    location: "Canada",
    balance: 7654.98,
  },
  {
    id: "4",
    name: "Emma Wilson",
    availability: "offline",
    avatar: "https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "au",
    email: "emma@example.com",
    company: "Tesla",
    role: "Developer",
    joined: "Sep, 2024",
    location: "Australia",
    balance: 3456.45,
  },
  {
    id: "5",
    name: "David Kim",
    availability: "online",
    avatar: "https://images.unsplash.com/photo-1607990281513-2c110a25bd8c?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "de",
    email: "david@example.com",
    company: "SAP",
    role: "Lawyer",
    joined: "Nov, 2023",
    location: "Germany",
    balance: 9876.54,
  },
  {
    id: "6",
    name: "Aron Thompson",
    availability: "away",
    avatar: "https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80",
    status: "active",
    flag: "my",
    email: "lisa@example.com",
    company: "Keenthemes",
    role: "Director",
    joined: "Feb, 2022",
    location: "Malaysia",
    balance: 6214.22,
  },
  {
    id: "7",
    name: "James Brown",
    availability: "busy",
    avatar: "https://images.unsplash.com/photo-1543299750-19d1d6297053?w=96&h=96&dpr=2&q=80",
    status: "inactive",
    flag: "es",
    email: "james@example.com",
    company: "BBVA",
    role: "Product Manager",
    joined: "Aug, 2024",
    location: "Spain",
    balance: 5321.77,
  },
]

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: (ctx) => h(DataGridColumnHeader, { column: ctx.column as never, title: "User", visibility: true }),
    cell: (info) =>
      h("div", { class: "flex items-center gap-3" }, [
        h(Avatar, { class: "size-8" }, () => [
          h(AvatarImage, { src: info.row.original.avatar, alt: info.row.original.name }),
          h(AvatarFallback, () => info.row.original.name.split(" ").map((n: string) => n[0]).join("")),
        ]),
        h("div", { class: "space-y-px" }, [
          h("div", { class: "text-foreground font-medium" }, info.row.original.name),
          h("div", { class: "text-muted-foreground" }, info.row.original.email),
        ]),
      ]),
    minSize: 200,
    meta: {
      autoSize: true,
    },
    enableSorting: true,
    enableHiding: false,
    enableResizing: true,
  },
  {
    accessorKey: "location",
    id: "location",
    header: (ctx) => h(DataGridColumnHeader, { column: ctx.column as never, title: "Location", visibility: true }),
    cell: (info) =>
      h("div", { class: "flex items-center gap-1.5" }, [
        h("img", {
          src: `https://flagcdn.com/${info.row.original.flag.toLowerCase()}.svg`,
          alt: info.row.original.flag,
          class: "size-4 rounded-full object-cover",
        }),
        h("div", { class: "text-foreground font-medium" }, info.row.original.location),
      ]),
    size: 200,
    enableSorting: true,
    enableHiding: true,
    enableResizing: true,
  },
  {
    accessorKey: "status",
    id: "status",
    header: (ctx) => h(DataGridColumnHeader, { column: ctx.column as never, title: "Status", visibility: true }),
    cell: (info) =>
      info.row.original.status === "active"
        ? h(Badge, { variant: "primary-outline" }, () => "Approved")
        : h(Badge, { variant: "destructive-outline" }, () => "Pending"),
    size: 200,
    enableSorting: true,
    enableHiding: true,
    enableResizing: false,
  },
]

const columnOrder = ref<string[]>(columns.map((column) => column.id as string))

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
    },
    get columnOrder() {
      return columnOrder.value
    },
  },
  onPaginationChange: (updater) => {
    pagination.value = typeof updater === "function" ? updater(pagination.value) : updater
  },
  onSortingChange: (updater) => {
    sorting.value = typeof updater === "function" ? updater(sorting.value) : updater
  },
  onColumnOrderChange: (updater) => {
    columnOrder.value = typeof updater === "function" ? updater(columnOrder.value) : updater
  },
  getCoreRowModel: getCoreRowModel(),
  getFilteredRowModel: getFilteredRowModel(),
  getPaginationRowModel: getPaginationRowModel(),
  getSortedRowModel: getSortedRowModel(),
})
</script>

<template>
  <DataGrid
    :table="table"
    :record-count="demoData?.length || 0"
    :table-class-names="{ edgeCell: 'px-5' }"
    :table-layout="{ columnsPinnable: true, columnsResizable: true, columnsMovable: true, columnsVisibility: true }"
  >
    <Card class="w-full gap-3 py-3.5">
      <CardHeader class="flex items-center justify-between px-3.5">
        <CardTitle>Users</CardTitle>
        <CardAction>
          <Button size="sm">
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /><circle cx="9" cy="7" r="4" /><line x1="19" x2="19" y1="8" y2="14" /><line x1="22" x2="16" y1="11" y2="11" /></svg>
            Add User
          </Button>
        </CardAction>
      </CardHeader>
      <DataGridContainer class="border-y">
        <DataGridScrollArea>
          <DataGridTable />
        </DataGridScrollArea>
      </DataGridContainer>
      <CardFooter class="border-none bg-transparent! px-3.5 py-0">
        <DataGridPagination />
      </CardFooter>
    </Card>
  </DataGrid>
</template>

Установка

npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-data-grid-19.json

Зависимости реестра

npm-зависимости

  • @tanstack/vue-table

Источник: порт из ReUI (Keenthemes, MIT)