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";
2980import { ref , onBeforeUnmount , watch , computed , toRaw , watchEffect , PropType } from " vue" ;
3081import { TYPES } from " sprotty" ;
3182import { RequestBoundsAction } from " sprotty-protocol" ;
32-
3383import {
3484 createContainer ,
3585 CreateRelationContext ,
@@ -44,7 +94,7 @@ import { MonacoEditorLanguageClientWrapper, UserConfig } from "monaco-editor-wra
4494import { shallowRef } from " vue" ;
4595import { inject } from " vue" ;
4696import { Disposable } from " vscode-languageserver-protocol" ;
47- import { asyncComputed , refThrottled , watchImmediate } from " @vueuse/core" ;
97+ import { asyncComputed , refThrottled } from " @vueuse/core" ;
4898import { v4 as uuid } from " uuid" ;
4999import " @codingame/monaco-vscode-yaml-default-extension" ;
50100import { useData } from " vitepress" ;
@@ -53,6 +103,9 @@ import { parseModel } from "../util/parseModel";
53103import { settingsKey } from " ../theme/settings" ;
54104import { validateModel } from " ../util/validateModel" ;
55105import { 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
57110const id = uuid ();
58111
@@ -106,7 +159,7 @@ const modelSource = shallowRef<ModelSource | undefined>();
106159
107160const 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
118171const parsedModel = computed (() => state .value .parsedModel );
119172const 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
121196const settings = inject (settingsKey );
122197
123198const layoutServerUrl = computed (() => settings ! .value .serverUrl ?? " " );
124199
125- const throttledParsedModel = refThrottled (parsedModel , 800 , true , true );
200+ const throttledFilteredModel = refThrottled (filteredModel , 800 , true , true );
126201
127202const shapeGenerator = new ShapeGenerator ();
128203
@@ -163,12 +238,12 @@ function enhanceModelElement(
163238
164239const 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
189264const layout = computed (() => fetchResult .value .data );
190- const metadata = computed (() => JSON . stringify ( fetchResult .value .meta , null , 4 ) );
265+ const metadata = computed (() => fetchResult .value .meta );
191266
192267watchEffect (() => {
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+
202293onMounted (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 {
0 commit comments