Migrate Designer to typescript

This commit is contained in:
Paulo Gustavo Veiga 2022-01-08 15:22:59 -08:00
parent 5c60c38d23
commit a0a5beb2ec
3 changed files with 88 additions and 76 deletions

View File

@ -45,13 +45,30 @@ import EventBusDispatcher from './layout/EventBusDispatcher';
import LayoutManager from './layout/LayoutManager'; import LayoutManager from './layout/LayoutManager';
import INodeModel, { TopicShape } from './model/INodeModel'; import { TopicShape } from './model/INodeModel';
import { $notify } from './widget/ToolbarNotifier'; import { $notify } from './widget/ToolbarNotifier';
import ImageExpoterFactory from './export/ImageExporterFactory'; import ImageExpoterFactory from './export/ImageExporterFactory';
import TextExporterFactory from './export/TextExporterFactory'; import TextExporterFactory from './export/TextExporterFactory';
import RelationshipModel from './model/RelationshipModel';
import { Mindmap } from '..';
import NodeModel from './model/NodeModel';
import Topic from './Topic';
import Point from '@wisemapping/web2d';
import { DesignerOptions } from './DesignerOptions';
class Designer extends Events { class Designer extends Events {
constructor(options, divElement) { private _mindmap: Mindmap;
private _options: DesignerOptions;
private _actionDispatcher: StandaloneActionDispatcher;
private _model: DesignerModel;
private _workspace: Workspace;
private _eventBussDispatcher: EventBusDispatcher;
private _dragManager: DragManager;
private _relPivot: RelationshipPivot;
private _clipboard: any[];
private _cleanScreen: any;
constructor(options: DesignerOptions, divElement) {
$assert(options, 'options must be defined'); $assert(options, 'options must be defined');
$assert(options.zoom, 'zoom must be defined'); $assert(options.zoom, 'zoom must be defined');
$assert(options.size, 'size must be defined'); $assert(options.size, 'size must be defined');
@ -83,7 +100,7 @@ class Designer extends Events {
this._workspace = new Workspace(screenManager, this._model.getZoom(), !!options.readOnly); this._workspace = new Workspace(screenManager, this._model.getZoom(), !!options.readOnly);
// Init layout manager ... // Init layout manager ...
this._eventBussDispatcher = new EventBusDispatcher(this.getModel()); this._eventBussDispatcher = new EventBusDispatcher();
// Register events // Register events
if (!this.isReadOnly()) { if (!this.isReadOnly()) {
@ -109,29 +126,20 @@ class Designer extends Events {
global.designer = this; global.designer = this;
} }
/** private _registerWheelEvents(): void {
* @private
*/
_registerWheelEvents() {
const zoomFactor = 1.006; const zoomFactor = 1.006;
const me = this;
// Zoom In and Zoom Out must active event
document.addEventListener('wheel', (event) => { document.addEventListener('wheel', (event) => {
if (event.deltaX > 0 || event.deltaY > 0) { if (event.deltaX > 0 || event.deltaY > 0) {
me.zoomOut(zoomFactor); this.zoomOut(zoomFactor);
} else { } else {
me.zoomIn(zoomFactor); this.zoomIn(zoomFactor);
} }
event.preventDefault(); event.preventDefault();
}, { passive: false }); }, { passive: false });
} }
/** // @ts-ignore
* @param {String} type the event type addEvent(type: string, listener: Function): void {
* @param {Function} listener
* forwards to the TopicEventDispatcher or the parent Events class, depending on the type
*/
addEvent(type, listener) {
if (type === TopicEvent.EDIT || type === TopicEvent.CLICK) { if (type === TopicEvent.EDIT || type === TopicEvent.CLICK) {
const editor = TopicEventDispatcher.getInstance(); const editor = TopicEventDispatcher.getInstance();
editor.addEvent(type, listener); editor.addEvent(type, listener);
@ -140,10 +148,7 @@ class Designer extends Events {
} }
} }
/** private _registerMouseEvents() {
* @private
*/
_registerMouseEvents() {
const workspace = this._workspace; const workspace = this._workspace;
const screenManager = workspace.getScreenManager(); const screenManager = workspace.getScreenManager();
const me = this; const me = this;
@ -181,7 +186,7 @@ class Designer extends Events {
* @return {mindplot.DragManager} the new dragManager for the workspace with events * @return {mindplot.DragManager} the new dragManager for the workspace with events
* registered * registered
*/ */
_buildDragManager(workspace) { _buildDragManager(workspace: Workspace) {
const designerModel = this.getModel(); const designerModel = this.getModel();
const dragConnector = new DragConnector(designerModel, this._workspace); const dragConnector = new DragConnector(designerModel, this._workspace);
const dragManager = new DragManager(workspace, this._eventBussDispatcher); const dragManager = new DragManager(workspace, this._eventBussDispatcher);
@ -229,7 +234,7 @@ class Designer extends Events {
* @return {mindplot.CentralTopic|mindplot.MainTopic} the topic to the given model, * @return {mindplot.CentralTopic|mindplot.MainTopic} the topic to the given model,
* connected, added to the drag manager, with events registered - complying type & read mode * connected, added to the drag manager, with events registered - complying type & read mode
*/ */
_buildNodeGraph(model, readOnly) { _buildNodeGraph(model: NodeModel, readOnly: boolean) {
// Create node graph ... // Create node graph ...
const topic = create(model, { readOnly }); const topic = create(model, { readOnly });
this.getModel().addTopic(topic); this.getModel().addTopic(topic);
@ -292,7 +297,7 @@ class Designer extends Events {
* sets focus to the given currentObject and removes it from any other objects if not * sets focus to the given currentObject and removes it from any other objects if not
* triggered with Ctrl pressed * triggered with Ctrl pressed
*/ */
onObjectFocusEvent(currentObject, event) { onObjectFocusEvent(currentObject: Topic = null, event = null): void {
// Close node editors .. // Close node editors ..
const topics = this.getModel().getTopics(); const topics = this.getModel().getTopics();
topics.forEach((topic) => topic.closeEditors()); topics.forEach((topic) => topic.closeEditors());
@ -310,7 +315,7 @@ class Designer extends Events {
} }
/** sets focus to all model entities, i.e. relationships and topics */ /** sets focus to all model entities, i.e. relationships and topics */
selectAll() { selectAll(): void {
const model = this.getModel(); const model = this.getModel();
const objects = model.getEntities(); const objects = model.getEntities();
objects.forEach((object) => { objects.forEach((object) => {
@ -319,7 +324,7 @@ class Designer extends Events {
} }
/** removes focus from all model entities, i.e. relationships and topics */ /** removes focus from all model entities, i.e. relationships and topics */
deselectAll() { deselectAll(): void {
const objects = this.getModel().getEntities(); const objects = this.getModel().getEntities();
objects.forEach((object) => { objects.forEach((object) => {
object.setOnFocus(false); object.setOnFocus(false);
@ -330,7 +335,7 @@ class Designer extends Events {
* Set the zoom of the map * Set the zoom of the map
* @param {Number} zoom number between 0.3 and 1.9 * @param {Number} zoom number between 0.3 and 1.9
*/ */
setZoom(zoom) { setZoom(zoom: number): void {
if (zoom > 1.9 || zoom < 0.3) { if (zoom > 1.9 || zoom < 0.3) {
$notify($msg('ZOOM_IN_ERROR')); $notify($msg('ZOOM_IN_ERROR'));
return; return;
@ -343,7 +348,7 @@ class Designer extends Events {
* @param {Number=} factor * @param {Number=} factor
* zoom out by the given factor, or 1.2, if undefined * zoom out by the given factor, or 1.2, if undefined
*/ */
zoomOut(factor = 1.2) { zoomOut(factor: number = 1.2) {
const model = this.getModel(); const model = this.getModel();
const scale = model.getZoom() * factor; const scale = model.getZoom() * factor;
if (scale <= 1.9) { if (scale <= 1.9) {
@ -354,7 +359,7 @@ class Designer extends Events {
} }
} }
export(formatType) { export(formatType: 'png' | 'svg' | 'jpg' | 'wxml'): String {
const workspace = this._workspace; const workspace = this._workspace;
const svgElement = workspace.getSVGElement(); const svgElement = workspace.getSVGElement();
const size = workspace.getSize(); const size = workspace.getSize();
@ -378,11 +383,12 @@ class Designer extends Events {
return exporter.export(); return exporter.export();
} }
/** /**
* @param {Number=} factor * @param {Number=} factor
* zoom in by the given factor, or 1.2, if undefined * zoom in by the given factor, or 1.2, if undefined
*/ */
zoomIn(factor = 1.2) { zoomIn(factor: number = 1.2) {
const model = this.getModel(); const model = this.getModel();
const scale = model.getZoom() / factor; const scale = model.getZoom() / factor;
@ -395,7 +401,7 @@ class Designer extends Events {
} }
/** copy selected topics to a private clipboard */ /** copy selected topics to a private clipboard */
copyToClipboard() { copyToClipboard(): void {
let topics = this.getModel().filterSelectedTopics(); let topics = this.getModel().filterSelectedTopics();
if (topics.length <= 0) { if (topics.length <= 0) {
// If there are more than one node selected, // If there are more than one node selected,
@ -420,7 +426,7 @@ class Designer extends Events {
} }
/** paste clipboard contents to the mindmap */ /** paste clipboard contents to the mindmap */
pasteClipboard() { pasteClipboard(): void {
if (this._clipboard.length === 0) { if (this._clipboard.length === 0) {
$notify($msg('CLIPBOARD_IS_EMPTY')); $notify($msg('CLIPBOARD_IS_EMPTY'));
return; return;
@ -429,8 +435,7 @@ class Designer extends Events {
this._clipboard = []; this._clipboard = [];
} }
/** @return {mindplot.DesignerModel} model */ getModel(): DesignerModel {
getModel() {
return this._model; return this._model;
} }
@ -475,7 +480,7 @@ class Designer extends Events {
/** /**
* @private * @private
*/ */
_copyNodeProps(sourceModel, targetModel) { _copyNodeProps(sourceModel: NodeModel, targetModel: NodeModel) {
// I don't copy the font size if the target is the source is the central topic. // I don't copy the font size if the target is the source is the central topic.
if (sourceModel.getType() !== 'CentralTopic') { if (sourceModel.getType() !== 'CentralTopic') {
const fontSize = sourceModel.getFontSize(); const fontSize = sourceModel.getFontSize();
@ -526,7 +531,7 @@ class Designer extends Events {
* @param {Point} mousePos the mouse position * @param {Point} mousePos the mouse position
* @return {NodeModel} the node model for the new child * @return {NodeModel} the node model for the new child
*/ */
_createChildModel(topic, mousePos) { _createChildModel(topic: Topic, mousePos: Point = null): NodeModel {
// Create a new node ... // Create a new node ...
const parentModel = topic.getModel(); const parentModel = topic.getModel();
const mindmap = parentModel.getMindmap(); const mindmap = parentModel.getMindmap();
@ -545,7 +550,7 @@ class Designer extends Events {
return childModel; return childModel;
} }
addDraggedNode(event, model) { addDraggedNode(event, model: NodeModel) {
$assert(event, 'event can not be null'); $assert(event, 'event can not be null');
$assert(model, 'model can not be null'); $assert(model, 'model can not be null');
@ -601,7 +606,7 @@ class Designer extends Events {
* @param {mindplot.Topic} topic the topic to create the sibling to * @param {mindplot.Topic} topic the topic to create the sibling to
* @return {mindplot.NodeModel} the node model of the sibling * @return {mindplot.NodeModel} the node model of the sibling
*/ */
_createSiblingModel(topic) { _createSiblingModel(topic: Topic) {
let result = null; let result = null;
let model = null; let model = null;
const parentTopic = topic.getOutgoingConnectedTopic(); const parentTopic = topic.getOutgoingConnectedTopic();
@ -648,16 +653,16 @@ class Designer extends Events {
} }
/** /**
* @param {mindplot.Mindmap} model * @param {mindplot.Mindmap} mindmap
* @throws will throw an error if mindmapModel is null or undefined * @throws will throw an error if mindmapModel is null or undefined
*/ */
loadMap(model) { loadMap(mindmap: Mindmap) {
$assert(model, 'mindmapModel can not be null'); $assert(mindmap, 'mindmapModel can not be null');
this._mindmap = model; this._mindmap = mindmap;
// Init layout manager ... // Init layout manager ...
const size = { width: 25, height: 25 }; const size = { width: 25, height: 25 };
const layoutManager = new LayoutManager(model.getCentralTopic().getId(), size); const layoutManager = new LayoutManager(mindmap.getCentralTopic().getId(), size);
const me = this; const me = this;
layoutManager.addEvent('change', (event) => { layoutManager.addEvent('change', (event) => {
const id = event.getId(); const id = event.getId();
@ -668,14 +673,14 @@ class Designer extends Events {
this._eventBussDispatcher.setLayoutManager(layoutManager); this._eventBussDispatcher.setLayoutManager(layoutManager);
// Building node graph ... // Building node graph ...
const branches = model.getBranches(); const branches = mindmap.getBranches();
branches.forEach((branch) => { branches.forEach((branch) => {
const nodeGraph = this.nodeModelToNodeGraph(branch); const nodeGraph = this.nodeModelToNodeGraph(branch);
nodeGraph.setBranchVisibility(true); nodeGraph.setBranchVisibility(true);
}); });
// Connect relationships ... // Connect relationships ...
const relationships = model.getRelationships(); const relationships = mindmap.getRelationships();
relationships.forEach((relationship) => this._relationshipModelToRelationship(relationship)); relationships.forEach((relationship) => this._relationshipModelToRelationship(relationship));
// Place the focus on the Central Topic // Place the focus on the Central Topic
@ -688,8 +693,7 @@ class Designer extends Events {
this.fireEvent('loadSuccess'); this.fireEvent('loadSuccess');
} }
/** */ getMindmap(): Mindmap {
getMindmap() {
return this._mindmap; return this._mindmap;
} }
@ -713,7 +717,7 @@ class Designer extends Events {
* @param {mindplot.model.NodeModel} nodeModel * @param {mindplot.model.NodeModel} nodeModel
* @return {mindplot.Topic} the topic (extends mindplot.NodeGraph) created to the model * @return {mindplot.Topic} the topic (extends mindplot.NodeGraph) created to the model
*/ */
nodeModelToNodeGraph(nodeModel) { nodeModelToNodeGraph(nodeModel: NodeModel) {
$assert(nodeModel, 'Node model can not be null'); $assert(nodeModel, 'Node model can not be null');
let children = nodeModel.getChildren().slice(); let children = nodeModel.getChildren().slice();
children = children.sort((a, b) => a.getOrder() - b.getOrder()); children = children.sort((a, b) => a.getOrder() - b.getOrder());
@ -736,7 +740,7 @@ class Designer extends Events {
* @return {mindplot.Relationship} the relationship created to the model * @return {mindplot.Relationship} the relationship created to the model
* @throws will throw an error if model is null or undefined * @throws will throw an error if model is null or undefined
*/ */
_relationshipModelToRelationship(model) { private _relationshipModelToRelationship(model: RelationshipModel) {
$assert(model, 'Node model can not be null'); $assert(model, 'Node model can not be null');
const result = this._buildRelationshipShape(model); const result = this._buildRelationshipShape(model);
@ -757,7 +761,7 @@ class Designer extends Events {
* @param {mindplot.model.RelationshipModel} model * @param {mindplot.model.RelationshipModel} model
* @return {mindplot.Relationship} the relationship added to the mindmap * @return {mindplot.Relationship} the relationship added to the mindmap
*/ */
addRelationship(model) { addRelationship(model: RelationshipModel) {
const mindmap = this.getMindmap(); const mindmap = this.getMindmap();
mindmap.addRelationship(model); mindmap.addRelationship(model);
return this._relationshipModelToRelationship(model); return this._relationshipModelToRelationship(model);
@ -787,7 +791,7 @@ class Designer extends Events {
* @return {mindplot.Relationship} the new relationship with events registered * @return {mindplot.Relationship} the new relationship with events registered
* @throws will throw an error if the target topic cannot be found * @throws will throw an error if the target topic cannot be found
*/ */
_buildRelationshipShape(model) { _buildRelationshipShape(model: RelationshipModel) {
const dmodel = this.getModel(); const dmodel = this.getModel();
const sourceTopicId = model.getFromNode(); const sourceTopicId = model.getFromNode();
@ -898,7 +902,7 @@ class Designer extends Events {
} }
/** */ /** */
changeFontFamily(font) { changeFontFamily(font: string) {
const topicsIds = this.getModel().filterTopicsIds(); const topicsIds = this.getModel().filterTopicsIds();
if (topicsIds.length > 0) { if (topicsIds.length > 0) {
this._actionDispatcher.changeFontFamilyToTopic(topicsIds, font); this._actionDispatcher.changeFontFamilyToTopic(topicsIds, font);
@ -906,8 +910,9 @@ class Designer extends Events {
} }
/** */ /** */
changeFontStyle() { changeFontStyle(): void {
const topicsIds = this.getModel().filterTopicsIds(); const topicsIds = this.getModel()
.filterTopicsIds();
if (topicsIds.length > 0) { if (topicsIds.length > 0) {
this._actionDispatcher.changeFontStyleToTopic(topicsIds); this._actionDispatcher.changeFontStyleToTopic(topicsIds);
} }
@ -917,7 +922,8 @@ class Designer extends Events {
changeFontColor(color) { changeFontColor(color) {
$assert(color, 'color can not be null'); $assert(color, 'color can not be null');
const topicsIds = this.getModel().filterTopicsIds(); const topicsIds = this.getModel()
.filterTopicsIds();
if (topicsIds.length > 0) { if (topicsIds.length > 0) {
this._actionDispatcher.changeFontColorToTopic(topicsIds, color); this._actionDispatcher.changeFontColorToTopic(topicsIds, color);
} }
@ -935,7 +941,7 @@ class Designer extends Events {
} }
/** */ /** */
changeBorderColor(color) { changeBorderColor(color:string) {
const validateFunc = (topic) => topic.getShapeType() !== TopicShape.LINE; const validateFunc = (topic) => topic.getShapeType() !== TopicShape.LINE;
const validateError = 'Color can not be set to line topics.'; const validateError = 'Color can not be set to line topics.';
const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError); const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError);
@ -945,7 +951,7 @@ class Designer extends Events {
} }
/** */ /** */
changeFontSize(size) { changeFontSize(size:number) {
const topicsIds = this.getModel().filterTopicsIds(); const topicsIds = this.getModel().filterTopicsIds();
if (topicsIds.length > 0) { if (topicsIds.length > 0) {
this._actionDispatcher.changeFontSizeToTopic(topicsIds, size); this._actionDispatcher.changeFontSizeToTopic(topicsIds, size);

View File

@ -16,11 +16,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { $assert, $defined } from '@wisemapping/core-js'; import { $assert, $defined } from '@wisemapping/core-js';
import { DesignerOptions } from './DesignerOptions';
import Events from './Events'; import Events from './Events';
import Relationship from './Relationship';
import Topic from './Topic';
import { $notify } from './widget/ToolbarNotifier'; import { $notify } from './widget/ToolbarNotifier';
class DesignerModel extends Events { class DesignerModel extends Events {
constructor(options) { _zoom: number;
_topics: Topic[];
_relationships: Relationship[];
constructor(options: DesignerOptions) {
super(); super();
this._zoom = options.zoom; this._zoom = options.zoom;
this._topics = []; this._topics = [];
@ -54,7 +61,7 @@ class DesignerModel extends Events {
} }
/** @return {mindplot.Topic[]} selected topics */ /** @return {mindplot.Topic[]} selected topics */
filterSelectedTopics() { filterSelectedTopics(): Topic[] {
const result = []; const result = [];
for (let i = 0; i < this._topics.length; i++) { for (let i = 0; i < this._topics.length; i++) {
if (this._topics[i].isOnFocus()) { if (this._topics[i].isOnFocus()) {
@ -126,37 +133,28 @@ class DesignerModel extends Events {
this._relationships.push(rel); this._relationships.push(rel);
} }
/** filterTopicsIds(validate: (topic: Topic) => boolean = null, errorMsg = null): Topic[] {
* @param {Function=} validate a function to validate nodes
* @param {String=} errorMsg an error message to display if the validation fails
* @return {String} returns an array of the selected (and, if applicable, valid) topics' ids
*/
filterTopicsIds(validate, errorMsg) {
const result = []; const result = [];
const topics = this.filterSelectedTopics(); const topics = this.filterSelectedTopics();
let isValid = true; let isValid = true;
for (let i = 0; i < topics.length; i++) { topics.forEach(topic => {
const selectedNode = topics[i];
if ($defined(validate)) { if ($defined(validate)) {
isValid = validate(selectedNode); isValid = validate(topic);
} }
// Add node only if it's valid. // Add node only if it's valid.
if (isValid) { if (isValid) {
result.push(selectedNode.getId()); result.push(topic.getId());
} else { } else {
$notify(errorMsg); $notify(errorMsg);
} }
} });
return result; return result;
} }
/** selectedTopic(): Topic {
* @return {mindplot.Topic} the first selected topic if one or more are found by the
* filterSelectedTopics function, null otherwise
*/
selectedTopic() {
const topics = this.filterSelectedTopics(); const topics = this.filterSelectedTopics();
return (topics.length > 0) ? topics[0] : null; return (topics.length > 0) ? topics[0] : null;
} }
@ -165,7 +163,7 @@ class DesignerModel extends Events {
* @param {String} id the id of the topic to be retrieved * @param {String} id the id of the topic to be retrieved
* @return {mindplot.Topic} the topic with the respective id * @return {mindplot.Topic} the topic with the respective id
*/ */
findTopicById(id) { findTopicById(id:Number):Topic {
let result = null; let result = null;
for (let i = 0; i < this._topics.length; i++) { for (let i = 0; i < this._topics.length; i++) {
const topic = this._topics[i]; const topic = this._topics[i];

View File

@ -0,0 +1,8 @@
export type DesignerOptions = {
locale: string,
zoom: number,
size: { height: number, witdh: number },
readOnly: boolean,
viewPort: { height: number, witdh: number },
};