francy-renderer-vis
Version:
Francy - An Interactive Discrete Mathematics Framework for GAP - A Vis.js Renderer
124 lines (104 loc) • 4.98 kB
JavaScript
import { CompositeRenderer, Decorators, Logger, RENDERING_EVENTS } from 'francy-core';
import ChartGeneric from './chart/generic';
import GraphGeneric from './graph/generic';
/* global d3 */
export default class Canvas extends CompositeRenderer {
constructor({ appendTo, callbackHandler }, context) {
super({ appendTo: appendTo, callbackHandler: callbackHandler }, context);
this.graphFactory = new GraphGeneric(this.options, this.context);
this.chartFactory = new ChartGeneric(this.options, this.context);
this.visLayoutSortMethod = ['directed', 'hubsize'];
this.visLayoutDirections = { 'UD': 'Top to Bottom', 'LR': 'Left to Right', 'DU': 'Bottom to Top', 'RL': 'Right to Left' };
// this only adds if does not exist
this.context.configuration.addProperty('visLayoutSortMethod', 'directed');
this.context.configuration.addProperty('visLayoutDirection', 'UD');
}
@Decorators.Data.requires('canvas')
async render() {
const canvasId = `Canvas-${this.data.canvas.id}`;
this.element = d3.select(`div#${canvasId}`);
// check if the canvas is already present
if (!this.element.node()) {
// create a svg element detached from the DOM!
Logger.debug(`(${this.context.instanceId}) Creating Canvas [${canvasId}]...`);
this.element = this.parent.append('div')
.classed('francy-canvas', true)
.attr('id', canvasId);
}
// cannot continue if canvas is not present
if (!this.element.node()) {
throw new Error(`Oops, could not create canvas with id [${canvasId}]... Cannot proceed.`);
}
this.zoomToFit = () => true;
this.element.zoomToFit = this.zoomToFit;
Logger.debug(`(${this.context.instanceId}) Canvas updated [${canvasId}]...`);
this._buildMenu();
this.removeChildren();
this.addChild(this.graphFactory).addChild(this.chartFactory);
this.handlePromise(this.renderChildren());
return this;
}
_buildMenu() {
var self = this;
// here we have access to MainMenu
this.visLayoutSortMethod.forEach((method) => {
this.parentClass.MainMenu.addMultiMenuOnSettingsMenu({
menuId: 'vis-sort-method-entry',
menuTitle: 'Sort Method',
entryId: `vis-sort-method-${method}-entry`,
entryTitle: `${self.context.configuration.object.visLayoutSortMethod === method ? '☑' : '☐'} ${method}`,
entryOnClickCallback: function () {
self.context.configuration.object.visLayoutSortMethod = method;
},
entryOnEachCallback: function () {
let methodCheckId = `vis-sort-method-checkbox-${self.data.canvas.id}`;
self.context.configuration.subscribe('visLayoutSortMethod', function (value) {
d3.select(this).html(`${value === method ? '☑' : '☐'} ${method}`);
}, methodCheckId);
}
});
});
function reRender() {
// remove previous rendered canvas
self.element.select('g').selectAll('*').remove();
// re-render
setTimeout(() => {
let Renderer = self.context.renderingManager.activeRenderer();
self.options.appendTo.data = self.options.appendTo.canvas.data;
self.options.appendTo.canvas = new Renderer(self.options, self.context);
self.handlePromise(self.options.appendTo.render());
}, 100);
}
// re-render when engine changes
let engineRenderId = `vis-sort-method-reRender-${self.data.canvas.id}`;
self.context.configuration.subscribe('visLayoutSortMethod', reRender, engineRenderId);
Object.keys(this.visLayoutDirections).forEach((dir) => {
this.parentClass.MainMenu.addMultiMenuOnSettingsMenu({
menuId: 'vis-dir-entry',
menuTitle: 'Direction',
entryId: `vis-dir-${dir}-entry`,
entryTitle: `${self.context.configuration.object.visLayoutDirection === dir ? '☑' : '☐'} ${self.visLayoutDirections[dir]}`,
entryOnClickCallback: function () {
self.context.configuration.object.visLayoutDirection = dir;
},
entryOnEachCallback: function () {
let dirCheckId = `vis-dir-${self.data.canvas.id}`;
self.context.configuration.subscribe('visLayoutDirection', function (value) {
d3.select(this).html(`${value === dir ? '☑' : '☐'} ${dir}`);
}, dirCheckId);
}
});
});
// re-render when engine changes
let dirRenderId = `vis-dir-reRender-${self.data.canvas.id}`;
self.context.configuration.subscribe('visLayoutDirection', reRender, dirRenderId);
// remove menu if renderer is disabled
let rendererDisableId = `vis-disable-${self.data.canvas.id}`;
self.context.renderingManager.subscribe(RENDERING_EVENTS.STATUS, function (r) {
if (!r.enable) {
self.parentClass.MainMenu.removeMenuEntry('vis-sort-method-entry');
self.parentClass.MainMenu.removeMenuEntry('vis-dir-entry');
}
}, rendererDisableId);
}
}