|
| 1 | +import styles from "./DiagramNode.module.css"; |
| 2 | + |
| 3 | +interface DiagramNodeProps { |
| 4 | + label: string; |
| 5 | + onClick: () => void; |
| 6 | +} |
| 7 | + |
| 8 | +enum NodeCategory { |
| 9 | + InterfaceAdapters = "Interface Adapters", |
| 10 | + ApplicationBusinessRules = "Application Business Rules", |
| 11 | + EnterpriseBusinessRules = "Enterprise Business Rules", |
| 12 | + FrameworksAndDrivers = "Frameworks & Drivers", |
| 13 | +} |
| 14 | + |
| 15 | +const titleToCategory: Record<string, NodeCategory> = { |
| 16 | + controller: NodeCategory.InterfaceAdapters, |
| 17 | + presenter: NodeCategory.InterfaceAdapters, |
| 18 | + "view model": NodeCategory.InterfaceAdapters, |
| 19 | + "input data": NodeCategory.ApplicationBusinessRules, |
| 20 | + "input boundary": NodeCategory.ApplicationBusinessRules, |
| 21 | + "use case interactor": NodeCategory.ApplicationBusinessRules, |
| 22 | + "output data": NodeCategory.ApplicationBusinessRules, |
| 23 | + "output boundary": NodeCategory.ApplicationBusinessRules, |
| 24 | + "data access interface": NodeCategory.ApplicationBusinessRules, |
| 25 | + entities: NodeCategory.EnterpriseBusinessRules, |
| 26 | + view: NodeCategory.FrameworksAndDrivers, |
| 27 | + "data access": NodeCategory.FrameworksAndDrivers, |
| 28 | + database: NodeCategory.FrameworksAndDrivers, |
| 29 | +}; |
| 30 | + |
| 31 | +const categoryToColor: Record<NodeCategory, string> = { |
| 32 | + [NodeCategory.InterfaceAdapters]: "var(--color-brand-green)", |
| 33 | + [NodeCategory.ApplicationBusinessRules]: "var(--color-brand-pink)", |
| 34 | + [NodeCategory.EnterpriseBusinessRules]: "var(--color-brand-yellow)", |
| 35 | + [NodeCategory.FrameworksAndDrivers]: "var(--color-brand-blue)", |
| 36 | +}; |
| 37 | + |
| 38 | +export const DiagramNode = ({ label, onClick }: DiagramNodeProps) => { |
| 39 | + const category = titleToCategory[label.toLowerCase()]; |
| 40 | + if (!category) throw new Error(`DiagramNode: unknown label: "${label}"`); |
| 41 | + |
| 42 | + return ( |
| 43 | + <svg |
| 44 | + width="160" |
| 45 | + height="40" |
| 46 | + viewBox="0 0 150 36" |
| 47 | + onClick={onClick} |
| 48 | + className={styles.node} |
| 49 | + > |
| 50 | + <rect |
| 51 | + width="100%" |
| 52 | + height="100%" |
| 53 | + rx="5" |
| 54 | + ry="5" |
| 55 | + fill={categoryToColor[category]} |
| 56 | + /> |
| 57 | + <text |
| 58 | + x="50%" |
| 59 | + y="50%" |
| 60 | + textAnchor="middle" |
| 61 | + dominantBaseline="middle" |
| 62 | + className={`text-mono ${styles.nodeLabel}`} |
| 63 | + > |
| 64 | + {label} |
| 65 | + </text> |
| 66 | + </svg> |
| 67 | + ); |
| 68 | +}; |
0 commit comments