Skip to content

Commit 5a797a4

Browse files
authored
Merge pull request #4 from ccims/feature/improve_ui
Feature/improve UI
2 parents b757cf9 + 7110317 commit 5a797a4

10 files changed

Lines changed: 334 additions & 274 deletions

File tree

.vitepress/components/GraphEditor.vue

Lines changed: 163 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,58 @@
1414
</div>
1515
</div>
1616
<div v-if="metadata" class="modal-container metadata">
17-
<p>{{ metadata }}</p>
17+
<template v-for="[key, value] in Object.entries(metadata)" :key="key">
18+
<details v-if="typeof value == 'object'" class="details custom-block">
19+
<summary>{{ key }}</summary>
20+
<pre>{{ JSON.stringify(value, null, 4) }}</pre>
21+
</details>
22+
<div v-else class="details custom-block plain">
23+
<p>
24+
<span class="title">{{ key }}</span
25+
>: {{ value }}
26+
</p>
27+
</div>
28+
</template>
29+
</div>
30+
<div class="modal-container views">
31+
<div class="views-header">
32+
<div v-if="showFilterDialog" class="title">Components</div>
33+
<IconButton
34+
:icon="showFilterDialog ? 'vpi-close' : 'vpi-filter'"
35+
label="Filter"
36+
@click="showFilterDialog = !showFilterDialog"
37+
/>
38+
</div>
39+
<template v-if="showFilterDialog">
40+
<div v-for="template in componentTemplates" :key="template" class="template-toggle">
41+
<VPSwitch
42+
:class="{ checked: !disabledComponentTemplates.has(template) }"
43+
@click="toggleComponentTemplate(template)"
44+
/><span>{{ template }}</span>
45+
</div>
46+
<div class="title">Relations</div>
47+
<div v-for="template in relationTemplates" :key="template" class="template-toggle">
48+
<VPSwitch
49+
:class="{ checked: !disabledRelationTemplates.has(template) }"
50+
@click="toggleRelationTemplate(template)"
51+
/><span>{{ template }}</span>
52+
</div>
53+
<div class="title">Misc</div>
54+
<div class="template-toggle">
55+
<VPSwitch
56+
:class="{ checked: hideDisconnected }"
57+
@click="hideDisconnected = !hideDisconnected"
58+
/>
59+
<span>Hide disconnected</span>
60+
</div>
61+
<div class="template-toggle">
62+
<VPSwitch
63+
:class="{ checked: hideInterfaces }"
64+
@click="hideInterfaces = !hideInterfaces"
65+
/>
66+
<span>Hide interfaces</span>
67+
</div>
68+
</template>
1869
</div>
1970
</div>
2071
</Pane>
@@ -29,7 +80,6 @@ import "splitpanes/dist/splitpanes.css";
2980
import { ref, onBeforeUnmount, watch, computed, toRaw, watchEffect, PropType } from "vue";
3081
import { TYPES } from "sprotty";
3182
import { RequestBoundsAction } from "sprotty-protocol";
32-
3383
import {
3484
createContainer,
3585
CreateRelationContext,
@@ -44,7 +94,7 @@ import { MonacoEditorLanguageClientWrapper, UserConfig } from "monaco-editor-wra
4494
import { shallowRef } from "vue";
4595
import { inject } from "vue";
4696
import { Disposable } from "vscode-languageserver-protocol";
47-
import { asyncComputed, refThrottled, watchImmediate } from "@vueuse/core";
97+
import { asyncComputed, refThrottled } from "@vueuse/core";
4898
import { v4 as uuid } from "uuid";
4999
import "@codingame/monaco-vscode-yaml-default-extension";
50100
import { useData } from "vitepress";
@@ -53,6 +103,9 @@ import { parseModel } from "../util/parseModel";
53103
import { settingsKey } from "../theme/settings";
54104
import { validateModel } from "../util/validateModel";
55105
import { View } from "../util/view";
106+
import VPSwitch from "vitepress/dist/client/theme-default/components/VPSwitch.vue";
107+
import { filterModel } from "../util/filterModel";
108+
import IconButton from "./IconButton.vue";
56109
57110
const id = uuid();
58111
@@ -106,7 +159,7 @@ const modelSource = shallowRef<ModelSource | undefined>();
106159
107160
const state = computed(() => {
108161
try {
109-
const parsedModel = parseModel(model.value, props.view);
162+
const parsedModel = parseModel(model.value);
110163
validateModel(parsedModel);
111164
return { parsedModel, errorMessage: null };
112165
} catch (e) {
@@ -117,12 +170,34 @@ const state = computed(() => {
117170
118171
const parsedModel = computed(() => state.value.parsedModel);
119172
const errorMessage = computed(() => state.value.errorMessage);
173+
const componentTemplates = computed(
174+
() => new Set(parsedModel.value?.components.map((component) => component.template))
175+
);
176+
const relationTemplates = computed(() => new Set(parsedModel.value?.relations.map((relation) => relation.template)));
177+
const disabledComponentTemplates = ref(new Set<string>());
178+
const disabledRelationTemplates = ref(new Set<string>());
179+
const hideDisconnected = ref(false);
180+
const hideInterfaces = ref(false);
181+
const showFilterDialog = ref(false);
182+
183+
const filteredModel = computed(() => {
184+
if (parsedModel.value == undefined) {
185+
return undefined;
186+
}
187+
return filterModel(
188+
parsedModel.value,
189+
disabledComponentTemplates.value,
190+
disabledRelationTemplates.value,
191+
hideDisconnected.value,
192+
hideInterfaces.value
193+
);
194+
});
120195
121196
const settings = inject(settingsKey);
122197
123198
const layoutServerUrl = computed(() => settings!.value.serverUrl ?? "");
124199
125-
const throttledParsedModel = refThrottled(parsedModel, 800, true, true);
200+
const throttledFilteredModel = refThrottled(filteredModel, 800, true, true);
126201
127202
const shapeGenerator = new ShapeGenerator();
128203
@@ -163,12 +238,12 @@ function enhanceModelElement(
163238
164239
const fetchResult = asyncComputed<{ data: GraphLayout; meta: any }>(
165240
async () => {
166-
const modelValue = throttledParsedModel.value;
241+
const modelValue = throttledFilteredModel.value;
167242
if (modelValue == undefined || layoutServerUrl.value == "" || modelSource.value == undefined) {
168243
return {};
169244
}
170245
const boundsRes = await modelSource.value.actionDispatcher.request(
171-
RequestBoundsAction.create((modelSource.value as any).createRoot(throttledParsedModel.value, {}, true))
246+
RequestBoundsAction.create((modelSource.value as any).createRoot(throttledFilteredModel.value, {}, true))
172247
);
173248
const sizesMap = new Map<string, { width: number; height: number }>();
174249
for (const bound of boundsRes.bounds) {
@@ -187,18 +262,34 @@ const fetchResult = asyncComputed<{ data: GraphLayout; meta: any }>(
187262
);
188263
189264
const layout = computed(() => fetchResult.value.data);
190-
const metadata = computed(() => JSON.stringify(fetchResult.value.meta, null, 4));
265+
const metadata = computed(() => fetchResult.value.meta);
191266
192267
watchEffect(() => {
193-
if (layout.value != undefined && modelSource.value != undefined && throttledParsedModel.value != null) {
268+
if (layout.value != undefined && modelSource.value != undefined && throttledFilteredModel.value != null) {
194269
modelSource.value.updateGraph({
195-
graph: throttledParsedModel.value,
270+
graph: throttledFilteredModel.value,
196271
layout: layout.value,
197272
fitToBounds: true
198273
});
199274
}
200275
});
201276
277+
function toggleComponentTemplate(template: string) {
278+
if (disabledComponentTemplates.value.has(template)) {
279+
disabledComponentTemplates.value.delete(template);
280+
} else {
281+
disabledComponentTemplates.value.add(template);
282+
}
283+
}
284+
285+
function toggleRelationTemplate(template: string) {
286+
if (disabledRelationTemplates.value.has(template)) {
287+
disabledRelationTemplates.value.delete(template);
288+
} else {
289+
disabledRelationTemplates.value.add(template);
290+
}
291+
}
292+
202293
onMounted(async () => {
203294
const wrapper = new MonacoEditorLanguageClientWrapper();
204295
disposables.value.push(wrapper);
@@ -285,10 +376,11 @@ onBeforeUnmount(() => {
285376
}
286377
287378
.modal-container {
288-
padding: 30px;
289-
padding-top: 25px;
379+
padding: 10px;
290380
border-radius: 12px;
291381
transition: all 0.3s ease;
382+
gap: 8px;
383+
display: grid;
292384
}
293385
294386
.modal-container.error {
@@ -298,15 +390,72 @@ onBeforeUnmount(() => {
298390
color: var(--vp-c-neutral);
299391
}
300392
301-
.modal-container.metadata {
393+
.modal-container.metadata,
394+
.modal-container.views {
302395
position: absolute;
303396
top: 20px;
304-
right: 20px;
305397
background-color: var(--vp-c-bg-alt);
306398
color: var(--vp-c-neutral-7);
307399
white-space: preserve;
308400
box-shadow: var(--vp-shadow-3);
309401
}
402+
403+
.modal-container.metadata {
404+
right: 20px;
405+
}
406+
407+
.modal-container.views {
408+
left: 20px;
409+
}
410+
411+
.views-header {
412+
display: flex;
413+
align-items: center;
414+
415+
.title {
416+
flex-grow: 1;
417+
}
418+
}
419+
420+
.modal-container.views .views-header :deep(.icon-button:has(.vpi-close)) {
421+
width: 20px;
422+
height: 20px;
423+
}
424+
425+
.custom-block.details {
426+
padding: 8px;
427+
}
428+
429+
.custom-block.details summary {
430+
margin: 0;
431+
}
432+
433+
.custom-block.details.plain {
434+
padding-left: 24px;
435+
}
436+
437+
.title {
438+
font-weight: 700;
439+
}
440+
441+
.template-toggle {
442+
display: flex;
443+
444+
button {
445+
margin-right: 8px;
446+
}
447+
}
448+
449+
.VPSwitch.checked :deep(.check) {
450+
transform: translateX(18px);
451+
}
452+
453+
.VPSwitch.checked {
454+
background-color: var(--vp-c-brand);
455+
transition:
456+
border-color 0.25s,
457+
background-color 0.4s ease !important;
458+
}
310459
</style>
311460
<style>
312461
.splitpanes {

.vitepress/components/Home.vue

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@
1010
<Teleport to="#export-diagram">
1111
<IconButton label="Download source" icon="vpi-download" @click="downloadSource" />
1212
</Teleport>
13-
<Teleport to="#views-list">
14-
<ViewsList v-model="view" />
15-
</Teleport>
1613
<Teleport to="#diagram-list">
1714
<DiagramList @open-diagram="code = $event" />
1815
</Teleport>
@@ -29,7 +26,6 @@ import fileSaver from "file-saver";
2926
import { serialize, deserialize } from "../util/serialization.js";
3027
import { onBeforeMount } from "vue";
3128
import DiagramList from "./DiagramList.vue";
32-
import ViewsList from "./ViewsList.vue";
3329
import { View } from "../util/view";
3430
3531
const GraphEditor = defineClientComponent(() => import("./GraphEditor.vue"));

0 commit comments

Comments
 (0)