Skip to content

Commit

Permalink
Merge pull request #322 from liam-hq/feat/group-nodes
Browse files Browse the repository at this point in the history
✨ Grouping TableNodes without Relationships into a GroupNode and Applying a New Layout Definition
  • Loading branch information
MH4GF authored Dec 20, 2024
2 parents ff3b794 + 25726dc commit 167f507
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 16 deletions.
6 changes: 6 additions & 0 deletions frontend/.changeset/dry-ligers-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@liam-hq/erd-core": patch
"@liam-hq/cli": patch
---

✨ Enhance node conversion functions to support hierarchical structure and layout options for NonRelatedTableGroup nodes
6 changes: 6 additions & 0 deletions frontend/.changeset/mean-rings-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@liam-hq/erd-core": patch
"@liam-hq/cli": patch
---

✨ Add NonRelatedTableGroupNode component with styling
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { type FC, useCallback } from 'react'
import { CardinalityMarkers } from './CardinalityMarkers'
import styles from './ERDContent.module.css'
import { ERDContentProvider, useERDContentContext } from './ERDContentContext'
import { NonRelatedTableGroupNode } from './NonRelatedTableGroupNode'
import { RelationshipEdge } from './RelationshipEdge'
import { Spinner } from './Spinner'
import { TableNode } from './TableNode'
Expand All @@ -28,6 +29,7 @@ import { useSyncHighlightsActiveTableChange } from './useSyncHighlightsActiveTab

const nodeTypes = {
table: TableNode,
nonRelatedTableGroup: NonRelatedTableGroupNode,
}

const edgeTypes = {
Expand Down Expand Up @@ -73,7 +75,7 @@ export const ERDContentInner: FC<Props> = ({
} = useERDContentContext()
const { tableName: activeTableName } = useUserEditingActiveStore()

useInitialAutoLayout()
useInitialAutoLayout(nodes)
useFitViewWhenActiveTableChange(
enabledFeatures?.fitViewWhenActiveTableChange ?? true,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.wrapper {
width: 100%;
height: 100%;
opacity: 1;
}

:global([data-loading='true']) .wrapper {
opacity: 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { Node } from '@xyflow/react'
import type { FC } from 'react'
import styles from './NonRelatedTableGroupNode.module.css'

export type NonRelatedTableGroupNodeType = Node<
Record<string, unknown>,
'nonRelatedTableGroup'
>

export const NonRelatedTableGroupNode: FC = () => {
return <div className={styles.wrapper} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './NonRelatedTableGroupNode'
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ export function convertElkNodesToNodes(
width: elkNode.width ?? 0,
height: elkNode.height ?? 0,
})

if (elkNode.children) {
for (const child of elkNode.children) {
nodes.push(...convertElkNodesToNodes([child], originNodes))
}
}
}

return nodes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,39 @@ import type { Node } from '@xyflow/react'
import type { ElkNode } from 'elkjs'

export function convertNodesToElkNodes(nodes: Node[]): ElkNode[] {
return nodes.map((node) => ({
...node,
width: node.measured?.width ?? 0,
height: node.measured?.height ?? 0,
}))
const nodeMap: Record<string, ElkNode> = {}

const elkNodes: ElkNode[] = []
for (const node of nodes) {
const elkNode: ElkNode = {
...node,
width: node.measured?.width ?? 0,
height: node.measured?.height ?? 0,
layoutOptions: {
/**
* NOTE
* For NonRelatedTableGroup Nodes, arrange child Nodes in a vertical layout.
* For all other cases, use default values.
*/
'elk.aspectRatio':
node.type === 'nonRelatedTableGroup' ? '0.5625' : '1.6f',
},
}
nodeMap[node.id] = elkNode

if (node.parentId) {
const parentNode = nodeMap[node.parentId]
if (!parentNode) continue

if (!parentNode.children) {
parentNode.children = []
}

parentNode.children.push(elkNode)
} else {
elkNodes.push(elkNode)
}
}

return elkNodes
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { QueryParam } from '@/schemas/queryParam'
import { addHiddenNodeIds, updateActiveTableName } from '@/stores'
import { decompressFromEncodedURIComponent } from '@/utils'
import { useNodesInitialized } from '@xyflow/react'
import { useEffect } from 'react'
import type { Node } from '@xyflow/react'
import { useEffect, useMemo } from 'react'
import { useERDContentContext } from './ERDContentContext'
import { useAutoLayout } from './useAutoLayout'

Expand All @@ -25,8 +25,15 @@ const getHiddenNodeIdsFromUrl = async (): Promise<string[]> => {
return hiddenNodeIds ? hiddenNodeIds.split(',') : []
}

export const useInitialAutoLayout = () => {
const nodesInitialized = useNodesInitialized()
export const useInitialAutoLayout = (nodes: Node[]) => {
const tableNodesInitialized = useMemo(
() =>
nodes
.filter((node) => node.type === 'table')
.some((node) => node.measured),
[nodes],
)

const {
state: { initializeComplete },
} = useERDContentContext()
Expand All @@ -47,11 +54,11 @@ export const useInitialAutoLayout = () => {
? { maxZoom: 1, duration: 300, nodes: [{ id: tableNameFromUrl }] }
: undefined

if (nodesInitialized) {
if (tableNodesInitialized) {
handleLayout(fitViewOptions, hiddenNodeIds)
}
}

initialize()
}, [nodesInitialized, initializeComplete, handleLayout])
}, [tableNodesInitialized, initializeComplete, handleLayout])
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import type { Edge, Node } from '@xyflow/react'
import { columnHandleId } from './columnHandleId'
import { zIndex } from './constants'

const NON_RELATED_TABLE_GROUP_NODE_ID = 'non-related-table-group'

type Params = {
dbStructure: DBStructure
showMode: ShowMode
Expand All @@ -18,12 +20,16 @@ export const convertDBStructureToNodes = ({
} => {
const tables = Object.values(dbStructure.tables)
const relationships = Object.values(dbStructure.relationships)

const tablesWithRelationships = new Set<string>()
const sourceColumns = new Map<string, string>()
const tableColumnCardinalities = new Map<
string,
Record<string, Cardinality>
>()
for (const relationship of relationships) {
tablesWithRelationships.add(relationship.primaryTableName)
tablesWithRelationships.add(relationship.foreignTableName)
sourceColumns.set(
relationship.primaryTableName,
relationship.primaryColumnName,
Expand All @@ -34,8 +40,14 @@ export const convertDBStructureToNodes = ({
})
}

const nodes: Node[] = tables.map((table) => {
return {
const nodes: Node[] = [
{
id: NON_RELATED_TABLE_GROUP_NODE_ID,
type: 'nonRelatedTableGroup',
data: {},
position: { x: 0, y: 0 },
},
...tables.map((table) => ({
id: table.name,
type: 'table',
data: {
Expand All @@ -45,8 +57,11 @@ export const convertDBStructureToNodes = ({
},
position: { x: 0, y: 0 },
zIndex: zIndex.nodeDefault,
}
})
...(!tablesWithRelationships.has(table.name)
? { parentId: NON_RELATED_TABLE_GROUP_NODE_ID }
: {}),
})),
]

const edges: Edge[] = relationships.map((rel) => ({
id: rel.name,
Expand Down

0 comments on commit 167f507

Please sign in to comment.