diff --git a/client/src/components/Common/GTable.types.ts b/client/src/components/Common/GTable.types.ts index ae82c138f73..68966e5094f 100644 --- a/client/src/components/Common/GTable.types.ts +++ b/client/src/components/Common/GTable.types.ts @@ -9,6 +9,17 @@ export type SortOrder = "asc" | "desc"; /** Table field alignment options */ export type FieldAlignment = "left" | "center" | "right"; +/** Shared class value shape used by row/cell class bindings */ +export type TableClassValue = string | readonly string[] | Record; + +/** Optional per-item class metadata a row can carry to style itself/its cells */ +export interface TableItemClassMeta { + /** CSS classes applied to the row */ + class?: TableClassValue; + /** CSS classes applied to individual cells, keyed by field key */ + cellClass?: Record; +} + /** Table field definition */ export interface TableField { /** Unique key for the field (matches data property name) */ @@ -18,11 +29,11 @@ export interface TableField { /** Whether the column is sortable */ sortable?: boolean; /** Custom CSS classes for the column */ - class?: string; + class?: TableClassValue; /** Custom CSS classes for the header cell */ headerClass?: string; /** Custom CSS classes for data cells */ - cellClass?: string; + cellClass?: TableClassValue; /** Column alignment */ align?: FieldAlignment; /** Width of the column (CSS value) */ diff --git a/client/src/components/Common/GTable.vue b/client/src/components/Common/GTable.vue index f7986abf624..4fb2851c12c 100644 --- a/client/src/components/Common/GTable.vue +++ b/client/src/components/Common/GTable.vue @@ -15,6 +15,7 @@ import type { TableAction, TableEmptyState, TableField, + TableItemClassMeta, } from "./GTable.types"; import GOverlay from "@/components/BaseComponents/GOverlay.vue"; @@ -186,6 +187,14 @@ interface Props { */ perPage?: number; + /** + * Item field to expose as each row's `data-pk` attribute, enabling row + * selection by key (e.g. in selenium selectors). When unset, no `data-pk` + * is rendered. + * @default "" + */ + primaryKey?: string; + /** * Whether to show striped rows * @default true @@ -210,6 +219,13 @@ interface Props { */ selectedItems?: number[]; + /** + * Array of item indices whose selection is partial/mixed. Their row checkbox + * renders as indeterminate (e.g. a folder with only some children selected). + * @default [] + */ + indeterminateItems?: number[]; + /** * Whether to show the empty state message when no items are available * @default false @@ -288,9 +304,11 @@ const props = withDefaults(defineProps(), { noSelectOnClick: false, overlayLoading: false, perPage: undefined, + primaryKey: "", selectable: false, selectCheckboxTitle: "Select for bulk actions", selectedItems: () => [], + indeterminateItems: () => [], showEmpty: false, showSelectAll: false, sortBy: "", @@ -322,7 +340,7 @@ const emit = defineEmits<{ * Emitted when select all checkbox is toggled * @event select-all */ - (e: "select-all"): void; + (e: "select-all", selected: boolean): void; /** * Emitted when a row is selected/deselected @@ -527,7 +545,7 @@ function onRowClick(item: T, index: number, event: MouseEvent | KeyboardEvent) { } function onSelectAll(selected: boolean) { - emit("select-all"); + emit("select-all", selected); } /** @@ -592,15 +610,17 @@ function getAlignmentClass(align?: FieldAlignment) { } /** - * Get cell variant class for Bootstrap color variants (e.g., "success", "danger", "info") - * Supports the _cellVariants convention from b-table for backward compatibility + * Get row class from item metadata */ -function getCellVariantClass(item: T, field: TableField) { - const cellVariants = item._cellVariants as Record | undefined; - if (!cellVariants || !cellVariants[field.key]) { - return undefined; - } - return `table-${cellVariants[field.key]}`; +function getRowClass(item: T) { + return (item as TableItemClassMeta).class; +} + +/** + * Get cell class from item metadata + */ +function getItemCellClass(item: T, fieldKey: string) { + return (item as TableItemClassMeta).cellClass?.[fieldKey]; } /** @@ -610,6 +630,10 @@ function isRowSelected(index: number) { return props.selectedItems.includes(index); } +function isRowIndeterminate(index: number) { + return props.indeterminateItems.includes(index); +} + /** * Get status icon for a row */ @@ -659,7 +683,11 @@ defineExpose({