Skip to content

Commit

Permalink
Merge pull request #98 from mermaid-js/fix/alignment-issues-#88-#89
Browse files Browse the repository at this point in the history
fix: alignment issue #88 #89
  • Loading branch information
MrCoder committed Dec 7, 2023
2 parents de2bad7 + f8873db commit b58d0c5
Show file tree
Hide file tree
Showing 18 changed files with 310 additions and 115 deletions.
25 changes: 25 additions & 0 deletions cy/self-sync-message-at-root.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<title>Self Sync Message at Root</title>
<style>
body {
margin: 0; /* mostly for demo on mobile */
}
</style>
</head>
<body>
<div id="diagram" class="diagram">
<pre class="zenuml" style="margin: 0">
@Starter(A)
new B()
</pre>
</div>

<!-- built files will be auto injected -->
<script type="module" src="/src/main-cy.ts"></script>
</body>
</html>
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified cypress/e2e/__image_snapshots__/Smoke test creation #0.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified cypress/e2e/__image_snapshots__/Smoke test fragmentIssue #0.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified cypress/e2e/__image_snapshots__/Smoke test interaction #0.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified cypress/e2e/__image_snapshots__/Smoke test return #0.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions cypress/e2e/self-sync-message-at-root.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable no-undef */
import "cypress-plugin-snapshots/commands";
describe("Smoke test", function () {
it("Self Sync Message at Root", function () {
cy.visit("http://127.0.0.1:8080/cy/self-sync-message-at-root.html");
// This line is to make sure the privacy icon is loaded
cy.get(".privacy>span>svg", { timeout: 5000 }).should("be.visible");
cy.document().toMatchImageSnapshot({
imageConfig: { threshold: 0.01 },
capture: "viewport",
});
});
});
45 changes: 30 additions & 15 deletions src/components/DiagramFrame/SeqDiagram/LifeLineLayer/LifeLine.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,31 @@
class="lifeline absolute flex flex-col mx-2 transform -translate-x-1/2 h-full"
:style="{ paddingTop: top + 'px', left: left + 'px' }"
>
<div v-show="debug">{{centerOf(entity.name)}}</div>
<div v-show="debug">{{ centerOf(entity.name) }}</div>
<participant v-if="renderParticipants" :entity="entity" :offsetTop="top" />
<div v-else class="line w0 mx-auto flex-grow w-px"></div>
</div>
</template>

