Files
supabase/apps/studio/components/interfaces/Database/Tables/ColumnList.utils.ts
T
Danny WhiteandJoshen Lim 3980bb162d feat(studio): improve database column list scanning (#44208)
## What kind of change does this PR introduce?

UI improvements. Resolves DEPR-419.

## What is the current behavior?

- `ColumnList` splits type information across separate `Data Type` and
`Format` columns, which makes scanning less efficient
- The list only surfaces a limited subset of column metadata up front,
so key structural information like primary keys, foreign keys,
uniqueness, and identity is not easy to scan
- Constraint and nullability affordances are not aligned with the visual
language already used in the Table Editor and Schema Visualiser

## What is the new behavior?

- `ColumnList` now shows a leading type affordance icon, while keeping
the exact Postgres type visible in the `Type` column
- The standalone `Data Type` column is removed, and `format` is now the
primary textual type shown in the table
- The `Constraints` column now surfaces explicit icon-plus-label tokens
for:
  - `Primary`
  - `Foreign key`
  - `Unique`
  - `Identity`
  - `Nullable` / `Non-nullable`
- Constraint tokens use a purpose-built segmented style based on
`ComputeBadge`, including a green primary variant for primary keys
- Nullability now matches Schema Visualiser semantics exactly: outlined
diamond for nullable, filled diamond for non-nullable
- Existing search, descriptions, row actions, and permission gating are
preserved

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-03-26 15:55:32 +08:00

79 lines
2.2 KiB
TypeScript

import { POSTGRES_DATA_TYPE_OPTIONS } from 'components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.constants'
type TableConstraintSource = {
schema: string
name: string
primary_keys: ReadonlyArray<{ name: string }>
relationships: ReadonlyArray<{
source_schema: string
source_table_name: string
source_column_name: string
}>
unique_indexes?: ReadonlyArray<{ columns: ReadonlyArray<string> }>
}
export type ColumnAffordanceKind = 'number' | 'time' | 'text' | 'json' | 'bool' | 'other'
export interface ColumnTypeAffordance {
kind: ColumnAffordanceKind
label: string
}
const COLUMN_AFFORDANCE_LABELS: Record<ColumnAffordanceKind, string> = {
number: 'Numeric',
time: 'Date / time',
text: 'Text',
json: 'JSON',
bool: 'Boolean',
other: 'Other',
}
const normalizeColumnFormat = (format: string) => format.replaceAll('"', '').replace(/\[\]$/, '')
export function getColumnTypeAffordance(format: string): ColumnTypeAffordance {
const normalizedFormat = normalizeColumnFormat(format)
const optionType = POSTGRES_DATA_TYPE_OPTIONS.find(
(option) => option.name === normalizedFormat
)?.type
switch (optionType) {
case 'number':
case 'time':
case 'text':
case 'json':
case 'bool':
return { kind: optionType, label: COLUMN_AFFORDANCE_LABELS[optionType] }
default:
return { kind: 'other', label: COLUMN_AFFORDANCE_LABELS.other }
}
}
export function getPrimaryKeyColumnNames(table?: TableConstraintSource) {
return new Set(table?.primary_keys.map((primaryKey) => primaryKey.name) ?? [])
}
export function getForeignKeyColumnNames(table?: TableConstraintSource) {
if (!table) {
return new Set<string>()
}
const { schema, name, relationships } = table
return new Set(
relationships
.filter(
(relationship) =>
relationship.source_schema === schema && relationship.source_table_name === name
)
.map((relationship) => relationship.source_column_name)
)
}
export function getUniqueIndexColumnNames(table?: TableConstraintSource) {
return new Set(
table?.unique_indexes
?.filter((uniqueIndex) => uniqueIndex.columns.length === 1)
.flatMap((uniqueIndex) => uniqueIndex.columns) ?? []
)
}