feat: update versions table (#6502)

* feat: add new versions table, move environments column to be feature flag toggled, and add header/cell class props to Table.vue

* feat: implement much better table column widths

* feat: update version filter control to use multiselect

* feat: implement clicking table row to go to version

* feat: mobile pass on versions table

* feat: use multiselect for platform, and fix game version show all versions when dont exist

* pnpm prepr

* feat: replace dashboard projects table with new table

* feat: update projects so its out of card

* fix: version actions buttons column width

* feat: implement overflow on loaders cell

* update platform and version width

* feat: update project icon

* feat: change edit links to use floating action bar

* pnpm prepr
This commit is contained in:
Truman Gao
2026-06-29 15:01:07 +00:00
committed by GitHub
parent fc3e384f54
commit b0c6a06909
39 changed files with 1251 additions and 1143 deletions
+48 -7
View File
@@ -8,7 +8,8 @@
</div>
<div class="overflow-x-auto overflow-y-hidden">
<table
class="w-full table-fixed border-separate border-spacing-0 border-surface-4"
class="w-full border-separate border-spacing-0 border-surface-4"
:class="tableLayout === 'auto' ? 'table-auto' : 'table-fixed'"
:style="tableMinWidth ? { minWidth: tableMinWidth } : undefined"
>
<colgroup>
@@ -36,6 +37,7 @@
:class="[
`text-${column.align ?? 'left'}`,
column.enableSorting ? 'cursor-pointer select-none' : '',
column.headerClass,
]"
:style="column.width ? { width: column.width } : undefined"
@click="column.enableSorting ? handleSort(column.key) : undefined"
@@ -80,7 +82,8 @@
<tr
v-for="(row, rowIndex) in renderedRows"
:key="getRowRenderKey(row, getAbsoluteRowIndex(rowIndex))"
:class="getRowClass(getAbsoluteRowIndex(rowIndex))"
:class="getRowClass(row, getAbsoluteRowIndex(rowIndex))"
@click="handleRowClick(row, getAbsoluteRowIndex(rowIndex), $event)"
>
<td
v-if="showSelection"
@@ -96,7 +99,7 @@
v-for="column in columns"
:key="column.key"
class="text-secondary h-14 overflow-hidden first:pl-4 last:pr-4 border-solid border-0 border-t border-surface-4"
:class="`text-${column.align ?? 'left'}`"
:class="[`text-${column.align ?? 'left'}`, column.cellClass]"
>
<slot
:name="`cell-${column.key}`"
@@ -132,7 +135,8 @@
<tr
v-for="(row, rowIndex) in renderedRows"
:key="getRowRenderKey(row, getAbsoluteRowIndex(rowIndex))"
:class="getRowClass(getAbsoluteRowIndex(rowIndex))"
:class="getRowClass(row, getAbsoluteRowIndex(rowIndex))"
@click="handleRowClick(row, getAbsoluteRowIndex(rowIndex), $event)"
>
<td
v-if="showSelection"
@@ -148,7 +152,7 @@
v-for="column in columns"
:key="column.key"
class="text-secondary h-14 overflow-hidden first:pl-4 last:pr-4 border-solid border-0 border-t border-surface-4"
:class="`text-${column.align ?? 'left'}`"
:class="[`text-${column.align ?? 'left'}`, column.cellClass]"
>
<slot
:name="`cell-${column.key}`"
@@ -188,6 +192,7 @@ import Checkbox from './Checkbox.vue'
export type TableColumnAlign = 'left' | 'center' | 'right'
export type SortDirection = 'asc' | 'desc'
export type TableLayout = 'fixed' | 'auto'
/**
* Defines a table column configuration.
@@ -204,6 +209,8 @@ export interface TableColumn<K extends string = string> {
* Accepts any valid CSS width (e.g., '200px', '20%', '10rem', 'auto', 'fit-content').
*/
width?: string
headerClass?: string
cellClass?: string
}
const props = withDefaults(
@@ -223,10 +230,14 @@ const props = withDefaults(
* Sets a minimum width for the table content, allowing horizontal overflow below that width.
*/
tableMinWidth?: string
tableLayout?: TableLayout
rowClass?: string | ((row: T, index: number) => string)
rowClickable?: boolean | ((row: T, index: number) => boolean)
}>(),
{
showSelection: false,
rowKey: 'id' as keyof T,
tableLayout: 'fixed',
virtualized: false,
virtualRowHeight: 56,
virtualBufferSize: 5,
@@ -267,6 +278,7 @@ const bottomSpacerHeight = computed(() => {
const emit = defineEmits<{
sort: [column: string, direction: SortDirection]
rowClick: [row: T, index: number, event: MouseEvent]
}>()
const selectableRows = computed(() => props.selectionData ?? props.data)
@@ -319,8 +331,37 @@ function getRowRenderKey(row: T, rowIndex: number): PropertyKey {
return rowIndex
}
function getRowClass(rowIndex: number): string {
return rowIndex % 2 === 0 ? 'bg-surface-2' : 'bg-surface-1.5'
function getRowClass(row: T, rowIndex: number): string[] {
const baseClass = rowIndex % 2 === 0 ? 'bg-surface-2' : 'bg-surface-1.5'
const customClass =
typeof props.rowClass === 'function' ? props.rowClass(row, rowIndex) : props.rowClass
return customClass ? [baseClass, customClass] : [baseClass]
}
function isRowClickable(row: T, rowIndex: number): boolean {
return typeof props.rowClickable === 'function'
? props.rowClickable(row, rowIndex)
: props.rowClickable === true
}
function isNoRowClickTarget(event: MouseEvent): boolean {
const target = event.target
const currentTarget = event.currentTarget
if (!(target instanceof Element) || !(currentTarget instanceof Element)) {
return false
}
const noRowClickTarget = target.closest('[data-no-row-click]')
return noRowClickTarget !== null && noRowClickTarget !== currentTarget
}
function handleRowClick(row: T, rowIndex: number, event: MouseEvent) {
if (!isRowClickable(row, rowIndex) || isNoRowClickTarget(event)) {
return
}
emit('rowClick', row, rowIndex, event)
}
function isSelected(row: T): boolean {