summaryrefslogtreecommitdiff
path: root/pages/index.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'pages/index.tsx')
-rw-r--r--pages/index.tsx119
1 files changed, 89 insertions, 30 deletions
diff --git a/pages/index.tsx b/pages/index.tsx
index 90d9686..6eb86b1 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -131,15 +131,72 @@ export function GraphPage() {
const { setEmacsTheme } = useContext(ThemeContext)
const [threeDim, setThreeDim] = useState(false)
+ const [scope, setScope] = useState<Scope>({ nodeIds: [] })
const graphRef = useRef<any>(null)
+ const followBehavior = (
+ command: string,
+ emacsNode: string,
+ speed: number = 2000,
+ padding: number = 200,
+ ) => {
+ const fg = graphRef.current
+ const links = linksByNodeIdRef.current[emacsNode] ?? []
+ const nodes = Object.fromEntries(
+ [emacsNode as string, ...links.flatMap((link) => [link.source, link.target])].map(
+ (nodeId) => [nodeId, {}],
+ ),
+ )
+ if (command === 'zoom') {
+ if (scope.nodeIds.length) {
+ setScope({ nodeIds: [] })
+ }
+ setTimeout(() => fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!]), 50)
+ return
+ }
+ if (!scope.nodeIds.length) {
+ setScope({ nodeIds: [emacsNode] })
+ setTimeout(() => {
+ fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!])
+ }, 50)
+ return
+ }
+ if (behavior.localSame !== 'add') {
+ setScope({ nodeIds: [emacsNode] })
+ setTimeout(() => {
+ fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!])
+ }, 50)
+ return
+ }
+
+ // if the node is in the scopednodes, add it to scope instead of replacing it
+ if (
+ !scope.nodeIds.includes(emacsNode) ||
+ !scope.nodeIds.some((scopeId: string) => {
+ return nodes[scopeId]
+ })
+ ) {
+ setScope({ nodeIds: [emacsNode] })
+ setTimeout(() => {
+ fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!])
+ }, 50)
+ return
+ }
+ setScope((currentScope: Scope) => ({
+ ...currentScope,
+ nodeIds: [...currentScope.nodeIds, emacsNode as string],
+ }))
+ setTimeout(() => fg.zoomToFit(speed, padding, (node: OrgRoamNode) => nodes[node.id!]), 50)
+ }
+
useEffect(() => {
const socket = new ReconnectingWebSocket('ws://localhost:35903')
socket.addEventListener('open', (event) => {
console.log('Connection with Emacs established')
})
socket.addEventListener('message', (event) => {
+ const fg = graphRef.current
const message = JSON.parse(event.data)
switch (message.type) {
case 'graphdata':
@@ -148,21 +205,28 @@ export function GraphPage() {
return setEmacsTheme(message.data)
case 'command':
switch (message.data.commandName) {
- case 'follow':
- return setEmacsNodeId(message.data.id)
+ case 'local':
+ const speed = behavior.zoomSpeed
+ const padding = behavior.zoomPadding
+ followBehavior('local', message.data.id, speed, padding)
+ setEmacsNodeId(message.data.id)
+ break
case 'zoom': {
- const links = linksByNodeIdRef.current[message.data.id!] ?? []
- const nodes = Object.fromEntries(
- [
- message.data.id! as string,
- ...links.flatMap((link) => [link.source, link.target]),
- ].map((nodeId) => [nodeId, {}]),
- )
- const fg = graphRef.current
- fg.zoomToFit(2000, 200, (node: OrgRoamNode) => nodes[node.id!])
+ const speed = message?.data?.speed || behavior.zoomSpeed
+ const padding = message?.data?.padding || behavior.zoomPadding
+ followBehavior('zoom', message.data.id, speed, padding)
+ setEmacsNodeId(message.data.id)
+ break
}
- case 'toggle': {
- /* setBehavior({ ...behavior, followLocalorZoom: !behavior.followLocalOrZoom }) */
+ case 'follow': {
+ followBehavior(
+ behavior.follow,
+ message.data.id,
+ behavior.zoomSpeed,
+ behavior.zoomPadding,
+ )
+ setEmacsNodeId(message.data.id)
+ break
}
default:
return console.error('unknown message type', message.type)
@@ -189,6 +253,8 @@ export function GraphPage() {
setVisuals,
mouse,
setMouse,
+ behavior,
+ setBehavior,
}}
/>
<Box position="absolute" alignItems="top">
@@ -205,6 +271,8 @@ export function GraphPage() {
visuals,
behavior,
mouse,
+ scope,
+ setScope,
}}
/>
</Box>
@@ -223,6 +291,8 @@ export interface GraphProps {
visuals: typeof initialVisuals
behavior: typeof initialBehavior
mouse: typeof initialMouse
+ scope: Scope
+ setScope: any
}
export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
@@ -237,6 +307,8 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
visuals,
behavior,
mouse,
+ scope,
+ setScope,
} = props
// react-force-graph does not track window size
@@ -245,7 +317,6 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
const [windowWidth, windowHeight] = useWindowSize()
const [hoverNode, setHoverNode] = useState<NodeObject | null>(null)
- const [scope, setScope] = useState<Scope>({ nodeIds: [] })
const handleClick = (click: string, node: NodeObject) => {
switch (click) {
@@ -254,7 +325,7 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
if (scope.nodeIds.includes(node.id as string)) {
break
}
- setScope((currentScope) => ({
+ setScope((currentScope: Scope) => ({
...currentScope,
nodeIds: [...currentScope.nodeIds, node.id as string],
}))
@@ -278,27 +349,15 @@ export const Graph = forwardRef(function (props: GraphProps, graphRef: any) {
)
}
+ const centralHighlightedNode = useRef<NodeObject | null>(null)
+
useEffect(() => {
if (!emacsNodeId) {
return
}
- const fg = graphRef.current
- if (behavior.followLocalOrZoom) {
- setScope({ nodeIds: [emacsNodeId] })
- setTimeout(() => {
- fg?.zoomToFit(
- 2000,
- numberWithinRange(20, 200, windowWidth / 8),
- (node: NodeObject) => getNeighborNodes(emacsNodeId)[node.id!],
- )
- }, 1)
- } else {
- fg?.zoomToFit(1000, 200, (node: NodeObject) => getNeighborNodes(emacsNodeId)[node.id!])
- setHoverNode(nodeById[emacsNodeId] as NodeObject)
- }
+ centralHighlightedNode.current = nodeById[emacsNodeId] as NodeObject
}, [emacsNodeId])
- const centralHighlightedNode = useRef<NodeObject | null>(null)
centralHighlightedNode.current = hoverNode
const highlightedNodes = useMemo(() => {
if (!centralHighlightedNode.current) {