<!-- SPEC: PJ2026-010405 云端控制台. -->
<!-- Implementation reference: draft-2026-07-13-p0-cloud-console. -->

<script setup lang="ts" generic="T extends object">
export interface DataGridColumn {
  key: string;
  label: string;
  align?: "left" | "right";
  width?: string;
  sortable?: boolean;
  hidden?: boolean;
}

const props = withDefaults(defineProps<{
  columns: DataGridColumn[];
  rows: T[];
  rowKey: (row: T) => string;
  selectedId?: string | null;
  density?: "compact" | "comfortable";
  ariaLabel?: string;
  emptyText?: string;
  interactive?: boolean;
  sort?: string;
}>(), {
  selectedId: null,
  density: "comfortable",
  ariaLabel: "数据表格",
  emptyText: "暂无记录。",
  interactive: true,
  sort: ""
});

const emit = defineEmits<{ select: [row: T]; sort: [key: string] }>();

function cellValue(row: T, key: string): unknown {
  return (row as Record<string, unknown>)[key];
}

function onRowKeydown(event: KeyboardEvent, row: T): void {
  if (!props.interactive) return;
  if (event.key === "Enter" || event.key === " ") {
    event.preventDefault();
    emit("select", row);
    return;
  }
  if (event.key !== "ArrowDown" && event.key !== "ArrowUp") return;
  event.preventDefault();
  const current = event.currentTarget;
  if (!(current instanceof HTMLTableRowElement)) return;
  const sibling = event.key === "ArrowDown" ? current.nextElementSibling : current.previousElementSibling;
  if (sibling instanceof HTMLTableRowElement) sibling.focus();
}

function ariaSort(column: DataGridColumn): "ascending" | "descending" | "none" | undefined {
  if (!column.sortable) return undefined;
  if (props.sort === column.key) return "ascending";
  if (props.sort === `-${column.key}`) return "descending";
  return "none";
}
</script>

<template>
  <div class="console-data-grid-wrap">
    <table v-if="rows.length" class="console-data-grid" :data-density="density" :aria-label="ariaLabel">
      <thead>
        <tr>
          <th
            v-for="column in columns.filter((item) => !item.hidden)"
            :key="column.key"
            :data-align="column.align || 'left'"
            :style="column.width ? { width: column.width } : undefined"
            :aria-sort="ariaSort(column)"
          >
            <button v-if="column.sortable" type="button" class="console-grid-sort" @click="emit('sort', column.key)">{{ column.label }}</button>
            <template v-else>{{ column.label }}</template>
          </th>
        </tr>
      </thead>
      <tbody>
        <tr
          v-for="(row, index) in rows"
          :key="rowKey(row)"
          :data-selected="rowKey(row) === selectedId ? 'true' : 'false'"
          :data-interactive="interactive ? 'true' : 'false'"
          :tabindex="interactive ? (rowKey(row) === selectedId || (!selectedId && index === 0) ? 0 : -1) : undefined"
          :aria-selected="interactive ? rowKey(row) === selectedId : undefined"
          @click="interactive ? emit('select', row) : undefined"
          @keydown="onRowKeydown($event, row)"
        >
          <td v-for="column in columns.filter((item) => !item.hidden)" :key="column.key" :data-align="column.align || 'left'" :data-label="column.label">
            <slot :name="`cell-${column.key}`" :row="row" :value="cellValue(row, column.key)">{{ cellValue(row, column.key) }}</slot>
          </td>
        </tr>
      </tbody>
    </table>
    <slot v-else name="empty"><p class="muted-box">{{ emptyText }}</p></slot>
  </div>
</template>

<style scoped>
.console-grid-sort {
  border: 0;
  background: transparent;
  padding: 0;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}
</style>
