Add center position action.

Improve screenmanager positioning
Change several clasess to typescript
This commit is contained in:
Paulo Gustavo Veiga 2022-01-09 10:43:04 -08:00
parent 1bebfa3dff
commit cdd044c41d
23 changed files with 233 additions and 209 deletions

View File

@ -32,6 +32,7 @@
}, },
"private": false, "private": false,
"dependencies": { "dependencies": {
"@types/jquery": "^3.5.11",
"@wisemapping/core-js": "^0.4.0", "@wisemapping/core-js": "^0.4.0",
"@wisemapping/web2d": "^0.4.0", "@wisemapping/web2d": "^0.4.0",
"jest": "^27.4.5", "jest": "^27.4.5",

View File

@ -1 +0,0 @@
MessageBundle_*

View File

@ -54,8 +54,9 @@ import { Mindmap } from '..';
import NodeModel from './model/NodeModel'; import NodeModel from './model/NodeModel';
import Topic from './Topic'; import Topic from './Topic';
import Point from '@wisemapping/web2d'; import Point from '@wisemapping/web2d';
import { DesignerOptions } from './DesignerOptions'; import { DesignerOptions } from './DesignerOptionsBuilder';
import MainTopic from './MainTopic'; import MainTopic from './MainTopic';
import DragTopic from './DragTopic';
class Designer extends Events { class Designer extends Events {
private _mindmap: Mindmap; private _mindmap: Mindmap;
@ -69,7 +70,7 @@ class Designer extends Events {
private _clipboard: any[]; private _clipboard: any[];
private _cleanScreen: any; private _cleanScreen: any;
constructor(options: DesignerOptions, divElement) { constructor(options: DesignerOptions, divElement:JQuery) {
$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');
@ -166,12 +167,12 @@ class Designer extends Events {
}); });
// Deselect on click ... // Deselect on click ...
screenManager.addEvent('click', (event) => { screenManager.addEvent('click', (event:UIEvent) => {
me.onObjectFocusEvent(null, event); me.onObjectFocusEvent(null, event);
}); });
// Create nodes on double click... // Create nodes on double click...
screenManager.addEvent('dblclick', (event) => { screenManager.addEvent('dblclick', (event:MouseEvent) => {
if (workspace.isWorkspaceEventsEnabled()) { if (workspace.isWorkspaceEventsEnabled()) {
const mousePos = screenManager.getWorkspaceMousePosition(event); const mousePos = screenManager.getWorkspaceMousePosition(event);
const centralTopic = me.getModel().getCentralTopic(); const centralTopic = me.getModel().getCentralTopic();
@ -181,13 +182,7 @@ class Designer extends Events {
}); });
} }
/** private _buildDragManager(workspace: Workspace):DragManager {
* @private
* @param {mindplot.Workspace} workspace
* @return {mindplot.DragManager} the new dragManager for the workspace with events
* registered
*/
_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);
@ -198,7 +193,7 @@ class Designer extends Events {
topics.forEach((topic) => topic.setMouseEventsEnabled(false)); topics.forEach((topic) => topic.setMouseEventsEnabled(false));
}); });
dragManager.addEvent('dragging', (event, dragTopic) => { dragManager.addEvent('dragging', (event:MouseEvent, dragTopic:DragTopic) => {
dragTopic.updateFreeLayout(event); dragTopic.updateFreeLayout(event);
if (!dragTopic.isFreeLayoutOn(event)) { if (!dragTopic.isFreeLayoutOn(event)) {
// The node is being drag. Is the connection still valid ? // The node is being drag. Is the connection still valid ?
@ -210,7 +205,7 @@ class Designer extends Events {
} }
}); });
dragManager.addEvent('enddragging', (event, dragTopic) => { dragManager.addEvent('enddragging', (event:MouseEvent, dragTopic:DragTopic) => {
topics.forEach((topic) => topic.setMouseEventsEnabled(true)); topics.forEach((topic) => topic.setMouseEventsEnabled(true));
dragTopic.applyChanges(workspace); dragTopic.applyChanges(workspace);
}); });
@ -218,11 +213,7 @@ class Designer extends Events {
return dragManager; return dragManager;
} }
/** private setViewPort(size: { height: number, width: number }):void {
* @param {{width:Number, height:Number}} size
* sets width and height of the workspace
*/
setViewPort(size: { height: number, width: number }) {
this._workspace.setViewPort(size); this._workspace.setViewPort(size);
const model = this.getModel(); const model = this.getModel();
this._workspace.setZoom(model.getZoom(), true); this._workspace.setZoom(model.getZoom(), true);
@ -325,10 +316,6 @@ class Designer extends Events {
}); });
} }
/**
* Set the zoom of the map
* @param {Number} zoom number between 0.3 and 1.9
*/
setZoom(zoom: number): void { 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'));
@ -338,6 +325,11 @@ class Designer extends Events {
this._workspace.setZoom(zoom); this._workspace.setZoom(zoom);
} }
setZoomToFit(): void {
this.getModel().setZoom(1);
this._workspace.setZoom(1,true);
}
zoomOut(factor: number = 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;
@ -374,11 +366,7 @@ class Designer extends Events {
} }
/** zoomIn(factor: number = 1.2): void {
* @param {Number=} factor
* zoom in by the given factor, or 1.2, if undefined
*/
zoomIn(factor: number = 1.2) {
const model = this.getModel(); const model = this.getModel();
const scale = model.getZoom() / factor; const scale = model.getZoom() / factor;
@ -390,7 +378,6 @@ class Designer extends Events {
} }
} }
/** copy selected topics to a private clipboard */
copyToClipboard(): void { copyToClipboard(): void {
let topics = this.getModel().filterSelectedTopics(); let topics = this.getModel().filterSelectedTopics();
if (topics.length <= 0) { if (topics.length <= 0) {
@ -415,7 +402,6 @@ class Designer extends Events {
$notify($msg('SELECTION_COPIED_TO_CLIPBOARD')); $notify($msg('SELECTION_COPIED_TO_CLIPBOARD'));
} }
/** paste clipboard contents to the mindmap */
pasteClipboard(): void { pasteClipboard(): void {
if (this._clipboard.length === 0) { if (this._clipboard.length === 0) {
$notify($msg('CLIPBOARD_IS_EMPTY')); $notify($msg('CLIPBOARD_IS_EMPTY'));
@ -429,8 +415,7 @@ class Designer extends Events {
return this._model; return this._model;
} }
/** collapse the subtree of the selected topic */ shrinkSelectedBranch(): void {
shrinkSelectedBranch() {
const nodes = this.getModel().filterSelectedTopics(); const nodes = this.getModel().filterSelectedTopics();
if (nodes.length <= 0 || nodes.length !== 1) { if (nodes.length <= 0 || nodes.length !== 1) {
// If there are more than one node selected, // If there are more than one node selected,
@ -445,7 +430,7 @@ class Designer extends Events {
} }
/** create a NodeModel for the selected node's child and add it via the ActionDispatcher */ /** create a NodeModel for the selected node's child and add it via the ActionDispatcher */
createChildForSelectedNode() { createChildForSelectedNode():void {
const nodes = this.getModel().filterSelectedTopics(); const nodes = this.getModel().filterSelectedTopics();
if (nodes.length <= 0) { if (nodes.length <= 0) {
// If there are more than one node selected, // If there are more than one node selected,
@ -467,10 +452,7 @@ class Designer extends Events {
this._actionDispatcher.addTopics([childModel], [parentTopicId]); this._actionDispatcher.addTopics([childModel], [parentTopicId]);
} }
/** private _copyNodeProps(sourceModel: NodeModel, targetModel: NodeModel) {
* @private
*/
_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();
@ -515,13 +497,7 @@ class Designer extends Events {
} }
} }
/** private _createChildModel(topic: Topic, mousePos: Point = null): NodeModel {
* @private
* @param {Topic} topic the parent topic of the child to create the NodeModel for
* @param {Point} mousePos the mouse position
* @return {NodeModel} the node model for the new child
*/
_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();
@ -554,11 +530,7 @@ class Designer extends Events {
topic.fireEvent('mousedown', event); topic.fireEvent('mousedown', event);
} }
/** createSiblingForSelectedNode():void {
* creates a sibling or child node of the selected node, if the selected node is the
* central topic
*/
createSiblingForSelectedNode() {
const nodes = this.getModel().filterSelectedTopics(); const nodes = this.getModel().filterSelectedTopics();
if (nodes.length <= 0) { if (nodes.length <= 0) {
// If there are no nodes selected, // If there are no nodes selected,
@ -591,12 +563,7 @@ class Designer extends Events {
} }
} }
/** private _createSiblingModel(topic: Topic):NodeModel {
* @private
* @param {mindplot.Topic} topic the topic to create the sibling to
* @return {mindplot.NodeModel} the node model of the sibling
*/
_createSiblingModel(topic: Topic) {
let result = null; let result = null;
let model = null; let model = null;
const parentTopic = topic.getOutgoingConnectedTopic(); const parentTopic = topic.getOutgoingConnectedTopic();
@ -617,10 +584,7 @@ class Designer extends Events {
return result; return result;
} }
/** showRelPivot(event):void {
* @param {Event} event
*/
showRelPivot(event) {
const nodes = this.getModel().filterSelectedTopics(); const nodes = this.getModel().filterSelectedTopics();
if (nodes.length <= 0) { if (nodes.length <= 0) {
// This could not happen ... // This could not happen ...
@ -761,7 +725,7 @@ class Designer extends Events {
* deletes the relationship from the linked topics, DesignerModel, Workspace and Mindmap * deletes the relationship from the linked topics, DesignerModel, Workspace and Mindmap
* @param {mindplot.Relationship} rel the relationship to delete * @param {mindplot.Relationship} rel the relationship to delete
*/ */
deleteRelationship(rel) { deleteRelationship(rel: Relationship) {
const sourceTopic = rel.getSourceTopic(); const sourceTopic = rel.getSourceTopic();
sourceTopic.deleteRelationship(rel); sourceTopic.deleteRelationship(rel);

View File

@ -22,29 +22,35 @@ import Designer from './Designer';
import Menu from './widget/Menu'; import Menu from './widget/Menu';
import { $notifyModal } from './widget/ModalDialogNotifier'; import { $notifyModal } from './widget/ModalDialogNotifier';
import { $msg } from './Messages'; import { $msg } from './Messages';
import { DesignerOptions } from './DesignerOptionsBuilder';
let designer = null; let designer = null;
export function buildDesigner(options) { export function buildDesigner(options: DesignerOptions): Designer {
const divContainer = $(`#${options.container}`); const divContainer = $(`#${options.container}`);
$assert(divContainer, 'container could not be null'); $assert(divContainer, 'container could not be null');
// Register load events ... // Register load events ...
designer = new Designer(options, divContainer); designer = new Designer(options, divContainer);
designer.addEvent('loadSuccess', () => { designer.addEvent('loadSuccess', () => {
// @ts-ignore
window.mindmapLoadReady = true; window.mindmapLoadReady = true;
console.log('Map loadded successfully'); console.log('Map loadded successfully');
}); });
const onerrorFn = (message, url, lineNo) => { const onerrorFn = (message:string, url, lineNo) => {
// Close loading dialog ... // Close loading dialog ...
// @ts-ignore
if (window.waitDialog) { if (window.waitDialog) {
// @ts-ignore
window.waitDialog.close(); window.waitDialog.close();
// @ts-ignore
window.waitDialog = null; window.waitDialog = null;
} }
// Open error dialog only in case of mindmap loading errors. The rest of the error are reported but not display the dialog. // Open error dialog only in case of mindmap loading errors. The rest of the error are reported but not display the dialog.
// Remove this in the near future. // Remove this in the near future.
// @ts-ignore
if (!window.mindmapLoadReady) { if (!window.mindmapLoadReady) {
$notifyModal($msg('UNEXPECTED_ERROR_LOADING')); $notifyModal($msg('UNEXPECTED_ERROR_LOADING'));
} }
@ -58,7 +64,7 @@ export function buildDesigner(options) {
// Register toolbar event ... // Register toolbar event ...
if ($('#toolbar').length) { if ($('#toolbar').length) {
const menu = new Menu(designer, 'toolbar', options.mapId, ''); const menu = new Menu(designer, 'toolbar', options.mapId);
// If a node has focus, focus can be move to another node using the keys. // If a node has focus, focus can be move to another node using the keys.
designer._cleanScreen = function _cleanScreen() { designer._cleanScreen = function _cleanScreen() {
@ -69,39 +75,3 @@ export function buildDesigner(options) {
return designer; return designer;
} }
export function buildOptions(options) {
$assert(options.persistenceManager, 'persistence must be defined');
// Set workspace screen size as default. In this way, resize issues are solved.
const containerSize = {
height: Number.parseInt(window.screen.height, 10),
width: Number.parseInt(window.screen.width, 10),
};
const viewPort = {
height: Number.parseInt(window.innerHeight, 10),
width: Number.parseInt(window.innerWidth, 10),
};
const defaultOptions = {
readOnly: false,
zoom: 0.85,
saveOnLoad: true,
size: containerSize,
viewPort,
container: 'mindplot',
locale: 'en',
};
return { ...defaultOptions, ...options };
}
export async function loadOptions(jsonConf, options) {
const result = await $.ajax({
url: jsonConf,
dataType: 'json',
method: 'get',
});
return { ...result, ...buildOptions(options) };
}

View File

@ -16,7 +16,8 @@
* 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 CentralTopic from './CentralTopic';
import { DesignerOptions } from './DesignerOptionsBuilder';
import Events from './Events'; import Events from './Events';
import Relationship from './Relationship'; import Relationship from './Relationship';
import Topic from './Topic'; import Topic from './Topic';
@ -34,30 +35,25 @@ class DesignerModel extends Events {
this._relationships = []; this._relationships = [];
} }
/** @return {Number} zoom between 0.3 (largest text) and 1.9 */ getZoom():number {
getZoom() {
return this._zoom; return this._zoom;
} }
/** @param {Number} zoom number between 0.3 and 1.9 to set the zoom to */ setZoom(zoom: number):void {
setZoom(zoom: number) {
this._zoom = zoom; this._zoom = zoom;
} }
/** @return {@link mindplot.Topic[]} all topics */
getTopics(): Topic[] { getTopics(): Topic[] {
return this._topics; return this._topics;
} }
/** @return {mindplot.Relationship[]} all relationships */
getRelationships(): Relationship[] { getRelationships(): Relationship[] {
return this._relationships; return this._relationships;
} }
/** @return {mindplot.CentralTopic} the central topic */ getCentralTopic(): CentralTopic {
getCentralTopic(): Topic {
const topics = this.getTopics(); const topics = this.getTopics();
return topics[0]; return topics[0] as CentralTopic;
} }
/** @return {mindplot.Topic[]} selected topics */ /** @return {mindplot.Topic[]} selected topics */
@ -71,9 +67,6 @@ class DesignerModel extends Events {
return result; return result;
} }
/**
* @return {mindplot.Relationship[]} selected relationships
*/
filterSelectedRelationships(): Relationship[] { filterSelectedRelationships(): Relationship[] {
const result = []; const result = [];
for (let i = 0; i < this._relationships.length; i++) { for (let i = 0; i < this._relationships.length; i++) {
@ -84,50 +77,28 @@ class DesignerModel extends Events {
return result; return result;
} }
/**
* @return {Array.<mindplot.Relationship, mindplot.Topic>} all topics and relationships
*/
getEntities(): (Relationship | Topic)[] { getEntities(): (Relationship | Topic)[] {
let result = [].concat(this._topics); let result = [].concat(this._topics);
result = result.concat(this._relationships); result = result.concat(this._relationships);
return result; return result;
} }
/** removeTopic(topic:Topic):void {
* removes occurrences of the given topic from the topic array
* @param {mindplot.Topic} topic the topic to remove
*/
removeTopic(topic) {
$assert(topic, 'topic can not be null'); $assert(topic, 'topic can not be null');
this._topics = this._topics.filter((t) => t !== topic); this._topics = this._topics.filter((t) => t !== topic);
} }
/** removeRelationship(rel:Relationship):void {
* removes occurrences of the given relationship from the relationship array
* @param {mindplot.Relationship} rel the relationship to remove
*/
removeRelationship(rel) {
$assert(rel, 'rel can not be null'); $assert(rel, 'rel can not be null');
this._relationships = this._relationships.filter((r) => r !== rel); this._relationships = this._relationships.filter((r) => r !== rel);
} }
/**
* adds the given topic to the topic array
* @param {mindplot.Topic} topic the topic to add
* @throws will throw an error if topic is null or undefined
* @throws will throw an error if the topic's id is not a number
*/
addTopic(topic: Topic): void { addTopic(topic: Topic): void {
$assert(topic, 'topic can not be null'); $assert(topic, 'topic can not be null');
$assert(typeof topic.getId() === 'number', `id is not a number:${topic.getId()}`); $assert(typeof topic.getId() === 'number', `id is not a number:${topic.getId()}`);
this._topics.push(topic); this._topics.push(topic);
} }
/**
* adds the given relationship to the relationship array
* @param {mindplot.Relationship} rel the relationship to add
* @throws will throw an error if rel is null or undefined
*/
addRelationship(rel: Relationship): void { addRelationship(rel: Relationship): void {
$assert(rel, 'rel can not be null'); $assert(rel, 'rel can not be null');
this._relationships.push(rel); this._relationships.push(rel);
@ -159,10 +130,6 @@ class DesignerModel extends Events {
return (topics.length > 0) ? topics[0] : null; return (topics.length > 0) ? topics[0] : null;
} }
/**
* @param {String} id the id of the topic to be retrieved
* @return {mindplot.Topic} the topic with the respective id
*/
findTopicById(id: Number): Topic { 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++) {

View File

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

View File

@ -0,0 +1,75 @@
/*
* Copyright [2021] [wisemapping]
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { $assert } from '@wisemapping/core-js';
import PersistenceManager from './PersistenceManager';
export type DesignerOptions = {
zoom: number,
viewPort?: { height: number, width: number },
size?: { height: number, width: number },
readOnly?: boolean,
mapId?: string,
container: string,
persistenceManager?: PersistenceManager,
saveOnLoad?: boolean,
locale?: string,
};
class OptionsBuilder {
static buildOptions(options: DesignerOptions): DesignerOptions {
$assert(options.persistenceManager, 'persistence must be defined');
let containerSize = options.size;
if (options.size == null) {
// If it has not been defined, use browser size ...
containerSize = {
width: window.screen.availWidth,
height: window.screen.availHeight,
}
}
// Is offset adjustment required
const viewPort = {
height: window.innerHeight,
width: window.innerWidth,
};
const defaultOptions: DesignerOptions = {
readOnly: false,
zoom: 0.85,
saveOnLoad: true,
size: containerSize,
viewPort: viewPort,
container: 'mindplot',
locale: 'en',
};
return { ...defaultOptions, ...options };
}
static async loadOptions(jsonConf: string, options: DesignerOptions): Promise<DesignerOptions> {
const result = await $.ajax({
url: jsonConf,
dataType: 'json',
method: 'get',
});
return { ...result, ...OptionsBuilder.buildOptions(options) };
}
}
export default OptionsBuilder;

View File

@ -210,7 +210,7 @@ class DragTopic {
return this.getConnectedToTopic() != null; return this.getConnectedToTopic() != null;
} }
isFreeLayoutOn() { isFreeLayoutOn(dragTopic) {
// return this._isFreeLayoutEnabled; // return this._isFreeLayoutEnabled;
// Disable free layout ... // Disable free layout ...
return false; return false;

View File

@ -19,44 +19,50 @@ import { $assert } from '@wisemapping/core-js';
import { Point } from '@wisemapping/web2d'; import { Point } from '@wisemapping/web2d';
class ScreenManager { class ScreenManager {
constructor(divElement) { _divContainer: JQuery;
_padding: { x: number; y: number; };
_clickEvents: ((event: UIEvent)=>void)[];
_scale: number;
constructor(divElement: JQuery) {
$assert(divElement, 'can not be null'); $assert(divElement, 'can not be null');
this._divContainer = divElement; this._divContainer = divElement;
this._padding = { x: 0, y: 0 }; this._padding = { x: 0, y: 0 };
// Ignore default click event propagation. Prevent 'click' event on drag. // Ignore default click event propagation. Prevent 'click' event on drag.
this._clickEvents = []; this._clickEvents = [];
this._divContainer.bind('click', (event) => { this._divContainer.bind('click', (event: { stopPropagation: () => void; }) => {
event.stopPropagation(); event.stopPropagation();
}); });
this._divContainer.bind('dblclick', (event) => { this._divContainer.bind('dblclick', (event: { stopPropagation: () => void; preventDefault: () => void; }) => {
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
}); });
} }
setScale(scale) { setScale(scale: number) {
$assert(scale, 'Screen scale can not be null'); $assert(scale, 'Screen scale can not be null');
this._scale = scale; this._scale = scale;
} }
addEvent(event, listener) { addEvent(eventType: string, listener:any) {
if (event === 'click') this._clickEvents.push(listener); if (eventType === 'click') this._clickEvents.push(listener);
else this._divContainer.bind(event, listener); else this._divContainer.bind(eventType, listener);
} }
removeEvent(event, listener) { removeEvent(event: string, listener: any) {
if (event === 'click') { if (event === 'click') {
// @ts-ignore @Todo: needs review ...
this._clickEvents.remove(listener); this._clickEvents.remove(listener);
} else { } else {
this._divContainer.unbind(event, listener); this._divContainer.unbind(event, listener);
} }
} }
fireEvent(type, event) { fireEvent(type: string, event: UIEvent = null) {
if (type === 'click') { if (type === 'click') {
this._clickEvents.forEach((listener) => { this._clickEvents.forEach((listener: (arg0: any, arg1: any) => void) => {
listener(type, event); listener(type, event);
}); });
} else { } else {
@ -64,7 +70,7 @@ class ScreenManager {
} }
} }
_getElementPosition(elem) { _getElementPosition(elem: { getPosition: () => any; }) {
// Retrieve current element position. // Retrieve current element position.
const elementPosition = elem.getPosition(); const elementPosition = elem.getPosition();
let { x } = elementPosition; let { x } = elementPosition;
@ -82,7 +88,7 @@ class ScreenManager {
return { x, y }; return { x, y };
} }
getWorkspaceIconPosition(e) { getWorkspaceIconPosition(e: { getImage: () => any; getSize: () => any; getGroup: () => any; }) {
// Retrieve current icon position. // Retrieve current icon position.
const image = e.getImage(); const image = e.getImage();
const elementPosition = image.getPosition(); const elementPosition = image.getPosition();
@ -117,15 +123,15 @@ class ScreenManager {
return { x: x + topicPosition.x, y: y + topicPosition.y }; return { x: x + topicPosition.x, y: y + topicPosition.y };
} }
getWorkspaceMousePosition(event) { getWorkspaceMousePosition(event: MouseEvent) {
// Retrieve current mouse position. // Retrieve current mouse position.
let x = event.clientX; let x = event.clientX;
let y = event.clientY; let y = event.clientY;
// FIXME: paulo: why? Subtract div position. // Adjust the deviation of the container positioning ...
/* var containerPosition = this.getContainer().position(); const containerPosition = this.getContainer().position();
x = x - containerPosition.x; x = x - containerPosition.left;
y = y - containerPosition.y; */ y = y - containerPosition.top;
// Scale coordinate in order to be relative to the workspace. That's coordSize/size; // Scale coordinate in order to be relative to the workspace. That's coordSize/size;
x *= this._scale; x *= this._scale;
@ -143,7 +149,7 @@ class ScreenManager {
return this._divContainer; return this._divContainer;
} }
setOffset(x, y) { setOffset(x: number, y: number) {
this._padding.x = x; this._padding.x = x;
this._padding.y = y; this._padding.y = y;
} }

View File

@ -59,7 +59,7 @@ class Workspace {
return this._isReadOnly; return this._isReadOnly;
} }
_createWorkspace() { private _createWorkspace() {
// Initialize workspace ... // Initialize workspace ...
const coordOriginX = -(this._screenWidth / 2); const coordOriginX = -(this._screenWidth / 2);
const coordOriginY = -(this._screenHeight / 2); const coordOriginY = -(this._screenHeight / 2);
@ -109,10 +109,18 @@ class Workspace {
return this._workspace.getCoordSize(); return this._workspace.getCoordSize();
} }
setZoom(zoom: number, center: boolean = false) { setZoom(zoom: number, center: boolean = false): void {
this._zoom = zoom; this._zoom = zoom;
const workspace = this._workspace; const workspace = this._workspace;
// Calculate the original boxview size ...
let origWidth = 0;
let origHeight = 0
if (this._viewPort) {
origWidth = this._viewPort.width;
origHeight = this._viewPort.height;
}
// Update coord scale... // Update coord scale...
const coordWidth = zoom * this._screenWidth; const coordWidth = zoom * this._screenWidth;
const coordHeight = zoom * this._screenHeight; const coordHeight = zoom * this._screenHeight;
@ -125,8 +133,8 @@ class Workspace {
} }
// Center topic.... // Center topic....
let coordOriginX; let coordOriginX: number;
let coordOriginY; let coordOriginY: number;
if (center) { if (center) {
if (this._viewPort) { if (this._viewPort) {
@ -137,11 +145,18 @@ class Workspace {
coordOriginY = -(coordHeight / 2); coordOriginY = -(coordHeight / 2);
} }
} else { } else {
const coordOrigin = workspace.getCoordOrigin(); // Zoom keeping the center in the same place ...
coordOriginX = coordOrigin.x / 2; // const xPaddinng = (this._viewPort.width - origWidth) / 2;
coordOriginY = coordOrigin.y / 2; // const yPaddinng = (this._viewPort.height - origHeight) / 2;
}
// const coordOrigin = workspace.getCoordOrigin();
// coordOriginX = coordOrigin.x - xPaddinng;
// coordOriginY = coordOrigin.y - yPaddinng;
const coordOrigin = workspace.getCoordOrigin();
coordOriginX = coordOrigin.x;
coordOriginY = coordOrigin.y;
}
workspace.setCoordOrigin(coordOriginX, coordOriginY); workspace.setCoordOrigin(coordOriginX, coordOriginY);
// Update screen. // Update screen.
@ -228,7 +243,7 @@ class Workspace {
screenManager.addEvent('mousedown', mouseDownListener); screenManager.addEvent('mousedown', mouseDownListener);
} }
setViewPort(size:{ height: number, width: number }) { setViewPort(size: { height: number, width: number }) {
this._viewPort = size; this._viewPort = size;
} }
} }

View File

@ -1,7 +0,0 @@
import $ from 'jquery';
try {
$(document).trigger('loadcomplete', 'mind');
} catch (e) {
console.error(e.stack);
}

View File

@ -31,7 +31,7 @@ import AccountSettingsPanel from './AccountSettingsPanel';
import Designer from '../Designer'; import Designer from '../Designer';
class Menu extends IMenu { class Menu extends IMenu {
constructor(designer: Designer, containerId: string, mapId: string, readOnly: boolean = false, baseUrl = '') { constructor(designer: Designer, containerId: string, mapId: string, readOnly: boolean = false, baseUrl:string = '') {
super(designer, containerId, mapId); super(designer, containerId, mapId);
const saveElem = $('#save'); const saveElem = $('#save');
@ -259,6 +259,12 @@ class Menu extends IMenu {
}); });
Menu._registerTooltip('zoom-minus', $msg('ZOOM_OUT')); Menu._registerTooltip('zoom-minus', $msg('ZOOM_OUT'));
this._addButton('position', false, false, () => {
designer.setZoomToFit();
});
Menu._registerTooltip('position', $msg('CENTER_POSITION'));
const undoButton = this._addButton('undoEdition', false, false, () => { const undoButton = this._addButton('undoEdition', false, false, () => {
designer.undo(); designer.undo();
}); });

View File

@ -25,13 +25,15 @@ import {
import LoadingModal from './components/widget/LoadingModal'; import LoadingModal from './components/widget/LoadingModal';
import { import {
buildDesigner, buildDesigner,
buildOptions,
} from './components/DesignerBuilder'; } from './components/DesignerBuilder';
import RESTPersistenceManager from './components/RestPersistenceManager'; import RESTPersistenceManager from './components/RestPersistenceManager';
import PersistenceManager from './components/PersistenceManager'; import PersistenceManager from './components/PersistenceManager';
import LocalStorageManager from './components/LocalStorageManager'; import LocalStorageManager from './components/LocalStorageManager';
import DesignerOptionsBuilder from './components/DesignerOptionsBuilder';
// This hack is required to initialize Bootstrap. In future, this should be removed. // This hack is required to initialize Bootstrap. In future, this should be removed.
//@ts-ignore
global.jQuery = jquery; global.jQuery = jquery;
require('@libraries/bootstrap/js/bootstrap'); require('@libraries/bootstrap/js/bootstrap');
@ -39,7 +41,7 @@ const loadingModal = new LoadingModal();
loadingModal.show(); loadingModal.show();
// Configure designer options ... // Configure designer options ...
let persistence; let persistence:PersistenceManager;
if (!global.memoryPersistence && !global.readOnly) { if (!global.memoryPersistence && !global.readOnly) {
persistence = new RESTPersistenceManager({ persistence = new RESTPersistenceManager({
documentUrl: '/c/restful/maps/{id}/document', documentUrl: '/c/restful/maps/{id}/document',
@ -55,13 +57,15 @@ if (!global.memoryPersistence && !global.readOnly) {
// Obtain map zoom from query param if it was specified... // Obtain map zoom from query param if it was specified...
const params = new URLSearchParams(window.location.search.substring(1)); const params = new URLSearchParams(window.location.search.substring(1));
const zoomParam = Number.parseFloat(params.get('zoom'), 10); const zoomParam = Number.parseFloat(params.get('zoom'));
const options = buildOptions({ const options = DesignerOptionsBuilder.buildOptions(
persistenceManager: persistence, {
readOnly: global.readOnly || false, persistenceManager: persistence,
mapId: global.mapId, readOnly: Boolean(global.readOnly || false),
zoom: zoomParam || global.userOptions.zoom, mapId: global.mapId,
}); container: 'mindplot',
zoom: zoomParam || global.userOptions.zoom,
});
// Build designer ... // Build designer ...
const designer = buildDesigner(options); const designer = buildDesigner(options);
@ -76,5 +80,5 @@ const mindmap = instance.load(global.mapId);
designer.loadMap(mindmap); designer.loadMap(mindmap);
if (global.mindmapLocked) { if (global.mindmapLocked) {
$notify(global.mindmapLockedMsg, false); $notify(global.mindmapLockedMsg);
} }

View File

@ -19,7 +19,6 @@ body {
div#mindplot { div#mindplot {
position: relative; position: relative;
top: @header-toolbar-height; top: @header-toolbar-height;
top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -154,6 +153,22 @@ div#bottom-logo {
height: 40px; height: 40px;
} }
div#position {
margin-top: 5px;
}
#position-button {
cursor: pointer;
border: solid black 1px;
width: 40px;
height: 40px;
background-position: center;
background-repeat: no-repeat;
background-size: 40px 40px;
background-color: #FFF;
border-radius: 8px;
}
#zoom-button { #zoom-button {
width: 40px; width: 40px;
border: 0; border: 0;
@ -161,6 +176,7 @@ div#bottom-logo {
#zoom-plus, #zoom-plus,
#zoom-minus { #zoom-minus {
border: solid black 1px;
height: 40px; height: 40px;
width: 40px; width: 40px;
background-repeat: no-repeat; background-repeat: no-repeat;

View File

@ -1,10 +1,8 @@
@header-height : 0px;
@header-toolbar-height : 50px; @header-toolbar-height : 50px;
@header-info-height : @header-height - @header-toolbar-height;
div#header { div#header {
width: 100%; width: 100%;
height: @header-height; height: 0px;
background: #202020; background: #202020;
z-index: 1000; z-index: 1000;
position: absolute; position: absolute;

View File

@ -115,6 +115,11 @@
<img src="images/minus.svg" /> <img src="images/minus.svg" />
</button> </button>
</div> </div>
<div id="position">
<button id="position-button">
<img src="images/center_focus.svg" />
</button>
</div>
</div> </div>
<div id="bottom-logo"></div> <div id="bottom-logo"></div>
<div id="headerNotifier"></div> <div id="headerNotifier"></div>

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm8.94 3c-.46-4.17-3.77-7.48-7.94-7.94V1h-2v2.06C6.83 3.52 3.52 6.83 3.06 11H1v2h2.06c.46 4.17 3.77 7.48 7.94 7.94V23h2v-2.06c4.17-.46 7.48-3.77 7.94-7.94H23v-2h-2.06zM12 19c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/></svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@ -1,7 +1,8 @@
import '../css/editor.less'; import '../css/editor.less';
import { buildDesigner, buildOptions } from '../../../../src/components/DesignerBuilder'; import { buildDesigner } from '../../../../src/components/DesignerBuilder';
import { PersistenceManager, LocalStorageManager } from '../../../../src'; import { PersistenceManager, LocalStorageManager } from '../../../../src';
import LoadingModal from '../../../../src/components/widget/LoadingModal'; import LoadingModal from '../../../../src/components/widget/LoadingModal';
import DesignerOptionsBuilder from '../../../../src/components/DesignerOptionsBuilder';
// Account details ... // Account details ...
global.accountName = 'Test User'; global.accountName = 'Test User';
@ -11,11 +12,14 @@ const loadingModal = new LoadingModal();
loadingModal.show(); loadingModal.show();
const p = new LocalStorageManager('samples/{id}.wxml'); const p = new LocalStorageManager('samples/{id}.wxml');
const options = buildOptions({ persistenceManager: p }); const options = DesignerOptionsBuilder.buildOptions({
persistenceManager: p
});
const designer = buildDesigner(options); const designer = buildDesigner(options);
designer.addEvent('loadSuccess', () => { designer.addEvent('loadSuccess', () => {
loadingModal.hide(); loadingModal.hide();
// Hack for automation testing ...
document.getElementById('mindplot').classList.add('ready'); document.getElementById('mindplot').classList.add('ready');
}); });

View File

@ -1,10 +1,11 @@
import '../css/embedded.less'; import '../css/embedded.less';
import { buildDesigner, buildOptions } from '../../../../src/components/DesignerBuilder'; import { buildDesigner } from '../../../../src/components/DesignerBuilder';
import { PersistenceManager, LocalStorageManager } from '../../../../src'; import { PersistenceManager, LocalStorageManager } from '../../../../src';
import DesignerOptionsBuilder from '../../../../src/components/DesignerOptionsBuilder';
// Options has been defined in by a external ile ? // Options has been defined in by a external ile ?
const p = new LocalStorageManager('samples/{id}.wxml'); const p = new LocalStorageManager('samples/{id}.wxml');
const options = buildOptions({ persistenceManager: p }); const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p });
const designer = buildDesigner(options); const designer = buildDesigner(options);
designer.addEvent('loadSuccess', () => { designer.addEvent('loadSuccess', () => {

View File

@ -1,9 +1,11 @@
import '../css/viewmode.less'; import '../css/viewmode.less';
import { buildDesigner, buildOptions } from '../../../../src/components/DesignerBuilder'; import { buildDesigner } from '../../../../src/components/DesignerBuilder';
import { PersistenceManager, LocalStorageManager } from '../../../../src'; import { PersistenceManager, LocalStorageManager } from '../../../../src';
import DesignerOptionsBuilder from '../../../../src/components/DesignerOptionsBuilder';
const p = new LocalStorageManager('samples/{id}.wxml'); const p = new LocalStorageManager('samples/{id}.wxml');
const options = buildOptions({ persistenceManager: p, readOnly: true, saveOnLoad: false }); const options = DesignerOptionsBuilder.buildOptions({ persistenceManager: p, readOnly: true, saveOnLoad: false });
// Obtain map id from query param // Obtain map id from query param
const params = new URLSearchParams(window.location.search.substring(1)); const params = new URLSearchParams(window.location.search.substring(1));

View File

@ -11,7 +11,7 @@ module.exports = {
}, },
entry: { entry: {
mindplot: './src/index.js', mindplot: './src/index.js',
loader: './src/indexLoader.js', loader: './src/indexLoader.ts',
}, },
mode: 'production', mode: 'production',
devtool: 'source-map', devtool: 'source-map',

View File

@ -78,8 +78,6 @@ class Workspace extends ElementClass {
*/ */
static _createDivContainer() { static _createDivContainer() {
const container = window.document.createElement('div'); const container = window.document.createElement('div');
// container.id = 'workspaceContainer';
// container.style.overflow = 'hidden';
container.style.position = 'relative'; container.style.position = 'relative';
container.style.top = '0px'; container.style.top = '0px';
container.style.left = '0px'; container.style.left = '0px';

View File

@ -2561,6 +2561,13 @@
jest-diff "^27.0.0" jest-diff "^27.0.0"
pretty-format "^27.0.0" pretty-format "^27.0.0"
"@types/jquery@^3.5.11":
version "3.5.11"
resolved "https://registry.yarnpkg.com/@types/jquery/-/jquery-3.5.11.tgz#fb2a255e8376779e89a10ddd04bfc1a93398f861"
integrity sha512-lYZGdfOtUa0XFjIATQgiogqeTY5PNNMOmp3Jq48ghmJALL8t/IqABRqlEwdHfuUdA8iIE1uGD1HoI4a7Tiy6OA==
dependencies:
"@types/sizzle" "*"
"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": "@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.9" version "7.0.9"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
@ -2671,7 +2678,7 @@
resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz#0ecc1b9259b76598ef01942f547904ce61a6a77d" resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz#0ecc1b9259b76598ef01942f547904ce61a6a77d"
integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A== integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==
"@types/sizzle@^2.3.2": "@types/sizzle@*", "@types/sizzle@^2.3.2":
version "2.3.3" version "2.3.3"
resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef" resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef"
integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ== integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==