Files
sub2api/frontend/src/components/common
erio 2475d4a205 feat: add marquee selection box overlay during drag-to-select
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>
2026-03-08 01:45:22 +08:00
..
2025-12-18 13:50:39 +08:00
2025-12-18 14:26:55 +08:00

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 formatter
  • data: any[] - Array of data objects to display
  • loading?: boolean - Show loading skeleton
  • defaultSortKey?: 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 localStorage
  • rowKey?: string | (row: any) => string | number - Row key field or resolver (defaults to row.id, falls back to index)

Slots:

  • empty - Custom empty state content
  • cell-{key} - Custom cell renderer for specific column (receives row and value)

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 items
  • page: number - Current page (1-indexed)
  • pageSize: number - Items per page
  • pageSizeOptions?: number[] - Available page size options (default: [10, 20, 50, 100])

Events:

  • update:page - Emitted when page changes
  • update: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 visibility
  • title: string - Modal title
  • size?: '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 content
  • footer - 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 visibility
  • title: string - Dialog title
  • message: string - Confirmation message
  • confirmText?: 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 confirms
  • cancel - 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 title
  • value: number | string - Main value to display
  • icon?: Component - Icon component
  • change?: number - Percentage change value
  • changeType?: '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 component
  • title: string - Empty state title
  • description: string - Empty state description
  • actionText?: string - Action button text
  • actionTo?: string | object - Router link destination
  • actionIcon?: boolean - Show plus icon in button (default: true)

Slots:

  • icon - Custom icon content
  • action - 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