Skip to content

Commit 5d9763e

Browse files
authored
Merge pull request #177 from ls1intum/fix/handling-diagramTypes
Fix/handling diagram types
2 parents f89505d + 8d89541 commit 5d9763e

23 files changed

+295
-98
lines changed

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v20.18.0
1+
v22.14.0

library/lib/App.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import "@xyflow/react/dist/style.css"
1616
import "@/styles/app.css"
1717
import { useDiagramStore, useMetadataStore } from "./store/context"
1818
import { useShallow } from "zustand/shallow"
19-
import { ApollonMode, DiagramType } from "./types"
19+
import { ApollonMode } from "./types"
2020
import {
2121
MIN_SCALE_TO_ZOOM_OUT,
2222
MAX_SCALE_TO_ZOOM_IN,
@@ -75,9 +75,7 @@ function App({ onReactFlowInit }: AppProps) {
7575
flexGrow: 1,
7676
}}
7777
>
78-
{diagramMode === ApollonMode.Modelling && (
79-
<Sidebar selectedDiagramType={DiagramType.ClassDiagram} />
80-
)}
78+
{diagramMode === ApollonMode.Modelling && <Sidebar />}
8179
<SvgMarkers />
8280
<ReactFlow
8381
id={`react-flow-library-${diagramId}`}

library/lib/components/Sidebar.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import React, { FC, useCallback, useEffect, useState } from "react"
1+
import React, { useCallback, useEffect, useState } from "react"
22
import {
33
DropElementConfig,
44
dropElementConfigs,
55
transformScale,
66
} from "@/constants/dropElementConfig"
77
import { DividerLine } from "./DividerLine"
8-
import { DiagramType, DropNodeData } from "@/types"
8+
import { DropNodeData } from "@/types"
99
import { createPortal } from "react-dom"
1010
import { useReactFlow, type Node } from "@xyflow/react"
1111
import { MOUSE_UP_OFFSET_IN_PIXELS, SNAP_TO_GRID_PX } from "@/constants"
1212
import { generateUUID, getPositionOnCanvas, resizeAllParents } from "@/utils"
13-
import { useDiagramStore } from "@/store/context"
13+
import { useDiagramStore, useMetadataStore } from "@/store/context"
1414
import { useShallow } from "zustand/shallow"
1515

1616
/* ========================================================================
@@ -30,11 +30,14 @@ const enableScroll = () => {
3030
Sidebar Component
3131
Renders the draggable elements based on the selected diagram type.
3232
======================================================================== */
33-
interface SidebarProps {
34-
selectedDiagramType: DiagramType
35-
}
3633

