Appearance
Table Component Demos
Basic Table
Simple table with sorting capabilities and hover effects.
vue
<template>
<OTable
:headers="headers"
:tableData="tableData"
@cellClicked="handleCellClick"
/>
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "Name", key: "name", sortable: true },
{ text: "Email", key: "email", sortable: true },
{ text: "Age", key: "age", classes: "center-align", sortable: true },
{ text: "Department", key: "department" },
];
const tableData = ref([
{
_id: "1",
name: "John Doe",
email: "john@example.com",
age: 30,
department: "Engineering",
},
{
_id: "2",
name: "Jane Smith",
email: "jane@example.com",
age: 25,
department: "Marketing",
},
{
_id: "3",
name: "Bob Johnson",
email: "bob@example.com",
age: 35,
department: "Sales",
},
]);
const handleCellClick = (rowData, cell) => {
console.log("Cell clicked:", rowData, cell);
};
</script>Name
Email
Age
Department
John Doe
john@example.com
30
Engineering
Jane Smith
jane@example.com
25
Marketing
Bob Johnson
bob@example.com
35
Sales
Alice Brown
alice@example.com
28
HR
Charlie Wilson
charlie@example.com
32
Engineering
Table with Sorting
Table with sortable columns. Click headers to sort (asc → desc → none).
vue
<template>
<OTable
:headers="headers"
:tableData="tableData"
@cellClicked="handleCellClick"
/>
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "Name", key: "name", sortable: true },
{ text: "Email", key: "email", sortable: true },
{ text: "Age", key: "age", classes: "center-align", sortable: true },
{ text: "Department", key: "department" },
];
const tableData = ref([
{
_id: "1",
name: "John Doe",
email: "john@example.com",
age: 30,
department: "Engineering",
},
{
_id: "2",
name: "Jane Smith",
email: "jane@example.com",
age: 25,
department: "Marketing",
},
{
_id: "3",
name: "Bob Johnson",
email: "bob@example.com",
age: 35,
department: "Sales",
},
]);
</script>Name
Email
Age
Department
John Doe
john@example.com
30
Engineering
Jane Smith
jane@example.com
25
Marketing
Bob Johnson
bob@example.com
35
Sales
Alice Brown
alice@example.com
28
HR
Charlie Wilson
charlie@example.com
32
Engineering
Table with Filtering
Table with filterable columns. Click the filter icon to open filter menu.
vue
<template>
<OTable
:headers="headers"
:tableData="tableData"
@cellClicked="handleCellClick"
/>
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "Name", key: "name", sortable: true },
{
text: "Department",
key: "department",
filterable: true,
filter: [
{ text: "Engineering", value: "Engineering" },
{ text: "Marketing", value: "Marketing" },
{ text: "Sales", value: "Sales" },
{ text: "HR", value: "HR" },
],
},
{ text: "Status", key: "status", classes: "center-align" },
];
const tableData = ref([
{ _id: "1", name: "John Doe", department: "Engineering", status: "Active" },
{ _id: "2", name: "Jane Smith", department: "Marketing", status: "Active" },
{ _id: "3", name: "Bob Johnson", department: "Sales", status: "Inactive" },
{ _id: "4", name: "Alice Brown", department: "HR", status: "Active" },
]);
</script>Name
Department
Status
John Doe
Engineering
Active
Jane Smith
Marketing
Active
Bob Johnson
Sales
Inactive
Alice Brown
HR
Active
Charlie Wilson
Engineering
Active
Table with Row Selection
Table with row selection checkboxes. Select individual rows or use "Select All" in header.
vue
<template>
<OTable
:headers="headers"
:tableData="tableData"
:allowSelect="true"
:selected="selectedRows"
:rowKey="'_id'"
@onSelect="handleSelect"
@cellClicked="handleCellClick"
/>
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "Name", key: "name", sortable: true },
{ text: "Email", key: "email" },
{ text: "Department", key: "department" },
{ text: "Status", key: "status", classes: "center-align" },
];
const tableData = ref([
{
_id: "1",
name: "John Doe",
email: "john@example.com",
department: "Engineering",
status: "Active",
},
{
_id: "2",
name: "Jane Smith",
email: "jane@example.com",
department: "Marketing",
status: "Active",
},
{
_id: "3",
name: "Bob Johnson",
email: "bob@example.com",
department: "Sales",
status: "Inactive",
},
]);
const selectedRows = ref([]);
const handleSelect = (selected) => {
selectedRows.value = selected;
console.log("Selected rows:", selected);
};
</script>Name
Email
Department
Status
John Doe
john@example.com
Engineering
Active
Jane Smith
jane@example.com
Marketing
Active
Bob Johnson
bob@example.com
Sales
Inactive
Table with Custom Cell Content
Table with custom cell rendering using slots.
vue
<template>
<OTable
:headers="headers"
:tableData="tableData"
@cellClicked="handleCellClick"
>
<template #cell-status="{ rowData }">
<span :class="['status-badge', rowData.status.toLowerCase()]">
{{ rowData.status }}
</span>
</template>
<template #cell-actions="{ rowData }">
<button @click="editUser(rowData)" class="edit-btn">Edit</button>
<button @click="deleteUser(rowData)" class="delete-btn">Delete</button>
</template>
</OTable>
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "Name", key: "name", sortable: true },
{ text: "Email", key: "email" },
{ text: "Status", key: "status" },
{ text: "Actions", key: "actions", classes: "center-align" },
];
const tableData = ref([
{ _id: "1", name: "John Doe", email: "john@example.com", status: "Active" },
{
_id: "2",
name: "Jane Smith",
email: "jane@example.com",
status: "Inactive",
},
{ _id: "3", name: "Bob Johnson", email: "bob@example.com", status: "Active" },
]);
const editUser = (user) => {
console.log("Edit user:", user);
};
const deleteUser = (user) => {
console.log("Delete user:", user);
};
</script>Name
Email
Status
Actions
John Doe
john@example.com
Active
-
Jane Smith
jane@example.com
Inactive
-
Bob Johnson
bob@example.com
Active
-
Table with Custom Column Widths
Table with custom column widths.
vue
<template>
<OTable :headers="headers" :tableData="data" :defaultCellWidth="'1fr'" />
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "Name", key: "name", width: "200px" },
{ text: "Email", key: "email", width: "250px" },
{ text: "Role", key: "role", width: "150px" },
{ text: "Status", key: "status" },
];
const data = ref([
{
_id: "1",
name: "John Doe",
email: "john@example.com",
role: "Developer",
status: "Active",
},
{
_id: "2",
name: "Jane Smith",
email: "jane@example.com",
role: "Designer",
status: "Active",
},
{
_id: "3",
name: "Bob Johnson",
email: "bob@example.com",
role: "Manager",
status: "Away",
},
]);
</script>Name
Email
Role
Status
John Doe
john@example.com
Developer
Active
Jane Smith
jane@example.com
Designer
Active
Bob Johnson
bob@example.com
Manager
Away
Table with Custom Headers
Table with custom header content using header slots.
vue
<template>
<OTable
:headers="headers"
:tableData="tableData"
@cellClicked="handleCellClick"
>
<template #header-name="{ header }">
<div class="custom-header">
<span>{{ header.text }}</span>
<span class="required">*</span>
</div>
</template>
<template #header-email="{ header }">
<div class="custom-header">
<span>{{ header.text }}</span>
<span class="info-icon">ℹ️</span>
</div>
</template>
</OTable>
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "Name", key: "name", sortable: true },
{ text: "Email", key: "email", sortable: true },
{ text: "Age", key: "age", classes: "center-align", sortable: true },
];
const tableData = ref([
{ _id: "1", name: "John Doe", email: "john@example.com", age: 30 },
{ _id: "2", name: "Jane Smith", email: "jane@example.com", age: 25 },
{ _id: "3", name: "Bob Johnson", email: "bob@example.com", age: 35 },
]);
</script>Name*
Email
Age
Department
John Doe
john@example.com
30
Engineering
Jane Smith
jane@example.com
25
Marketing
Bob Johnson
bob@example.com
35
Sales
Alice Brown
alice@example.com
28
HR
Charlie Wilson
charlie@example.com
32
Engineering
Table with Infinite Scroll
Table with infinite scroll functionality for loading more data.
vue
<template>
<OTable
:headers="headers"
:tableData="tableData"
:enableInfiniteScroll="true"
@scrolledToEndInTable="loadMoreData"
/>
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "Name", key: "name", sortable: true },
{ text: "Email", key: "email", sortable: true },
{ text: "Age", key: "age", classes: "center-align", sortable: true },
{ text: "Department", key: "department" },
];
const tableData = ref([
{
_id: "1",
name: "John Doe",
email: "john@example.com",
age: 30,
department: "Engineering",
},
{
_id: "2",
name: "Jane Smith",
email: "jane@example.com",
age: 25,
department: "Marketing",
},
{
_id: "3",
name: "Bob Johnson",
email: "bob@example.com",
age: 35,
department: "Sales",
},
]);
const loadMoreData = () => {
console.log("Loading more data...");
// In a real application, you would fetch more data from an API
// and append it to tableData
};
</script>Name
Email
Age
Department
John Doe
john@example.com
30
Engineering
Jane Smith
jane@example.com
25
Marketing
Bob Johnson
bob@example.com
35
Sales
Alice Brown
alice@example.com
28
HR
Charlie Wilson
charlie@example.com
32
Engineering
Table with Custom Render Function
Table using render function to transform cell data before display.
vue
<template>
<OTable :headers="headers" :tableData="data" />
</template>
<script setup>
import { ref } from "vue";
const formatCurrency = (rowData) => {
const value = rowData.salary;
if (!value && value !== 0) return "-";
return `$${parseFloat(value).toFixed(2)}`;
};
const headers = [
{ text: "Name", key: "name" },
{
text: "Salary",
key: "salary",
render: formatCurrency,
classes: "right-align",
},
];
const data = ref([
{ _id: "1", name: "John Doe", salary: 75000 },
{ _id: "2", name: "Jane Smith", salary: 82000 },
{ _id: "3", name: "Bob Johnson", salary: 65000 },
]);
</script>Name
Salary
John Doe
$75000.00
Jane Smith
$82000.00
Bob Johnson
$65000.00
Table with Infinite Scroll and Loading State
Table with infinite scroll that shows loading state while fetching more data.
vue
<template>
<div>
<OTable
:headers="headers"
:tableData="tableData"
:enableInfiniteScroll="true"
@scrolledToEndInTable="loadMoreData"
/>
<div v-if="isLoading" class="loading-indicator">Loading more data...</div>
</div>
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "ID", key: "id", sortable: true },
{ text: "Name", key: "name", sortable: true },
{ text: "Email", key: "email" },
{ text: "Status", key: "status", classes: "center-align" },
];
const tableData = ref([
{
_id: "1",
id: 1,
name: "John Doe",
email: "john@example.com",
status: "Active",
},
{
_id: "2",
id: 2,
name: "Jane Smith",
email: "jane@example.com",
status: "Active",
},
{
_id: "3",
id: 3,
name: "Bob Johnson",
email: "bob@example.com",
status: "Inactive",
},
]);
const isLoading = ref(false);
let itemCounter = 4;
const loadMoreData = () => {
if (isLoading.value) return;
isLoading.value = true;
// Simulate API call
setTimeout(() => {
const newItems = Array.from({ length: 5 }, (_, i) => ({
_id: String(itemCounter + i),
id: itemCounter + i,
name: `User ${itemCounter + i}`,
email: `user${itemCounter + i}@example.com`,
status: i % 2 === 0 ? "Active" : "Inactive",
}));
tableData.value.push(...newItems);
itemCounter += 5;
isLoading.value = false;
}, 1000);
};
</script>ID
Name
Email
Status
1
John Doe
john@example.com
Active
2
Jane Smith
jane@example.com
Active
3
Bob Johnson
bob@example.com
Inactive
4
Alice Brown
alice@example.com
Active
5
Charlie Wilson
charlie@example.com
Active
Table with Pagination Component
Table integrated with OPagination component for page-based navigation.
vue
<template>
<div>
<OTable
:headers="headers"
:tableData="paginatedData"
:enableInfiniteScroll="false"
@cellClicked="handleCellClick"
/>
<div class="pagination-wrapper">
<OPagination
v-model="currentPage"
:total-pages="totalPages"
@update:model-value="handlePageChange"
/>
</div>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
const headers = [
{ text: "ID", key: "id", sortable: true },
{ text: "Name", key: "name", sortable: true },
{ text: "Email", key: "email" },
{ text: "Department", key: "department" },
];
const tableData = ref([
{
_id: "1",
id: 1,
name: "John Doe",
email: "john@example.com",
department: "Engineering",
},
{
_id: "2",
id: 2,
name: "Jane Smith",
email: "jane@example.com",
department: "Marketing",
},
{
_id: "3",
id: 3,
name: "Bob Johnson",
email: "bob@example.com",
department: "Sales",
},
{
_id: "4",
id: 4,
name: "Alice Brown",
email: "alice@example.com",
department: "HR",
},
{
_id: "5",
id: 5,
name: "Charlie Wilson",
email: "charlie@example.com",
department: "Engineering",
},
{
_id: "6",
id: 6,
name: "Diana Prince",
email: "diana@example.com",
department: "Marketing",
},
{
_id: "7",
id: 7,
name: "Edward Norton",
email: "edward@example.com",
department: "Sales",
},
{
_id: "8",
id: 8,
name: "Fiona Gallagher",
email: "fiona@example.com",
department: "HR",
},
{
_id: "9",
id: 9,
name: "George Martin",
email: "george@example.com",
department: "Engineering",
},
{
_id: "10",
id: 10,
name: "Helen Troy",
email: "helen@example.com",
department: "Marketing",
},
]);
const currentPage = ref(1);
const itemsPerPage = 3;
const totalPages = computed(() =>
Math.ceil(tableData.value.length / itemsPerPage)
);
const paginatedData = computed(() => {
const start = (currentPage.value - 1) * itemsPerPage;
const end = start + itemsPerPage;
return tableData.value.slice(start, end);
});
const handlePageChange = (page) => {
currentPage.value = page;
console.log("Page changed to:", page);
};
</script>ID
Name
Email
Department
1
John Doe
john@example.com
Engineering
2
Jane Smith
jane@example.com
Marketing
3
Bob Johnson
bob@example.com
Sales
Async Table with Server-side Sorting, Filtering, and Infinite Scroll
Table configured for server-side data handling with async mode, including infinite scroll functionality. Scroll to the bottom to load more data.
vue
<template>
<div class="async-table-wrapper">
<div class="async-table-container">
<OTable
:headers="headers"
:tableData="tableData"
:async="true"
:sort="sortState"
:filters="filterState"
:enableInfiniteScroll="true"
@onSort="handleSort"
@onFilter="handleFilter"
@scrolledToEndInTable="loadMoreData"
/>
</div>
<div v-if="isLoading" class="loading-indicator">Loading more data...</div>
</div>
</template>
<script setup>
import { ref } from "vue";
const headers = [
{ text: "ID", key: "id", sortable: true },
{ text: "Name", key: "name", sortable: true },
{
text: "Department",
key: "department",
filterable: true,
filter: [
{ text: "Engineering", value: "Engineering" },
{ text: "Marketing", value: "Marketing" },
{ text: "Sales", value: "Sales" },
{ text: "HR", value: "HR" },
],
},
{ text: "Status", key: "status", classes: "center-align" },
];
// Initial data - enough to create scrollable area
const tableData = ref(
Array.from({ length: 15 }, (_, i) => ({
_id: String(i + 1),
id: i + 1,
name: `User ${i + 1}`,
department: ["Engineering", "Marketing", "Sales", "HR"][i % 4],
status: i % 2 === 0 ? "Active" : "Inactive",
}))
);
const sortState = ref({ sortBy: null, sortOrder: null });
const filterState = ref({});
const isLoading = ref(false);
let itemCounter = 16;
const handleSort = ({ header, sortBy, sortOrder }) => {
sortState.value = { sortBy, sortOrder };
console.log("Sort:", sortBy, sortOrder);
// In real app, fetch data from server with new sort parameters
// Reset and fetch first page
itemCounter = 16;
tableData.value = Array.from({ length: 15 }, (_, i) => ({
_id: String(i + 1),
id: i + 1,
name: `User ${i + 1}`,
department: ["Engineering", "Marketing", "Sales", "HR"][i % 4],
status: i % 2 === 0 ? "Active" : "Inactive",
}));
};
const handleFilter = (filters) => {
filterState.value = filters;
console.log("Filters:", filters);
// In real app, fetch data from server with new filter parameters
// Reset and fetch first page
itemCounter = 16;
tableData.value = Array.from({ length: 15 }, (_, i) => ({
_id: String(i + 1),
id: i + 1,
name: `User ${i + 1}`,
department: ["Engineering", "Marketing", "Sales", "HR"][i % 4],
status: i % 2 === 0 ? "Active" : "Inactive",
}));
};
const loadMoreData = () => {
if (isLoading.value) return;
isLoading.value = true;
console.log("Loading more data from server...");
// Simulate API call to fetch more data
setTimeout(() => {
const newItems = Array.from({ length: 10 }, (_, i) => ({
_id: String(itemCounter + i),
id: itemCounter + i,
name: `User ${itemCounter + i}`,
department: ["Engineering", "Marketing", "Sales", "HR"][
(itemCounter + i) % 4
],
status: (itemCounter + i) % 2 === 0 ? "Active" : "Inactive",
}));
tableData.value.push(...newItems);
itemCounter += 10;
isLoading.value = false;
}, 1000);
};
</script>ID
Name
Department
Status
1
User 1
Engineering
Active
2
User 2
Marketing
Inactive
3
User 3
Sales
Active
4
User 4
HR
Inactive
5
User 5
Engineering
Active
6
User 6
Marketing
Inactive
7
User 7
Sales
Active
8
User 8
HR
Inactive
9
User 9
Engineering
Active
10
User 10
Marketing
Inactive
11
User 11
Sales
Active
12
User 12
HR
Inactive
13
User 13
Engineering
Active
14
User 14
Marketing
Inactive
15
User 15
Sales
Active