Show a semi-transparent blue rectangle overlay while dragging to select rows, matching the project's primary color theme with dark mode support. The box spans the full table width from drag start to current mouse position. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Common Components
This directory contains reusable Vue 3 components built with Composition API, TypeScript, and TailwindCSS.
Components
DataTable.vue
A generic data table component with sorting, loading states, and custom cell rendering.
Props:
columns: Column[]- Array of column definitions with key, label, sortable, and formatterdata: any[]- Array of data objects to displayloading?: boolean- Show loading skeletondefaultSortKey?: string- Default sort key (only used if no persisted sort state)defaultSortOrder?: 'asc' | 'desc'- Default sort order (default:asc)sortStorageKey?: string- Persist sort state (key + order) to localStoragerowKey?: string | (row: any) => string | number- Row key field or resolver (defaults torow.id, falls back to index)
Slots:
empty- Custom empty state contentcell-{key}- Custom cell renderer for specific column (receivesrowandvalue)
Usage:
<DataTable
:columns="[
{ key: 'name', label: 'Name', sortable: true },
{ key: 'email', label: 'Email' },
{ key: 'status', label: 'Status', formatter: (val) => val.toUpperCase() }
]"
:data="users"
:loading="isLoading"
>
<template #cell-actions="{ row }">
<button @click="editUser(row)">Edit</button>
</template>
</DataTable>
Pagination.vue
Pagination component with page numbers, navigation, and page size selector.
Props:
total: number- Total number of itemspage: number- Current page (1-indexed)pageSize: number- Items per pagepageSizeOptions?: number[]- Available page size options (default: [10, 20, 50, 100])
Events:
update:page- Emitted when page changesupdate:pageSize- Emitted when page size changes
Usage:
<Pagination
:total="totalUsers"
:page="currentPage"
:pageSize="pageSize"
@update:page="currentPage = $event"
@update:pageSize="pageSize = $event"
/>
Modal.vue
Modal dialog with customizable size and close behavior.
Props:
show: boolean- Control modal visibilitytitle: string- Modal titlesize?: 'sm' | 'md' | 'lg' | 'xl' | 'full'- Modal size (default: 'md')closeOnEscape?: boolean- Close on Escape key (default: true)closeOnClickOutside?: boolean- Close on backdrop click (default: true)
Events:
close- Emitted when modal should close
Slots:
default- Modal body contentfooter- Modal footer content
Usage:
<Modal :show="showModal" title="Edit User" size="lg" @close="showModal = false">
<form @submit.prevent="saveUser">
<!-- Form content -->
</form>
<template #footer>
<button @click="showModal = false">Cancel</button>
<button @click="saveUser">Save</button>
</template>
</Modal>
ConfirmDialog.vue
Confirmation dialog built on top of Modal component.
Props:
show: boolean- Control dialog visibilitytitle: string- Dialog titlemessage: string- Confirmation messageconfirmText?: string- Confirm button text (default: 'Confirm')cancelText?: string- Cancel button text (default: 'Cancel')danger?: boolean- Use danger/red styling (default: false)
Events:
confirm- Emitted when user confirmscancel- Emitted when user cancels
Usage:
<ConfirmDialog
:show="showDeleteConfirm"
title="Delete User"
message="Are you sure you want to delete this user? This action cannot be undone."
confirm-text="Delete"
cancel-text="Cancel"
danger
@confirm="deleteUser"
@cancel="showDeleteConfirm = false"
/>
StatCard.vue
Statistics card component for displaying metrics with optional change indicators.
Props:
title: string- Card titlevalue: number | string- Main value to displayicon?: Component- Icon componentchange?: number- Percentage change valuechangeType?: 'up' | 'down' | 'neutral'- Change direction (default: 'neutral')formatValue?: (value) => string- Custom value formatter
Usage:
<StatCard title="Total Users" :value="1234" :icon="UserIcon" :change="12.5" change-type="up" />
Toast.vue
Toast notification component that automatically displays toasts from the app store.
Usage:
<!-- Add once in App.vue or layout -->
<Toast />
// Trigger toasts from anywhere using the app store
import { useAppStore } from '@/stores/app'
const appStore = useAppStore()
appStore.addToast({
type: 'success',
title: 'Success!',
message: 'User created successfully',
duration: 3000
})
appStore.addToast({
type: 'error',
message: 'Failed to delete user'
})
LoadingSpinner.vue
Simple animated loading spinner.
Props:
size?: 'sm' | 'md' | 'lg' | 'xl'- Spinner size (default: 'md')color?: 'primary' | 'secondary' | 'white' | 'gray'- Spinner color (default: 'primary')
Usage:
<LoadingSpinner size="lg" color="primary" />
EmptyState.vue
Empty state placeholder with icon, message, and optional action button.
Props:
icon?: Component- Icon componenttitle: string- Empty state titledescription: string- Empty state descriptionactionText?: string- Action button textactionTo?: string | object- Router link destinationactionIcon?: boolean- Show plus icon in button (default: true)
Slots:
icon- Custom icon contentaction- Custom action button/link
Usage:
<EmptyState
title="No users found"
description="Get started by creating your first user account."
action-text="Add User"
:action-to="{ name: 'users-create' }"
/>
Import
You can import components individually:
import { DataTable, Pagination, Modal } from '@/components/common'
Or import specific components:
import DataTable from '@/components/common/DataTable.vue'
Features
All components include:
- TypeScript support with proper type definitions
- Accessibility with ARIA attributes and keyboard navigation
- Responsive design with mobile-friendly layouts
- TailwindCSS styling for consistent design
- Vue 3 Composition API with
<script setup> - Slot support for customization