Skip to content

UITable

UITable

A versatile table component that provides advanced features for displaying and managing tabular data, including sorting, filtering, pagination, row selection, and custom cell rendering.

<template>
<UITable
:data="tableData"
:columns="columns"
:sortable="true"
:selectable="true"
:loading="isLoading"
:pagination="pagination"
@sort="handleSort"
@select="handleSelect"
>
<template #header-cell="{ column }">
<div class="flex items-center space-x-2">
<span>{{ column.label }}</span>
<InfoIcon
v-if="column.tooltip"
class="w-4 h-4 text-gray-400"
v-tooltip="column.tooltip"
/>
</div>
</template>
<template #cell-actions="{ row }">
<div class="flex items-center space-x-2">
<UIButton
size="sm"
@click="editRow(row)"
>
Edit
</UIButton>
<UIButton
size="sm"
variant="error"
@click="deleteRow(row)"
>
Delete
</UIButton>
</div>
</template>
</UITable>
</template>
<script setup lang="ts">
interface TableColumn {
key: string
label: string
sortable?: boolean
tooltip?: string
width?: string | number
}
const columns: TableColumn[] = [
{
key: 'name',
label: 'Name',
sortable: true
},
{
key: 'email',
label: 'Email',
sortable: true
},
{
key: 'status',
label: 'Status',
tooltip: 'Current user status'
},
{
key: 'actions',
label: 'Actions',
width: '120px'
}
]
const tableData = ref([])
const isLoading = ref(false)
const pagination = reactive({
page: 1,
perPage: 10,
total: 0
})
const handleSort = (column: TableColumn) => {
// Handle sorting
}
const handleSelect = (selectedRows: any[]) => {
console.log('Selected:', selectedRows)
}
</script>

Props

  • data (array): Table data
  • columns (array): Column definitions
  • sortable (boolean): Enable sorting
  • selectable (boolean): Enable row selection
  • loading (boolean): Show loading state
  • pagination (object): Pagination config
  • rowKey (string): Unique row identifier
  • bordered (boolean): Show borders
  • striped (boolean): Striped rows
  • hover (boolean): Row hover effect
  • dense (boolean): Compact layout
  • stickyHeader (boolean): Fixed header
  • emptyText (string): No data message
  • loadingText (string): Loading message

Events

  • sort: Column sort
  • select: Row selection
  • page-change: Page changed
  • row-click: Row clicked
  • cell-click: Cell clicked
  • header-click: Header clicked

Slots

  • header-cell: Custom header cell
  • cell: Custom cell content
  • empty: No data state
  • loading: Loading state
  • pagination: Custom pagination
  • toolbar: Table toolbar

Usage Examples

  1. Basic Table:
<template>
<UITable
:data="users"
:columns="columns"
/>
</template>
<script setup>
const columns = [
{ key: 'name', label: 'Name' },
{ key: 'email', label: 'Email' },
{ key: 'role', label: 'Role' }
]
const users = [
{
name: 'John Doe',
email: 'john@example.com',
role: 'Admin'
}
// ... more users
]
</script>
  1. Advanced Table with Custom Cells:
<template>
<UITable
:data="products"
:columns="columns"
:sortable="true"
:pagination="pagination"
class="w-full"
>
<template #cell-image="{ row }">
<img
:src="row.image"
class="w-12 h-12 rounded object-cover"
/>
</template>
<template #cell-price="{ row }">
<div class="font-medium tabular-nums">
{{ formatCurrency(row.price) }}
</div>
</template>
<template #cell-status="{ row }">
<UIBadge
:variant="getStatusVariant(row.status)"
>
{{ row.status }}
</UIBadge>
</template>
<template #cell-actions="{ row }">
<div class="flex space-x-2">
<UIButton
size="sm"
@click="editProduct(row)"
>
Edit
</UIButton>
<UIButton
size="sm"
variant="error"
@click="deleteProduct(row)"
>
Delete
</UIButton>
</div>
</template>
</UITable>
</template>
<script setup>
const columns = [
{
key: 'image',
label: 'Image',
width: '100px'
},
{
key: 'name',
label: 'Product Name',
sortable: true
},
{
key: 'price',
label: 'Price',
sortable: true
},
{
key: 'status',
label: 'Status',
sortable: true
},
{
key: 'actions',
label: 'Actions',
width: '150px'
}
]
const getStatusVariant = (status) => {
switch (status) {
case 'In Stock': return 'success'
case 'Low Stock': return 'warning'
case 'Out of Stock': return 'error'
default: return 'gray'
}
}
</script>
  1. Selectable Table with Toolbar:
