From 54d6ca915b13f4d10cd1c2e486c17f7e5477ae0b Mon Sep 17 00:00:00 2001 From: junkisai Date: Thu, 19 Dec 2024 17:29:19 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Add=20NonRelatedTableGroupNode?= =?UTF-8?q?=20component=20with=20styling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.changeset/mean-rings-walk.md | 6 ++++++ .../components/ERDRenderer/ERDContent/ERDContent.tsx | 2 ++ .../NonRelatedTableGroupNode.module.css | 9 +++++++++ .../NonRelatedTableGroupNode.tsx | 12 ++++++++++++ .../ERDContent/NonRelatedTableGroupNode/index.ts | 1 + 5 files changed, 30 insertions(+) create mode 100644 frontend/.changeset/mean-rings-walk.md create mode 100644 frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/NonRelatedTableGroupNode.module.css create mode 100644 frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/NonRelatedTableGroupNode.tsx create mode 100644 frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/index.ts diff --git a/frontend/.changeset/mean-rings-walk.md b/frontend/.changeset/mean-rings-walk.md new file mode 100644 index 00000000..3bd7cac2 --- /dev/null +++ b/frontend/.changeset/mean-rings-walk.md @@ -0,0 +1,6 @@ +--- +"@liam-hq/erd-core": patch +"@liam-hq/cli": patch +--- + +✨ Add NonRelatedTableGroupNode component with styling diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx index 16fd370f..26b559c3 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx @@ -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' @@ -28,6 +29,7 @@ import { useSyncHighlightsActiveTableChange } from './useSyncHighlightsActiveTab const nodeTypes = { table: TableNode, + nonRelatedTableGroup: NonRelatedTableGroupNode, } const edgeTypes = { diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/NonRelatedTableGroupNode.module.css b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/NonRelatedTableGroupNode.module.css new file mode 100644 index 00000000..03c15df8 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/NonRelatedTableGroupNode.module.css @@ -0,0 +1,9 @@ +.wrapper { + width: 100%; + height: 100%; + opacity: 1; +} + +:global([data-loading='true']) .wrapper { + opacity: 0; +} diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/NonRelatedTableGroupNode.tsx b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/NonRelatedTableGroupNode.tsx new file mode 100644 index 00000000..8ca9bc4d --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/NonRelatedTableGroupNode.tsx @@ -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, + 'nonRelatedTableGroup' +> + +export const NonRelatedTableGroupNode: FC = () => { + return
+} diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/index.ts b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/index.ts new file mode 100644 index 00000000..36898d67 --- /dev/null +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/NonRelatedTableGroupNode/index.ts @@ -0,0 +1 @@ +export * from './NonRelatedTableGroupNode' From 0a129c2cca3ae38b1a2814acfc971f19a4dfb258 Mon Sep 17 00:00:00 2001 From: junkisai Date: Thu, 19 Dec 2024 17:30:32 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20Enhance=20node=20conversion=20f?= =?UTF-8?q?unctions=20to=20support=20hierarchical=20structure=20and=20layo?= =?UTF-8?q?ut=20options=20for=20NonRelatedTableGroup=20nodes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.changeset/dry-ligers-work.md | 6 +++ .../useAutoLayout/convertElkNodesToNodes.ts | 6 +++ .../useAutoLayout/convertNodesToElkNodes.ts | 40 ++++++++++++++++--- .../ERDContent/useInitialAutoLayout.ts | 14 +++++-- .../ERDRenderer/convertDBStructureToNodes.ts | 23 +++++++++-- 5 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 frontend/.changeset/dry-ligers-work.md diff --git a/frontend/.changeset/dry-ligers-work.md b/frontend/.changeset/dry-ligers-work.md new file mode 100644 index 00000000..181f5d5a --- /dev/null +++ b/frontend/.changeset/dry-ligers-work.md @@ -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 diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useAutoLayout/convertElkNodesToNodes.ts b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useAutoLayout/convertElkNodesToNodes.ts index af07589d..12283482 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useAutoLayout/convertElkNodesToNodes.ts +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useAutoLayout/convertElkNodesToNodes.ts @@ -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 diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useAutoLayout/convertNodesToElkNodes.ts b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useAutoLayout/convertNodesToElkNodes.ts index fd712035..9c535bb7 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useAutoLayout/convertNodesToElkNodes.ts +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useAutoLayout/convertNodesToElkNodes.ts @@ -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 = {} + + 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 } diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useInitialAutoLayout.ts b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useInitialAutoLayout.ts index b167c8b4..de800024 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useInitialAutoLayout.ts +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useInitialAutoLayout.ts @@ -1,7 +1,7 @@ import type { QueryParam } from '@/schemas/queryParam' import { addHiddenNodeIds, updateActiveTableName } from '@/stores' import { decompressFromEncodedURIComponent } from '@/utils' -import { useNodesInitialized } from '@xyflow/react' +import { useReactFlow } from '@xyflow/react' import { useEffect } from 'react' import { useERDContentContext } from './ERDContentContext' import { useAutoLayout } from './useAutoLayout' @@ -26,7 +26,9 @@ const getHiddenNodeIdsFromUrl = async (): Promise => { } export const useInitialAutoLayout = () => { - const nodesInitialized = useNodesInitialized() + const { getNodes } = useReactFlow() + const nodes = getNodes() + const { state: { initializeComplete }, } = useERDContentContext() @@ -38,6 +40,10 @@ export const useInitialAutoLayout = () => { return } + const tableNodesInitialized = nodes + .filter((node) => node.type === 'table') + .some((node) => node.measured) + const tableNameFromUrl = getActiveTableNameFromUrl() updateActiveTableName(tableNameFromUrl) const hiddenNodeIds = await getHiddenNodeIdsFromUrl() @@ -47,11 +53,11 @@ export const useInitialAutoLayout = () => { ? { maxZoom: 1, duration: 300, nodes: [{ id: tableNameFromUrl }] } : undefined - if (nodesInitialized) { + if (tableNodesInitialized) { handleLayout(fitViewOptions, hiddenNodeIds) } } initialize() - }, [nodesInitialized, initializeComplete, handleLayout]) + }, [initializeComplete, nodes, handleLayout]) } diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/convertDBStructureToNodes.ts b/frontend/packages/erd-core/src/components/ERDRenderer/convertDBStructureToNodes.ts index 8543ee43..8c77a721 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/convertDBStructureToNodes.ts +++ b/frontend/packages/erd-core/src/components/ERDRenderer/convertDBStructureToNodes.ts @@ -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 @@ -18,12 +20,16 @@ export const convertDBStructureToNodes = ({ } => { const tables = Object.values(dbStructure.tables) const relationships = Object.values(dbStructure.relationships) + + const tablesWithRelationships = new Set() const sourceColumns = new Map() const tableColumnCardinalities = new Map< string, Record >() for (const relationship of relationships) { + tablesWithRelationships.add(relationship.primaryTableName) + tablesWithRelationships.add(relationship.foreignTableName) sourceColumns.set( relationship.primaryTableName, relationship.primaryColumnName, @@ -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: { @@ -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, From 25726dca019aabc9758eeaa4e40ffff0a9cf3082 Mon Sep 17 00:00:00 2001 From: junkisai Date: Fri, 20 Dec 2024 16:59:46 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9B=20=20Update=20useInitialAutoLa?= =?UTF-8?q?yout=20to=20accept=20nodes=20as=20a=20parameter=20for=20improve?= =?UTF-8?q?d=20layout=20initialization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ERDRenderer/ERDContent/ERDContent.tsx | 2 +- .../ERDContent/useInitialAutoLayout.ts | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx index 26b559c3..a01153e7 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/ERDContent.tsx @@ -75,7 +75,7 @@ export const ERDContentInner: FC = ({ } = useERDContentContext() const { tableName: activeTableName } = useUserEditingActiveStore() - useInitialAutoLayout() + useInitialAutoLayout(nodes) useFitViewWhenActiveTableChange( enabledFeatures?.fitViewWhenActiveTableChange ?? true, ) diff --git a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useInitialAutoLayout.ts b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useInitialAutoLayout.ts index de800024..f91805b9 100644 --- a/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useInitialAutoLayout.ts +++ b/frontend/packages/erd-core/src/components/ERDRenderer/ERDContent/useInitialAutoLayout.ts @@ -1,8 +1,8 @@ import type { QueryParam } from '@/schemas/queryParam' import { addHiddenNodeIds, updateActiveTableName } from '@/stores' import { decompressFromEncodedURIComponent } from '@/utils' -import { useReactFlow } 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' @@ -25,9 +25,14 @@ const getHiddenNodeIdsFromUrl = async (): Promise => { return hiddenNodeIds ? hiddenNodeIds.split(',') : [] } -export const useInitialAutoLayout = () => { - const { getNodes } = useReactFlow() - const nodes = getNodes() +export const useInitialAutoLayout = (nodes: Node[]) => { + const tableNodesInitialized = useMemo( + () => + nodes + .filter((node) => node.type === 'table') + .some((node) => node.measured), + [nodes], + ) const { state: { initializeComplete }, @@ -40,10 +45,6 @@ export const useInitialAutoLayout = () => { return } - const tableNodesInitialized = nodes - .filter((node) => node.type === 'table') - .some((node) => node.measured) - const tableNameFromUrl = getActiveTableNameFromUrl() updateActiveTableName(tableNameFromUrl) const hiddenNodeIds = await getHiddenNodeIdsFromUrl() @@ -59,5 +60,5 @@ export const useInitialAutoLayout = () => { } initialize() - }, [initializeComplete, nodes, handleLayout]) + }, [tableNodesInitialized, initializeComplete, handleLayout]) }