-
Notifications
You must be signed in to change notification settings - Fork 26
Sequence diagram first part #2302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 40 commits
b148921
105cc68
337fdef
dd2cd69
36d7815
b281cf3
f706a7f
db87e15
ef84c78
2616724
0065abb
6933c0f
10ae9af
0a510d0
ba62324
60e4bfa
322f801
9cf0ac4
338d370
3b206eb
9e77b04
be36fd2
c2f286e
83592dc
0a07896
6a3fdab
b2e1204
72df26f
a89730a
90a0052
eb5ffa5
7350293
19171da
f24f540
a9b6488
5108f2a
39929c2
29f435a
2de64d6
c926f54
0b0f590
e94251f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ import HeartbeatsList from "./HeartbeatsList.vue"; | |
| import { ref } from "vue"; | ||
| import ConfirmDialog from "@/components/ConfirmDialog.vue"; | ||
| import ResultsCount from "../ResultsCount.vue"; | ||
| import { LogicalEndpoint } from "@/resources/Heartbeat"; | ||
|
|
||
| enum Operation { | ||
| Track = "track", | ||
|
|
@@ -35,7 +36,7 @@ async function proceedWarningDialog() { | |
|
|
||
| try { | ||
| await store.updateEndpointSettings( | ||
| filteredEndpoints.value.filter((endpoint) => (dialogWarningOperation.value === Operation.Track && !endpoint.track_instances) || (dialogWarningOperation.value === Operation.DoNotTrack && endpoint.track_instances)) | ||
| filteredEndpoints.value.filter((endpoint: LogicalEndpoint) => (dialogWarningOperation.value === Operation.Track && !endpoint.track_instances) || (dialogWarningOperation.value === Operation.DoNotTrack && endpoint.track_instances)) | ||
|
||
| ); | ||
| useShowToast(TYPE.SUCCESS, `All endpoints set to '${dialogWarningOperation.value}'`, "", false, { timeout: 1000 }); | ||
| } catch { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| <script setup lang="ts"> | ||
| import Endpoints from "./SequenceDiagram/EndpointsComponent.vue"; | ||
| import Timeline from "./SequenceDiagram/TimelineComponent.vue"; | ||
| import Handlers from "./SequenceDiagram/HandlersComponent.vue"; | ||
| import Routes from "./SequenceDiagram/RoutesComponent.vue"; | ||
| import { useSequenceDiagramStore } from "@/stores/SequenceDiagramStore"; | ||
| import { storeToRefs } from "pinia"; | ||
| const store = useSequenceDiagramStore(); | ||
| store.setConversationId("39907d51-12e5-4202-82c3-b2b30077ebd4"); | ||
| const { maxWidth, maxHeight } = storeToRefs(store); | ||
| </script> | ||
|
|
||
| <template> | ||
| <div class="outer"> | ||
| <svg class="sequence-diagram" :width="`max(100%, ${isNaN(maxWidth) ? 0 : maxWidth}px)`" :height="maxHeight + 20"> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we be rendering at all if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will add this to the latest branch rather than this one |
||
| <Endpoints /> | ||
| <Timeline /> | ||
| <Handlers /> | ||
| <Routes /> | ||
| </svg> | ||
| </div> | ||
| </template> | ||
|
|
||
| <style scoped> | ||
| .outer { | ||
| max-width: 100%; | ||
| max-height: calc(100vh - 27em); | ||
| overflow: auto; | ||
| } | ||
| .sequence-diagram { | ||
| --error: red; | ||
| --gray20: #333333; | ||
| --gray30: #444444; | ||
| --gray40: #666666; | ||
| --gray60: #999999; | ||
| --gray80: #cccccc; | ||
| --gray90: #e6e6e6; | ||
| --gray95: #b3b3b3; | ||
| --highlight: #0b6eef; | ||
| --highlight-background: #c5dee9; | ||
| background: white; | ||
| } | ||
| </style> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| <script setup lang="ts"> | ||
| import { Endpoint } from "@/resources/SequenceDiagram/Endpoint"; | ||
| import { EndpointCentrePoint, useSequenceDiagramStore } from "@/stores/SequenceDiagramStore"; | ||
| import { storeToRefs } from "pinia"; | ||
| import { computed, ref, watch } from "vue"; | ||
| interface EndpointWithLocation extends Endpoint { | ||
| width: number; | ||
| textWidth: number; | ||
| x?: number; | ||
| surround: EndpointSurround; | ||
| } | ||
| interface EndpointSurround { | ||
| x: number; | ||
| y: number; | ||
| width: number; | ||
| height: number; | ||
| fill: string; | ||
| rx: string; | ||
| strokeWidth: string; | ||
| stroke: string; | ||
| } | ||
| const Endpoint_Width = 260; | ||
| const Endpoint_Gap = 30; | ||
| const Endpoint_Image_Width = 20; | ||
| const store = useSequenceDiagramStore(); | ||
| const { endpoints } = storeToRefs(store); | ||
| const epRefs = ref<SVGTextElement[]>([]); | ||
| const endpointItems = computed(() => | ||
| endpoints.value.map((x, index) => { | ||
| const endpoint = x as EndpointWithLocation; | ||
| const el = epRefs.value[index]; | ||
| if (el) { | ||
| const bounds = el.getBBox(); | ||
| const previousEndpoint = index > 0 ? endpointItems.value[index - 1] : undefined; | ||
| endpoint.width = Math.max(Endpoint_Width, bounds.width); | ||
| endpoint.textWidth = bounds.width; | ||
| endpoint.x = (previousEndpoint?.x ?? Endpoint_Width / 2) + (previousEndpoint?.width ?? 0) + Endpoint_Gap; | ||
| if (!endpoint.surround && el.isConnected) { | ||
| const style = getComputedStyle(el); | ||
| const padding_top = parseInt(style.getPropertyValue("padding-top")); | ||
| const padding_left = parseInt(style.getPropertyValue("padding-left")); | ||
| const padding_right = parseInt(style.getPropertyValue("padding-right")); | ||
| const padding_bottom = parseInt(style.getPropertyValue("padding-bottom")); | ||
| endpoint.surround = { | ||
| x: endpoint.x - endpoint.width / 2 - padding_left, | ||
| y: bounds.y - padding_top, | ||
| width: endpoint.width + padding_left + padding_right, | ||
| height: bounds.height + padding_top + padding_bottom, | ||
| fill: style.getPropertyValue("background-color"), | ||
| rx: style.getPropertyValue("border-radius"), | ||
| strokeWidth: style.getPropertyValue("border-top-width"), | ||
| stroke: style.getPropertyValue("border-top-color"), | ||
| }; | ||
| } | ||
| } | ||
| return endpoint; | ||
| }) | ||
| ); | ||
| watch(endpointItems, () => { | ||
| store.setEndpointCentrePoints(endpointItems.value.map((endpoint) => ({ name: endpoint.name, centre: endpoint.x ?? 0, top: (endpoint.surround?.y ?? 0) + (endpoint.surround?.height ?? 0) + 15 }) as EndpointCentrePoint)); | ||
| const lastEndpoint = endpointItems.value[endpointItems.value.length - 1]; | ||
| store.setMaxWidth((lastEndpoint.x ?? 0) + lastEndpoint.width); | ||
| }); | ||
| function setEndpointRef(el: SVGTextElement, index: number) { | ||
| if (el) epRefs.value[index] = el; | ||
| } | ||
| </script> | ||
|
|
||
| <template> | ||
| <g v-for="(endpoint, i) in endpointItems" :key="endpoint.name" transform="translate(0,15)"> | ||
| <rect | ||
| v-if="endpoint.surround" | ||
| :x="endpoint.surround.x" | ||
| :y="endpoint.surround.y" | ||
| :width="endpoint.surround.width" | ||
| :height="endpoint.surround.height" | ||
| :fill="endpoint.surround.fill" | ||
| :rx="endpoint.surround.rx" | ||
| :stroke-width="endpoint.surround.strokeWidth" | ||
| :stroke="endpoint.surround.stroke" | ||
| ></rect> | ||
| <g :transform="`translate(${(endpoint.x ?? Endpoint_Width / 2) - ((endpoint.textWidth ?? 0) + Endpoint_Image_Width) / 2},0)`"> | ||
PhilBastian marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| <path fill="var(--gray40)" d="M 0,0 M 18,18 M 0,2 v 14 h 14 v -4 h -6 v -6 h 6 v -4 h -14 M 9,7 v 4 h 9 v -4"></path> | ||
| <text :x="Endpoint_Image_Width" y="10" alignment-baseline="middle" text-anchor="start" :ref="(el) => setEndpointRef(el as SVGTextElement, i)">{{ endpoint.name }}</text> | ||
PhilBastian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </g> | ||
| </g> | ||
| </template> | ||
|
|
||
| <style scoped> | ||
| text { | ||
| background: var(--gray90); | ||
| border-radius: 5px; | ||
| padding: 0.5em; | ||
| } | ||
| </style> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| <script setup lang="ts"> | ||
| import { HandlerState } from "@/resources/SequenceDiagram/Handler"; | ||
| import { computed, ref } from "vue"; | ||
| import { Direction } from "@/resources/SequenceDiagram/RoutedMessage"; | ||
| import { useSequenceDiagramStore } from "@/stores/SequenceDiagramStore"; | ||
| import { storeToRefs } from "pinia"; | ||
| const Height_Per_Out = 40; | ||
| const Handler_Gap = 20; | ||
| const Handler_Width = 14; | ||
| const store = useSequenceDiagramStore(); | ||
| const { handlers, endpointCentrePoints, highlightId } = storeToRefs(store); | ||
| const messageTypeRefs = ref<SVGTextElement[]>([]); | ||
| const handlerItems = computed(() => { | ||
| let nextY = 0; | ||
| const result = handlers.value.map((handler, index) => { | ||
| const endpoint = endpointCentrePoints.value.find((cp) => cp.name === handler.endpoint.name)!; | ||
| const messageTypeElement = messageTypeRefs.value[index]; | ||
| const count = handler.outMessages.length; | ||
| const height = (count === 0 ? 1 : count) * Height_Per_Out; | ||
| if (nextY === 0) nextY += Handler_Gap + (endpoint?.top ?? 0); | ||
| const y = nextY; | ||
| nextY += height + Handler_Gap; | ||
| const fill = (() => { | ||
| if (handler.id === "First") return "black"; | ||
| if (handler.state === HandlerState.Fail) return "var(--error)"; | ||
| if (handler.route?.name === highlightId.value) return "var(--highlight-background)"; | ||
| return "var(--gray60)"; | ||
| })(); | ||
| const icon = (() => { | ||
| if (handler.id === "First") return "M0,0L8,4 0,8z"; | ||
| if (handler.state === HandlerState.Fail) return "M6,0L0,6 6,12 12,6 6,0z M7,9L5,9 5,8 7,8 7,9z M5,7L5,3 7,3 7,7 5,7z"; | ||
| return null; | ||
| })(); | ||
| const iconSize = (() => { | ||
| if (handler.id === "First") return 8; | ||
| if (handler.state === HandlerState.Fail) return 12; | ||
| return 0; | ||
| })(); | ||
| return { | ||
| id: handler.id, | ||
| incomingId: handler.route?.name, | ||
| left: (endpoint?.centre ?? 0) - Handler_Width / 2, | ||
| right: (endpoint?.centre ?? 0) + Handler_Width / 2, | ||
| y, | ||
| height, | ||
| fill, | ||
| icon, | ||
| iconSize, | ||
| messageType: handler.name, | ||
| messageTypeOffset: handler.direction === Direction.Right ? ((messageTypeElement?.getBBox().width ?? 0) + 24) * -1 : 20, | ||
| messageTypeHighlight: handler.route?.name === highlightId.value, | ||
| }; | ||
| }); | ||
| store.setMaxHeight(nextY); | ||
| store.setHandlerLocations(result.map((handler) => ({ id: handler.id, left: handler.left, right: handler.right, y: handler.y, height: handler.height }))); | ||
PhilBastian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return result; | ||
| }); | ||
| function setMessageTypeRef(el: SVGTextElement, index: number) { | ||
| if (el) messageTypeRefs.value[index] = el; | ||
| } | ||
| </script> | ||
|
|
||
| <template> | ||
| <g> | ||
|
||
| <g v-for="(handler, i) in handlerItems" :key="handler.id" :transform="`translate(${handler.left}, ${handler.y})`"> | ||
| <!--Handler Activation Box--> | ||
| <rect :width="Handler_Width" :height="handler.height" :class="handler.incomingId && 'clickable'" :fill="handler.fill" @mouseover="() => store.setHighlightId(handler.incomingId)" @mouseleave="() => store.setHighlightId()" /> | ||
| <path v-if="handler.icon" :d="handler.icon" fill="white" :transform="`translate(${Handler_Width / 2 - handler.iconSize / 2}, ${handler.height / 2 - handler.iconSize / 2})`" /> | ||
| <!--Message Type and Icon--> | ||
| <g | ||
| v-if="handler.messageType" | ||
| :transform="`translate(${handler.messageTypeOffset}, 4)`" | ||
| class="clickable" | ||
| :fill="handler.messageTypeHighlight ? 'var(--highlight)' : 'var(--gray40)'" | ||
| @mouseover="() => store.setHighlightId(handler.incomingId)" | ||
| @mouseleave="() => store.setHighlightId()" | ||
| > | ||
| <path d="M9,3L9,3 9,0 0,0 0,3 4,3 4,6 0,6 0,9 4,9 4,12 0,12 0,15 9,15 9,12 5,12 5,9 9,9 9,6 5,6 5,3z" /> | ||
| <text x="14" y="10" alignment-baseline="middle" :ref="(el) => setMessageTypeRef(el as SVGTextElement, i)">{{ handler.messageType }}</text> | ||
| </g> | ||
| </g> | ||
| </g> | ||
| </template> | ||
|
|
||
| <style scoped> | ||
| .clickable { | ||
| cursor: pointer; | ||
| } | ||
| </style> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really needed?
Isn't the array typed already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it started failing my build. Will try removing it again