1
+ import { HamburgerIcon } from '@chakra-ui/icons'
1
2
import graphDataJson from '../graphdata.json'
2
3
import {
3
4
Box ,
4
5
Flex ,
6
+ Heading ,
5
7
IconButton ,
8
+ Slide ,
6
9
Tooltip ,
7
10
useDisclosure ,
8
11
useOutsideClick ,
9
12
useTheme ,
10
13
} from '@chakra-ui/react'
11
14
import { useAnimation } from '@lilib/hooks'
12
- import { useWindowSize } from '@react-hook/window-size'
15
+ import { useWindowSize , useWindowWidth } from '@react-hook/window-size'
13
16
import * as d3int from 'd3-interpolate'
14
- import { GraphData , NodeObject } from 'force-graph'
17
+ import { GraphData , LinkObject , NodeObject } from 'force-graph'
15
18
import Head from 'next/head'
16
19
import React , {
17
20
ComponentPropsWithoutRef ,
@@ -61,7 +64,8 @@ import { getNodeColor } from '../util/getNodeColor'
61
64
import { isLinkRelatedToNode } from '../util/isLinkRelatedToNode'
62
65
import { getLinkColor } from '../util/getLinkColor'
63
66
import { Search } from '../components/Search'
64
- import { useRouter , useSearchParams } from 'next/navigation'
67
+ import { useRouter } from "next/router" ;
68
+ import { usePathname , useSearchParams } from 'next/navigation'
65
69
import { TITLE_NAME } from '../constants/action'
66
70
67
71
const d3promise = import ( 'd3-force-3d' )
@@ -119,8 +123,7 @@ export function GraphPage() {
119
123
const [ threeDim , setThreeDim ] = usePersistantState ( '3d' , false )
120
124
const [ tagColors , setTagColors ] = usePersistantState < TagColors > ( 'tagCols' , { } )
121
125
const [ scope , setScope ] = useState < Scope > ( { nodeIds : [ ] , excludedNodeIds : [ ] } )
122
- const params = useSearchParams ( ) ;
123
- const graphParam = params . get ( 'graph' ) ;
126
+
124
127
const [ physics , setPhysics ] = usePersistantState ( 'physics' , initialPhysics )
125
128
const [ filter , setFilter ] = usePersistantState ( 'filter' , initialFilter )
126
129
const [ visuals , setVisuals ] = usePersistantState ( 'visuals' , initialVisuals )
@@ -130,13 +133,6 @@ export function GraphPage() {
130
133
const [ mouse , setMouse ] = usePersistantState ( 'mouse' , initialMouse )
131
134
const [ coloring , setColoring ] = usePersistantState ( 'coloring' , initialColoring )
132
135
const [ local , setLocal ] = usePersistantState ( 'local' , initialLocal )
133
- const router = useRouter ( ) ;
134
-
135
- useEffect ( ( ) => {
136
- if ( graphParam === 'local' && window . location . hash ) {
137
- setScope ( { nodeIds : [ window . location . hash . replace ( '#' , '' ) ] , excludedNodeIds : [ ] } )
138
- }
139
- } , [ graphParam ] ) ;
140
136
141
137
const [
142
138
previewNodeState ,
@@ -370,6 +366,75 @@ export function GraphPage() {
370
366
const WebSocketRef = useRef < ReconnectingWebSocket | null > ( null )
371
367
372
368
scopeRef . current = scope
369
+ const followBehavior = (
370
+ command : string ,
371
+ emacsNode : string ,
372
+ speed : number = 2000 ,
373
+ padding : number = 200 ,
374
+ ) => {
375
+ if ( command === 'color' ) {
376
+ return
377
+ }
378
+ const fg = graphRef . current
379
+ const sr = scopeRef . current
380
+ const bh = behaviorRef . current
381
+ const links = linksByNodeIdRef . current [ emacsNode ] ?? [ ]
382
+ const nodes = Object . fromEntries (
383
+ [ emacsNode as string , ...links . flatMap ( ( link ) => [ link . source , link . target ] ) ] . map (
384
+ ( nodeId ) => [ nodeId , { } ] ,
385
+ ) ,
386
+ )
387
+ if ( command === 'zoom' ) {
388
+ if ( sr . nodeIds . length ) {
389
+ setScope ( { nodeIds : [ ] , excludedNodeIds : [ ] } )
390
+ }
391
+ setTimeout (
392
+ ( ) => fg . zoomToFit ( speed , padding , ( node : NodeObject ) => nodes [ node . id as string ] ) ,
393
+ 50 ,
394
+ )
395
+ return
396
+ }
397
+ if ( ! sr . nodeIds . length ) {
398
+ setScope ( ( current : Scope ) => ( { ...current , nodeIds : [ emacsNode ] } ) )
399
+ setTimeout ( ( ) => {
400
+ fg . centerAt ( 0 , 0 , 10 )
401
+ fg . zoomToFit ( 1 , padding )
402
+ } , 50 )
403
+ return
404
+ }
405
+ if ( bh . localSame !== 'add' ) {
406
+ setScope ( ( current : Scope ) => ( { ...current , nodeIds : [ emacsNode ] } ) )
407
+ setTimeout ( ( ) => {
408
+ fg . centerAt ( 0 , 0 , 10 )
409
+ fg . zoomToFit ( 1 , padding )
410
+ } , 50 )
411
+ return
412
+ }
413
+
414
+ // if the node is in the scoped nodes, add it to scope instead of replacing it
415
+ if (
416
+ ! sr . nodeIds . includes ( emacsNode ) ||
417
+ ! sr . nodeIds . some ( ( scopeId : string ) => {
418
+ return nodes [ scopeId ]
419
+ } )
420
+ ) {
421
+ setScope ( ( current : Scope ) => ( { ...current , nodeIds : [ emacsNode ] } ) )
422
+ setTimeout ( ( ) => {
423
+ fg . centerAt ( 0 , 0 , 10 )
424
+ fg . zoomToFit ( 1 , padding )
425
+ } , 50 )
426
+ return
427
+ }
428
+ setScope ( ( currentScope : Scope ) => ( {
429
+ ...currentScope ,
430
+ nodeIds : [ ...currentScope . nodeIds , emacsNode as string ] ,
431
+ } ) )
432
+ setTimeout ( ( ) => {
433
+ fg . centerAt ( 0 , 0 , 10 )
434
+ fg . zoomToFit ( 1 , padding )
435
+ } , 50 )
436
+ }
437
+
373
438
374
439
useEffect ( ( ) => {
375
440
const fg = graphRef . current
@@ -383,7 +448,7 @@ export function GraphPage() {
383
448
setTimeout ( ( ) => {
384
449
fg . zoomToFit ( 5 , 200 )
385
450
} , 50 )
386
- } , [ physics . gravityOn , scope . nodeIds ] )
451
+ } , [ scope . nodeIds ] )
387
452
388
453
const [ windowWidth , windowHeight ] = useWindowSize ( )
389
454
@@ -427,9 +492,6 @@ export function GraphPage() {
427
492
return
428
493
}
429
494
if ( command === 'replace' ) {
430
- const newParams = new URLSearchParams ( { graph : 'local' } ) ;
431
- const newUrl = `${ window . location . pathname } ?${ newParams . toString ( ) } #${ node . id } ` ;
432
- history . replaceState ( null , '' , newUrl ) ;
433
495
setScope ( { nodeIds : [ node . id ] , excludedNodeIds : [ ] } )
434
496
return
435
497
}
@@ -538,21 +600,25 @@ export function GraphPage() {
538
600
< Box position = "relative" zIndex = { 4 } width = "100%" >
539
601
< Flex className = "headerBar" h = { 10 } flexDir = "column" >
540
602
< Flex alignItems = "center" h = { 10 } justifyContent = "flex-end" >
603
+ { /* <Flex flexDir="row" alignItems="center">
604
+ * <Box color="blue.500" bgColor="alt.100" h="100%" p={3} mr={4}>
605
+ * {mainItem.icon}
606
+ * </Box>
607
+ * <Heading size="sm">{mainItem.title}</Heading>
608
+ * </Flex> */ }
541
609
< Flex height = "100%" flexDirection = "row" >
542
610
{ scope . nodeIds . length > 0 && (
543
611
< Tooltip label = "Return to main graph" >
544
612
< IconButton
545
613
m = { 1 }
546
614
icon = { < BiNetworkChart /> }
547
615
aria-label = "Exit local mode"
548
- onClick = { ( ) => {
549
- const hash = window . location . hash ;
550
- history . replaceState ( null , '' , hash ? window . location . pathname : window . location . pathname + `#${ hash } ` ) ;
616
+ onClick = { ( ) =>
551
617
setScope ( ( currentScope : Scope ) => ( {
552
618
...currentScope ,
553
619
nodeIds : [ ] ,
554
620
} ) )
555
- } }
621
+ }
556
622
variant = "subtle"
557
623
/>
558
624
</ Tooltip >
@@ -562,7 +628,6 @@ export function GraphPage() {
562
628
setPreviewNode = { setPreviewNode }
563
629
onClickResultItem = { ( id ) => {
564
630
setEmacsNodeId ( id )
565
- setScope ( { nodeIds : [ ] , excludedNodeIds : [ ] } )
566
631
} }
567
632
/>
568
633
< Tooltip label = { isOpen ? 'Close sidebar' : 'Open sidebar' } >
@@ -710,7 +775,7 @@ export const Graph = function (props: GraphProps) {
710
775
switch ( click ) {
711
776
case mouse . preview : {
712
777
setPreviewNode ( node )
713
- history . replaceState ( null , '' , window . location . pathname + window . location . search + `#${ node . id } ` )
778
+ history . replaceState ( null , '' , window . location . pathname + `#${ node . id } ` )
714
779
break
715
780
}
716
781
case mouse . local : {
0 commit comments