buche-cytoscape
Advanced tools
Comparing version 0.0.4 to 0.1.0
208
lib/index.js
let cytoscape = require('cytoscape'); | ||
let cyqtip = require('cytoscape-qtip'); | ||
let cypopper = require('cytoscape-popper'); | ||
let tippy = require('tippy.js'); | ||
let dagre = require('cytoscape-dagre'); | ||
let debounce = require('debounce'); | ||
let cola = require('cytoscape-cola'); | ||
@@ -9,75 +11,55 @@ let {existsSync, readFileSync} = require('fs'); | ||
function installStyle() { | ||
let link = document.createElement('link'); | ||
link.setAttribute('type', 'text/css'); | ||
link.setAttribute('rel', 'stylesheet'); | ||
link.setAttribute('href', `${__dirname}/../resources/jquery.qtip.min.css`); | ||
document.head.appendChild(link); | ||
} | ||
cyqtip(cytoscape); | ||
cytoscape.use(dagre); | ||
cytoscape.use(cola); | ||
installStyle(); | ||
function setup(container, options) { | ||
options.container = container; | ||
if (typeof(options.style) === 'string' && existsSync(options.style)) { | ||
options.style = readFileSync(options.style, 'utf8'); | ||
class CytoscapeGraph extends BucheElement { | ||
setup(config, children) { | ||
this.nodes = {}; | ||
this.innerHTML = "<CytoscapeGraph> waiting for configuration."; | ||
this.style.position = 'relative'; | ||
this.style.display = 'block'; | ||
this.style.width = config.width || "800px"; | ||
this.style.height = config.height || "500px"; | ||
this.style.border = "1px solid black"; | ||
this.relayout = debounce( | ||
() => this.cy.layout(this.options.layout).run(), | ||
100 | ||
) | ||
} | ||
if (!options.layout) { | ||
options.layout = {name: "cola"}; | ||
} | ||
let cy = cytoscape(options); | ||
// Cytoscape will fail to properly paint canvas inside panes that | ||
// have display:none. Thankfully, Buche's tabs emit the 'display' | ||
// event on panes when they are activated, so we just go up the whole | ||
// hierarchy and set event listeners for 'display'. When triggered, | ||
// we repaint using cy.resize(). Seems to work well enough. | ||
let parent = container; | ||
while (parent) { | ||
parent.addEventListener('display', e => cy.resize()) | ||
parent = parent.parentNode || parent.host; | ||
} | ||
cy.nodes().each(function (node) { | ||
let data = node.data(); | ||
if (data.tooltip) { | ||
node.qtip({content: data.tooltip}); | ||
command_configure(path, cmd, _options) { | ||
let options = { | ||
boxSelectionEnabled: true, | ||
autounselectify: true, | ||
layout: {name: "cola"}, | ||
} | ||
}); | ||
return cy; | ||
} | ||
Object.assign(options, _options); | ||
this.innerHTML = ""; | ||
this._container = document.createElement('div'); | ||
this._container.style = "width: 100%; height: 100%"; | ||
this._appendChild(this._container); | ||
options.container = this._container; | ||
if (typeof(options.style) === 'string' && existsSync(options.style)) { | ||
options.style = readFileSync(options.style, 'utf8'); | ||
} | ||
this.options = options | ||
let cy = cytoscape(options); | ||
global['cy'] = cy | ||
class CytoscapeChannel extends Buche.Channel { | ||
setup() { | ||
this.config = Object.assign({}, this.options.options || {}); | ||
this.cy = setup(this.element, this.config); | ||
this.nodes = {}; | ||
// Cytoscape will fail to properly paint canvas inside panes that | ||
// have display:none. Thankfully, Buche's tabs emit the 'display' | ||
// event on panes when they are activated, so we just go up the whole | ||
// hierarchy and set event listeners for 'display'. When triggered, | ||
// we repaint using cy.resize(). Seems to work well enough. | ||
let parent = this; | ||
while (parent) { | ||
parent.addEventListener('display', _ => cy.resize()) | ||
parent = parent.parentNode || parent.host; | ||
} | ||
cy.elements().each((node) => this.installTooltip(node)); | ||
this.cy = cy | ||
} | ||
makeElement() { | ||
let d = document.createElement('div'); | ||
d.classList.add('cytoscape-channel'); | ||
d.style.width = "100%"; | ||
d.style.height = "100%"; | ||
return d; | ||
} | ||
dispatch_element(message) { | ||
let elem = message.options || {}; | ||
for (let field of ['data', 'classes']) { | ||
if (message[field]) { | ||
elem[field] = message[field]; | ||
} | ||
command_element(path, cmd, elem) { | ||
if (!elem.data) { | ||
elem.data = {}; | ||
} | ||
elem.data = elem.data || {}; | ||
for (let field of ['source', 'target', 'id']) { | ||
if (message[field]) { | ||
elem.data[field] = message[field]; | ||
} | ||
} | ||
let src = elem.data.source; | ||
@@ -98,69 +80,33 @@ let targ = elem.data.target; | ||
} | ||
this.cy.add(elem); | ||
let cyelem = this.cy.add(elem); | ||
this.installTooltip(cyelem); | ||
this.cy.resize(); | ||
this.cy.layout(this.config.layout).run(); | ||
this.relayout(); | ||
} | ||
} | ||
class CytoscapeElement extends Buche.BucheElement { | ||
setupEnd() { | ||
this.cy = setup(this, this.options); | ||
} | ||
template(children) { | ||
this.style.position = "relative"; | ||
let options = { | ||
container: this, | ||
boxSelectionEnabled: true, | ||
autounselectify: false, | ||
elements: [], | ||
style: "", | ||
layout: {name: this.getAttribute('layout') || 'cose'} | ||
}; | ||
for (let child of children) { | ||
let tag = child.tagName; | ||
let d = null; | ||
switch (tag) { | ||
case 'STYLE': | ||
options.style += child.textContent; | ||
break; | ||
case 'OPTIONS': | ||
case 'CONFIG': | ||
d = JSON.parse(child.textContent); | ||
options = Object.assign(options, d); | ||
break; | ||
case 'ELEMENT': | ||
d = JSON.parse(child.textContent); | ||
options.elements.push(d); | ||
break; | ||
} | ||
installTooltip(node) { | ||
let data = node.data(); | ||
if (data.tooltip) { | ||
let ref = node.popperRef(); | ||
let content = document.createElement('div'); | ||
content.innerHTML = data.tooltip; | ||
let tip = tippy(ref, { | ||
html: content, | ||
trigger: 'manual', | ||
arrow: true, | ||
theme: this.options.tooltipTheme || 'dark', | ||
}).tooltips[0]; | ||
node.on('tap', (() => { | ||
setTimeout(() => tip.show(), 0) | ||
})); | ||
} | ||
this.options = options; | ||
return []; | ||
} | ||
} | ||
addItem(item) { | ||
this.cy.add(item); | ||
} | ||
css() { | ||
let width = this.getAttribute('width') || "500px" | ||
let height = this.getAttribute('height') || "500px" | ||
return { | ||
"cytoscape-graph": { | ||
position: "relative", | ||
display: "block", | ||
border: "1px solid black", | ||
width: width, | ||
height: height | ||
} | ||
} | ||
} | ||
function bucheInstall() { | ||
cytoscape.use(cypopper); | ||
cytoscape.use(dagre); | ||
cytoscape.use(cola); | ||
customElements.define('cytoscape-graph', CytoscapeGraph); | ||
} | ||
@@ -170,9 +116,3 @@ | ||
module.exports = { | ||
isBuchePlugin: true, | ||
channels: { | ||
'cytoscape': CytoscapeChannel | ||
}, | ||
components: { | ||
'cytoscape-graph': CytoscapeElement | ||
} | ||
'bucheInstall': bucheInstall, | ||
} |
{ | ||
"name": "buche-cytoscape", | ||
"description": "Cytoscape channel and element for Buche.", | ||
"version": "0.0.4", | ||
"version": "0.1.0", | ||
"format": "cjs", | ||
@@ -18,15 +18,2 @@ "repository": { | ||
"main": "./lib/index.js", | ||
"buche": { | ||
"requireHowto": { | ||
"command": "require", | ||
"path": "/", | ||
"pluginName": "cytoscape", | ||
"channels": { | ||
"cytoscape": true | ||
}, | ||
"components": { | ||
"cytoscape-graph": true | ||
} | ||
} | ||
}, | ||
"dependencies": { | ||
@@ -36,5 +23,7 @@ "cytoscape": "^3.2.5", | ||
"cytoscape-dagre": "^2.1.0", | ||
"cytoscape-qtip": "^2.7.1" | ||
"cytoscape-popper": "^1.0.2", | ||
"debounce": "^1.2.0", | ||
"tippy.js": "^2.6.0" | ||
}, | ||
"devDependencies": {} | ||
} |
142
README.md
@@ -6,3 +6,3 @@ | ||
Provides the `cytoscape-graph` component and the `cytoscape` channel type. | ||
Provides the `cytoscape-graph` component. | ||
@@ -15,3 +15,3 @@ | ||
```json | ||
{"command":"require","path":"/","pluginName":"cytoscape"} | ||
{"command":"plugin","name":"cytoscape"} | ||
``` | ||
@@ -22,85 +22,103 @@ | ||
## `cytoscape` channel | ||
## `<cytoscape-graph>` component | ||
The `cytoscape` channel lets you define a graph and add nodes and edges in real time. | ||
The component is instantiated in HTML as follows: | ||
```html | ||
<cytoscape-graph width="1000px" height="500px"> | ||
<script type="buche/configure"> | ||
{ | ||
"style": "<style or path to style>", | ||
"layout": {"name": "cola"}, | ||
"elements": [ | ||
{"data": {"id": "A"}, | ||
{"data": {"id": "B"}, | ||
{"data": {"id": "C"}, | ||
{"data": {"source": "A", "target": "C"}}, | ||
{"data": {"source": "B", "target": "A"}}, | ||
{"data": {"source": "C", "target": "B"}} | ||
] | ||
} | ||
</script> | ||
</cytoscape-graph>` | ||
``` | ||
### Create the channel | ||
* [Configuration options](http://js.cytoscape.org/#getting-started/specifying-basic-options) | ||
* [Element options](http://js.cytoscape.org/#notation/elements-json) | ||
* [Styling nodes and edges](http://js.cytoscape.org/#style) | ||
```json | ||
{"command":"open","path":"/graph","type":"cytoscape","options":{"style":"<style or path>","layout":{"name":"cola"}, ...}} | ||
``` | ||
For the possible `options`, see: http://js.cytoscape.org/#core/initialisation | ||
### `configure` command | ||
The style can be given as a string, as a path to a css file, or as a JSON structure as described in cytoscape's documentation. See: http://js.cytoscape.org/#style | ||
If this is more convenient to you, you can leave out the configuration in `<cytoscape-graph>`, and use a separate command to configure it. For example (using node.js): | ||
Then you can use the `element` command to add nodes and edges. There is a simple and basic way to do it, and a more full-featured one. | ||
```javascript | ||
function buche(cfg) { | ||
console.log(JSON.stringify(cfg)); | ||
} | ||
### Add a node | ||
buche({ | ||
parent: "/", | ||
tag: 'cytoscape-graph', | ||
attributes: { | ||
address: 'graph', | ||
width: '1000px', | ||
height: '1000px', | ||
} | ||
}) | ||
This create a node with id `A`: | ||
```json | ||
{"command":"element","path":"/graph","id":"A"} | ||
buche({ | ||
parent: "/graph", | ||
command: "configure", | ||
style: `${__dirname}/graph-style.css`, | ||
layout: {name: "cola"}, | ||
elements: [ | ||
{"data": {"id": "A"}, | ||
{"data": {"id": "B"}, | ||
{"data": {"id": "C"}, | ||
{"data": {"source": "A", "target": "C"}}, | ||
{"data": {"source": "B", "target": "A"}}, | ||
{"data": {"source": "C", "target": "B"}} | ||
] | ||
}) | ||
``` | ||
### Add an edge | ||
The `<cytoscape-graph>` must have an `address` attribute in order for this to work, and the command must be directed to that address (see `parent` above). | ||
This creates an edge between nodes `A` and `B`, but note that both must exist before the edge can be defined. | ||
```json | ||
{"command":"element","path":"/graph","source":"A","target":"B"} | ||
``` | ||
### `element` command | ||
### Full featured | ||
Nodes and edges can be added incrementally using the `element` command. For example, to add node `D` and edge `A-D`: | ||
You can give an `options` object with more customization. Available options: http://js.cytoscape.org/#notation/elements-json | ||
```javascript | ||
buche({ | ||
parent: '/graph', | ||
command: 'element', | ||
data: { | ||
id: "D" | ||
} | ||
}); | ||
```json | ||
{"command":"element","path":"/graph","options": {"data": {"source":"A","target":"B"},"classes":"abc"}} | ||
buche({ | ||
parent: '/graph', | ||
command: 'element', | ||
data: { | ||
source: "A", | ||
target: "D" | ||
} | ||
}); | ||
``` | ||
## `cytoscape-graph` component | ||
It is allowed to omit the declaration of the nodes, since they will be created automatically if edges refer to them. You will need to declare the nodes, however, if you want to give them labels that differ from their id, or tooltips, or custom styles. | ||
Defines a self-contained graph in a single tag. | ||
Documentation for element creation is [here](http://js.cytoscape.org/#notation/elements-json). | ||
Children of the `cytoscape-graph` tag must be: | ||
* `<style>...</style>`, a the style for the graph. See: http://js.cytoscape.org/#style | ||
* `<config>...</config>` contains the options. See: http://js.cytoscape.org/#core/initialisation | ||
* `<element>...</element>` defines a graph element. See: http://js.cytoscape.org/#notation/elements-json | ||
### Tooltips | ||
```html | ||
<cytoscape-graph> | ||
<style> | ||
node { | ||
background-color: blue; | ||
content: data(id); | ||
} | ||
edge { | ||
line-color: green; | ||
target-arrow-color: green; | ||
target-arrow-shape: triangle; | ||
curve-style: bezier; | ||
} | ||
</style> | ||
<config> | ||
{ | ||
"layout": {"name": "cose"} | ||
} | ||
</config> | ||
<element>{"data": {"id": "A"}}</element> | ||
<element>{"data": {"id": "B"}}</element> | ||
<element>{"data": {"id": "C"}}</element> | ||
<element>{"data": {"id": "D"}}</element> | ||
<element>{"data": {"id": "E"}}</element> | ||
<element>{"data": {"source": "A", "target": "B"}}</element> | ||
<element>{"data": {"source": "B", "target": "C"}}</element> | ||
<element>{"data": {"source": "C", "target": "A"}}</element> | ||
<element>{"data": {"source": "A", "target": "D"}}</element> | ||
<element>{"data": {"source": "D", "target": "E"}}</element> | ||
</cytoscape-graph> | ||
``` | ||
Tooltips can be easily associated to any node or edge by setting the element's `data.tooltip` to some arbitrary HTML expression. See `examples/features.js`. | ||
### Examples | ||
The `examples/` directory contains a few examples in node.js or Python. There is also raw JSON output in the `.jsonl` files, so you can simply run `buche cat examples/features.jsonl` to check it out without having to run or install node.js. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23423
10
122
6
265
1
+ Addedcytoscape-popper@^1.0.2
+ Addeddebounce@^1.2.0
+ Addedtippy.js@^2.6.0
+ Addedcytoscape-popper@1.0.7(transitive)
+ Addeddebounce@1.2.1(transitive)
+ Addedpopper.js@1.16.1(transitive)
+ Addedtippy.js@2.6.0(transitive)
- Removedcytoscape-qtip@^2.7.1
- Removedcytoscape-qtip@2.8.0(transitive)
- Removedev-emitter@2.1.2(transitive)
- Removedimagesloaded@5.0.0(transitive)
- Removedjquery@3.7.1(transitive)
- Removedqtip2@3.0.3(transitive)