Files
QuantEngineByItz/oms-wms-erp/src/components/primitives/AccessibleTable.vue
T
kjh2064 b34b0dd7d6
Validators (Pushes and Pull Requests) / UI & Storage Validation (pull_request) Failing after 12s
Validators (Pushes and Pull Requests) / Database & Schema Validation (pull_request) Successful in 13s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (pull_request) Failing after 28s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / CI Workflow Lint (pull_request) Failing after 10s
Validators (Pushes and Pull Requests) / Security & Secrets (pull_request) Successful in 12s
Validators (Pushes and Pull Requests) / Notify PR Results (pull_request) Successful in 2s
Frontend CI Pipeline / ci-frontend-8-steps (pull_request) Failing after 2m50s
Add OMS WMS ERP platform
2026-07-27 00:45:39 +09:00

209 lines
4.0 KiB
Vue

<template>
<div class="table-wrapper">
<table
class="table table-striped"
:aria-label="caption"
role="grid"
>
<caption v-if="caption" class="caption-text">
{{ caption }}
</caption>
<thead>
<tr role="row">
<th
v-for="(column, idx) in columns"
:key="idx"
:scope="column.sortable ? 'col' : 'col'"
:aria-sort="column.sortable ? (sortedBy === column.key ? (sortAsc ? 'ascending' : 'descending') : 'none') : undefined"
:role="column.sortable ? 'columnheader' : 'columnheader'"
class="table-header"
>
<button
v-if="column.sortable"
type="button"
class="sort-button"
@click="toggleSort(column.key)"
:aria-label="`Sort by ${column.label}`"
>
{{ column.label }}
<span v-if="sortedBy === column.key" class="sort-icon">
{{ sortAsc ? '↑' : '↓' }}
</span>
</button>
<span v-else>{{ column.label }}</span>
</th>
</tr>
</thead>
<tbody>
<tr v-if="rows.length === 0" class="empty-state">
<td :colspan="columns.length" class="text-center text-muted py-4">
<slot name="empty">No data available</slot>
</td>
</tr>
<tr
v-for="(row, rowIdx) in rows"
:key="rowIdx"
role="row"
class="data-row"
>
<td
v-for="(column, cellIdx) in columns"
:key="`${rowIdx}-${cellIdx}`"
:class="['table-cell', column.align ? `text-${column.align}` : '']"
role="gridcell"
>
<slot :name="`cell-${column.key}`" :row="row" :value="row[column.key]">
{{ row[column.key] }}
</slot>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
interface Column {
key: string
label: string
sortable?: boolean
align?: 'left' | 'center' | 'right'
}
interface Props {
columns: Column[]
rows: Record<string, any>[]
caption?: string
}
defineProps<Props>()
const sortedBy = ref<string | null>(null)
const sortAsc = ref(true)
const toggleSort = (columnKey: string) => {
if (sortedBy.value === columnKey) {
sortAsc.value = !sortAsc.value
} else {
sortedBy.value = columnKey
sortAsc.value = true
}
}
</script>
<style scoped>
.table-wrapper {
overflow-x: auto;
}
.table {
width: 100%;
border-collapse: collapse;
margin-bottom: 1rem;
font-size: 0.95rem;
}
caption {
padding: 0.5rem 0;
text-align: left;
font-size: 0.875rem;
color: #6c757d;
caption-side: top;
}
.caption-text {
font-weight: 600;
margin-bottom: 0.5rem;
}
thead {
background-color: #f8f9fa;
border-bottom: 2px solid #dee2e6;
}
.table-header {
padding: 0.75rem;
text-align: left;
font-weight: 600;
color: #212529;
vertical-align: middle;
border-bottom: 1px solid #dee2e6;
}
.sort-button {
background: none;
border: none;
cursor: pointer;
color: #0d6efd;
text-decoration: none;
font-weight: 600;
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0;
font-size: inherit;
}
.sort-button:hover {
text-decoration: underline;
}
.sort-icon {
font-size: 0.75em;
display: inline-block;
}
tbody tr {
border-bottom: 1px solid #dee2e6;
transition: background-color 0.2s;
}
tbody tr:hover {
background-color: #f8f9fa;
}
.table-cell {
padding: 0.75rem;
vertical-align: middle;
}
.empty-state td {
padding: 2rem 1rem;
border: none;
}
.text-center {
text-align: center;
}
.text-muted {
color: #6c757d;
}
.py-4 {
padding-top: 1.5rem;
padding-bottom: 1.5rem;
}
.text-left {
text-align: left;
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.table-striped tbody tr:nth-child(odd) {
background-color: rgba(0, 0, 0, 0.02);
}
</style>