Skip to content

Commit a8cd636

Browse files
committed
improvement(tables): route reference metadata through one registry accessor
1 parent 5172521 commit a8cd636

19 files changed

Lines changed: 294 additions & 283 deletions

File tree

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/cell-content.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import {
66
type ReferenceCellAction,
77
resolveCellRender,
88
} from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/cell-render'
9+
import { InlineEditor } from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells/inline-editors'
10+
import type { DisplayColumn } from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/types'
11+
import type { SaveReason } from '@/app/workspace/[workspaceId]/tables/[tableId]/types'
912
import type { TimezoneState } from '@/hooks/queries/general-settings'
10-
import type { SaveReason } from '../../../types'
11-
import type { DisplayColumn } from '../types'
12-
import { InlineEditor } from './inline-editors'
1313

1414
interface CellContentProps {
1515
value: unknown

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/data-row.tsx

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,18 @@ import { Button, Checkbox, cn, handleKeyboardActivation } from '@sim/emcn'
55
import { PlayOutline, Square } from '@sim/emcn/icons'
66
import type { ActiveDispatch } from '@/lib/api/contracts/tables'
77
import type { TableRow as TableRowType, WorkflowGroup } from '@/lib/table'
8-
import { columnTypeOf } from '@/lib/table/column-types'
8+
import { columnReferencedTableIds, columnTypeOf } from '@/lib/table/column-types'
99
import { getUnmetGroupDeps } from '@/lib/table/deps'
10+
import { CellContent } from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/cells'
11+
import {
12+
CELL,
13+
CELL_CHECKBOX,
14+
CELL_CONTENT,
15+
CELL_OVERLAY_INSET,
16+
FIND_MATCH_TINT_BG,
17+
SELECTION_OVERLAY,
18+
SELECTION_TINT_BG,
19+
} from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/constants'
1020
import type {
1121
DisplayColumn,
1222
ReferencePreviewTarget,
@@ -16,18 +26,8 @@ import {
1626
type NormalizedSelection,
1727
resolveCellExec,
1828
} from '@/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/utils'
29+
import type { SaveReason } from '@/app/workspace/[workspaceId]/tables/[tableId]/types'
1930
import type { TimezoneState } from '@/hooks/queries/general-settings'
20-
import type { SaveReason } from '../../types'
21-
import { CellContent } from './cells'
22-
import {
23-
CELL,
24-
CELL_CHECKBOX,
25-
CELL_CONTENT,
26-
CELL_OVERLAY_INSET,
27-
FIND_MATCH_TINT_BG,
28-
SELECTION_OVERLAY,
29-
SELECTION_TINT_BG,
30-
} from './constants'
3131

3232
export interface DataRowProps {
3333
row: TableRowType
@@ -331,11 +331,10 @@ export const DataRow = React.memo(function DataRow({
331331
pendingCellValue && column.key in pendingCellValue
332332
? pendingCellValue[column.key]
333333
: row.data[column.key]
334-
const referencePreview = referenceColumnsEnabled
335-
? columnTypeOf(column).referencePreview
336-
: undefined
337-
const referenceRowId = referencePreview?.getRowId(value) ?? null
338-
const referenceTableId = referencePreview?.getTableId(column)
334+
const referenceRowId = referenceColumnsEnabled
335+
? (columnTypeOf(column).referencePreview?.getRowId(value) ?? null)
336+
: null
337+
const [referenceTableId] = referenceColumnsEnabled ? columnReferencedTableIds(column) : []
339338
const referenceTarget =
340339
referenceTableId && referenceRowId
341340
? {

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/reference-row-preview.test.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,8 @@ const { previewQuery } = vi.hoisted(() => ({
1414

1515
vi.mock('@/lib/table/column-types', () => ({
1616
columnTypeById: () => ({ icon: () => null }),
17-
columnTypeOf: (column: { type: string; referenceTableId?: string }) => ({
18-
referencePreview:
19-
column.type === 'reference'
20-
? {
21-
getTableId: () => column.referenceTableId,
22-
}
23-
: undefined,
24-
}),
17+
columnReferencedTableIds: (column: { type: string; referenceTableId?: string }) =>
18+
column.type === 'reference' && column.referenceTableId ? [column.referenceTableId] : [],
2519
}))
2620

2721
vi.mock('@sim/emcn/icons', () => ({

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/reference-row-preview.tsx

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ export const REFERENCE_ROW_PREVIEW_HEIGHT = 144
2222

2323
const ReferenceIcon = columnTypeById('reference').icon
2424

25+
const NO_MATCHING_ROW = 'No matching row'
26+
27+
/** Centered muted line shared by every state that renders copy instead of the row. */
28+
function PreviewMessage({ children }: { children: ReactNode }) {
29+
return (
30+
<div className='flex h-full items-center justify-center text-[var(--text-muted)] text-small'>
31+
{children}
32+
</div>
33+
)
34+
}
35+
2536
interface ReferenceRowPreviewBaseProps {
2637
workspaceId: string
2738
timezoneStatus: TimezoneState['status']
@@ -109,6 +120,11 @@ export const ReferenceRowPreview = memo(function ReferenceRowPreview(
109120
const previewViewport = previewViewportRef.current
110121
if (!previewViewport) return
111122

123+
/**
124+
* Only a mostly-horizontal gesture is taken: cancelling a wheel event is all-or-nothing, and
125+
* this strip has no vertical overflow of its own, so hijacking a diagonal pan would strand the
126+
* grid's own vertical scroll.
127+
*/
112128
const handleWheel = (event: WheelEvent) => {
113129
if (Math.abs(event.deltaX) <= Math.abs(event.deltaY)) return
114130
event.preventDefault()
@@ -120,18 +136,8 @@ export const ReferenceRowPreview = memo(function ReferenceRowPreview(
120136
}, [status])
121137

122138
let content: ReactNode
123-
if (columns.length === 0 && !row) {
124-
content = (
125-
<div className='flex h-full items-center justify-center text-[var(--text-muted)] text-small'>
126-
No matching row
127-
</div>
128-
)
129-
} else if (columns.length === 0) {
130-
content = (
131-
<div className='flex h-full items-center justify-center text-[var(--text-muted)] text-small'>
132-
This table has no columns
133-
</div>
134-
)
139+
if (columns.length === 0) {
140+
content = <PreviewMessage>{row ? 'This table has no columns' : NO_MATCHING_ROW}</PreviewMessage>
135141
} else {
136142
content = (
137143
<div role='table' className='grid h-full w-full min-w-max grid-rows-2 text-small'>
@@ -159,7 +165,7 @@ export const ReferenceRowPreview = memo(function ReferenceRowPreview(
159165
role='cell'
160166
className='flex min-w-full flex-1 items-center justify-center text-[var(--text-muted)]'
161167
>
162-
No matching row
168+
{NO_MATCHING_ROW}
163169
</div>
164170
) : (
165171
<>
@@ -208,9 +214,9 @@ export const ReferenceRowPreview = memo(function ReferenceRowPreview(
208214
<Loader animate className='size-[14px] text-[var(--text-muted)]' />
209215
</div>
210216
) : status === 'error' || status === 'missing' ? (
211-
<div className='flex h-full items-center justify-center text-[var(--text-muted)] text-small'>
217+
<PreviewMessage>
212218
{status === 'missing' ? 'Table not found' : "Couldn't load reference"}
213-
</div>
219+
</PreviewMessage>
214220
) : (
215221
<>
216222
<div className='flex h-9 shrink-0 items-center gap-1.5 px-3 text-[var(--text-primary)] text-small'>

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/table-grid.tsx

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ import type {
2525
WorkflowGroup,
2626
} from '@/lib/table'
2727
import { getColumnId } from '@/lib/table/column-keys'
28-
import { columnTypeOf } from '@/lib/table/column-types'
28+
import {
29+
collectColumnReferencedTableIds,
30+
columnReferencedTableIds,
31+
columnTypeOf,
32+
} from '@/lib/table/column-types'
2933
import { TABLE_LIMITS } from '@/lib/table/constants'
3034
import { isEmptyCellValue } from '@/lib/table/deps'
3135
import { cellValueFilterConditions } from '@/lib/table/query-builder/cell-filter'
@@ -45,6 +49,7 @@ import type { RemoteTableSelection } from '@/app/workspace/[workspaceId]/tables/
4549
import type { BlockedTableAction } from '@/app/workspace/[workspaceId]/tables/[tableId]/lock-copy'
4650
import { LOCK_TOOLTIPS } from '@/app/workspace/[workspaceId]/tables/[tableId]/lock-copy'
4751
import { useTimezoneState } from '@/hooks/queries/general-settings'
52+
import { useReferenceRowPreview } from '@/hooks/queries/table-reference-preview'
4853
import {
4954
useAddTableColumn,
5055
useBatchCreateTableRows,
@@ -53,7 +58,6 @@ import {
5358
useDeleteColumn,
5459
useDeleteWorkflowGroup,
5560
useFindTableRows,
56-
useReferenceRowPreview,
5761
useTableNames,
5862
useTableRunState,
5963
useUpdateColumn,
@@ -457,6 +461,14 @@ async function chunkBatchUpdates(
457461
)
458462
}
459463

464+
/** The referenced row's load state, as the preview renders it. */
465+
function resolveReferencePreviewState(query: ReturnType<typeof useReferenceRowPreview>) {
466+
if (query.isError) return { status: 'error' } as const
467+
if (query.isFetching || !query.data) return { status: 'loading' } as const
468+
if (query.data.table === null) return { status: 'missing' } as const
469+
return { status: 'ready', table: query.data.table, row: query.data.row } as const
470+
}
471+
460472
export function TableGrid({
461473
workspaceId: propWorkspaceId,
462474
tableId: propTableId,
@@ -625,23 +637,14 @@ export function TableGrid({
625637
filter: effectiveFilter,
626638
} = useTable({ workspaceId, tableId, queryOptions })
627639
const referencedTableIds = useMemo(
628-
() =>
629-
referenceColumnsEnabled
630-
? columns.flatMap((column) => {
631-
const referenceTableId = columnTypeOf(column).referencePreview?.getTableId(column)
632-
return referenceTableId ? [referenceTableId] : []
633-
})
634-
: [],
640+
() => (referenceColumnsEnabled ? collectColumnReferencedTableIds(columns) : []),
635641
[columns, referenceColumnsEnabled]
636642
)
637643
const { data: referencedTables } = useTableNames(workspaceId, referencedTableIds)
638-
const referenceTableNames = useMemo(() => {
639-
const names = new Map<string, string>()
640-
for (const table of referencedTables ?? []) {
641-
names.set(table.id, table.name)
642-
}
643-
return names
644-
}, [referencedTables])
644+
const referenceTableNames = useMemo(
645+
() => new Map((referencedTables ?? []).map((table) => [table.id, table.name])),
646+
[referencedTables]
647+
)
645648

646649
/** Sort is single-column, so only the first spec entry can be active. */
647650
const activeSort = queryOptions.sort?.[0]
@@ -946,9 +949,9 @@ export function TableGrid({
946949
)
947950
const referencePreview = sourceColumn ? columnTypeOf(sourceColumn).referencePreview : undefined
948951
if (!sourceRow || !sourceColumn || !referencePreview) return null
952+
const [referenceTableId] = columnReferencedTableIds(sourceColumn)
949953
return referencePreview.getRowId(sourceRow.data[expandedReference.sourceColumnKey]) ===
950-
expandedReference.referenceRowId &&
951-
referencePreview.getTableId(sourceColumn) === expandedReference.referenceTableId
954+
expandedReference.referenceRowId && referenceTableId === expandedReference.referenceTableId
952955
? expandedReference
953956
: null
954957
}, [displayColumns, rows, expandedReference, referenceColumnsEnabled])
@@ -966,17 +969,7 @@ export function TableGrid({
966969
}
967970
return names
968971
}, [referenceTableNames, referencePreviewQuery.data?.referenceTables])
969-
const referencePreviewState = referencePreviewQuery.isError
970-
? ({ status: 'error' } as const)
971-
: referencePreviewQuery.isFetching || !referencePreviewQuery.data
972-
? ({ status: 'loading' } as const)
973-
: referencePreviewQuery.data.table === null
974-
? ({ status: 'missing' } as const)
975-
: ({
976-
status: 'ready',
977-
table: referencePreviewQuery.data.table,
978-
row: referencePreviewQuery.data.row,
979-
} as const)
972+
const referencePreviewState = resolveReferencePreviewState(referencePreviewQuery)
980973
const expandedSourceRowId = activeReferenceTarget?.sourceRowId ?? null
981974

982975
const rowVirtualizer = useVirtualizer({

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/table-grid/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
WorkflowGroup,
1313
} from '@/lib/table'
1414
import { getColumnId } from '@/lib/table/column-keys'
15-
import { columnTypeOf } from '@/lib/table/column-types'
15+
import { columnReferencedTableIds } from '@/lib/table/column-types'
1616
import { TABLE_LIMITS } from '@/lib/table/constants'
1717
import { areGroupDepsSatisfied, areOutputsFilled } from '@/lib/table/deps'
1818
import type {
@@ -177,7 +177,7 @@ function resolveReferenceTableName(
177177
column: ColumnDefinition,
178178
referenceTableNames: ReadonlyMap<string, string> | undefined
179179
): string | undefined {
180-
const tableId = columnTypeOf(column).referencePreview?.getTableId(column)
180+
const [tableId] = columnReferencedTableIds(column)
181181
return tableId ? referenceTableNames?.get(tableId) : undefined
182182
}
183183

apps/sim/app/workspace/[workspaceId]/tables/tables.tsx

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -620,21 +620,30 @@ function TablesContent() {
620620
return selectionLabel(count, firstName)
621621
}, [selectedTableIds, selectedFolderIds, tables, folderById])
622622

623-
const deleteFolderIds =
624-
isDeleteFolderDialogOpen && activeFolder
625-
? [activeFolder.id]
626-
: isBulkDeleteDialogOpen
627-
? selectedFolderIds
628-
: []
629623
/** Tables the open delete confirmation would archive, including every table inside a folder. */
630-
const pendingDeleteTableIds = isDeleteDialogOpen
631-
? activeTable
632-
? [activeTable.id]
633-
: []
634-
: [
635-
...(isBulkDeleteDialogOpen ? selectedTableIds : []),
636-
...tableIdsInFolderSubtrees(tables, deleteFolderIds, descendantFolderIds),
624+
const pendingDeleteTableIds = useMemo(() => {
625+
if (isDeleteDialogOpen) return activeTable ? [activeTable.id] : []
626+
if (isDeleteFolderDialogOpen && activeFolder) {
627+
return tableIdsInFolderSubtrees(tables, [activeFolder.id], descendantFolderIds)
628+
}
629+
if (isBulkDeleteDialogOpen) {
630+
return [
631+
...selectedTableIds,
632+
...tableIdsInFolderSubtrees(tables, selectedFolderIds, descendantFolderIds),
637633
]
634+
}
635+
return []
636+
}, [
637+
isDeleteDialogOpen,
638+
isDeleteFolderDialogOpen,
639+
isBulkDeleteDialogOpen,
640+
activeTable,
641+
activeFolder,
642+
tables,
643+
descendantFolderIds,
644+
selectedTableIds,
645+
selectedFolderIds,
646+
])
638647
const referencedByWarning = useReferencedByWarning(workspaceId, pendingDeleteTableIds)
639648

640649
const currentFolderActions: DropdownOption[] | undefined = useMemo(() => {

0 commit comments

Comments
 (0)