import type { ColumnCellKind } from "../components/data-table/types" /** Matches DataTable's empty-group sentinel. */ export const GROUP_ROW_EMPTY_KEY = "—" /** Unit separator — unlikely inside person names; stable array grouping. */ const GROUP_ARRAY_SEP = "\u001f" /** * Stable string key for grouping rows by a column value. Avoids `[object Object]` * when the field holds arrays (people-rail, tags) or plain objects. */ export function groupRowKey(value: unknown, cellKind?: ColumnCellKind): string { if (value == null || value === "") return GROUP_ROW_EMPTY_KEY if (cellKind === "people-rail" && Array.isArray(value)) { return value .map(item => { if (item && typeof item === "object") { const person = item as { name?: string; initials?: string } return `${person.initials ?? ""}|${person.name ?? ""}` } return String(item) }) .sort() .join(GROUP_ARRAY_SEP) } if (cellKind === "tags" && Array.isArray(value)) { return value.map(String).sort().join(GROUP_ARRAY_SEP) } if (typeof value === "object") { if (cellKind === "person") { const person = value as { name?: string; email?: string } return person.name ?? person.email ?? GROUP_ROW_EMPTY_KEY } try { return JSON.stringify(value) } catch { return GROUP_ROW_EMPTY_KEY } } return String(value) } /** Screen-reader / count-badge label for non-chip group headings. */ export function groupRowAccessibilityLabel( value: unknown, cellKind: ColumnCellKind | undefined, fallbackKey: string, ): string { if (fallbackKey === GROUP_ROW_EMPTY_KEY) return "None" if (cellKind === "people-rail" && Array.isArray(value)) { const names = value .map(item => (item && typeof item === "object" ? (item as { name?: string }).name : undefined)) .filter((name): name is string => Boolean(name)) return names.length > 0 ? names.join(", ") : "None" } if (cellKind === "person" && value && typeof value === "object") { const person = value as { name?: string } return person.name ?? fallbackKey } return fallbackKey }