<template>
<div class="space-y-4">
<UITable
:data="employees"
:columns="columns"
:selectable="true"
:selected="selected"
@select="handleSelect"
>
<template #toolbar>
<div class="flex items-center justify-between p-4">
<div class="flex items-center space-x-4">
<UISearch
v-model="search"
placeholder="Search employees..."
@search="handleSearch"
/>
<UISelect
v-model="filters.department"
:options="departments"
placeholder="Department"
class="w-40"
/>
</div>
<div class="flex items-center space-x-2">
<UIButton
v-if="selected.length"
variant="error"
@click="bulkDelete"
>
Delete Selected
</UIButton>
<UIButton
type="primary"
@click="addEmployee"
>
Add Employee
</UIButton>
</div>
</div>
</template>
<template #cell-avatar="{ row }">
<div class="flex items-center space-x-3">
<img
:src="row.avatar"
class="w-8 h-8 rounded-full"
/>
<div>
<div class="font-medium">
{{ row.name }}
</div>
<div class="text-sm text-gray-500">
{{ row.email }}
</div>
</div>
</div>
</template>
</UITable>
<UIPagination
v-model="pagination.page"
:total="pagination.total"
:per-page="pagination.perPage"
@change="handlePageChange"
/>
</div>
</template>
<script setup>
const selected = ref([])
const search = ref('')
const filters = reactive({
department: ''
})
const pagination = reactive({
page: 1,
perPage: 10,
total: 0
})
const handleSelect = (rows) => {
selected.value = rows
}
const handleSearch = async (query) => {
// Implement search
}
const handlePageChange = async (page) => {
pagination.page = page
await loadData()
}
</script>

Best Practices

  1. Performance:

    • Virtual scrolling
    • Lazy loading
    • Optimized rendering
    • Efficient sorting
  2. User Experience:

    • Clear headers
    • Responsive layout
    • Loading states
    • Error handling
  3. Accessibility:

    • ARIA labels
    • Keyboard navigation
    • Screen reader support
    • Focus management
  4. Data Management:

    • Efficient updates
    • State persistence
    • Error recovery
    • Data validation

Common Use Cases

  1. Data Grid:
<template>
<DataGrid
:data="gridData"
:loading="loading"
:error="error"
>
<template #toolbar>
<GridToolbar
v-model:filters="filters"
@refresh="refreshData"
@export="exportData"
/>
</template>
<UITable
:data="filteredData"
:columns="columns"
:sortable="true"
:selectable="true"
:loading="loading"
>
<!-- Custom cell templates -->
</UITable>
<template #footer>
<GridFooter
:pagination="pagination"
:selection="selected"
@page-change="handlePageChange"
/>
</template>
</DataGrid>
</template>
  1. CRUD Interface:
<template>
<div class="crud-interface">
<CrudToolbar
:selected="selected"
@create="openCreateModal"
@delete="confirmDelete"
@refresh="refreshData"
/>
<UITable
:data="items"
:columns="columns"
:selectable="true"
:loading="loading"
@select="handleSelect"
>
<template #cell-actions="{ row }">
<CrudActions
:item="row"
@edit="openEditModal"
@delete="confirmDelete"
@view="viewDetails"
/>
</template>
</UITable>
<CrudModal
v-model="showModal"
:mode="modalMode"
:item="selectedItem"
@save="handleSave"
/>
</div>
</template>
  1. Analytics Dashboard:
<template>
<div class="analytics-dashboard">
<DashboardFilters
v-model="filters"
@apply="applyFilters"
/>
<div class="grid grid-cols-3 gap-6 mb-6">
<MetricCard
v-for="metric in metrics"
:key="metric.id"
:metric="metric"
/>
</div>
<UITable
:data="analyticsData"
:columns="columns"
:sortable="true"
:loading="loading"
>
<template #cell-trend="{ row }">
<TrendIndicator
:value="row.trend"
:change="row.change"
/>
</template>
<template #cell-chart="{ row }">
<SparklineChart
:data="row.chartData"
:type="row.chartType"
/>
</template>
</UITable>
</div>
</template>