1919 <sba-panel :title =" sbomId" >
2020 <div ref =" treeContainer" class =" x-scroller" ></div >
2121 </sba-panel >
22+
23+ <div
24+ class =" flex flex-wrap items-center justify-center gap-6 p-4 bg-white rounded-lg shadow-sm border text-sm"
25+ >
26+ <div class =" flex items-center gap-2" >
27+ {{ t('instances.sbom.legend.title') }}
28+ </div >
29+ <div class =" flex items-center gap-2" >
30+ <div class =" node w-3 h-3 rounded-full shadow-sm" ></div >
31+ <span >{{ t('instances.sbom.legend.node') }}</span >
32+ </div >
33+ <div class =" flex items-center gap-2" >
34+ <div class =" node-with-children w-3 h-3 rounded-full shadow-sm" ></div >
35+ <span >{{ t('instances.sbom.legend.node_with_children') }}</span >
36+ </div >
37+ </div >
2238 </sba-instance-section >
2339</template >
2440
25- <script lang="ts">
41+ <script setup lang="ts">
2642import { debounce } from ' lodash-es' ;
2743import { computed , onMounted , ref , watch } from ' vue' ;
44+ import { useI18n } from ' vue-i18n' ;
2845
2946import SbaPanel from ' @/components/sba-panel.vue' ;
3047
@@ -41,103 +58,101 @@ import {
4158} from ' @/views/instances/sbomdependencytrees/sbomUtils' ;
4259import SbaInstanceSection from ' @/views/instances/shell/sba-instance-section.vue' ;
4360
44- export default {
45- name: ' TreeGraph' ,
46- components: { SbaInstanceSection , SbaPanel },
47- props: {
48- sbomId: {
49- type: String ,
50- required: true ,
51- },
52- instance: {
53- type: Instance ,
54- required: true ,
55- },
56- filter: {
57- type: String ,
58- default: ' ' ,
59- },
60- },
61- setup(props ) {
62- const treeContainer = ref <HTMLElement | null >(null );
63- const dependencies = ref <SbomDependency []>([]);
64- const rootNode = ref <D3DependencyTree | null >(null );
65- const error = ref <string | null >(null );
66- const isLoading = ref <boolean | null >(false );
67-
68- const normalizedData = computed (() => normalizeData (dependencies .value ));
69- const filteredData = computed (() =>
70- filterTree (normalizedData .value , props .filter ),
71- );
72-
73- const fetchSbomDependencies = async (sbomId : string ): Promise <void > => {
74- error .value = null ;
75- isLoading .value = true ;
76- try {
77- const res = await props .instance .fetchSbom (sbomId );
78- dependencies .value = res .data .dependencies ;
79- await renderTree ();
80- } catch (err ) {
81- console .warn (' Fetching sbom failed:' , err );
82- error .value = err ;
83- } finally {
84- isLoading .value = false ;
85- }
86- };
87-
88- const renderTree = async (): Promise <void > => {
89- rootNode .value = await createDependencyTree (
90- treeContainer .value ! ,
91- filteredData .value ,
92- );
93- };
94-
95- const updateTree = async (): Promise <void > => {
96- isLoading .value = true ;
97- await rerenderDependencyTree (rootNode .value , filteredData .value );
98- isLoading .value = false ;
99- };
100-
101- const rerenderOrUpdateTree = async (
102- newVal : string ,
103- oldVal : string ,
104- ): Promise <void > => {
105- if (dependencies .value .length > 0 ) {
106- if (! newVal .trim () || newVal === oldVal ) {
107- await renderTree ();
108- } else {
109- await updateTree ();
110- }
111- }
112- };
113-
114- const debouncedRerenderOrUpdateTree = debounce (rerenderOrUpdateTree , 1000 );
115-
116- watch (
117- () => props .filter ,
118- (newVal , oldVal ) => {
119- if (newVal !== oldVal && treeContainer .value !== null ) {
120- debouncedRerenderOrUpdateTree (newVal , oldVal );
121- }
122- },
123- { immediate: true },
124- );
125-
126- onMounted (() => {
127- fetchSbomDependencies (props .sbomId );
128- });
129-
130- return {
131- treeContainer ,
132- error ,
133- isLoading ,
134- };
135- },
61+ const props = defineProps <{
62+ sbomId: string ;
63+ instance: Instance ;
64+ filter? : string ;
65+ }>();
66+ const { t } = useI18n ();
67+
68+ const treeContainer = ref <HTMLElement | null >(null );
69+ const dependencies = ref <SbomDependency []>([]);
70+ const rootNode = ref <D3DependencyTree | null >(null );
71+ const error = ref <string | null >(null );
72+ const isLoading = ref <boolean | null >(false );
73+
74+ const normalizedData = computed (() => normalizeData (dependencies .value ));
75+ const filteredData = computed (() =>
76+ filterTree (normalizedData .value , props .filter || ' ' ),
77+ );
78+
79+ const fetchSbomDependencies = async (sbomId : string ): Promise <void > => {
80+ error .value = null ;
81+ isLoading .value = true ;
82+ try {
83+ const res = await props .instance .fetchSbom (sbomId );
84+ dependencies .value = res .data .dependencies ;
85+ await renderTree ();
86+ } catch (err ) {
87+ console .warn (' Fetching sbom failed:' , err );
88+ error .value = err ;
89+ } finally {
90+ isLoading .value = false ;
91+ }
92+ };
93+
94+ const renderTree = async (): Promise <void > => {
95+ rootNode .value = await createDependencyTree (
96+ treeContainer .value ! ,
97+ filteredData .value ,
98+ );
99+ };
100+
101+ const updateTree = async (): Promise <void > => {
102+ isLoading .value = true ;
103+ await rerenderDependencyTree (rootNode .value , filteredData .value );
104+ isLoading .value = false ;
105+ };
106+
107+ const rerenderOrUpdateTree = async (
108+ newVal : string ,
109+ oldVal : string ,
110+ ): Promise <void > => {
111+ if (dependencies .value .length > 0 ) {
112+ if (! newVal .trim () || newVal === oldVal ) {
113+ await renderTree ();
114+ } else {
115+ await updateTree ();
116+ }
117+ }
136118};
119+
120+ const debouncedRerenderOrUpdateTree = debounce (rerenderOrUpdateTree , 1000 );
121+
122+ watch (
123+ () => props .filter ,
124+ (newVal , oldVal ) => {
125+ if (newVal !== oldVal && treeContainer .value !== null ) {
126+ debouncedRerenderOrUpdateTree (newVal || ' ' , oldVal || ' ' );
127+ }
128+ },
129+ { immediate: true },
130+ );
131+
132+ onMounted (() => {
133+ fetchSbomDependencies (props .sbomId );
134+ });
137135 </script >
138136
139137<style scoped>
140138.x-scroller {
141- overflow-x : scroll ;
139+ overflow-x : auto ;
140+ }
141+
142+ :deep(.node ) {
143+ --color : #d0f7df ;
144+ fill : var (--color );
145+ background-color : var (--color );
146+ }
147+
148+ :deep(.node-with-children ) {
149+ --color : #91e8e0 ;
150+ fill : var (--color );
151+ background-color : var (--color );
152+ }
153+
154+ :deep(.edge ) {
155+ stroke : #cccccc ;
156+ stroke-width : 1 ;
142157}
143158 </style >
0 commit comments