-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0b3654b
Showing
55 changed files
with
26,618 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 2 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
node_modules | ||
dist | ||
test/e2e | ||
lib |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"root": true, | ||
"parser": "@typescript-eslint/parser", | ||
"plugins": ["@typescript-eslint"], | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/eslint-recommended", | ||
"plugin:@typescript-eslint/recommended" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"trailingComma": "all", | ||
"printWidth": 120 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
= React Sigma v2 | ||
|
||
A proof of concept (for now) to create Sigma v2 React component. | ||
|
||
|
||
== Philosophy | ||
|
||
This library shares the same philosophy as https://react-leaflet.js.org[react-leaflet], | ||
it just provides some bindings (and helpers) between React and Sigma. | ||
|
||
The main component, ie. `SigmaContainer` create a Sigma instance with an empty graph. | ||
If its option `initialSettings` or `graphOptions` is updated, the instance is killed and re-created. | ||
|
||
I recommend you to NOT UPDATE those options to avoid performance issues. | ||
Sigma (& graphology) comes with methods that allow the user to update the settings. | ||
|
||
Every child has access to the sigma instance (and so the graph instance) via the React context created by the `SigmaContainer`. | ||
In your components, you can use the hook `const sigma = useSigma()` that gives you the sigma instance (and so the underlying graph with `sigma.getGraph()`) | ||
|
||
This is an example of how to display a graph : | ||
|
||
[source, javascript] | ||
---- | ||
// Component that creates the graph | ||
const MyCustomGraph = () => { | ||
const sigma = useSigma() | ||
const graph = sigma.getGraph(); | ||
graph.addNode("Jessica", { label: "Jessica", x: 1, y: 1, color: "#FF0", size: 10 }); | ||
graph.addNode("Truman", { label: "Truman", x: 0, y: 0, color: "#00F",size: 5 }); | ||
graph.addEdge("Jessica", "Truman", { color: "#CCC",size: 1 }); | ||
return null; | ||
} | ||
// Put your component as a child of `SigmaContainer` | ||
ReactDOM.render( | ||
<React.StrictMode> | ||
<SigmaContainer> | ||
<MyCustomGraph /> | ||
</SigmaContainer> | ||
</React.StrictMode>, | ||
document.getElementById("root"), | ||
); | ||
---- | ||
|
||
== Components | ||
|
||
=== SigmaContainer | ||
|
||
This is the component's properties definition : | ||
|
||
[source, typescript] | ||
---- | ||
interface SigmaContainerProps { | ||
graphOptions?: GraphOptions; | ||
initialSettings?: Settings; | ||
id?: string; | ||
className?: string; | ||
style?: CSSProperties; | ||
children?: ReactNode; | ||
} | ||
---- | ||
|
||
* `graphOptions` is the options passed to the Graphology constructor. It defines the type of the graph, see https://graphology.github.io/instantiation.html#options | ||
* `initialSettings` is the settings passed to the Sigma constructor. | ||
|
||
=== ControlsContainer | ||
|
||
It's just a component wrapper that create a `div` with the adequate class, so we know where to display the controls. | ||
The only (optional) property of this component is : `position?: "top-right" | "top-left" | "bottom-right" | "bottom-left";` | ||
Default value is `bottom-right`. | ||
|
||
=== ZoomControl | ||
|
||
Create the zoom toolbar fir the graph. | ||
There is three controllers : `zoom in`, `zoom out` & `see the whole graph` | ||
|
||
=== ForceAtlasControl | ||
|
||
This is the component's properties definition : | ||
|
||
[source, typescript] | ||
---- | ||
interface Props { | ||
/** | ||
* The FA2 worker settings. | ||
*/ | ||
settings?: FA2LayoutSupervisorParameters; | ||
/** | ||
* Option to tell what we do when the component is mounted | ||
* - <code>-1</code> means that we do nothing (it's the same as no value) | ||
* - <code>0</code> means that we start the algo (and don't auto stop it) | ||
* - <code>X</code> mans that we start the algo, and stop it after X milliseconds | ||
*/ | ||
autoRunFor?: number; | ||
} | ||
---- | ||
|
||
The component creates an a action button to stop/start a forceatlas2 layout on the graph. | ||
Moreover, if you define the option `autoRunFor`, the layout will be run on component mount. | ||
|
||
== Hooks | ||
|
||
* `useSigma(): Sigma` : This is the main hook that give you access to the sigma instance. | ||
* `useLoadGraph(): (graph: Graph, clear?: boolean) => void` : It's an helper that allow you to load a graph into the sigma instance. It's pretty much a `sigma.getGraph().import(graph)` | ||
* `useRegisterEvents(): (eventHandlers: Partial<EventHandlers>) => void` : It's an helper that allow you to listen Sigma's events. | ||
* `useSetSettings(): (settings: Partial<Settings>) => void` : It's an helper that allow you to change the Sigma's settings. | ||
|
||
== How to install | ||
|
||
The package is not yet on npm, so you have to install it directly from the repository. | ||
Moreover, you have to install the peer dependencies that you need. | ||
|
||
So if you want to install the full package, this is the command line : | ||
|
||
[source, bash] | ||
---- | ||
$> npm install sigma graphology graphology-layout-forceatlas2 https://github.com/sim51/react-sigma-v2 | ||
---- | ||
|
||
IMPORTANT: This package is based on the version of SigmaV2 `2.0.0-beta3` that has introduced new features. | ||
So be sure to have this version or an upper one. | ||
|
||
|
||
== How to use it | ||
|
||
=== Import | ||
|
||
Package is composed of a css file and a list of react components & hooks. | ||
|
||
For the js part, everything is export in the package entrypoint, so you can do this | ||
|
||
[source, javascript] | ||
---- | ||
import { SigmaContainer, ...} from "react-sigma-v2"; | ||
---- | ||
|
||
You can also import just the components you need, they are exposed under the folder `./lib/esm` : | ||
|
||
[source, javascript] | ||
---- | ||
import { SigmaContainer, ...} from "react-sigma-v2/lib/esm/SigmaContainer"; | ||
---- | ||
|
||
For the css, you need to import the file `./lib/react-sigma-v2.css`. | ||
|
||
== Npm scripts | ||
|
||
* `npm run build` : Build the project | ||
* `npm run examples` : Run the examples on http://localshot:8080 | ||
|
||
== Example | ||
|
||
[source, javascript] | ||
---- | ||
include::./examples/index.tsx[] | ||
---- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/> | ||
<title><%= htmlWebpackPlugin.options.title %></title> | ||
<style> | ||
html,body, #root { | ||
height:100%; | ||
width: 100%; | ||
margin:0; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import React, { useEffect } from "react"; | ||
import ReactDOM from "react-dom"; | ||
import { UndirectedGraph } from "graphology"; | ||
import { NodeKey, EdgeKey } from "graphology-types"; | ||
import erdosRenyi from "graphology-generators/random/erdos-renyi"; | ||
import randomLayout from "graphology-layout/random"; | ||
import chroma from "chroma-js"; | ||
import faker from "faker"; | ||
import { Sigma } from "sigma/sigma"; | ||
import { Settings } from "sigma/settings"; | ||
import { | ||
ControlsContainer, | ||
EventHandlers, | ||
ForceAtlasControl, | ||
useSigma, | ||
useRegisterEvents, | ||
useLoadGraph, | ||
useSetSettings, | ||
SigmaContainer, | ||
ZoomControl, | ||
} from "../src/index"; | ||
import "../src/assets/index.scss"; | ||
|
||
export const MyCustomGraph: React.FC<React.PropsWithChildren> = ({ children }) => { | ||
const sigma = useSigma(); | ||
const registerEvents = useRegisterEvents(); | ||
const loadGraph = useLoadGraph(); | ||
const setSettings = useSetSettings(); | ||
|
||
useEffect(() => { | ||
// Create the graph | ||
const graph = erdosRenyi(UndirectedGraph, { order: 100, probability: 0.2 }); | ||
randomLayout.assign(graph); | ||
graph.nodes().forEach(node => { | ||
graph.mergeNodeAttributes(node, { | ||
label: faker.name.findName(), | ||
size: Math.max(4, Math.random() * 10), | ||
color: chroma.random().hex(), | ||
}); | ||
}); | ||
loadGraph(graph); | ||
|
||
// Register the events | ||
registerEvents({ | ||
enterNode: event => { | ||
const graph = sigma.getGraph(); | ||
graph.updateEachNodeAttributes((node, attr) => { | ||
return { ...attr, background: true, highlighted: false }; | ||
}); | ||
graph.setNodeAttribute(event.node, "background", false); | ||
graph.setNodeAttribute(event.node, "highlighted", true); | ||
graph.setNodeAttribute(event.node, "selected", true); | ||
graph.forEachNeighbor(event.node, (node, data) => { | ||
graph.setNodeAttribute(node, "background", false); | ||
graph.setNodeAttribute(node, "highlighted", true); | ||
}); | ||
sigma.refresh(); | ||
}, | ||
leaveNode: event => { | ||
const graph = sigma.getGraph(); | ||
graph.updateEachNodeAttributes((node, attr) => { | ||
attr.highlighted = false; | ||
attr.background = false; | ||
attr.selected = false; | ||
return attr; | ||
}); | ||
sigma.refresh(); | ||
}, | ||
}); | ||
|
||
setSettings({ | ||
nodeReducer: (node, data) => { | ||
if (data.background) return { ...data, color: "#eee", label: "" }; | ||
return data; | ||
}, | ||
edgeReducer: (edge, data) => { | ||
const graph = sigma.getGraph(); | ||
const nodes = graph.extremities(edge).map(node => graph.getNodeAttributes(node)); | ||
if (nodes && (nodes[0].background || nodes[1].background)) return { ...data, hidden: true }; | ||
if (nodes && (nodes[0].selected || nodes[1].selected)) return { ...data, color: "#000", hidden: false }; | ||
return { ...data, color: "#aaa", hidden: false }; | ||
}, | ||
}); | ||
}, []); | ||
|
||
return <>{children}</>; | ||
}; | ||
|
||
ReactDOM.render( | ||
<React.StrictMode> | ||
<SigmaContainer> | ||
<MyCustomGraph /> | ||
<ControlsContainer position={"bottom-right"}> | ||
<ZoomControl /> | ||
<ForceAtlasControl autoRunFor={2000} /> | ||
</ControlsContainer> | ||
</SigmaContainer> | ||
</React.StrictMode>, | ||
document.getElementById("root"), | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"compilerOptions": { | ||
"rootDir": "./../", | ||
"outDir": "./lib/esm", | ||
"sourceMap": true, | ||
"declaration": true, | ||
"declarationDir": "./lib/esm", | ||
"target": "es6", | ||
"esModuleInterop": true, | ||
"module": "commonjs", | ||
"jsx": "react", | ||
"lib": ["dom", "dom.iterable", "esnext"] | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
const HtmlWebpackPlugin = require("html-webpack-plugin"); | ||
|
||
module.exports = { | ||
mode: "development", | ||
entry: { | ||
index: "./examples/index.tsx", | ||
}, | ||
output: { | ||
filename: "[name].js", | ||
}, | ||
devtool: "source-map", | ||
resolve: { | ||
extensions: [".ts", ".tsx", ".js", ".jsx"], | ||
}, | ||
plugins: [ | ||
new HtmlWebpackPlugin({ | ||
filename: `index.html`, | ||
title: `Example`, | ||
template: "examples/index.ejs", | ||
}), | ||
], | ||
module: { | ||
rules: [ | ||
{ | ||
test: /\.tsx?$/, | ||
use: "ts-loader", | ||
}, | ||
{ | ||
test: /\.scss$/, | ||
use: ["style-loader", "css-loader", "sass-loader"], | ||
}, | ||
{ | ||
test: /\.svg$/, | ||
loader: "svg-url-loader", | ||
options: { | ||
noquotes: true, | ||
}, | ||
}, | ||
], | ||
}, | ||
devServer: { | ||
contentBase: "./", | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import React, { ReactNode } from "react"; | ||
interface Props { | ||
children?: ReactNode; | ||
position?: "top-right" | "top-left" | "bottom-right" | "bottom-left"; | ||
} | ||
export declare const ControlsContainer: React.FC<Props>; | ||
export {}; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.