Skip to content

Commit

Permalink
feat(brain): cyberlinking (#1296)
Browse files Browse the repository at this point in the history
now you can visually connect to particles in brain
  • Loading branch information
happylolonly authored Sep 19, 2024
1 parent f36112c commit c42d5ea
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 160 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import useCyberlinks from './useCyberlinks';
import { PORTAL_ID } from '../../../containers/application/App';
import GraphNew from '../GraphNew/GraphNew';
import CyberlinksGraph from './CyberlinksGraph';
import GraphFullscreenBtn from '../GraphFullscreenBtn/GraphFullscreenBtn';

enum Types {
'3d' = '3d',
Expand Down Expand Up @@ -63,11 +64,15 @@ function CyberlinksGraphContainer({
</p>
</div>
) : (
<Comp
data={data || fetchData}
size={size}
currentAddress={currentAddress}
/>
<>
<GraphFullscreenBtn />

<Comp
data={data || fetchData}
size={size}
currentAddress={currentAddress}
/>
</>
);

const portalEl = document.getElementById(PORTAL_ID);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.btn {
position: absolute;
right: 50px;
z-index: 10;
top: 200px;
font-size: 18px;
color: var(--primary-color);

&:hover {
opacity: 0.7;
}
}
47 changes: 47 additions & 0 deletions src/features/cyberlinks/GraphFullscreenBtn/GraphFullscreenBtn.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { PORTAL_ID } from 'src/containers/application/App';
import { useState } from 'react';
import useEventListener from 'src/hooks/dom/useEventListener';
import styles from './GraphFullscreenBtn.module.scss';

export function useFullscreen() {
const [isFullscreen, setIsFullscreen] = useState(false);

const handleFullscreenChange = () => {
setIsFullscreen(document.fullscreenElement !== null);
};

useEventListener('fullscreenchange', handleFullscreenChange, document);

function toggleFullscreen(element?: HTMLElement = document) {
if (!document.fullscreenElement) {
element.requestFullscreen();
} else if (document.exitFullscreen) {
document.exitFullscreen();
}
}

return {
isFullscreen,
toggleFullscreen,
};
}

function GraphFullscreenBtn() {
const { isFullscreen, toggleFullscreen } = useFullscreen();

function onClick() {
if (!document.fullscreenElement) {
toggleFullscreen(document.getElementById(PORTAL_ID));
} else if (document.exitFullscreen) {
document.exitFullscreen();
}
}

return (
<button className={styles.btn} onClick={onClick} type="button">
{isFullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'}
</button>
);
}

export default GraphFullscreenBtn;
162 changes: 128 additions & 34 deletions src/features/cyberlinks/GraphNew/GraphNew.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ import {
CosmographRef,
CosmographSearchRef,
} from '@cosmograph/react';

import { CosmosInputNode, CosmosInputLink } from '@cosmograph/cosmos';
import { ActionBar as ActionBarComponent } from 'src/components';
import { Node } from './data';
// import './styles.css';
import styles from './GraphNew.module.scss';
import { useFullscreen } from '../GraphFullscreenBtn/GraphFullscreenBtn';
import { useCyberlinkWithWaitAndAdviser } from '../hooks/useCyberlink';
import GraphHoverInfo from '../CyberlinksGraph/GraphHoverInfo/GraphHoverInfo';

export default function GraphNew({ address, data, size }) {
Expand All @@ -18,33 +23,40 @@ export default function GraphNew({ address, data, size }) {
const search = useRef<CosmographSearchRef>();
const [degree, setDegree] = useState<number[]>([]);

// max 2 nodes
const [selectedNodes, setSelectedNodes] = useState<CosmosInputNode[]>([]);

const [localData, setLocalData] = useState<{
nodes: CosmosInputNode[];
links: CosmosInputLink[];
}>({
links: [],
nodes: [],
});

const [hoverNode, setHoverNode] = useState(null);
const [nodePostion, setNodePostion] = useState(null);

const nodes = useMemo(() => {
return (
data?.nodes?.map((node) => {
return {
...node,
size: 0.5,
// value: 1,
color: 'rgba(0,100,235,1)',
};
}) ?? []
);
}, [data]);

const links = useMemo(() => {
return (
data?.links?.map((link) => {
return {
...link,
width: 2.5,
color: 'rgba(9,255,13,1)',
};
}) ?? []
);
}, [data]);
const { links, nodes } = useMemo(() => {
const nodes = [...data.nodes, ...localData.nodes].map((node) => {
return {
...node,
size: 0.5,
// value: 1,
color: node.color || 'rgba(0,100,235,1)',
};
});

const links = [...data.links, ...localData.links].map((link) => {
return {
...link,
width: 2.5,
color: link.color || 'rgba(9,255,13,1)',
};
});

return { links, nodes };
}, [data, localData]);

const scaleColor = useRef(
scaleSymlog<string, string>()
Expand Down Expand Up @@ -75,9 +87,9 @@ export default function GraphNew({ address, data, size }) {
// [degree]
// );

const [showLabelsFor, setShowLabelsFor] = useState<Node[] | undefined>(
undefined
);
// const [showLabelsFor, setShowLabelsFor] = useState<Node[] | undefined>(
// undefined
// );
const [selectedNode, setSelectedNode] = useState<Node | undefined>();

useEffect(() => {
Expand Down Expand Up @@ -110,15 +122,42 @@ export default function GraphNew({ address, data, size }) {
// setSelectedNode(n);
// }, []);

function callback() {
cosmograph.current?.unselectNodes();
// setShowLabelsFor(undefined);
setSelectedNode(undefined);

setLocalData({
nodes: [
...localData.nodes,
...selectedNodes.map((node) => ({ ...node, color: 'orange' })),
],
links: [
...localData.links,
{
source: selectedNodes[0].id,
target: selectedNodes[1].id,
color: 'red',
},
],
});
}

console.log(localData);

const { isFullscreen } = useFullscreen();

return (
<div className={styles.wrapper}>
<div className={styles.total}>
<p>total nodes: {nodes.length} </p>
<p>total links: {links.length} </p>
</div>
{!isFullscreen && (
<div className={styles.total}>
<p>total nodes: {nodes.length} </p>
<p>total links: {links.length} </p>
</div>
)}
<GraphHoverInfo
node={hoverNode}
left={nodePostion?.x}
left={nodePostion?.x + 50}
top={nodePostion?.y}
size={size || window.innerWidth}
/>
Expand All @@ -141,10 +180,31 @@ export default function GraphNew({ address, data, size }) {
showDynamicLabels={false}
linkArrows={false}
linkWidth={2}
onClick={() => {
onClick={(node) => {
cosmograph.current?.pause();

if (node) {
// setShowLabelsFor([node]);

// check for duplicate

let newNodes = [...selectedNodes];

if (newNodes.length < 2) {
newNodes.push(node);
} else {
newNodes = [node];
}

setSelectedNodes(newNodes);
cosmograph.current?.selectNodes(newNodes);
} else {
cosmograph.current?.unselectNodes();
// setShowLabelsFor(undefined);
setSelectedNodes([]);
}
}}
showLabelsFor={showLabelsFor}
// showLabelsFor={showLabelsFor}
showHoveredNodeLabel={false}
nodeLabelColor="white"
simulationFriction={0.95}
Expand Down Expand Up @@ -197,6 +257,40 @@ export default function GraphNew({ address, data, size }) {
showAnimationControls
/> */}
</CosmographProvider>

<ActionBar selectedNodes={selectedNodes} callback={callback} />
</div>
);
}

type Props2 = {
selectedNodes: CosmosInputNode[];
callback: () => void;
};

function ActionBar({ selectedNodes, callback }: Props2) {
const { isReady, isLoading, execute } = useCyberlinkWithWaitAndAdviser({
to: selectedNodes[0]?.id,
from: selectedNodes[1]?.id,
callback,
});

let text;
if (selectedNodes.length !== 2 || selectedNodes.length === 0) {
text = `select ${2 - selectedNodes.length} particles`;
} else {
text = '';
}

return (
<ActionBarComponent
button={{
text: 'cyberlink particles',
onClick: execute,
disabled: !isReady || selectedNodes.length !== 2,
pending: isLoading,
}}
text={text}
/>
);
}
Loading

0 comments on commit c42d5ea

Please sign in to comment.