feat: implement Stories 2.4-2.7 — E-R, Org Chart, Architecture, Sequence diagram type renderers
Adds four diagram type renderers completing the core diagram type suite: - Story 2.4: E-R entity nodes with column tables, relationship edges with cardinality labels - Story 2.5: Org chart person nodes with role/department tags, hierarchy edges - Story 2.6: Architecture nodes (service, database, queue, load balancer, external), connection edges - Story 2.7: Sequence participant nodes with lifelines + activation bars, fragment nodes, 3 custom edge types (sync/async/return), custom time-ordered layout (not ELK) Story 2.7 includes code review fixes: computeLayout returns LayoutResult so enriched sequence edges flow through useAutoLayout, activation bar computation in layout, immutable layout function, self-message U-shaped loop rendering, sequence node size tests in buildElkGraph. 476 tests passing across 29 test files, zero regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,21 @@ import {
|
||||
BpmnMessageEdge,
|
||||
BpmnAssociationEdge,
|
||||
} from "../../types/bpmn";
|
||||
import { ErEntityNode } from "../../types/er/ErEntityNode";
|
||||
import { ErRelationshipEdge } from "../../types/er/ErRelationshipEdge";
|
||||
import { OrgchartPersonNode } from "../../types/orgchart/OrgchartPersonNode";
|
||||
import { OrgchartHierarchyEdge } from "../../types/orgchart/OrgchartHierarchyEdge";
|
||||
import { ArchServiceNode } from "../../types/architecture/ArchServiceNode";
|
||||
import { ArchDatabaseNode } from "../../types/architecture/ArchDatabaseNode";
|
||||
import { ArchQueueNode } from "../../types/architecture/ArchQueueNode";
|
||||
import { ArchLoadBalancerNode } from "../../types/architecture/ArchLoadBalancerNode";
|
||||
import { ArchExternalNode } from "../../types/architecture/ArchExternalNode";
|
||||
import { ArchConnectionEdge } from "../../types/architecture/ArchConnectionEdge";
|
||||
import { SeqParticipantNode } from "../../types/sequence/SeqParticipantNode";
|
||||
import { SeqFragmentNode } from "../../types/sequence/SeqFragmentNode";
|
||||
import { SeqSyncEdge } from "../../types/sequence/SeqSyncEdge";
|
||||
import { SeqAsyncEdge } from "../../types/sequence/SeqAsyncEdge";
|
||||
import { SeqReturnEdge } from "../../types/sequence/SeqReturnEdge";
|
||||
|
||||
const nodeTypes = {
|
||||
bpmnActivity: BpmnActivityNode,
|
||||
@@ -46,21 +61,37 @@ const nodeTypes = {
|
||||
bpmnPool: BpmnPoolNode,
|
||||
bpmnLane: BpmnLaneNode,
|
||||
bpmnGroup: BpmnGroupNode,
|
||||
erEntity: ErEntityNode,
|
||||
orgchartPerson: OrgchartPersonNode,
|
||||
archService: ArchServiceNode,
|
||||
archDatabase: ArchDatabaseNode,
|
||||
archQueue: ArchQueueNode,
|
||||
archLoadBalancer: ArchLoadBalancerNode,
|
||||
archExternal: ArchExternalNode,
|
||||
seqParticipant: SeqParticipantNode,
|
||||
seqFragment: SeqFragmentNode,
|
||||
};
|
||||
|
||||
const edgeTypes = {
|
||||
bpmnSequence: BpmnSequenceEdge,
|
||||
bpmnMessage: BpmnMessageEdge,
|
||||
bpmnAssociation: BpmnAssociationEdge,
|
||||
erRelationship: ErRelationshipEdge,
|
||||
orgchartHierarchy: OrgchartHierarchyEdge,
|
||||
archConnection: ArchConnectionEdge,
|
||||
seqSync: SeqSyncEdge,
|
||||
seqAsync: SeqAsyncEdge,
|
||||
seqReturn: SeqReturnEdge,
|
||||
};
|
||||
|
||||
/** Container node types that should not participate in BFS highlighting */
|
||||
const CONTAINER_TYPES = new Set(["bpmnPool", "bpmnLane", "bpmnGroup"]);
|
||||
const CONTAINER_TYPES = new Set(["bpmnPool", "bpmnLane", "bpmnGroup", "seqFragment"]);
|
||||
|
||||
function BpmnMarkerDefs() {
|
||||
function MarkerDefs() {
|
||||
return (
|
||||
<svg style={{ position: "absolute", width: 0, height: 0 }}>
|
||||
<defs>
|
||||
{/* BPMN markers */}
|
||||
<marker
|
||||
id="bpmn-arrow-filled"
|
||||
viewBox="0 0 10 10"
|
||||
@@ -91,6 +122,67 @@ function BpmnMarkerDefs() {
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</marker>
|
||||
{/* E-R markers */}
|
||||
<marker
|
||||
id="er-arrow"
|
||||
viewBox="0 0 10 10"
|
||||
refX={10}
|
||||
refY={5}
|
||||
markerWidth={8}
|
||||
markerHeight={8}
|
||||
orient="auto-start-reverse"
|
||||
>
|
||||
<path
|
||||
d="M 0 0 L 10 5 L 0 10 Z"
|
||||
fill="var(--diagram-er, #7c3aed)"
|
||||
/>
|
||||
</marker>
|
||||
{/* Architecture markers */}
|
||||
<marker
|
||||
id="arch-arrow"
|
||||
viewBox="0 0 10 10"
|
||||
refX={10}
|
||||
refY={5}
|
||||
markerWidth={8}
|
||||
markerHeight={8}
|
||||
orient="auto-start-reverse"
|
||||
>
|
||||
<path
|
||||
d="M 0 0 L 10 5 L 0 10 Z"
|
||||
fill="var(--diagram-architecture, #71717a)"
|
||||
/>
|
||||
</marker>
|
||||
{/* Sequence markers */}
|
||||
<marker
|
||||
id="seq-arrow-filled"
|
||||
viewBox="0 0 10 10"
|
||||
refX={10}
|
||||
refY={5}
|
||||
markerWidth={8}
|
||||
markerHeight={8}
|
||||
orient="auto-start-reverse"
|
||||
>
|
||||
<path
|
||||
d="M 0 0 L 10 5 L 0 10 Z"
|
||||
fill="var(--diagram-sequence, #f59e0b)"
|
||||
/>
|
||||
</marker>
|
||||
<marker
|
||||
id="seq-arrow-open"
|
||||
viewBox="0 0 10 10"
|
||||
refX={10}
|
||||
refY={5}
|
||||
markerWidth={8}
|
||||
markerHeight={8}
|
||||
orient="auto-start-reverse"
|
||||
>
|
||||
<path
|
||||
d="M 0 0 L 10 5 L 0 10"
|
||||
fill="none"
|
||||
stroke="var(--diagram-sequence, #f59e0b)"
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
</marker>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
@@ -168,7 +260,7 @@ function CanvasInner() {
|
||||
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<BpmnMarkerDefs />
|
||||
<MarkerDefs />
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
|
||||
Reference in New Issue
Block a user