feat(db-playground): animated edge path length

This commit is contained in:
Greg Richardson committed 2024-06-13 11:25:13 -06:00
1 parent debc6569c2
commit 0fb5faf114
2 files changed
+96 -16

No files matched your search

@@ -12,7 +12,7 @@ import ReactFlow, {
useReactFlow,
} from 'reactflow'
import SchemaGraphLegend from './legend'
import { TABLE_NODE_ROW_HEIGHT, TABLE_NODE_WIDTH, TableNode } from './table-node'
import { TABLE_NODE_ROW_HEIGHT, TABLE_NODE_WIDTH, TableEdge, TableNode } from './table-node'
export default function TablesGraph({ tables }: { tables: PostgresTable[] }) {
const { resolvedTheme } = useTheme()
@@ -24,6 +24,12 @@ export default function TablesGraph({ tables }: { tables: PostgresTable[] }) {
}),
[]
)
const edgeTypes = useMemo(
() => ({
table: TableEdge,
}),
[]
)
useEffect(() => {
getGraphDataFromTables(tables).then(({ nodes, edges }) => {
@@ -49,14 +55,17 @@ export default function TablesGraph({ tables }: { tables: PostgresTable[] }) {
defaultEdges={[]}
defaultEdgeOptions={{
type: 'smoothstep',
animated: true,
deletable: false,
style: {
stroke: 'hsl(var(--border-stronger))',
strokeWidth: 0.5,
strokeWidth: 1,
strokeDasharray: 5,
// Manually create animation so that it doesn't interfere with our custom edge component
animation: 'dashdraw 0.5s linear infinite',
},
}}
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
minZoom={0.8}
maxZoom={1.8}
proOptions={{ hideAttribution: true }}
@@ -103,18 +112,20 @@ async function getGraphDataFromTables(tables: PostgresTable[]): Promise<{
}
const nodes = tables.map((table) => {
const columns = (table.columns || []).map((column) => {
return {
id: column.id,
isPrimary: table.primary_keys.some((pk) => pk.name === column.name),
name: column.name,
format: column.format,
isNullable: column.is_nullable,
isUnique: column.is_unique,
isUpdateable: column.is_updatable,
isIdentity: column.is_identity,
}
})
const columns = (table.columns || [])
.sort((a, b) => a.ordinal_position - b.ordinal_position)
.map((column) => {
return {
id: column.id,
isPrimary: table.primary_keys.some((pk) => pk.name === column.name),
name: column.name,
format: column.format,
isNullable: column.is_nullable,
isUnique: column.is_unique,
isUpdateable: column.is_updatable,
isIdentity: column.is_identity,
}
})
return {
id: `${table.id}`,
@@ -163,6 +174,7 @@ async function getGraphDataFromTables(tables: PostgresTable[]): Promise<{
if (source) {
edges.push({
id: String(rel.id),
type: 'table',
source,
sourceHandle,
target: rel.constraint_name,
@@ -188,6 +200,7 @@ async function getGraphDataFromTables(tables: PostgresTable[]): Promise<{
if (source && target) {
edges.push({
id: String(rel.id),
type: 'table',
source,
sourceHandle,
target,
@@ -1,7 +1,14 @@
import { AnimatePresence, m } from 'framer-motion'
import { DiamondIcon, Fingerprint, Hash, Key, Table2 } from 'lucide-react'
import { useState } from 'react'
import { Handle, NodeProps, useUpdateNodeInternals } from 'reactflow'
import {
EdgeProps,
Handle,
NodeProps,
Position,
getSmoothStepPath,
useUpdateNodeInternals,
} from 'reactflow'
import { cn } from 'ui'
// ReactFlow is scaling everything by the factor of 2
@@ -33,6 +40,9 @@ const inOutTop = {
},
}
/**
* Custom node to display database tables.
*/
export const TableNode = ({
id,
data,
@@ -258,3 +268,60 @@ export const TableNode = ({
</m.div>
)
}
/**
* Custom edge that animates its path length.
*/
export function TableEdge({
id,
sourceX,
sourceY,
targetX,
targetY,
style,
sourcePosition = Position.Bottom,
targetPosition = Position.Top,
markerEnd,
markerStart,
pathOptions,
}: EdgeProps) {
const [path] = getSmoothStepPath({
sourceX,
sourceY,
sourcePosition,
targetX,
targetY,
targetPosition,
borderRadius: pathOptions?.borderRadius,
offset: pathOptions?.offset,
})
return (
<>
<defs>
{/* Create a mask with the same shape that animates its path length */}
<mask id={`mask-${id}`}>
<m.path
d={path}
fill="none"
stroke="white"
strokeWidth={10}
initial={{ pathLength: 0 }}
animate={{ pathLength: 1 }}
transition={{ duration: 0.25 }}
/>
</mask>
</defs>
<path
id={id}
d={path}
style={style}
className={cn(['react-flow__edge-path'])}
fill="none"
mask={`url(#mask-${id})`}
markerEnd={markerEnd}
markerStart={markerStart}
/>
</>
)
}