0.6.0.2
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
"use client"
|
||||
import React, { memo, useCallback } from "react"
|
||||
import type { FamilyMember } from "@/types/family"
|
||||
import { FamilyNode } from "./family-node"
|
||||
|
||||
interface TreeNodeProps {
|
||||
member: FamilyMember
|
||||
spouses: FamilyMember[]
|
||||
children: FamilyMember[]
|
||||
isRoot: boolean
|
||||
isHighlighted: boolean
|
||||
highlightedMemberId?: string | null
|
||||
relationMode: boolean
|
||||
isSelected: boolean
|
||||
selectedMembers: string[]
|
||||
canEdit: boolean
|
||||
hasChildren: boolean
|
||||
isCollapsed: boolean
|
||||
collapsedNodes: Set<string>
|
||||
onNodeClick: (member: FamilyMember) => void
|
||||
onAddRelation: (member: FamilyMember, type: "child" | "spouse") => void
|
||||
onToggleCollapse: (memberId: string) => void
|
||||
onNodeRef: (memberId: string, el: HTMLDivElement | null) => void
|
||||
getMember: (id: string) => FamilyMember | undefined
|
||||
sortedChildrenMap: Map<string, string[]>
|
||||
horizontalGap: number
|
||||
verticalGap: number
|
||||
}
|
||||
|
||||
// 独立的树节点组件,使用 memo 优化渲染
|
||||
export const TreeNode = memo(function TreeNode({
|
||||
member,
|
||||
spouses,
|
||||
children,
|
||||
isRoot,
|
||||
isHighlighted,
|
||||
highlightedMemberId,
|
||||
relationMode,
|
||||
isSelected,
|
||||
selectedMembers,
|
||||
canEdit,
|
||||
hasChildren,
|
||||
isCollapsed,
|
||||
collapsedNodes,
|
||||
onNodeClick,
|
||||
onAddRelation,
|
||||
onToggleCollapse,
|
||||
onNodeRef,
|
||||
getMember,
|
||||
sortedChildrenMap,
|
||||
horizontalGap,
|
||||
verticalGap,
|
||||
}: TreeNodeProps) {
|
||||
|
||||
const handleToggleCollapse = useCallback(() => {
|
||||
onToggleCollapse(member.id)
|
||||
}, [member.id, onToggleCollapse])
|
||||
|
||||
const handleAddRelation = useCallback((type: "child" | "spouse") => {
|
||||
onAddRelation(member, type)
|
||||
}, [member, onAddRelation])
|
||||
|
||||
const handleSelect = useCallback(() => {
|
||||
onNodeClick(member)
|
||||
}, [member, onNodeClick])
|
||||
|
||||
const handleRef = useCallback((el: HTMLDivElement | null) => {
|
||||
onNodeRef(member.id, el)
|
||||
}, [member.id, onNodeRef])
|
||||
|
||||
// 计算可见的子节点
|
||||
const visibleChildren = hasChildren && !isCollapsed
|
||||
? (sortedChildrenMap.get(member.id) || [])
|
||||
.map(childId => getMember(childId))
|
||||
.filter((child): child is FamilyMember => child !== undefined)
|
||||
: []
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center">
|
||||
{/* 节点本身 */}
|
||||
<div ref={handleRef} className="relative z-10">
|
||||
<FamilyNode
|
||||
member={member}
|
||||
spouses={spouses}
|
||||
onSelect={handleSelect}
|
||||
isRoot={isRoot}
|
||||
isHighlighted={isHighlighted}
|
||||
relationMode={relationMode}
|
||||
isSelected={isSelected}
|
||||
selectedMembers={selectedMembers}
|
||||
canEdit={canEdit}
|
||||
onAddRelation={handleAddRelation}
|
||||
hasChildren={hasChildren}
|
||||
isCollapsed={isCollapsed}
|
||||
onToggleCollapse={handleToggleCollapse}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 子节点 - 只在未折叠时显示 */}
|
||||
{visibleChildren.length > 0 && (
|
||||
<div
|
||||
className="flex items-start relative"
|
||||
style={{
|
||||
marginTop: `${verticalGap}px`,
|
||||
gap: `${horizontalGap}px`
|
||||
}}
|
||||
>
|
||||
{visibleChildren.map((child) => {
|
||||
const childSpouses = child.spouseIds && child.spouseIds.length > 0
|
||||
? child.spouseIds.map(id => getMember(id)).filter((s): s is FamilyMember => s !== undefined)
|
||||
: []
|
||||
const childHasChildren = child.childrenIds && child.childrenIds.length > 0
|
||||
const childChildren = childHasChildren
|
||||
? (sortedChildrenMap.get(child.id) || [])
|
||||
.map(id => getMember(id))
|
||||
.filter((c): c is FamilyMember => c !== undefined)
|
||||
: []
|
||||
|
||||
return (
|
||||
<TreeNode
|
||||
key={child.id}
|
||||
member={child}
|
||||
spouses={childSpouses}
|
||||
children={childChildren}
|
||||
isRoot={false}
|
||||
isHighlighted={child.id === highlightedMemberId}
|
||||
highlightedMemberId={highlightedMemberId}
|
||||
relationMode={relationMode}
|
||||
isSelected={selectedMembers.includes(child.id)}
|
||||
selectedMembers={selectedMembers}
|
||||
canEdit={canEdit}
|
||||
hasChildren={childHasChildren || false}
|
||||
isCollapsed={collapsedNodes.has(child.id)}
|
||||
collapsedNodes={collapsedNodes}
|
||||
onNodeClick={onNodeClick}
|
||||
onAddRelation={onAddRelation}
|
||||
onToggleCollapse={onToggleCollapse}
|
||||
onNodeRef={onNodeRef}
|
||||
getMember={getMember}
|
||||
sortedChildrenMap={sortedChildrenMap}
|
||||
horizontalGap={horizontalGap}
|
||||
verticalGap={verticalGap}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
Reference in New Issue
Block a user