<script>
import parentLogger from '../../../../logger/logger';
import EventBus from '../../../../EventBus';
import { mapGetters, mapState } from 'vuex';
import Participant from './Participant.vue';
const logger = parentLogger.child({ name: 'LifeLine' });
import parentLogger from "../../../../logger/logger";
import EventBus from "../../../../EventBus";
import { mapGetters, mapState } from "vuex";
import Participant from "./Participant.vue";
const logger = parentLogger.child({ name: "LifeLine" });
export default {
name: 'life-line',
name: "life-line",
components: { Participant },
props: ['entity', 'context', 'groupLeft', 'inGroup', 'renderParticipants'],
props: ["entity", "context", "groupLeft", "inGroup", "renderParticipants"],
data: () => {
return {
translateX: 0,
top: 0,
};
},
computed: {
...mapGetters(['centerOf']),
...mapState(['scale']),
...mapGetters(["centerOf"]),
...mapState(["scale"]),
debug() {
return !!localStorage.zenumlDebug;
},
Expand All @@ -43,7 +43,10 @@ export default {
logger.debug(`nextTick after updated for ${this.entity.name}`);
});
EventBus.$on('participant_set_top', () => this.$nextTick(() => this.setTop()));
EventBus.$on("participant_set_top", () =>
// eslint-disable-next-line vue/valid-next-tick
this.$nextTick(() => this.setTop()),
);
// setTimeout( () => {
// this.setTop()
Expand All @@ -65,15 +68,23 @@ export default {
},
methods: {
onSelect() {
this.$store.commit('onSelect', this.entity.name);
this.$store.commit("onSelect", this.entity.name);
},
setTop() {
// escape entity name to avoid 'not a valid selector' error.
const escapedName = this.entity.name.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g, '\\$1');
const escapedName = this.entity.name.replace(
// eslint-disable-next-line no-useless-escape
/([ #;&,.+*~\':"!^$[\]()=>|\/@])/g,
"\\$1",
);
const $el = this.$store.getters.diagramElement;
const firstMessage = $el?.querySelector(`[data-to="${escapedName}"]`);
const isVisible = firstMessage?.offsetParent != null;
if (firstMessage && firstMessage.attributes['data-type'].value === 'creation' && isVisible) {
if (
firstMessage &&
firstMessage.attributes["data-type"].value === "creation" &&
isVisible
) {
logger.debug(`First message to ${this.entity.name} is creation`);
const rootY = this.$el.getBoundingClientRect().y;
const messageY = firstMessage.getBoundingClientRect().y;
Expand All @@ -89,7 +100,11 @@ export default {
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.lifeline .line {
background: linear-gradient(to bottom, transparent 50%, var(--color-border-base) 50%);
background: linear-gradient(
to bottom,
transparent 50%,
var(--color-border-base) 50%
);
background-size: 1px 10px;
}
</style>
35 changes: 24 additions & 11 deletions src/components/DiagramFrame/SeqDiagram/MessageLayer/Block/Block.vue
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
<template>
<div class="block">
<div class="statement-container mt-1" v-for="(stat, index) in statements" :key="index">
<Statement :context="stat" :collapsed="collapsed" :selfCallIndent="selfCallIndent" :number="getNumber(index)" />
<div
class="statement-container mt-1"
v-for="(stat, index) in statements"
:key="index"
>
<Statement
:inheritFromOccurrence="inheritFromOccurrence"
:context="stat"
:collapsed="collapsed"
:selfCallIndent="selfCallIndent"
:number="getNumber(index)"
/>
</div>
</div>
</template>

<script lang="ts" setup>
import { computed } from 'vue';
import Statement from './Statement/Statement.vue';
import { increaseNumber } from '@/utils/Numbering';
import { computed } from "vue";
import Statement from "./Statement/Statement.vue";
import { increaseNumber } from "@/utils/Numbering";
const props = defineProps<{
context?: any;
selfCallIndent?: number;
number?: string;
incremental?: boolean;
collapsed?:boolean;
}>()
const statements = computed(() => props.context?.stat() || [])
collapsed?: boolean;
inheritFromOccurrence?: boolean;
}>();
const statements = computed(() => props.context?.stat() || []);
const getNumber = (index: number) => {
if (props.number) {
return props.incremental ? increaseNumber(props.number, index) : `${props.number}.${index + 1}`
return props.incremental
? increaseNumber(props.number, index)
: `${props.number}.${index + 1}`;
}
return String(index + 1)
}
return String(index + 1);
};
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
:class="{
highlight: isCurrent,
self: isSelf,
'from-no-occurrence': providedFrom && providedFrom !== origin,
'inited-from-occurrence': isInitedFromOccurrence,
'right-to-left': rightToLeft,
}"
Expand Down Expand Up @@ -64,12 +65,18 @@ import Occurrence from "./Occurrence/Occurrence.vue";
import Message from "../Message/Message.vue";
import { mapGetters } from "vuex";
import SelfInvocation from "./SelfInvocation/SelfInvocation.vue";
import { CodeRange } from "../../../../../../../parser/CodeRange";
import { ProgContext } from "../../../../../../../parser";
import { CodeRange } from "@/parser/CodeRange";
import { ProgContext } from "@/parser";
export default {
name: "interaction",
props: ["context", "selfCallIndent", "commentObj", "number"],
props: [
"context",
"selfCallIndent",
"commentObj",
"number",
// "inheritFromOccurrence",
],
computed: {
// add tracker to the mapGetters
...mapGetters(["participants", "distance2", "cursor", "onElementClick"]),
Expand Down Expand Up @@ -134,7 +141,9 @@ export default {
// selfCallIndent is introduced for sync self interaction. Each time we enter a self sync interaction the selfCallIndent
// increases by 6px (half of the width of an execution bar). However, we set the selfCallIndent back to 0 when
// it enters a non-self sync interaction.
return this.isSelf ? (this.selfCallIndent || 0) + 6 : 0;
return this.isSelf && !this.isRootBlock
? (this.selfCallIndent || 0) + 6
: 0;
},
interactionWidth: function () {
if (this.context && this.isSelf) {
Expand All @@ -156,7 +165,7 @@ export default {
return this.isSelf ? "SelfInvocation" : "Message";
},
isInitedFromOccurrence: function () {
return this.message?.isInitedFromOccurrence(this.from);
return this.message?.isInitedFromOccurrence(this.context);
},
},
methods: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,38 @@
:data-x-offset="center"
:data-debug-center-of="computedCenter"
>
<collapse-button v-if="hasAnyStatementsExceptReturn" :collapsed="collapsed" @click="this.toggle"/>
<collapse-button
v-if="hasAnyStatementsExceptReturn"
:collapsed="collapsed"
@click="this.toggle"
/>
<block
v-if="this.context.braceBlock()"
:context="context.braceBlock().block()"
:selfCallIndent="selfCallIndent"
:number="number"
:collapsed="collapsed"
:inheritFromOccurrence="true"
></block>
</div>
</template>

<script type="text/babel">
import { mapState, mapGetters } from 'vuex';
import CollapseButton from './CollapseButton.vue';
import EventBus from '../../../../../../../../EventBus';
import { mapState, mapGetters } from "vuex";
import CollapseButton from "./CollapseButton.vue";
import EventBus from "../../../../../../../../EventBus";
export default {
name: 'occurrence',
props: ['context', 'selfCallIndent', 'participant', 'rtl', 'number'],
name: "occurrence",
props: ["context", "selfCallIndent", "participant", "rtl", "number"],
data: function () {
return {
center: 0,
collapsed: false,
};
},
computed: {
...mapGetters(['centerOf', 'messageLayerLeft']),
...mapState(['code']),
...mapGetters(["centerOf", "messageLayerLeft"]),
...mapState(["code"]),
computedCenter: function () {
try {
return this.centerOf(this.participant);
Expand All @@ -43,37 +48,37 @@ export default {
}
},
hasAnyStatementsExceptReturn: function () {
let braceBlock=this.context.braceBlock();
if(!braceBlock)return false;
let stats=(braceBlock.block()?.stat() || []);
let len=stats.length;
if(len>1)return true;
let braceBlock = this.context.braceBlock();
if (!braceBlock) return false;
let stats = braceBlock.block()?.stat() || [];
let len = stats.length;
if (len > 1) return true;
//when the only one statement is not the RetContext
if(len==1 && stats[0]['ret']()==null)return true;
if (len == 1 && stats[0]["ret"]() == null) return true;
return false;
}
},
},
// The following code will cause the Block to be created and mounted AFTER the occurrence (and upto DiagramFrame) is updated.
// Block must be defined globally to ensure that it is rendered in the same time cycle as the whole diagram.
// components: {
// Block: () => import('../../../Block.vue')
// },
methods: {
toggle($event) {
toggle() {
this.collapsed = !this.collapsed;
//update participant top in this cases: has child and sibling creation statement
//e.g. : a.call() { b = new B(); b.call() { c = new C() c.call(){return}}}
EventBus.$emit('participant_set_top');
}
EventBus.$emit("participant_set_top");
},
},
components: { CollapseButton },
watch: {
context(v) {
if(this.collapsed) {
context() {
if (this.collapsed) {
this.collapsed = false;
}
}
},
},
};
</script>
Expand All @@ -91,7 +96,11 @@ export default {
transform: translateY(1px);
}
:deep(> .statement-container:last-child > .interaction.return:last-of-type > .message) {
:deep(
> .statement-container:last-child
> .interaction.return:last-of-type
> .message
) {
bottom: -17px; /* Move the absolutely positioned return message to the bottom. -17 to offset the padding of Occurrence. */
height: 0;
}
Expand Down

0 comments on commit b58d0c5

Please sign in to comment.