Skip to content

Commit

Permalink
wip: assemble nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
junkisai committed Dec 11, 2024
1 parent 3f7da05 commit e1e0c6b
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 76 deletions.
1 change: 1 addition & 0 deletions frontend/packages/erd-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"main": "src/index.ts",
"dependencies": {
"@liam-hq/ui": "workspace:*",
"@radix-ui/react-context-menu": "2.2.2",
"@radix-ui/react-toolbar": "1.1.0",
"@xyflow/react": "12.3.5",
"clsx": "2.1.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,15 @@
.handle {
top: auto;
}

.contextMenuContent {
padding: var(--spacing-4);
background-color: var(--node-background);
border: solid 1px var(--overlay-20);
}

.contextMenuItem {
display: flex;
align-items: center;
color: white;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import { updateActiveTableName, useDBStructureStore } from '@/stores'
import type { Table } from '@liam-hq/db-structure'
import { DiamondFillIcon, DiamondIcon, KeyRound } from '@liam-hq/ui'
import { Handle, type Node, type NodeProps, Position } from '@xyflow/react'
import * as ContextMenu from '@radix-ui/react-context-menu'
import {
Handle,
type Node,
type NodeProps,
Position,
useReactFlow,
} from '@xyflow/react'
import { type FC, useCallback } from 'react'
import { convertDBStructureToNodes } from '../../convertDBStructureToNodes'
import { extractDBStructureForTable } from './TableDetail/RelatedTables/extractDBStructureForTable'
import { TableHeader } from './TableHeader'
import styles from './TableNode.module.css'

Expand All @@ -14,85 +23,136 @@ type TableNodeType = Node<Data, 'Table'>

type Props = NodeProps<TableNodeType>

export const TableNode: FC<Props> = ({ data: { table } }) => {
const { relationships } = useDBStructureStore()
export const TableNode: FC<Props> = ({ id, data: { table } }) => {
const { getNodes, setNodes } = useReactFlow()

const dbStructure = useDBStructureStore()
const { relationships } = dbStructure
const extractedDBStructure = extractDBStructureForTable(table, dbStructure)
const { nodes } = convertDBStructureToNodes(extractedDBStructure)

const handleClick = useCallback(() => {
updateActiveTableName(table.name)
}, [table])

const handleClickAssemble = useCallback(() => {
const allNodes = getNodes()
const currentNode = allNodes.find((node) => node.id === id)

if (currentNode === undefined) return

const relatedNodes = nodes.filter((node) => node.id !== currentNode.id)

const updatedNodes = allNodes.map((node, index) => {
const isTarget = relatedNodes.some(
(relatedNode) => relatedNode.id === node.id,
)

if (isTarget) {
return {
...node,
position: {
x: currentNode.position.x + 10 * (index + 1),
y: currentNode.position.y + 10 * (index + 1),
},
}
}

return node
})

setNodes(updatedNodes)
}, [id, nodes, getNodes, setNodes])

return (
<button type="button" className={styles.wrapper} onClick={handleClick}>
<TableHeader name={table.name} />
<ul>
{Object.values(table.columns).map((column) => {
const handleId = `${table.name}-${column.name}`
const isSource = Object.values(relationships).some(
(relationship) =>
relationship.primaryTableName === table.name &&
relationship.primaryColumnName === column.name,
)
const isTarget = Object.values(relationships).some(
(relationship) =>
relationship.foreignTableName === table.name &&
relationship.foreignColumnName === column.name,
)

return (
<li key={column.name} className={styles.columnWrapper}>
{column.primary && (
<KeyRound
width={16}
height={16}
className={styles.primaryKeyIcon}
role="img"
aria-label="Primary Key"
/>
)}
{!column.primary &&
(column.notNull ? (
<DiamondFillIcon
width={16}
height={16}
className={styles.diamondIcon}
role="img"
aria-label="Not Null"
/>
) : (
<DiamondIcon
width={16}
height={16}
className={styles.diamondIcon}
role="img"
aria-label="Nullable"
/>
))}

<span className={styles.columnNameWrapper}>
<span className={styles.columnName}>{column.name}</span>
<span className={styles.columnType}>{column.type}</span>
</span>

{isSource && (
<Handle
id={handleId}
type="source"
position={Position.Right}
className={styles.handle}
/>
)}

{isTarget && (
<Handle
id={handleId}
type="target"
position={Position.Left}
className={styles.handle}
/>
)}
</li>
)
})}
</ul>
</button>
<ContextMenu.Root>
<ContextMenu.Trigger>
<button type="button" className={styles.wrapper} onClick={handleClick}>
<TableHeader name={table.name} />
<ul>
{Object.values(table.columns).map((column) => {
const handleId = `${table.name}-${column.name}`
const isSource = Object.values(relationships).some(
(relationship) =>
relationship.primaryTableName === table.name &&
relationship.primaryColumnName === column.name,
)
const isTarget = Object.values(relationships).some(
(relationship) =>
relationship.foreignTableName === table.name &&
relationship.foreignColumnName === column.name,
)

return (
<li key={column.name} className={styles.columnWrapper}>
{column.primary && (
<KeyRound
width={16}
height={16}
className={styles.primaryKeyIcon}
role="img"
aria-label="Primary Key"
/>
)}
{!column.primary &&
(column.notNull ? (
<DiamondFillIcon
width={16}
height={16}
className={styles.diamondIcon}
role="img"
aria-label="Not Null"
/>
) : (
<DiamondIcon
width={16}
height={16}
className={styles.diamondIcon}
role="img"
aria-label="Nullable"
/>
))}

<span className={styles.columnNameWrapper}>
<span className={styles.columnName}>{column.name}</span>
<span className={styles.columnType}>{column.type}</span>
</span>

{isSource && (
<Handle
id={handleId}
type="source"
position={Position.Right}
className={styles.handle}
/>
)}

{isTarget && (
<Handle
id={handleId}
type="target"
position={Position.Left}
className={styles.handle}
/>
)}
</li>
)
})}
</ul>
</button>
</ContextMenu.Trigger>
<ContextMenu.Portal>
<ContextMenu.Content
className={styles.contextMenuContent}
alignOffset={4}
>
<ContextMenu.Item className={styles.contextMenuItem}>
<button type="button" onClick={handleClickAssemble}>
Assemble!!
</button>
</ContextMenu.Item>
</ContextMenu.Content>
</ContextMenu.Portal>
</ContextMenu.Root>
)
}
30 changes: 30 additions & 0 deletions frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e1e0c6b

Please sign in to comment.