Skip to content

Commit 55d30e4

Browse files
committed
chore: update
1 parent c8b2ca2 commit 55d30e4

File tree

2 files changed

+271
-42
lines changed

2 files changed

+271
-42
lines changed
Lines changed: 197 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,219 @@
11
<script setup lang="ts">
22
import type { ModuleListItem, SessionContext } from '~~/shared/types'
3+
import { computedWithControl } from '@vueuse/core'
34
import { hideAllPoppers, Menu as VMenu } from 'floating-vue'
5+
import Fuse from 'fuse.js'
6+
import { computed, ref, watch } from 'vue'
47
5-
defineProps<{
8+
const props = defineProps<{
69
session: SessionContext
710
modules: ModuleListItem[]
811
}>()
912
1013
const emit = defineEmits<{
1114
(e: 'close'): void
15+
(e: 'select', nodes: { start: string, end: string }): void
1216
}>()
1317
14-
function selectNode(node: ModuleListItem) {
15-
return node
18+
const searchStart = ref<{
19+
search: string
20+
selected: string | null
21+
}>({
22+
search: '',
23+
selected: null,
24+
})
25+
26+
const modulesMap = computed(() => {
27+
const map = new Map<string, ModuleListItem>()
28+
props.modules.forEach((m) => {
29+
map.set(m.id, m)
30+
})
31+
return map
32+
})
33+
34+
const startFuse = computedWithControl(
35+
() => props.modules,
36+
() => new Fuse(props.modules, {
37+
includeScore: true,
38+
keys: ['id'],
39+
ignoreLocation: true,
40+
threshold: 0.4,
41+
}),
42+
)
43+
44+
const startModules = computed(() => {
45+
if (!searchStart.value.search) {
46+
return props.modules
47+
}
48+
else {
49+
return startFuse.value
50+
.search(searchStart.value.search)
51+
.map(r => r.item)
52+
}
53+
})
54+
55+
const endModules = computed(() => {
56+
function getAllImports(moduleId: string, visited = new Set<string>()): ModuleListItem[] {
57+
if (visited.has(moduleId))
58+
return []
59+
visited.add(moduleId)
60+
61+
const module = modulesMap.value.get(moduleId)
62+
if (!module?.imports?.length)
63+
return []
64+
65+
const res: ModuleListItem[] = []
66+
67+
for (const importItem of module.imports) {
68+
const importedModule = modulesMap.value.get(importItem.module_id)
69+
if (!importedModule)
70+
continue
71+
72+
if (!visited.has(importedModule.id)) {
73+
res.push(importedModule)
74+
res.push(...getAllImports(importedModule.id, visited))
75+
}
76+
}
77+
78+
return res
79+
}
80+
81+
return searchStart.value.selected ? getAllImports(searchStart.value.selected) : []
82+
})
83+
84+
const endFuse = computedWithControl(
85+
() => endModules.value,
86+
() => new Fuse(endModules.value, {
87+
includeScore: true,
88+
keys: ['id'],
89+
ignoreLocation: true,
90+
threshold: 0.4,
91+
}),
92+
)
93+
94+
const searchEnd = ref<{
95+
search: string
96+
selected: string | null
97+
}>({
98+
search: '',
99+
selected: null,
100+
})
101+
102+
const filteredEndModules = computed(() => {
103+
if (!searchEnd.value.search) {
104+
return endModules.value
105+
}
106+
else {
107+
return endFuse.value
108+
.search(searchEnd.value.search)
109+
.map(r => r.item)
110+
}
111+
})
112+
113+
function selectStartNode(node: ModuleListItem) {
114+
searchStart.value.selected = node.id
115+
searchStart.value.search = ''
116+
}
117+
118+
function selectEndNode(node: ModuleListItem) {
119+
searchEnd.value.selected = node.id
120+
searchEnd.value.search = ''
121+
}
122+
123+
function clearSelected(type: 'start' | 'end') {
124+
if (type === 'start') {
125+
searchStart.value.selected = null
126+
}
127+
searchEnd.value.selected = null
16128
}
129+
130+
watch([() => searchStart.value.selected, () => searchEnd.value.selected], () => {
131+
emit('select', {
132+
start: searchStart.value.selected ?? '',
133+
end: searchEnd.value.selected ?? '',
134+
})
135+
})
17136
</script>
18137

19138
<template>
20139
<div h12 px4 p2 relative flex="~ gap2 items-center">
21-
<VMenu inline :distance="15" :triggers="['click']" :auto-hide="false" :delay="{ show: 200, hide: 0 }">
22-
<input
23-
p1 px4 border="~ base rounded-1" style="outline: none"
24-
placeholder="Start"
25-
@blur="hideAllPoppers"
26-
>
27-
<template #popper>
28-
<div class="p2 w100" flex="~ col gap2">
29-
<ModulesFlatList
30-
:session="session"
31-
:modules="modules"
32-
disable-tooltip
33-
:link="false"
34-
@select="selectNode"
35-
/>
140+
<div flex="~ items-center gap2" class="flex-1" min-w-0>
141+
<div flex-1 w-0>
142+
<div v-if="searchStart.selected" w-full overflow-hidden flex="~ items-center" border="~ base rounded" p1 relative>
143+
<div overflow-hidden text-ellipsis pr6 py0.5 w-0 flex-1>
144+
<DisplayModuleId
145+
:id="searchStart.selected"
146+
:session="session"
147+
block text-nowrap
148+
:link="false"
149+
:disable-tooltip="true"
150+
/>
151+
</div>
152+
<button i-carbon-clean text-4 hover="op100" op50 title="Clear" absolute right-2 @click="clearSelected('start')" />
36153
</div>
37-
</template>
38-
</VMenu>
39-
<div class="i-carbon-arrow-right op50" />
40-
<VMenu inline :distance="15" :triggers="['click']" :auto-hide="false" :delay="{ show: 200, hide: 0 }">
41-
<input
42-
p1 px4 border="~ base rounded-1" style="outline: none"
43-
placeholder="End"
44-
@blur="hideAllPoppers"
45-
>
46-
<template #popper>
47-
<div class="p2 w100" flex="~ col gap2">
48-
<ModulesFlatList
49-
:session="session"
50-
:modules="modules"
51-
disable-tooltip
52-
:link="false"
53-
@select="selectNode"
54-
/>
154+
<VMenu v-else :distance="15" :triggers="['click']" :auto-hide="false" :delay="{ show: 300, hide: 120 }">
155+
<input
156+
v-model="searchStart.search"
157+
p1 px4 w-full border="~ base rounded-1" style="outline: none"
158+
placeholder="Start"
159+
@blur="hideAllPoppers"
160+
>
161+
<template #popper>
162+
<div class="p2 w100" flex="~ col gap2">
163+
<ModulesFlatList
164+
:session="session"
165+
:modules="startModules"
166+
disable-tooltip
167+
:link="false"
168+
@select="selectStartNode"
169+
/>
170+
</div>
171+
</template>
172+
</VMenu>
173+
</div>
174+
<div class="i-carbon-arrow-right op50" flex-shrink-0 />
175+
176+
<div flex-1 w-0>
177+
<div v-if="searchEnd.selected" w-full overflow-hidden flex="~ items-center" border="~ base rounded" p1 relative>
178+
<div overflow-hidden text-ellipsis pr6 py0.5 w-0 flex-1>
179+
<DisplayModuleId
180+
:id="searchEnd.selected"
181+
:session="session"
182+
block text-nowrap
183+
:link="false"
184+
:disable-tooltip="true"
185+
/>
186+
</div>
187+
<button i-carbon-clean text-4 hover="op100" op50 title="Clear" absolute right-2 @click="clearSelected('end')" />
55188
</div>
56-
</template>
57-
</VMenu>
189+
<VMenu v-else :distance="15" :triggers="['click']" :auto-hide="false" :delay="{ show: 300, hide: 120 }">
190+
<input
191+
v-model="searchEnd.search"
192+
p1 px4 w-full border="~ base rounded-1" style="outline: none"
193+
placeholder="End"
194+
@blur="hideAllPoppers"
195+
>
196+
<template #popper>
197+
<div class="p2 w100" flex="~ col gap2">
198+
<ModulesFlatList
199+
v-if="filteredEndModules.length"
200+
:session="session"
201+
:modules="filteredEndModules"
202+
disable-tooltip
203+
:link="false"
204+
@select="selectEndNode"
205+
/>
206+
<div v-else flex="~ items-center justify-center" w-full h-20>
207+
<span italic op50>
208+
No modules
209+
</span>
210+
</div>
211+
</div>
212+
</template>
213+
</VMenu>
214+
</div>
215+
</div>
58216

59-
<DisplayCloseButton class="absolute right-2" @click="emit('close')" />
217+
<DisplayCloseButton class="mr--2" @click="emit('close')" />
60218
</div>
61219
</template>

packages/vite/src/app/pages/session/[session]/graph/index.vue

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,18 @@ const props = defineProps<{
1616
1717
const route = useRoute()
1818
const router = useRouter()
19-
const navigatorVisible = ref(false)
19+
const navigatorVisible = ref(true)
20+
const navigatorNode = ref({
21+
start: '',
22+
end: '',
23+
})
24+
25+
function selectNavigatorNodes(nodes: { start: string, end: string }) {
26+
navigatorNode.value = {
27+
start: nodes.start,
28+
end: nodes.end,
29+
}
30+
}
2031
2132
const searchValue = ref<{
2233
search: string | false
@@ -116,6 +127,66 @@ const searched = computed(() => {
116127
.map(r => r.item)
117128
})
118129
130+
const filteredGraph = computed(() => {
131+
const { start, end } = navigatorNode.value
132+
if (!start && !end)
133+
return searched.value
134+
135+
const modulesMap = new Map(props.session.modulesList.map(m => [m.id, m]))
136+
const linkedNodes = new Set<string>()
137+
138+
const bfs = (startId: string, getNext: (id: string) => string[], stopAt?: string) => {
139+
const queue = [startId]
140+
const visited = new Set<string>()
141+
const pathMap = new Map<string, string[]>([[startId, [startId]]])
142+
143+
while (queue.length > 0) {
144+
const id = queue.shift()!
145+
if (visited.has(id))
146+
continue
147+
visited.add(id)
148+
149+
if (stopAt) {
150+
if (id === stopAt)
151+
pathMap.get(id)?.forEach(nodeId => linkedNodes.add(nodeId))
152+
}
153+
else {
154+
linkedNodes.add(id)
155+
}
156+
157+
if (!stopAt || id !== stopAt) {
158+
getNext(id).forEach((nextId) => {
159+
if (!visited.has(nextId)) {
160+
queue.push(nextId)
161+
if (stopAt)
162+
pathMap.set(nextId, [...(pathMap.get(id) || []), nextId])
163+
}
164+
})
165+
}
166+
}
167+
}
168+
169+
if (start && end) {
170+
bfs(start, id => modulesMap.get(id)?.imports.map(imp => imp.module_id) || [], end)
171+
}
172+
else if (start) {
173+
bfs(start, id => modulesMap.get(id)?.imports.map(imp => imp.module_id) || [])
174+
}
175+
else if (end) {
176+
bfs(end, id => modulesMap.get(id)?.importers || [])
177+
}
178+
179+
return filtered.value.filter(x => linkedNodes.has(x.id)).map((m) => {
180+
if (m.id === start) {
181+
return {
182+
...m,
183+
importers: [],
184+
}
185+
}
186+
return m
187+
})
188+
})
189+
119190
function toggleDisplay(type: ClientSettings['moduleGraphViewType']) {
120191
if (route.query.module) {
121192
router.replace({ query: { ...route.query, module: undefined } })
@@ -134,7 +205,7 @@ function toggleNavigator(state: boolean) {
134205
<div absolute left-4 top-4 z-panel-nav>
135206
<DataSearchPanel v-model="searchValue" :rules="searchFilterTypes">
136207
<template v-if="navigatorVisible" #search>
137-
<ModulesGraphNavigator :session="session" :modules="searched" @close="toggleNavigator(false)" />
208+
<ModulesGraphNavigator :session="session" :modules="searched" @select="selectNavigatorNodes" @close="toggleNavigator(false)" />
138209
</template>
139210
<template #search-end>
140211
<div h12 mr2 flex="~ items-center">
@@ -190,7 +261,7 @@ function toggleNavigator(state: boolean) {
190261
<template v-else-if="settings.moduleGraphViewType === 'graph'">
191262
<ModulesGraph
192263
:session="session"
193-
:modules="searched"
264+
:modules="filteredGraph"
194265
/>
195266
</template>
196267
<template v-else>

0 commit comments

Comments
 (0)