@@ -13,6 +13,7 @@ import "./components/search-command/search-command.js";
1313import { Settings } from "./components/views/settings/settings.js" ;
1414import { HomeView } from "./components/views/home/home.js" ;
1515import "./components/views/search/search.js" ;
16+ import "./components/drill-breadcrumb/drill-breadcrumb.js" ;
1617import { NetworkNavigation } from "./core/network-navigation.js" ;
1718import { i18n } from "./core/i18n.js" ;
1819import { initSearchNav } from "./core/search-nav.js" ;
@@ -24,7 +25,9 @@ let secureDataSet;
2425let nsn ;
2526let homeView ;
2627let searchview ;
28+ let drillBreadcrumb ;
2729let packageInfoOpened = false ;
30+ const drillStack = [ ] ;
2831
2932document . addEventListener ( "DOMContentLoaded" , async ( ) => {
3033 searchview = document . querySelector ( "search-view" ) ;
@@ -39,6 +42,17 @@ document.addEventListener("DOMContentLoaded", async() => {
3942 // update searchview after window.i18n is set
4043 searchview . requestUpdate ( ) ;
4144
45+ drillBreadcrumb = document . querySelector ( "drill-breadcrumb" ) ;
46+ drillBreadcrumb . addEventListener ( EVENTS . DRILL_RESET , resetDrill ) ;
47+ drillBreadcrumb . addEventListener ( EVENTS . DRILL_BACK , function handleDrillBack ( event ) {
48+ drillBackTo ( event . detail . index ) ;
49+ } ) ;
50+ drillBreadcrumb . addEventListener ( EVENTS . DRILL_SWITCH , function handleDrillSwitch ( event ) {
51+ const { stackIndex, nodeId } = event . detail ;
52+ drillStack . length = stackIndex ;
53+ drillInto ( nodeId ) ;
54+ } ) ;
55+
4256 await init ( ) ;
4357 window . dispatchEvent (
4458 new CustomEvent ( EVENTS . SETTINGS_SAVED , {
@@ -122,6 +136,107 @@ function dispatchSearchCommandInit() {
122136 window . dispatchEvent ( event ) ;
123137}
124138
139+ function computeSiblings ( parentId , excludeId ) {
140+ const seen = new Set ( ) ;
141+ const result = [ ] ;
142+
143+ for ( const edge of secureDataSet . rawEdgesData ) {
144+ if ( edge . to === parentId && edge . from !== excludeId && ! seen . has ( edge . from ) ) {
145+ seen . add ( edge . from ) ;
146+
147+ const entry = secureDataSet . linker . get ( edge . from ) ;
148+ result . push ( {
149+ nodeId : edge . from ,
150+ name : entry . name ,
151+ version : entry . version
152+ } ) ;
153+ }
154+ }
155+
156+ return result . sort ( ( nodeA , nodeB ) => nodeA . name . localeCompare ( nodeB . name ) ) ;
157+ }
158+
159+ function computeDrillSubtree ( rootNodeId ) {
160+ const subtreeIds = new Set ( [ rootNodeId ] ) ;
161+ const queue = [ rootNodeId ] ;
162+
163+ while ( queue . length > 0 ) {
164+ const current = queue . shift ( ) ;
165+ for ( const edge of secureDataSet . rawEdgesData ) {
166+ if ( edge . to === current && ! subtreeIds . has ( edge . from ) ) {
167+ subtreeIds . add ( edge . from ) ;
168+ queue . push ( edge . from ) ;
169+ }
170+ }
171+ }
172+
173+ return subtreeIds ;
174+ }
175+
176+ function applyDrill ( nodeId ) {
177+ const subtreeIds = computeDrillSubtree ( nodeId ) ;
178+ const updates = [ ...secureDataSet . linker . keys ( ) ] . map ( ( id ) => {
179+ return {
180+ id,
181+ hidden : ! subtreeIds . has ( id )
182+ } ;
183+ } ) ;
184+ nsn . nodes . update ( updates ) ;
185+ nsn . network . unselectAll ( ) ;
186+ updateDrillBreadcrumb ( ) ;
187+ PackageInfo . close ( ) ;
188+ nsn . neighbourHighlight ( { nodes : [ nodeId ] , edges : [ ] } ) ;
189+ }
190+
191+ function drillInto ( nodeId ) {
192+ const currentRoot = drillStack . length === 0 ? 0 : drillStack . at ( - 1 ) ;
193+ if ( nodeId === currentRoot ) {
194+ return ;
195+ }
196+
197+ drillStack . push ( nodeId ) ;
198+ applyDrill ( nodeId ) ;
199+ }
200+
201+ function drillBackTo ( stackIndex ) {
202+ drillStack . length = stackIndex + 1 ;
203+ applyDrill ( drillStack [ stackIndex ] ) ;
204+ }
205+
206+ function resetDrill ( ) {
207+ drillStack . length = 0 ;
208+ const updates = [ ...secureDataSet . linker . keys ( ) ] . map ( ( id ) => {
209+ return {
210+ id,
211+ hidden : false
212+ } ;
213+ } ) ;
214+ nsn . nodes . update ( updates ) ;
215+ updateDrillBreadcrumb ( ) ;
216+ PackageInfo . close ( ) ;
217+ }
218+
219+ function updateDrillBreadcrumb ( ) {
220+ const rootEntry = secureDataSet . linker . get ( 0 ) ;
221+ drillBreadcrumb . root = {
222+ name : rootEntry . name ,
223+ version : rootEntry . version
224+ } ;
225+ drillBreadcrumb . stack = drillStack . map ( ( nodeId ) => {
226+ const entry = secureDataSet . linker . get ( nodeId ) ;
227+
228+ return {
229+ name : entry . name ,
230+ version : entry . version
231+ } ;
232+ } ) ;
233+ drillBreadcrumb . siblings = drillStack . map ( ( nodeId , index ) => {
234+ const parentId = index === 0 ? 0 : drillStack [ index - 1 ] ;
235+
236+ return computeSiblings ( parentId , nodeId ) ;
237+ } ) ;
238+ }
239+
125240async function init ( options = { } ) {
126241 const { navigateToNetworkView = false } = options ;
127242
@@ -158,7 +273,22 @@ async function init(options = {}) {
158273 packageInfoOpened = false ;
159274 } ) ;
160275
161- nsn . network . on ( "click" , updateShowInfoMenu ) ;
276+ nsn . network . on ( "click" , ( params ) => {
277+ const srcEvent = params . event ?. srcEvent ;
278+ const isDrillClick = srcEvent ?. ctrlKey || srcEvent ?. metaKey ;
279+
280+ if ( isDrillClick && params . nodes . length > 0 ) {
281+ const nodeId = Number ( params . nodes [ 0 ] ) ;
282+ drillInto ( nodeId ) ;
283+
284+ return ;
285+ }
286+
287+ updateShowInfoMenu ( params ) ;
288+ } ) ;
289+
290+ drillStack . length = 0 ;
291+ updateDrillBreadcrumb ( ) ;
162292
163293 const networkNavigation = new NetworkNavigation ( secureDataSet , nsn ) ;
164294 window . networkNav = networkNavigation ;
0 commit comments