Skip to content

Commit a5d5cb8

Browse files
committed
refactor: module graph
1 parent 473f8ae commit a5d5cb8

File tree

3 files changed

+715
-4
lines changed

3 files changed

+715
-4
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
<script setup lang="ts" generic="T extends { id: string, imports: unknown[] }, I">
2+
import type { SessionContext } from '~~/shared/types'
3+
import type { ModuleGraphLink } from '~/composables/moduleGraph'
4+
import { linkHorizontal, linkVertical } from 'd3-shape'
5+
import { onMounted, unref, watch } from 'vue'
6+
import { useGraphDraggingScroll, useGraphZoom, useModuleGraph, useToggleGraphNodeExpanded } from '~/composables/moduleGraph'
7+
8+
const props = defineProps<{
9+
modules: T[]
10+
session: SessionContext
11+
}>()
12+
13+
const { isFirstCalculateGraph, childToParentMap, collapsedNodes, calculateGraph, container, width, height, scale, nodes, links, spacing, nodesRefMap } = useModuleGraph()
14+
const { isGrabbing, init } = useGraphDraggingScroll(container)
15+
const { zoomIn, zoomOut, ZOOM_MIN, ZOOM_MAX } = useGraphZoom()
16+
const { isGraphNodeToggling, toggleNode, expandAll, collapseAll } = useToggleGraphNodeExpanded({
17+
nodesRefMap,
18+
container,
19+
modules: props.modules,
20+
calculateGraph,
21+
collapsedNodes,
22+
})
23+
24+
const createLinkHorizontal = linkHorizontal()
25+
.x(d => d[0])
26+
.y(d => d[1])
27+
28+
const createLinkVertical = linkVertical()
29+
.x(d => d[0])
30+
.y(d => d[1])
31+
32+
function generateLink(link: ModuleGraphLink<T, I>) {
33+
if (link.target.x! <= link.source.x!) {
34+
return createLinkVertical({
35+
source: [link.source.x! + unref(spacing.width) / 2 - unref(spacing.linkOffset), link.source.y!],
36+
target: [link.target.x! - unref(spacing.width) / 2 + unref(spacing.linkOffset), link.target.y!],
37+
})
38+
}
39+
return createLinkHorizontal({
40+
source: [link.source.x! + unref(spacing.width) / 2 - unref(spacing.linkOffset), link.source.y!],
41+
target: [link.target.x! - unref(spacing.width) / 2 + unref(spacing.linkOffset), link.target.y!],
42+
})
43+
}
44+
45+
function getLinkColor(_link: ModuleGraphLink<T, I>) {
46+
return 'stroke-#8885'
47+
}
48+
49+
onMounted(() => {
50+
init()
51+
52+
watch(
53+
() => props.modules,
54+
() => {
55+
isFirstCalculateGraph.value = true
56+
collapsedNodes.clear()
57+
childToParentMap.clear()
58+
calculateGraph()
59+
},
60+
{ immediate: true },
61+
)
62+
})
63+
</script>
64+
65+
<template>
66+
<div
67+
ref="container"
68+
w-full h-screen of-scroll relative select-none
69+
:class="isGrabbing ? 'cursor-grabbing' : ''"
70+
>
71+
<div
72+
:style="{
73+
width: `${width * scale}px`,
74+
height: `${height * scale}px`,
75+
}"
76+
>
77+
<!-- Make this <div> in order to expand the scroll bar -->
78+
<div
79+
flex="~ items-center justify-center"
80+
:style="{ transform: `scale(${scale})`, transformOrigin: '0 0' }"
81+
>
82+
<div
83+
absolute left-0 top-0
84+
:style="{
85+
width: `${width}px`,
86+
height: `${height}px`,
87+
}"
88+
class="bg-dots"
89+
/>
90+
<svg pointer-events-none absolute left-0 top-0 z-graph-link :width="width" :height="height">
91+
<g>
92+
<path
93+
v-for="link of links"
94+
:key="link.id"
95+
:d="generateLink(link)!"
96+
:class="getLinkColor(link)"
97+
:stroke-dasharray="link.import?.kind === 'dynamic-import' ? '3 6' : undefined"
98+
fill="none"
99+
/>
100+
</g>
101+
</svg>
102+
<template
103+
v-for="node of nodes"
104+
:key="node.data.module.id"
105+
>
106+
<template v-if="node.data.module.id !== '~root'">
107+
<div
108+
absolute
109+
class="group z-graph-node flex gap-1 items-center"
110+
:style="{
111+
left: `${node.x}px`,
112+
top: `${node.y}px`,
113+
transform: 'translate(-50%, -50%)',
114+
}"
115+
>
116+
<div
117+
flex="~ items-center gap-1"
118+
bg-glass
119+
border="~ base rounded"
120+
class="group-hover:bg-active block px2 p1"
121+
:style="{
122+
minWidth: `${unref(spacing.width)}px`,
123+
maxWidth: `${unref(spacing.width)}px`,
124+
maxHeight: `${unref(spacing.height)}px`,
125+
overflow: 'hidden',
126+
transition: 'all 0.3s ease',
127+
}"
128+
>
129+
<slot :node="node" :nodes-ref-map="nodesRefMap" />
130+
</div>
131+
132+
<!-- Expand/Collapse Button -->
133+
<div class="w-4">
134+
<button
135+
v-if="node.data.hasChildren"
136+
w-4
137+
h-4
138+
rounded-full
139+
flex="items-center justify-center"
140+
text-xs
141+
border="~ active"
142+
class="flex cursor-pointer z-graph-node-active bg-base"
143+
:disabled="isGraphNodeToggling"
144+
:class="{ 'cursor-not-allowed': isGraphNodeToggling, 'hover:bg-active': !isGraphNodeToggling }"
145+
:title="node.data.expanded ? 'Collapse' : 'Expand'"
146+
@click.stop="toggleNode(node.data.module.id)"
147+
>
148+
<div
149+
class="text-primary h-4"
150+
:class="[
151+
node.data.expanded ? 'i-ph-minus' : 'i-ph-plus',
152+
]"
153+
transition="transform duration-200"
154+
/>
155+
</button>
156+
</div>
157+
</div>
158+
</template>
159+
</template>
160+
</div>
161+
</div>
162+
<div
163+
fixed right-6 bottom-6 z-panel-nav flex="~ col gap-2 items-center"
164+
>
165+
<div w-10 flex="~ items-center justify-center">
166+
<DisplayTimeoutView :content="`${Math.round(scale * 100)}%`" class="text-sm" />
167+
</div>
168+
169+
<div bg-glass rounded-full border border-base shadow flex="~ col gap-1 p1">
170+
<button
171+
v-tooltip.left="'Expand All'"
172+
w-10 h-10 rounded-full hover:bg-active op-fade
173+
hover:op100 flex="~ items-center justify-center"
174+
:disabled="isGraphNodeToggling"
175+
:class="{ 'op50 cursor-not-allowed': isGraphNodeToggling, 'hover:bg-active': !isGraphNodeToggling }"
176+
title="Expand All"
177+
@click="expandAll()"
178+
>
179+
<div class="i-carbon:expand-categories" />
180+
</button>
181+
<button
182+
v-tooltip.left="'Collapse All'"
183+
w-10 h-10 rounded-full hover:bg-active op-fade
184+
hover:op100 flex="~ items-center justify-center"
185+
:disabled="isGraphNodeToggling"
186+
:class="{ 'op50 cursor-not-allowed': isGraphNodeToggling, 'hover:bg-active': !isGraphNodeToggling }"
187+
title="Collapse All"
188+
@click="collapseAll()"
189+
>
190+
<div class="i-carbon:collapse-categories" />
191+
</button>
192+
193+
<div border="t base" my1 />
194+
195+
<button
196+
v-tooltip.left="'Zoom In (Ctrl + =)'"
197+
:disabled="scale >= ZOOM_MAX"
198+
w-10 h-10 rounded-full hover:bg-active op-fade
199+
hover:op100 disabled:op20 disabled:bg-none
200+
disabled:cursor-not-allowed
201+
flex="~ items-center justify-center"
202+
title="Zoom In (Ctrl + =)"
203+
@click="zoomIn()"
204+
>
205+
<div i-ph-magnifying-glass-plus-duotone />
206+
</button>
207+
<button
208+
v-tooltip.left="'Zoom Out (Ctrl + -)'"
209+
:disabled="scale <= ZOOM_MIN"
210+
w-10 h-10 rounded-full hover:bg-active op-fade hover:op100
211+
disabled:op20 disabled:bg-none disabled:cursor-not-allowed
212+
flex="~ items-center justify-center"
213+
title="Zoom Out (Ctrl + -)"
214+
@click="zoomOut()"
215+
>
216+
<div i-ph-magnifying-glass-minus-duotone />
217+
</button>
218+
</div>
219+
</div>
220+
</div>
221+
</template>

0 commit comments

Comments
 (0)