37-
export const Sidebar: FC<SidebarProps> = ({ selectedDiagramType }) => {
34+
export const Sidebar = () => {
35+
const diagramType = useMetadataStore(useShallow((state) => state.diagramType))
36+
37+
if (dropElementConfigs[diagramType].length === 0) {
38+
return null
39+
}
40+
3841
return (
3942
<aside style={{ height: "100%", backgroundColor: "#f0f0f0" }}>
4043
<div
@@ -45,7 +48,7 @@ export const Sidebar: FC<SidebarProps> = ({ selectedDiagramType }) => {
4548
margin: "10px",
4649
}}
4750
>
48-
{dropElementConfigs[selectedDiagramType].map((config) => (
51+
{dropElementConfigs[diagramType].map((config) => (
4952
<React.Fragment key={`${config.type}_${config.defaultData.name}`}>
5053
{config.type === "colorDescription" && (
5154
<DividerLine style={{ margin: "3px 0" }} height={2} />

library/lib/constants/dropElementConfig.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22
import { ClassSVG, PackageSVG } from "@/components"
33
import { generateUUID } from "@/utils"
4-
import { ClassType, DiagramType } from "@/types"
4+
import { ClassType, UMLDiagramType } from "@/types"
55
import { DiagramNodeTypeKeys } from "@/nodes"
66

77
export * from "./layoutConstants"
@@ -17,8 +17,8 @@ export type DropElementConfig = {
1717
svg: React.FC<any>
1818
}
1919

20-
export const dropElementConfigs: Record<DiagramType, DropElementConfig[]> = {
21-
[DiagramType.ClassDiagram]: [
20+
export const dropElementConfigs: Record<UMLDiagramType, DropElementConfig[]> = {
21+
[UMLDiagramType.ClassDiagram]: [
2222
{
2323
type: "package",
2424
width: droppedElementWidth,
@@ -78,7 +78,7 @@ export const dropElementConfigs: Record<DiagramType, DropElementConfig[]> = {
7878
svg: (props) => <ClassSVG {...props} />,
7979
},
8080
],
81-
[DiagramType.ObjectDiagram]: [
81+
[UMLDiagramType.ObjectDiagram]: [
8282
{
8383
type: "class",
8484
width: droppedElementWidth,
@@ -92,4 +92,14 @@ export const dropElementConfigs: Record<DiagramType, DropElementConfig[]> = {
9292
svg: (props) => <ClassSVG {...props} />,
9393
},
9494
],
95+
[UMLDiagramType.ActivityDiagram]: [],
96+
[UMLDiagramType.BPMN]: [],
97+
[UMLDiagramType.CommunicationDiagram]: [],
98+
[UMLDiagramType.ComponentDiagram]: [],
99+
[UMLDiagramType.DeploymentDiagram]: [],
100+
[UMLDiagramType.PetriNet]: [],
101+
[UMLDiagramType.ReachabilityGraph]: [],
102+
[UMLDiagramType.SyntaxTree]: [],
103+
[UMLDiagramType.UseCaseDiagram]: [],
104+
[UMLDiagramType.Flowchart]: [],
95105
}

library/lib/index.tsx

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@ import {
1111
mapFromReactFlowNodeToApollonNode,
1212
mapFromReactFlowEdgeToApollonEdge,
1313
} from "./utils"
14-
import { DiagramType } from "./types"
15-
export * from "./types"
16-
import { ApollonDiagram, ApollonOptions } from "./types/EditorOptions"
14+
import { UMLDiagramType } from "./types"
15+
import { ApollonOptions, UMLModel } from "./types/EditorOptions"
1716
import {
1817
createDiagramStore,
1918
DiagramStore,
@@ -31,6 +30,12 @@ import { StoreApi } from "zustand"
3130
import { createPopoverStore } from "./store"
3231
import { PopoverStore } from "./store/popoverStore"
3332

33+
export * from "./types"
34+
35+
type Subscribers = {
36+
[key: number]: () => void
37+
}
38+
3439
export class Apollon2 {
3540
private root: ReactDOM.Root
3641
private reactFlowInstance: ReactFlowInstance | null = null
@@ -39,7 +44,7 @@ export class Apollon2 {
3944
private readonly diagramStore: StoreApi<DiagramStore>
4045
private readonly metadataStore: StoreApi<MetadataStore>
4146
private readonly popoverStore: StoreApi<PopoverStore>
42-
47+
private subscribers: Subscribers = {}
4348
constructor(element: HTMLElement, options?: ApollonOptions) {
4449
if (!(element instanceof HTMLElement)) {
4550
throw new Error("Element is required to initialize Apollon2")
@@ -58,7 +63,6 @@ export class Apollon2 {
5863
const diagramId =
5964
options?.model?.id || Math.random().toString(36).substring(2, 15)
6065

61-
console.log("Diagram constructor called with diagramId", diagramId)
6266
// Initialize React root
6367
this.root = ReactDOM.createRoot(element, {
6468
identifierPrefix: `apollon2-${diagramId}`,
@@ -68,7 +72,7 @@ export class Apollon2 {
6872

6973
// Initialize metadata and diagram type
7074
const diagramName = options?.model?.title || "Untitled Diagram"
71-
const diagramType = options?.model?.type || DiagramType.ClassDiagram
75+
const diagramType = options?.model?.type || UMLDiagramType.ClassDiagram
7276
this.metadataStore
7377
.getState()
7478
.updateMetaData(diagramName, parseDiagramType(diagramType))
@@ -131,10 +135,14 @@ export class Apollon2 {
131135
return this.reactFlowInstance ? this.reactFlowInstance.getEdges() : []
132136
}
133137

138+
set diagramType(type: UMLDiagramType) {
139+
this.metadataStore.getState().updateDiagramType(type)
140+
this.diagramStore.getState().setNodesAndEdges([], [])
141+
}
142+
134143
public dispose() {
135144
const diagramId = this.diagramStore.getState().diagramId
136145
console.log("Disposing Apollon2 instance with diagramId", diagramId)
137-
138146
try {
139147
this.syncManager.stopSync()
140148
this.root.unmount()
@@ -214,15 +222,21 @@ export class Apollon2 {
214222
}
215223
}
216224

217-
public subscribeToModalNodeEdgeChange(
225+
private getNewSubscriptionId(subscribers: Subscribers): number {
226+
// largest key + 1
227+
if (Object.keys(subscribers).length === 0) return 0
228+
return Math.max(...Object.keys(subscribers).map((key) => parseInt(key))) + 1
229+
}
230+
231+
public subscribeToModelChange(
218232
callback: (state: DiagramStoreData) => void
219-
) {
220-
return this.diagramStore.subscribe((state) =>
221-
callback({
222-
nodes: state.nodes,
223-
edges: state.edges,
224-
})
233+
): number {
234+
const subscriberId = this.getNewSubscriptionId(this.subscribers)
235+
const unsubscribeCallback = this.diagramStore.subscribe(() =>
236+
callback(this.getDiagram())
225237
)
238+
this.subscribers[subscriberId] = unsubscribeCallback
239+
return subscriberId
226240
}
227241

228242
public subscribeToDiagramNameChange(
@@ -248,12 +262,12 @@ export class Apollon2 {
248262
return { diagramTitle, diagramType }
249263
}
250264

251-
public getDiagram(): ApollonDiagram {
265+
public getDiagram(): UMLModel {
252266
const { nodes, edges, diagramId } = this.diagramStore.getState()
253267
const { diagramTitle, diagramType } = this.metadataStore.getState()
254268
return {
255269
id: diagramId,
256-
version: "2.0",
270+
version: "4.0.0",
257271
title: diagramTitle,
258272
type: diagramType,
259273
nodes: nodes.map((node) => mapFromReactFlowNodeToApollonNode(node)),

library/lib/store/diagramStore.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ export type DiagramStore = {
4141
edges: Edge[]
4242
interactiveElementId: string | null
4343
diagramId: string
44-
setDiagramId: (diagramId: string) => void
4544
assessments: Record<string, Assessment>
45+
setDiagramId: (diagramId: string) => void
4646
setNodes: (payload: Node[] | ((nodes: Node[]) => Node[])) => void
4747
setEdges: (payload: Edge[] | ((edges: Edge[]) => Edge[])) => void
4848
setNodesAndEdges: (nodes: Node[], edges: Edge[]) => void

library/lib/store/metadataStore.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,35 @@ import { devtools, subscribeWithSelector } from "zustand/middleware"
33
import { parseDiagramType } from "@/utils"
44
import * as Y from "yjs"
55
import { getDiagramMetadata } from "@/sync/ydoc"
6-
import { DiagramType } from "@/types"
6+
import { UMLDiagramType } from "@/types"
77
import { ApollonMode } from "@/types/EditorOptions"
88

99
export type MetadataStore = {
1010
diagramTitle: string
11-
diagramType: DiagramType
11+
diagramType: UMLDiagramType
1212
mode: ApollonMode
1313
readonly: boolean
1414
popupEnabled: boolean
1515
setPopupEnabled: (isPopupEnabled: boolean) => void
1616
setMode: (mode: ApollonMode) => void
1717
setReadonly: (readonly: boolean) => void
1818
updateDiagramTitle: (diagramTitle: string) => void
19-
updateDiagramType: (diagramType: DiagramType) => void
20-
updateMetaData: (diagramTitle: string, diagramType: DiagramType) => void
19+
updateDiagramType: (diagramType: UMLDiagramType) => void
20+
updateMetaData: (diagramTitle: string, diagramType: UMLDiagramType) => void
2121
updateMetaDataFromYjs: () => void
2222
reset: () => void
2323
}
2424

2525
type InitialMetadataState = {
2626
diagramTitle: string
27-
diagramType: DiagramType
27+
diagramType: UMLDiagramType
2828
mode: ApollonMode
2929
readonly: boolean
3030
popupEnabled: boolean
3131
}
3232
const initialMetadataState: InitialMetadataState = {
3333
diagramTitle: "Untitled Diagram",
34-
diagramType: DiagramType.ClassDiagram,
34+
diagramType: UMLDiagramType.ClassDiagram,
3535
mode: ApollonMode.Modelling,
3636
readonly: false,
3737
popupEnabled: true,

library/lib/styles/app.css

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ body {
2121
--panel-shadow: 0 0 4px 0 rgb(0 0 0 / 0.2);
2222
--text: #0a0a0a;
2323
--xy-edge-stroke: black;
24+
--xy-minimap-mask-background-color-props: #00000020;
2425
}
2526

2627
.react-flow__panel,
@@ -35,11 +36,6 @@ body {
3536
display: block;
3637
}
3738

38-
.react-flow__minimap-mask {
39-
fill: var(--background);
40-
fill-opacity: 0.85;
41-
}
42-
4339
.react-flow__node-toolbar {
4440
display: flex;
4541
gap: 8px;

library/lib/types/Assessments.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export type Assessment = {
88
elementType: string
99
score: number
1010
feedback?: string
11-
// dropInfo?: any;
11+
dropInfo?: unknown
1212
label?: string
1313
labelColor?: string
1414
correctionStatus?: FeedbackCorrectionStatus

library/lib/types/DiagramType.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
export enum DiagramType {
2-
ClassDiagram = "ClassDiagram",
3-
ObjectDiagram = "ObjectDiagram",
4-
}
1+
export type UMLDiagramType = keyof typeof UMLDiagramType
2+
3+
export const UMLDiagramType = {
4+
ClassDiagram: "ClassDiagram",
5+
ObjectDiagram: "ObjectDiagram",
6+
ActivityDiagram: "ActivityDiagram",
7+
UseCaseDiagram: "UseCaseDiagram",
8+
CommunicationDiagram: "CommunicationDiagram",
9+
ComponentDiagram: "ComponentDiagram",
10+
DeploymentDiagram: "DeploymentDiagram",
11+
PetriNet: "PetriNet",
12+
ReachabilityGraph: "ReachabilityGraph",
13+
SyntaxTree: "SyntaxTree",
14+
Flowchart: "Flowchart",
15+
BPMN: "BPMN",
16+
} as const

library/lib/types/EditorOptions.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
import { Assessment } from "./Assessments"
2-
import { DiagramType } from "./DiagramType"
2+
import { UMLDiagramType } from "./DiagramType"
3+
4+
export enum Locale {
5+
en = "en",
6+
de = "de",
7+
}
8+
9+
export enum ApollonMode {
10+
Modelling = "Modelling",
11+
Exporting = "Exporting",
12+
Assessment = "Assessment",
13+
}
314

415
export type ApollonNode = {
516
id: string
@@ -28,14 +39,20 @@ export type ApollonEdge = {
2839
[key: string]: unknown
2940
}
3041
}
31-
export type ApollonDiagram = {
42+
43+
export type Selection = {
44+
nodes: { [id: string]: boolean }
45+
edges: { [id: string]: boolean }
46+
}
47+
48+
export type UMLModel = {
49+
version: `4.${number}.${number}`
3250
id: string
33-
version: string
3451
title: string
35-
type: DiagramType
52+
type: UMLDiagramType
3653
nodes: ApollonNode[]
3754
edges: ApollonEdge[]
38-
assessments: Record<string, Assessment>
55+
assessments: { [id: string]: Assessment }
3956
}
4057

4158
export enum ApollonView {
@@ -44,15 +61,15 @@ export enum ApollonView {
4461
Highlight = "Highlight",
4562
}
4663

47-
export enum ApollonMode {
48-
Modelling = "Modelling",
49-
Exporting = "Exporting",
50-
Assessment = "Assessment",
51-
}
52-
5364
export type ApollonOptions = {
65+
type?: UMLDiagramType
5466
mode?: ApollonMode
5567
readonly?: boolean
5668
enablePopups?: boolean
57-
model?: ApollonDiagram
69+
model?: UMLModel
70+
theme?: Partial<Record<string, string>>
71+
locale?: Locale
72+
copyPasteToClipboard?: boolean
73+
colorEnabled?: boolean
74+
scale?: number
5875
}

0 commit comments

Comments
 (0)