Skip to content

Commit 15356bb

Browse files
authored
change endpoint name to html element so that long names can wrap (#2376)
* change endpoint name to html element so that long names can wrap * change to use background-image since safari doesn't seem to support correct positioning of mask-image * removing filter since it also doesn't work in safari in the foreignObject * add occusion on top of endpoint names * fix start height if start element isn't tallest * fix height issue introduced by toolbar change on master
1 parent a590b2c commit 15356bb

File tree

4 files changed

+38
-52
lines changed

4 files changed

+38
-52
lines changed

src/Frontend/src/components/messages2/SequenceDiagram.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ onMounted(() => store.refreshConversation());
5656
}
5757
.outer {
5858
max-width: 100%;
59-
max-height: calc(100vh - 27em);
59+
max-height: calc(100vh - 30em);
6060
overflow: auto;
6161
}
6262

src/Frontend/src/components/messages2/SequenceDiagram/EndpointsComponent.vue

Lines changed: 35 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,8 @@ import { computed, ref, watch } from "vue";
77
interface EndpointWithLocation extends Endpoint {
88
width: number;
99
textWidth: number;
10-
x?: number;
11-
surround?: EndpointSurround;
12-
}
13-
14-
interface EndpointSurround {
1510
x: number;
16-
y: number;
17-
width: number;
1811
height: number;
19-
fill: string;
20-
rx: string;
21-
strokeWidth: string;
22-
stroke: string;
2312
}
2413
2514
const Endpoint_Gap = 30;
@@ -32,80 +21,77 @@ defineProps<{
3221
const store = useSequenceDiagramStore();
3322
const { startX, endpoints } = storeToRefs(store);
3423
35-
const epTextRefs = ref<SVGTextElement[]>([]);
24+
const epTextRefs = ref<Element[]>([]);
3625
const endpointItems = computed(() =>
3726
endpoints.value.map((x, index) => {
3827
const endpoint = x as EndpointWithLocation;
3928
const el = epTextRefs.value[index];
4029
if (el) {
41-
const bounds = el.getBBox();
30+
const bounds = el.getBoundingClientRect();
4231
const previousEndpoint = index > 0 ? endpointItems.value[index - 1] : undefined;
4332
endpoint.width = Math.max(Endpoint_Width, bounds.width);
4433
endpoint.textWidth = bounds.width;
4534
endpoint.x = (previousEndpoint?.x ?? startX.value) + (previousEndpoint?.width ?? 0) + Endpoint_Gap;
46-
47-
if (!endpoint.surround && el.isConnected) {
48-
const style = getComputedStyle(el);
49-
const padding_top = parseInt(style.getPropertyValue("padding-top"));
50-
const padding_left = parseInt(style.getPropertyValue("padding-left"));
51-
const padding_right = parseInt(style.getPropertyValue("padding-right"));
52-
const padding_bottom = parseInt(style.getPropertyValue("padding-bottom"));
53-
endpoint.surround = {
54-
x: endpoint.x - endpoint.width / 2 - padding_left,
55-
y: bounds.y - padding_top,
56-
width: endpoint.width + padding_left + padding_right,
57-
height: bounds.height + padding_top + padding_bottom,
58-
fill: style.getPropertyValue("background-color"),
59-
rx: style.getPropertyValue("border-radius"),
60-
strokeWidth: style.getPropertyValue("border-top-width"),
61-
stroke: style.getPropertyValue("border-top-color"),
62-
};
63-
}
35+
endpoint.height = bounds.height;
36+
endpoint.uiRef = el;
6437
}
6538
return endpoint;
6639
})
6740
);
6841
6942
watch(endpointItems, () => {
70-
store.setEndpointCentrePoints(endpointItems.value.map((endpoint) => ({ name: endpoint.name, centre: endpoint.x, top: (endpoint.surround?.y ?? 0) + (endpoint.surround?.height ?? 0) + 15 }) as EndpointCentrePoint));
43+
store.setEndpointCentrePoints(endpointItems.value.map((endpoint) => ({ name: endpoint.name, centre: endpoint.x ?? 0, top: (endpoint.height ?? 0) + 15 }) as EndpointCentrePoint));
7144
const lastEndpoint = endpointItems.value[endpointItems.value.length - 1];
7245
store.setMaxWidth((lastEndpoint.x ?? 0) + lastEndpoint.width);
7346
});
7447
7548
watch(startX, () => {
7649
epTextRefs.value = [];
77-
endpoints.value.forEach((endpoint) => ((endpoint as EndpointWithLocation).surround = undefined));
7850
});
7951
80-
function setEndpointTextRef(el: SVGTextElement, index: number) {
52+
function setEndpointTextRef(el: Element, index: number) {
8153
if (el) epTextRefs.value[index] = el;
8254
}
8355
</script>
8456

8557
<template>
86-
<g v-for="(endpoint, i) in endpointItems" :key="endpoint.name" :transform="`translate(0,${yOffset + 15})`" :ref="(el) => (endpoint.uiRef = el as SVGElement)">
87-
<rect
88-
v-if="endpoint.surround"
89-
:x="endpoint.surround.x"
90-
:y="endpoint.surround.y"
91-
:width="endpoint.surround.width"
92-
:height="endpoint.surround.height"
93-
:fill="endpoint.surround.fill"
94-
:rx="endpoint.surround.rx"
95-
:stroke-width="endpoint.surround.strokeWidth"
96-
:stroke="endpoint.surround.stroke"
97-
></rect>
58+
<!-- occlusion for top of diagram so that elements don't show above the endpoint names when scrolling -->
59+
<rect width="100%" height="15" :transform="`translate(0,${yOffset})`" fill="white" />
60+
<g v-for="(endpoint, i) in endpointItems" :key="endpoint.name" :transform="`translate(0,${yOffset + 5})`" style="outline: none">
9861
<g :transform="`translate(${(endpoint.x ?? Endpoint_Width / 2) - ((endpoint.textWidth ?? 0) + Endpoint_Image_Width) / 2}, 0)`">
99-
<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>
100-
<text :x="Endpoint_Image_Width" y="10" alignment-baseline="middle" text-anchor="start" :ref="(el) => setEndpointTextRef(el as SVGTextElement, i)">{{ endpoint.name }}</text>
62+
<foreignObject :x="Endpoint_Image_Width" y="10" :width="Endpoint_Width" height="100%" style="pointer-events: none">
63+
<div class="endpoint-surround" :ref="(el) => setEndpointTextRef(el as Element, i)">
64+
<i class="endpoint-icon" />
65+
<div class="endpoint-name">{{ endpoint.name }}</div>
66+
</div>
67+
</foreignObject>
10168
</g>
10269
</g>
10370
</template>
10471

10572
<style scoped>
106-
text {
73+
.endpoint-surround {
74+
width: 100%;
75+
display: flex;
10776
background: var(--gray90);
10877
border-radius: 5px;
10978
padding: 0.5em;
79+
align-items: center;
80+
justify-content: center;
81+
gap: 0.5em;
82+
pointer-events: all;
83+
}
84+
85+
.endpoint-icon {
86+
flex-shrink: 0;
87+
background-image: url("@/assets/endpoint.svg");
88+
background-position: center;
89+
background-repeat: no-repeat;
90+
height: 18px;
91+
width: 18px;
92+
}
93+
94+
.endpoint-name {
95+
overflow-wrap: anywhere;
11096
}
11197
</style>

src/Frontend/src/components/messages2/SequenceDiagram/HandlersComponent.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const handlerItems = computed(() => {
2929
const messageTypeElement = messageTypeRefs.value[index];
3030
const count = handler.outMessages.length;
3131
const height = (count === 0 ? 1 : count) * Height_Per_Out;
32-
if (nextY === 0) nextY += Handler_Gap + (endpoint?.top ?? 0);
32+
if (nextY === 0) nextY += Handler_Gap + (Math.max(...endpointCentrePoints.value.map((cp) => cp.top)) ?? 0);
3333
const y = nextY;
3434
nextY += height + Handler_Gap;
3535
const fill = (() => {

src/Frontend/src/resources/SequenceDiagram/Endpoint.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export interface Endpoint {
99
readonly handlers: Handler[];
1010
readonly host: string;
1111
readonly version: string;
12-
uiRef?: SVGElement;
12+
uiRef?: Element;
1313
addHandler(handler: Handler): void;
1414
}
1515

0 commit comments

Comments
 (0)