Merged feature/remove_actions into master

* Migration to TS

* Change to typescript

* Fix css style issue due to bootstrap.

* Fix eslit errors

* Remove print button on try
This commit is contained in:
Paulo Veiga 2022-03-05 17:09:40 +00:00
parent 165142a0f5
commit 2c4d937ffa
57 changed files with 562 additions and 788 deletions

20
packages/editor/src/bootstrap-fixes.css vendored Normal file
View File

@ -0,0 +1,20 @@
/*
These are patches or hacks to avoid boostrap interfering with Mui styles
This file is meant to be removed when removing bootstrap
*/
/*
/* bootstrap modal */
.wise-editor .modal {
overflow: hidden;
}
.modal-backdrop {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: rgba(0,0,0,0.5);
z-index: 1000;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -43,12 +43,12 @@ export default function Toolbar({
}: ToolbarPropsType): React.ReactElement { }: ToolbarPropsType): React.ReactElement {
const intl = useIntl(); const intl = useIntl();
return ( return (
<HeaderContainer> <HeaderContainer className="wise-editor">
<div id="toolbar"> <div id="toolbar">
<div id="backToList"> <div id="backToList">
<img src={BackIconSvg} /> <img src={BackIconSvg} />
</div> </div>
{editorMode === 'edition' && ( {(editorMode === 'edition-editor' || editorMode === 'edition-owner') && (
<div id="persist" className="buttonContainer"> <div id="persist" className="buttonContainer">
<ToolbarButton id="save" className="buttonOn"> <ToolbarButton id="save" className="buttonOn">
<img src={SaveSvg} /> <img src={SaveSvg} />
@ -112,7 +112,6 @@ export default function Toolbar({
</ToolbarButton> </ToolbarButton>
</div> </div>
<div id="separator" className="buttonContainer"></div> <div id="separator" className="buttonContainer"></div>
{editorMode === 'edition' && (
<ToolbarRightContainer> <ToolbarRightContainer>
<ToolbarButton <ToolbarButton
id="export" id="export"
@ -121,13 +120,17 @@ export default function Toolbar({
> >
<img src={ExportSvg} /> <img src={ExportSvg} />
</ToolbarButton> </ToolbarButton>
{(editorMode === 'edition-owner' || editorMode === 'edition-editor' || editorMode === 'edition-viewer') && (
<ToolbarButton <ToolbarButton
id="publishIt" id="print"
className="buttonOn" className="buttonOn"
onClick={() => onAction('publish')} onClick={() => onAction('print')}
> >
<img src={PublicSvg} /> <img src={PrintSvg} />
</ToolbarButton> </ToolbarButton>
)}
{editorMode === 'edition-owner' && (
<>
<ToolbarButton <ToolbarButton
id="history" id="history"
className="buttonOn" className="buttonOn"
@ -136,20 +139,26 @@ export default function Toolbar({
<img src={HistorySvg} /> <img src={HistorySvg} />
</ToolbarButton> </ToolbarButton>
<ToolbarButton <ToolbarButton
id="print" id="publishIt"
className="buttonOn" className="buttonOn"
onClick={() => onAction('print')} onClick={() => onAction('publish')}
> >
<img src={PrintSvg} /> <img src={PublicSvg} />
</ToolbarButton> </ToolbarButton>
</>
)}
{(editorMode === 'edition-owner' || editorMode === 'edition-editor') && (
<ToolbarButton id="account"> <ToolbarButton id="account">
<img src={AccountSvg} /> <img src={AccountSvg} />
</ToolbarButton> </ToolbarButton>
)}
{editorMode === 'edition-owner' && (
<ActionButton onClick={() => onAction('share')}> <ActionButton onClick={() => onAction('share')}>
{intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' })} {intl.formatMessage({ id: 'action.share', defaultMessage: 'Share' })}
</ActionButton> </ActionButton>
</ToolbarRightContainer>
)} )}
</ToolbarRightContainer>
</div> </div>
</HeaderContainer> </HeaderContainer>
); );

View File

@ -1,7 +1,8 @@
/********************************************************************************/ /********************************************************************************/
/* Header & Toolbar Styles */ /* Header & Toolbar Styles */
/********************************************************************************/ /********************************************************************************/
@import "bootstrap.min.css"; @import "bootstrap-prefix.min.css";
@import "bootstrap-fixes.css";
html { html {
/* avoid bootstrap overriding font-size and breaking Mui */ /* avoid bootstrap overriding font-size and breaking Mui */
@ -129,7 +130,7 @@ div.shareModalDialog {
background-color: #efefef; background-color: #efefef;
} }
.popover { .wise-editor .popover {
font-size: 13px; font-size: 13px;
max-width: none; max-width: none;
} }

View File

@ -13,7 +13,6 @@ import {
} from '@wisemapping/mindplot'; } from '@wisemapping/mindplot';
import './global-styled.css'; import './global-styled.css';
import I18nMsg from './classes/i18n-msg'; import I18nMsg from './classes/i18n-msg';
import Messages from '@wisemapping/mindplot/src/components/Messages';
declare global { declare global {
// used in mindplot // used in mindplot
@ -100,7 +99,8 @@ const Editor = ({
onAction={onAction} onAction={onAction}
/> />
} }
<div id="mindplot" style={mindplotStyle}></div> <div id="mindplot" style={mindplotStyle} className="wise-editor"></div>
<div id="mindplot-tooltips" className="wise-editor"></div>
<Footer editorMode={options.mode} /> <Footer editorMode={options.mode} />
</IntlProvider > </IntlProvider >
); );

View File

@ -36,7 +36,7 @@ const options: EditorOptions = {
zoom: 0.8, zoom: 0.8,
locked: false, locked: false,
mapTitle: "Develop Mindnap", mapTitle: "Develop Mindnap",
mode: 'edition', mode: 'edition-owner',
locale: 'en', locale: 'en',
enableKeyboardEvents: true enableKeyboardEvents: true
}; };

View File

@ -1,4 +1,4 @@
declare module "*.svg" { declare module '*.svg' {
const content: any; const content: any;
export default content; export default content;
} }

View File

@ -1,64 +0,0 @@
/*
* 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 Icon from './Icon';
class ActionIcon extends Icon {
constructor(topic, url) {
super(url);
this._node = topic;
}
getNode() {
return this._node;
}
setPosition(x, y) {
const size = this.getSize();
this.getImage().setPosition(x - size.width / 2, y - size.height / 2);
}
addEvent(event, fn) {
this.getImage().addEvent(event, fn);
}
addToGroup(group) {
group.append(this.getImage());
}
setVisibility(visible) {
this.getImage().setVisibility(visible);
}
isVisible() {
return this.getImage().isVisible();
}
setCursor(cursor) {
return this.getImage().setCursor(cursor);
}
moveToBack(cursor) {
return this.getImage().moveToBack(cursor);
}
moveToFront(cursor) {
return this.getImage().moveToFront(cursor);
}
}
export default ActionIcon;

View File

@ -41,20 +41,15 @@ class CommandContext {
} }
findTopics(topicIds: number[]): Topic[] { findTopics(topicIds: number[]): Topic[] {
$assert($defined(topicIds), 'topicsIds can not be null');
const topicsIds = Array.isArray(topicIds) ? topicIds : [topicIds]; const topicsIds = Array.isArray(topicIds) ? topicIds : [topicIds];
const designerTopics = this._designer.getModel().getTopics(); const designerTopics = this._designer.getModel().getTopics();
const result = designerTopics.filter((topic) => topicsIds.includes(topic.getId())); const result = designerTopics.filter((topic) => topicsIds.includes(topic.getId()));
if (result.length !== topicsIds.length) { if (result.length !== topicsIds.length) {
const ids = designerTopics.map((topic) => topic.getId()); const ids = designerTopics.map((topic) => topic.getId());
$assert( throw new Error(`Could not find topic. Result:${result
result.length === topicsIds.length,
`Could not find topic. Result:${result
} Filter Criteria:${topicsIds } Filter Criteria:${topicsIds
} Current Topics: [${ids } Current Topics: [${ids}])`);
}]`,
);
} }
return result; return result;
} }
@ -116,7 +111,7 @@ class CommandContext {
moveTopic(topic: Topic, position: Point) { moveTopic(topic: Topic, position: Point) {
$assert(topic, 'topic cannot be null'); $assert(topic, 'topic cannot be null');
$assert(position, 'position cannot be null'); $assert(position, 'position cannot be null');
EventBus.instance.fireEvent(EventBus.events.NodeMoveEvent, { EventBus.instance.fireEvent('topicMoved', {
node: topic.getModel(), node: topic.getModel(),
position, position,
}); });

View File

@ -93,8 +93,8 @@ class ConnectionLine {
return line; return line;
} }
setVisibility(value: boolean): void { setVisibility(value: boolean, fade = 0): void {
this._line2d.setVisibility(value); this._line2d.setVisibility(value, fade);
} }
isVisible() { isVisible() {

View File

@ -39,7 +39,7 @@ import Relationship from './Relationship';
import TopicEventDispatcher, { TopicEvent } from './TopicEventDispatcher'; import TopicEventDispatcher, { TopicEvent } from './TopicEventDispatcher';
import TopicFeatureFactory from './TopicFeature'; import TopicFeatureFactory from './TopicFeature';
import { create } from './NodeGraphUtils'; import TopicFactory from './TopicFactory';
import EventBus from './layout/EventBus'; import EventBus from './layout/EventBus';
import EventBusDispatcher from './layout/EventBusDispatcher'; import EventBusDispatcher from './layout/EventBusDispatcher';
@ -53,7 +53,6 @@ import Mindmap from './model/Mindmap';
import NodeModel from './model/NodeModel'; import NodeModel from './model/NodeModel';
import Topic from './Topic'; import Topic from './Topic';
import { DesignerOptions } from './DesignerOptionsBuilder'; import { DesignerOptions } from './DesignerOptionsBuilder';
import MainTopic from './MainTopic';
import DragTopic from './DragTopic'; import DragTopic from './DragTopic';
import CentralTopic from './CentralTopic'; import CentralTopic from './CentralTopic';
import FeatureType from './model/FeatureType'; import FeatureType from './model/FeatureType';
@ -225,9 +224,9 @@ class Designer extends Events {
return dragManager; return dragManager;
} }
private _buildNodeGraph(model: NodeModel, readOnly: boolean): MainTopic { private _buildNodeGraph(model: NodeModel, readOnly: boolean): Topic {
// Create node graph ... // Create node graph ...
const topic = create(model, { readOnly }); const topic = TopicFactory.create(model, { readOnly });
this.getModel().addTopic(topic); this.getModel().addTopic(topic);
const me = this; const me = this;
// Add Topic events ... // Add Topic events ...
@ -606,7 +605,7 @@ class Designer extends Events {
this.goToNode(centralTopic); this.goToNode(centralTopic);
// Finally, sort the map ... // Finally, sort the map ...
EventBus.instance.fireEvent(EventBus.events.DoLayout); EventBus.instance.fireEvent('forceLayout');
this.fireEvent('loadSuccess'); this.fireEvent('loadSuccess');
} }
@ -695,12 +694,6 @@ class Designer extends Events {
mindmap.deleteRelationship(rel.getModel()); mindmap.deleteRelationship(rel.getModel());
} }
/**
* @private
* @param {mindplot.model.RelationshipModel} model
* @return {mindplot.Relationship} the new relationship with events registered
* @throws will throw an error if the target topic cannot be found
*/
private _buildRelationshipShape(model: RelationshipModel): Relationship { private _buildRelationshipShape(model: RelationshipModel): Relationship {
const dmodel = this.getModel(); const dmodel = this.getModel();
@ -744,13 +737,9 @@ class Designer extends Events {
return result; return result;
} }
/** removeTopic(node: Topic): void {
* @param {mindplot.Topic} node the topic to remove
* removes the given topic and its children from Workspace, DesignerModel and NodeModel
*/
removeTopic(node) {
if (!node.isCentralTopic()) { if (!node.isCentralTopic()) {
const parent = node._parent; const parent = node.getParent();
node.disconnect(this._workspace); node.disconnect(this._workspace);
// remove children // remove children
@ -771,17 +760,13 @@ class Designer extends Events {
} }
} }
/** private _resetEdition() {
* @private
*/
_resetEdition() {
const screenManager = this._workspace.getScreenManager(); const screenManager = this._workspace.getScreenManager();
screenManager.fireEvent('update'); screenManager.fireEvent('update');
screenManager.fireEvent('mouseup'); screenManager.fireEvent('mouseup');
this._relPivot.dispose(); this._relPivot.dispose();
} }
/** */
deleteSelectedEntities() { deleteSelectedEntities() {
// Is there some action in progress ?. // Is there some action in progress ?.
this._resetEdition(); this._resetEdition();
@ -840,7 +825,7 @@ class Designer extends Events {
/** */ /** */
changeBackgroundColor(color: string) { changeBackgroundColor(color: string) {
const validateFunc = (topic) => topic.getShapeType() !== TopicShape.LINE; const validateFunc = (topic: 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);
@ -849,9 +834,8 @@ class Designer extends Events {
} }
} }
/** */
changeBorderColor(color: string) { changeBorderColor(color: string) {
const validateFunc = (topic) => topic.getShapeType() !== TopicShape.LINE; const validateFunc = (topic: 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);
if (topicsIds.length > 0) { if (topicsIds.length > 0) {
@ -859,7 +843,6 @@ class Designer extends Events {
} }
} }
/** */
changeFontSize(size: number) { changeFontSize(size: number) {
const topicsIds = this.getModel().filterTopicsIds(); const topicsIds = this.getModel().filterTopicsIds();
if (topicsIds.length > 0) { if (topicsIds.length > 0) {
@ -895,11 +878,7 @@ class Designer extends Events {
} }
} }
/** addLink(): void {
* lets the selected topic open the link editor where the user can define or modify an
* existing link
*/
addLink() {
const model = this.getModel(); const model = this.getModel();
const topic = model.selectedTopic(); const topic = model.selectedTopic();
if (topic) { if (topic) {
@ -908,8 +887,7 @@ class Designer extends Events {
} }
} }
/** */ addNote(): void {
addNote() {
const model = this.getModel(); const model = this.getModel();
const topic = model.selectedTopic(); const topic = model.selectedTopic();
if (topic) { if (topic) {

View File

@ -16,41 +16,50 @@
* limitations under the License. * limitations under the License.
*/ */
import { $assert } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import ActionDispatcher from './ActionDispatcher';
import Command from './Command';
import CommandContext from './CommandContext';
import DesignerUndoManager from './DesignerUndoManager'; import DesignerUndoManager from './DesignerUndoManager';
import EventBus from './layout/EventBus'; import EventBus from './layout/EventBus';
class DesignerActionRunner { class DesignerActionRunner {
constructor(commandContext, notifier) { private _undoManager: DesignerUndoManager;
private _context: CommandContext;
private _actionDisplatcher: ActionDispatcher;
constructor(commandContext: CommandContext, notifier: ActionDispatcher) {
$assert(commandContext, 'commandContext can not be null'); $assert(commandContext, 'commandContext can not be null');
this._undoManager = new DesignerUndoManager(); this._undoManager = new DesignerUndoManager();
this._context = commandContext; this._context = commandContext;
this._notifier = notifier; this._actionDisplatcher = notifier;
} }
execute(command) { execute(command: Command): void {
$assert(command, 'command can not be null'); $assert(command, 'command can not be null');
command.execute(this._context); command.execute(this._context);
this._undoManager.enqueue(command); this._undoManager.enqueue(command);
this.fireChangeEvent(); this.fireChangeEvent();
EventBus.instance.fireEvent(EventBus.events.DoLayout); EventBus.instance.fireEvent('forceLayout');
} }
undo() { undo(): void {
this._undoManager.execUndo(this._context); this._undoManager.execUndo(this._context);
this.fireChangeEvent(); this.fireChangeEvent();
EventBus.instance.fireEvent(EventBus.events.DoLayout); EventBus.instance.fireEvent('forceLayout');
} }
redo() { redo(): void {
this._undoManager.execRedo(this._context); this._undoManager.execRedo(this._context);
this.fireChangeEvent(); this.fireChangeEvent();
EventBus.instance.fireEvent(EventBus.events.DoLayout); EventBus.instance.fireEvent('forceLayout');
} }
fireChangeEvent() { fireChangeEvent(): void {
const event = this._undoManager.buildEvent(); const event = this._undoManager.buildEvent();
this._notifier.fireEvent('modelUpdate', event); this._actionDisplatcher.fireEvent('modelUpdate', event);
} }
} }

View File

@ -37,7 +37,7 @@ export function buildDesigner(options: DesignerOptions): Designer {
PersistenceManager.init(persistence); PersistenceManager.init(persistence);
// Register toolbar event ... // Register toolbar event ...
if (options.mode === 'edition' || options.mode === 'showcase') { if (options.mode === 'edition-owner' || options.mode === 'edition-editor' || options.mode === 'showcase') {
const menu = new Menu(designer, 'toolbar'); const menu = new Menu(designer, 'toolbar');
// 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.

View File

@ -46,7 +46,7 @@ class OptionsBuilder {
} }
const defaultOptions: DesignerOptions = { const defaultOptions: DesignerOptions = {
mode: 'edition', mode: 'edition-owner',
zoom: 0.85, zoom: 0.85,
saveOnLoad: true, saveOnLoad: true,
containerSize, containerSize,

View File

@ -24,8 +24,6 @@ import Workspace from './Workspace';
class DragManager { class DragManager {
private _workspace: Workspace; private _workspace: Workspace;
private _designerModel: Workspace;
private _isDragInProcess: boolean; private _isDragInProcess: boolean;
private _eventDispatcher: EventBusDispatcher; private _eventDispatcher: EventBusDispatcher;
@ -38,7 +36,6 @@ class DragManager {
constructor(workspace: Workspace, eventDispatcher: EventBusDispatcher) { constructor(workspace: Workspace, eventDispatcher: EventBusDispatcher) {
this._workspace = workspace; this._workspace = workspace;
this._designerModel = workspace;
this._listeners = {}; this._listeners = {};
this._isDragInProcess = false; this._isDragInProcess = false;
this._eventDispatcher = eventDispatcher; this._eventDispatcher = eventDispatcher;
@ -68,7 +65,7 @@ class DragManager {
// Register mouse up listeners ... // Register mouse up listeners ...
const mouseUpListener = dragManager._buildMouseUpListener( const mouseUpListener = dragManager._buildMouseUpListener(
workspace, topic, dragNode, dragManager, workspace, dragNode, dragManager,
); );
screen.addEvent('mouseup', mouseUpListener); screen.addEvent('mouseup', mouseUpListener);
@ -115,7 +112,7 @@ class DragManager {
return result; return result;
} }
protected _buildMouseUpListener(workspace: Workspace, topic: Topic, dragNode, dragManager: DragManager) { protected _buildMouseUpListener(workspace: Workspace, dragNode, dragManager: DragManager) {
const screen = workspace.getScreenManager(); const screen = workspace.getScreenManager();
const me = this; const me = this;
const result = (event: Event) => { const result = (event: Event) => {
@ -149,13 +146,7 @@ class DragManager {
return result; return result;
} }
/** addEvent(type: 'startdragging' | 'dragging' | 'enddragging', listener) {
* type:
* - startdragging.
* - dragging
* - enddragging
*/
addEvent(type: string, listener) {
this._listeners[type] = listener; this._listeners[type] = listener;
} }
} }

View File

@ -18,7 +18,6 @@
import { $assert, $defined } from '@wisemapping/core-js'; import { $assert, $defined } from '@wisemapping/core-js';
import { Point, CurvedLine, Rect } from '@wisemapping/web2d'; import { Point, CurvedLine, Rect } from '@wisemapping/web2d';
import DragTopicConfig from './DragTopicConfig';
import SizeType from './SizeType'; import SizeType from './SizeType';
import Topic from './Topic'; import Topic from './Topic';
import Shape from './util/Shape'; import Shape from './util/Shape';
@ -43,7 +42,7 @@ class DragPivot {
constructor() { constructor() {
this._position = new Point(); this._position = new Point();
this._size = DragTopicConfig.PIVOT_SIZE; this._size = DragPivot.DEFAULT_PIVOT_SIZE;
this._straightLine = this._buildStraightLine(); this._straightLine = this._buildStraightLine();
this._curvedLine = this._buildCurvedLine(); this._curvedLine = this._buildCurvedLine();
@ -251,6 +250,8 @@ class DragPivot {
this.setVisibility(false); this.setVisibility(false);
this._targetTopic = null; this._targetTopic = null;
} }
static DEFAULT_PIVOT_SIZE = { width: 50, height: 6 };
} }
export default DragPivot; export default DragPivot;

View File

@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import { $assert, $defined } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import { Point, ElementClass } from '@wisemapping/web2d'; import { Point, ElementClass } from '@wisemapping/web2d';
import ActionDispatcher from './ActionDispatcher'; import ActionDispatcher from './ActionDispatcher';
@ -27,12 +27,19 @@ import Workspace from './Workspace';
class DragTopic { class DragTopic {
private _elem2d: ElementClass; private _elem2d: ElementClass;
private _order: number | null; private _order: number | null;
private _draggedNode: NodeGraph; private _draggedNode: NodeGraph;
private _layoutManager: LayoutManager; private _layoutManager: LayoutManager;
private _position: any;
private _position: Point;
private _isInWorkspace: boolean; private _isInWorkspace: boolean;
static _dragPivot: any;
static _dragPivot: DragPivot = new DragPivot();
constructor(dragShape: ElementClass, draggedNode: NodeGraph, layoutManger: LayoutManager) { constructor(dragShape: ElementClass, draggedNode: NodeGraph, layoutManger: LayoutManager) {
$assert(dragShape, 'Rect can not be null.'); $assert(dragShape, 'Rect can not be null.');
$assert(draggedNode, 'draggedNode can not be null.'); $assert(draggedNode, 'draggedNode can not be null.');
@ -46,13 +53,13 @@ class DragTopic {
this._isInWorkspace = false; this._isInWorkspace = false;
} }
setOrder(order: number) { setOrder(order: number): void {
this._order = order; this._order = order;
} }
setPosition(x: number, y: number) { setPosition(x: number, y: number): void {
// Update drag shadow position .... // Update drag shadow position ....
let position = { x, y }; const position = { x, y };
this._position.setValue(position.x, position.y); this._position.setValue(position.x, position.y);
// Elements are positioned in the center. // Elements are positioned in the center.
@ -104,11 +111,7 @@ class DragTopic {
$assert(parent, 'Parent connection node can not be null.'); $assert(parent, 'Parent connection node can not be null.');
// Where it should be connected ? // Where it should be connected ?
const predict = this._layoutManager.predict(
// @todo: This is a hack for the access of the editor.
// It's required to review why this is needed forcing the declaration of a global variable.
const predict = global.designer._eventBussDispatcher._layoutManager.predict(
parent.getId(), parent.getId(),
this._draggedNode.getId(), this._draggedNode.getId(),
this.getPosition(), this.getPosition(),
@ -154,8 +157,8 @@ class DragTopic {
} }
} }
_getDragPivot(): DragPivot { private _getDragPivot(): DragPivot {
return DragTopic.__getDragPivot(); return DragTopic._dragPivot;
} }
getPosition(): Point { getPosition(): Point {
@ -206,18 +209,9 @@ class DragTopic {
static init(workspace: Workspace) { static init(workspace: Workspace) {
$assert(workspace, 'workspace can not be null'); $assert(workspace, 'workspace can not be null');
const pivot = DragTopic.__getDragPivot(); const pivot = DragTopic._dragPivot;
workspace.append(pivot); workspace.append(pivot);
};
static __getDragPivot() {
let result = DragTopic._dragPivot;
if (!$defined(result)) {
result = new DragPivot();
DragTopic._dragPivot = result;
} }
return result;
};
} }
export default DragTopic; export default DragTopic;

View File

@ -1,5 +0,0 @@
const PIVOT_SIZE = { width: 50, height: 6 };
export default {
PIVOT_SIZE,
};

View File

@ -17,20 +17,21 @@
*/ */
class EditorProperties { class EditorProperties {
private _zoom: number;
constructor() { constructor() {
this._zoom = 0; this._zoom = 0;
this._position = 0;
} }
setZoom(zoom) { setZoom(zoom: number) {
this._zoom = zoom; this._zoom = zoom;
} }
getZoom() { getZoom(): number {
return this._zoom; return this._zoom;
} }
asProperties() { asProperties(): string {
return `zoom=${this._zoom}\n`; return `zoom=${this._zoom}\n`;
} }
} }

View File

@ -1,2 +1,2 @@
type EditorRenderMode = 'viewonly' | 'edition' | 'showcase'; type EditorRenderMode = 'viewonly' | 'edition-owner' | 'edition-editor' | 'edition-viewer' | 'showcase';
export default EditorRenderMode; export default EditorRenderMode;

View File

@ -17,24 +17,24 @@
*/ */
class Events { class Events {
private $events; private _handlerByType;
constructor() { constructor() {
this.$events = {}; this._handlerByType = {};
} }
static _removeOn(string: string) { static _normalizeEventName(string: string) {
return string.replace(/^on([A-Z])/, (full, first) => first.toLowerCase()); return string.replace(/^on([A-Z])/, (_full, first) => first.toLowerCase());
} }
addEvent(typeName: string, fn?, internal?: boolean): Events { addEvent(typeName: string, fn?, internal?: boolean): Events {
const type = Events._removeOn(typeName); const type = Events._normalizeEventName(typeName);
// Add function had not been added yet // Add function had not been added yet
const funByType = this.$events[type] ? this.$events[type] : []; const funByType = this._handlerByType[type] ? this._handlerByType[type] : [];
if (!funByType.includes(fn)) { if (!funByType.includes(fn)) {
funByType.push(fn); funByType.push(fn);
this.$events[type] = funByType; this._handlerByType[type] = funByType;
} }
// Mark reference ... // Mark reference ...
@ -42,25 +42,21 @@ class Events {
return this; return this;
} }
fireEvent(typeName: string, eventArgs?, delay?: boolean): Events { fireEvent(typeName: string, eventArgs?): Events {
const type = Events._removeOn(typeName); const type = Events._normalizeEventName(typeName);
const events = this.$events[type]; const events = this._handlerByType[type];
if (!events) return this; if (!events) return this;
const args = Array.isArray(eventArgs) ? eventArgs : [eventArgs]; const args = Array.isArray(eventArgs) ? eventArgs : [eventArgs];
events.forEach(((fn) => { events.forEach(((fn) => {
if (delay) {
fn.delay(delay, this, args);
} else {
fn.apply(this, args); fn.apply(this, args);
}
})); }));
return this; return this;
} }
removeEvent(typeName: string, fn?): Events { removeEvent(typeName: string, fn?): Events {
const type = Events._removeOn(typeName); const type = Events._normalizeEventName(typeName);
const events = this.$events[type]; const events = this._handlerByType[type];
if (events && !fn.internal) { if (events && !fn.internal) {
const index = events.indexOf(fn); const index = events.indexOf(fn);
if (index !== -1) events.splice(index, 1); if (index !== -1) events.splice(index, 1);

View File

@ -16,14 +16,14 @@
* limitations under the License. * limitations under the License.
*/ */
import { $assert } from '@wisemapping/core-js'; import { $assert } from '@wisemapping/core-js';
import { Image } from '@wisemapping/web2d'; import { Image, Point } from '@wisemapping/web2d';
import IconGroup from './IconGroup'; import IconGroup from './IconGroup';
import { Point } from '@wisemapping/web2d';
import SizeType from './SizeType'; import SizeType from './SizeType';
import FeatureModel from './model/FeatureModel'; import FeatureModel from './model/FeatureModel';
abstract class Icon { abstract class Icon {
protected _image: Image; protected _image: Image;
protected _group: IconGroup; protected _group: IconGroup;
constructor(url: string) { constructor(url: string) {

View File

@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
// eslint-disable-next-line max-classes-per-file
import { import {
$assert, $assert,
$defined, $defined,
@ -23,12 +23,11 @@ import {
import { import {
Group, Group,
ElementClass, ElementClass,
Point,
} from '@wisemapping/web2d'; } from '@wisemapping/web2d';
import IconGroupRemoveTip from './IconGroupRemoveTip'; import IconGroupRemoveTip from './IconGroupRemoveTip';
import { Point } from '@wisemapping/web2d';
import Icon from './Icon'; import Icon from './Icon';
import SizeType from './SizeType'; import SizeType from './SizeType';
import IconModel from './model/IconModel';
import FeatureModel from './model/FeatureModel'; import FeatureModel from './model/FeatureModel';
const ORDER_BY_TYPE = new Map<string, number>(); const ORDER_BY_TYPE = new Map<string, number>();
@ -38,10 +37,15 @@ ORDER_BY_TYPE.set('link', 2);
class IconGroup { class IconGroup {
private _icons: Icon[]; private _icons: Icon[];
private _group: any; private _group: any;
private _removeTip: IconGroupRemoveTip; private _removeTip: IconGroupRemoveTip;
private _iconSize: SizeType; private _iconSize: SizeType;
private _topicId: number; private _topicId: number;
constructor(topicId: number, iconSize: number) { constructor(topicId: number, iconSize: number) {
$assert($defined(topicId), 'topicId can not be null'); $assert($defined(topicId), 'topicId can not be null');
$assert($defined(iconSize), 'iconSize can not be null'); $assert($defined(iconSize), 'iconSize can not be null');
@ -59,7 +63,6 @@ class IconGroup {
this._removeTip = new IconGroupRemoveTip(this._group); this._removeTip = new IconGroupRemoveTip(this._group);
this.seIconSize(iconSize, iconSize); this.seIconSize(iconSize, iconSize);
this._registerListeners(); this._registerListeners();
} }
setPosition(x: number, y: number): void { setPosition(x: number, y: number): void {
@ -189,7 +192,6 @@ class IconGroup {
} }
static ICON_PADDING = 5; static ICON_PADDING = 5;
} }
export default IconGroup; export default IconGroup;

View File

@ -26,8 +26,11 @@ import FeatureModel from './model/FeatureModel';
class LinkIcon extends Icon { class LinkIcon extends Icon {
private _linksModel: FeatureModel; private _linksModel: FeatureModel;
private _topic: Topic; private _topic: Topic;
private _readOnly: boolean; private _readOnly: boolean;
private _tip: LinkIconTooltip; private _tip: LinkIconTooltip;
constructor(topic: Topic, linkModel: LinkModel, readOnly: boolean) { constructor(topic: Topic, linkModel: LinkModel, readOnly: boolean) {
@ -73,8 +76,8 @@ class LinkIcon extends Icon {
getModel(): FeatureModel { getModel(): FeatureModel {
return this._linksModel; return this._linksModel;
} }
static IMAGE_URL = LinksImage;
static IMAGE_URL = LinksImage;
} }
export default LinkIcon; export default LinkIcon;

View File

@ -1,37 +0,0 @@
import { $assert } from '@wisemapping/core-js';
import CentralTopic from './CentralTopic';
import MainTopic from './MainTopic';
/**
* creates a new topic from the given node model
* @memberof mindplot.Nodegraph
* @param {mindplot.model.NodeModel} nodeModel
* @param {Object} options
* @throws will throw an error if nodeModel is null or undefined
* @throws will throw an error if the nodeModel's type is null or undefined
* @throws will throw an error if the node type cannot be recognized as either central or main
* topic type
* @return {mindplot.CentralTopic|mindplot.MainTopic} the new topic
*/
export const create = (nodeModel, options) => {
$assert(nodeModel, 'Model can not be null');
const type = nodeModel.getType();
$assert(type, 'Node model type can not be null');
let result;
if (type === 'CentralTopic') {
result = new CentralTopic(nodeModel, options);
} else if (type === 'MainTopic') {
result = new MainTopic(nodeModel, options);
} else {
$assert(false, `unsupported node type:${type}`);
}
return result;
};
export default {
create,
};

View File

@ -27,8 +27,11 @@ import FeatureModel from './model/FeatureModel';
class NoteIcon extends Icon { class NoteIcon extends Icon {
private _linksModel: NoteModel; private _linksModel: NoteModel;
private _topic: Topic; private _topic: Topic;
private _readOnly: boolean; private _readOnly: boolean;
private _tip: FloatingTip; private _tip: FloatingTip;
constructor(topic: Topic, noteModel: NoteModel, readOnly: boolean) { constructor(topic: Topic, noteModel: NoteModel, readOnly: boolean) {
@ -55,7 +58,6 @@ class NoteIcon extends Icon {
} }
this._tip = new FloatingTip($(me.getImage().peer._native), { this._tip = new FloatingTip($(me.getImage().peer._native), {
title: $msg('NOTE'), title: $msg('NOTE'),
container: 'body',
// Content can also be a function of the target element! // Content can also be a function of the target element!
content() { content() {
return me._buildTooltipContent(); return me._buildTooltipContent();
@ -88,7 +90,6 @@ class NoteIcon extends Icon {
} }
static IMAGE_URL = NotesImage; static IMAGE_URL = NotesImage;
} }
export default NoteIcon; export default NoteIcon;

View File

@ -277,10 +277,10 @@ class Relationship extends ConnectionLine {
return this._isInWorkspace; return this._isInWorkspace;
} }
setVisibility(value: boolean) { setVisibility(value: boolean, fade = 0) {
super.setVisibility(value); super.setVisibility(value, fade);
if (this._showEndArrow) this._endArrow.setVisibility(this._showEndArrow); if (this._showEndArrow) this._endArrow.setVisibility(this._showEndArrow);
this._startArrow.setVisibility(this._showStartArrow && value); this._startArrow.setVisibility(this._showStartArrow && value, fade);
} }
setOpacity(opacity: number) { setOpacity(opacity: number) {

View File

@ -78,8 +78,8 @@ class ShirinkConnector {
this._isShrink = isShrink; this._isShrink = isShrink;
} }
setVisibility(value: boolean): void { setVisibility(value: boolean, fade = 0): void {
this._ellipse.setVisibility(value); this._ellipse.setVisibility(value, fade);
} }
setOpacity(opacity: number): void { setOpacity(opacity: number): void {

View File

@ -81,7 +81,7 @@ class StandaloneActionDispatcher extends ActionDispatcher {
const commandFunc = (topic: Topic, pos: Point) => { const commandFunc = (topic: Topic, pos: Point) => {
const result = topic.getPosition(); const result = topic.getPosition();
EventBus.instance.fireEvent(EventBus.events.NodeMoveEvent, { EventBus.instance.fireEvent('topicMoved', {
node: topic.getModel(), node: topic.getModel(),
position: pos, position: pos,
}); });
@ -110,8 +110,7 @@ class StandaloneActionDispatcher extends ActionDispatcher {
this.execute(command); this.execute(command);
} }
/** */ changeTextToTopic(topicsIds: number[], text: string): void {
changeTextToTopic(topicsIds: number[], text: string) {
$assert($defined(topicsIds), 'topicsIds can not be null'); $assert($defined(topicsIds), 'topicsIds can not be null');
const commandFunc = (topic: Topic, value: string) => { const commandFunc = (topic: Topic, value: string) => {
@ -252,7 +251,6 @@ class StandaloneActionDispatcher extends ActionDispatcher {
this.execute(command); this.execute(command);
} }
/** */
addFeatureToTopic(topicId: number, featureType: FeatureType, attributes) { addFeatureToTopic(topicId: number, featureType: FeatureType, attributes) {
const command = new AddFeatureToTopicCommand(topicId, featureType, attributes); const command = new AddFeatureToTopicCommand(topicId, featureType, attributes);
this.execute(command); this.execute(command);

View File

@ -28,7 +28,6 @@ import TopicStyle from './TopicStyle';
import TopicFeatureFactory from './TopicFeature'; import TopicFeatureFactory from './TopicFeature';
import ConnectionLine from './ConnectionLine'; import ConnectionLine from './ConnectionLine';
import IconGroup from './IconGroup'; import IconGroup from './IconGroup';
import FadeEffect from './util/FadeEffect';
import EventBus from './layout/EventBus'; import EventBus from './layout/EventBus';
import ShirinkConnector from './ShrinkConnector'; import ShirinkConnector from './ShrinkConnector';
import NoteEditor from './widget/NoteEditor'; import NoteEditor from './widget/NoteEditor';
@ -337,11 +336,6 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
/**
* assigns the new feature model to the topic's node model and adds the respective icon
* @param {mindplot.model.FeatureModel} featureModel
* @return {mindplot.Icon} the icon corresponding to the feature model
*/
addFeature(featureModel: FeatureModel): Icon { addFeature(featureModel: FeatureModel): Icon {
const iconGroup = this.getOrBuildIconGroup(); const iconGroup = this.getOrBuildIconGroup();
this.closeEditors(); this.closeEditors();
@ -350,7 +344,7 @@ abstract class Topic extends NodeGraph {
const model = this.getModel(); const model = this.getModel();
model.addFeature(featureModel); model.addFeature(featureModel);
const result = TopicFeatureFactory.createIcon(this, featureModel, this.isReadOnly()); const result: Icon = TopicFeatureFactory.createIcon(this, featureModel, this.isReadOnly());
iconGroup.addIcon( iconGroup.addIcon(
result, result,
featureModel.getType() === TopicFeatureFactory.Icon.id && !this.isReadOnly(), featureModel.getType() === TopicFeatureFactory.Icon.id && !this.isReadOnly(),
@ -360,7 +354,6 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
/** */
findFeatureById(id: number) { findFeatureById(id: number) {
const model = this.getModel(); const model = this.getModel();
return model.findFeatureById(id); return model.findFeatureById(id);
@ -431,7 +424,6 @@ abstract class Topic extends NodeGraph {
this.adjustShapes(); this.adjustShapes();
} }
/** */
setFontSize(value: number, updateModel?: boolean) { setFontSize(value: number, updateModel?: boolean) {
const textShape = this.getTextShape(); const textShape = this.getTextShape();
textShape.setSize(value); textShape.setSize(value);
@ -443,8 +435,7 @@ abstract class Topic extends NodeGraph {
this.adjustShapes(); this.adjustShapes();
} }
/** */ setFontStyle(value: string, updateModel?: boolean) {
setFontStyle(value, updateModel) {
const textShape = this.getTextShape(); const textShape = this.getTextShape();
textShape.setStyle(value); textShape.setStyle(value);
if ($defined(updateModel) && updateModel) { if ($defined(updateModel) && updateModel) {
@ -454,8 +445,7 @@ abstract class Topic extends NodeGraph {
this.adjustShapes(); this.adjustShapes();
} }
/** */ setFontWeight(value: string, updateModel?: boolean) {
setFontWeight(value, updateModel) {
const textShape = this.getTextShape(); const textShape = this.getTextShape();
textShape.setWeight(value); textShape.setWeight(value);
if ($defined(updateModel) && updateModel) { if ($defined(updateModel) && updateModel) {
@ -465,7 +455,6 @@ abstract class Topic extends NodeGraph {
this.adjustShapes(); this.adjustShapes();
} }
/** */
getFontWeight() { getFontWeight() {
const model = this.getModel(); const model = this.getModel();
let result = model.getFontWeight(); let result = model.getFontWeight();
@ -476,8 +465,7 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
/** */ getFontFamily(): string {
getFontFamily() {
const model = this.getModel(); const model = this.getModel();
let result = model.getFontFamily(); let result = model.getFontFamily();
if (!$defined(result)) { if (!$defined(result)) {
@ -487,8 +475,7 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
/** */ getFontColor(): string {
getFontColor() {
const model = this.getModel(); const model = this.getModel();
let result = model.getFontColor(); let result = model.getFontColor();
if (!$defined(result)) { if (!$defined(result)) {
@ -498,8 +485,7 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
/** */ getFontStyle(): string {
getFontStyle() {
const model = this.getModel(); const model = this.getModel();
let result = model.getFontStyle(); let result = model.getFontStyle();
if (!$defined(result)) { if (!$defined(result)) {
@ -509,8 +495,7 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
/** */ getFontSize(): number {
getFontSize() {
const model = this.getModel(); const model = this.getModel();
let result = model.getFontSize(); let result = model.getFontSize();
if (!$defined(result)) { if (!$defined(result)) {
@ -520,8 +505,7 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
/** */ setFontColor(value: string, updateModel?: boolean) {
setFontColor(value, updateModel) {
const textShape = this.getTextShape(); const textShape = this.getTextShape();
textShape.setColor(value); textShape.setColor(value);
if ($defined(updateModel) && updateModel) { if ($defined(updateModel) && updateModel) {
@ -530,7 +514,7 @@ abstract class Topic extends NodeGraph {
} }
} }
_setText(text: string, updateModel: boolean) { private _setText(text: string, updateModel?: boolean) {
const textShape = this.getTextShape(); const textShape = this.getTextShape();
textShape.setText(text == null ? TopicStyle.defaultText(this) : text); textShape.setText(text == null ? TopicStyle.defaultText(this) : text);
@ -540,7 +524,6 @@ abstract class Topic extends NodeGraph {
} }
} }
/** */
setText(text: string) { setText(text: string) {
// Avoid empty nodes ... // Avoid empty nodes ...
if (!text || $.trim(text).length === 0) { if (!text || $.trim(text).length === 0) {
@ -552,7 +535,6 @@ abstract class Topic extends NodeGraph {
this.adjustShapes(); this.adjustShapes();
} }
/** */
getText(): string { getText(): string {
const model = this.getModel(); const model = this.getModel();
let result = model.getText(); let result = model.getText();
@ -562,12 +544,11 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
/** */
setBackgroundColor(color: string) { setBackgroundColor(color: string) {
this._setBackgroundColor(color, true); this._setBackgroundColor(color, true);
} }
_setBackgroundColor(color: string, updateModel: boolean) { private _setBackgroundColor(color: string, updateModel: boolean) {
const innerShape = this.getInnerShape(); const innerShape = this.getInnerShape();
innerShape.setFill(color); innerShape.setFill(color);
@ -593,11 +574,11 @@ abstract class Topic extends NodeGraph {
} }
/** */ /** */
setBorderColor(color: string) { setBorderColor(color: string): void {
this._setBorderColor(color, true); this._setBorderColor(color, true);
} }
_setBorderColor(color: string, updateModel: boolean) { private _setBorderColor(color: string, updateModel: boolean): void {
const innerShape = this.getInnerShape(); const innerShape = this.getInnerShape();
innerShape.setAttribute('strokeColor', color); innerShape.setAttribute('strokeColor', color);
@ -612,8 +593,7 @@ abstract class Topic extends NodeGraph {
} }
} }
/** */ getBorderColor(): string {
getBorderColor() {
const model = this.getModel(); const model = this.getModel();
let result = model.getBorderColor(); let result = model.getBorderColor();
if (!$defined(result)) { if (!$defined(result)) {
@ -622,7 +602,7 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
_buildTopicShape() { _buildTopicShape(): ElementClass {
const groupAttributes = { const groupAttributes = {
width: 100, width: 100,
height: 100, height: 100,
@ -660,17 +640,17 @@ abstract class Topic extends NodeGraph {
group.setTestId(model.getId()); group.setTestId(model.getId());
} }
_registerDefaultListenersToElement(elem, topic) { _registerDefaultListenersToElement(elem: ElementClass, topic: Topic) {
const mouseOver = function mouseOver(event) { const mouseOver = function mouseOver() {
if (topic.isMouseEventsEnabled()) { if (topic.isMouseEventsEnabled()) {
topic.handleMouseOver(event); topic.handleMouseOver();
} }
}; };
elem.addEvent('mouseover', mouseOver); elem.addEvent('mouseover', mouseOver);
const outout = function outout(event) { const outout = function outout() {
if (topic.isMouseEventsEnabled()) { if (topic.isMouseEventsEnabled()) {
topic.handleMouseOut(event); topic.handleMouseOut();
} }
}; };
elem.addEvent('mouseout', outout); elem.addEvent('mouseout', outout);
@ -697,13 +677,12 @@ abstract class Topic extends NodeGraph {
} }
/** */ /** */
areChildrenShrunken() { areChildrenShrunken(): boolean {
const model = this.getModel(); const model = this.getModel();
return model.areChildrenShrunken() && !this.isCentralTopic(); return model.areChildrenShrunken() && !this.isCentralTopic();
} }
/** */ isCollapsed(): boolean {
isCollapsed() {
let result = false; let result = false;
let current = this.getParent(); let current = this.getParent();
@ -714,42 +693,27 @@ abstract class Topic extends NodeGraph {
return result; return result;
} }
/** */ setChildrenShrunken(value: boolean) {
setChildrenShrunken(value) {
// Update Model ... // Update Model ...
const model = this.getModel(); const model = this.getModel();
model.setChildrenShrunken(value); model.setChildrenShrunken(value);
// Change render base on the state. // Change render base on the state.
const shrinkConnector = this.getShrinkConnector(); const shrinkConnector = this.getShrinkConnector();
if ($defined(shrinkConnector)) { if (shrinkConnector) {
shrinkConnector.changeRender(value); shrinkConnector.changeRender(value);
} }
// Do some fancy animation .... // Do some fancy animation ....
const elements = this._flatten2DElements(this); const elements = this._flatten2DElements(this);
const fade = new FadeEffect(elements, !value);
const me = this;
fade.addEvent('complete', () => {
// Set focus on the parent node ...
if (value) {
me.setOnFocus(true);
}
// Set focus in false for all the children ...
elements.forEach((elem) => { elements.forEach((elem) => {
if (elem.setOnFocus) { elem.setVisibility(!value, 250);
elem.setOnFocus(false);
}
}); });
});
fade.start();
EventBus.instance.fireEvent(EventBus.events.NodeShrinkEvent, model); EventBus.instance.fireEvent('childShrinked', model);
} }
/** */ getShrinkConnector(): ShirinkConnector | undefined {
getShrinkConnector(): ShirinkConnector {
let result = this._connector; let result = this._connector;
if (this._connector == null) { if (this._connector == null) {
this._connector = new ShirinkConnector(this); this._connector = new ShirinkConnector(this);
@ -928,7 +892,6 @@ abstract class Topic extends NodeGraph {
this._relationships.forEach((r) => r.redraw()); this._relationships.forEach((r) => r.redraw());
} }
/** */
setBranchVisibility(value: boolean): void { setBranchVisibility(value: boolean): void {
let current: Topic = this; let current: Topic = this;
let parent: Topic = this; let parent: Topic = this;
@ -939,20 +902,19 @@ abstract class Topic extends NodeGraph {
current.setVisibility(value); current.setVisibility(value);
} }
/** */ setVisibility(value: boolean, fade = 0): void {
setVisibility(value: boolean): void { this._setTopicVisibility(value, fade);
this._setTopicVisibility(value);
// Hide all children... // Hide all children...
this._setChildrenVisibility(value); this._setChildrenVisibility(value, fade);
// If there there are connection to the node, topic must be hidden. // If there there are connection to the node, topic must be hidden.
this._setRelationshipLinesVisibility(value); this._setRelationshipLinesVisibility(value, fade);
// If it's connected, the connection must be rendered. // If it's connected, the connection must be rendered.
const outgoingLine = this.getOutgoingLine(); const outgoingLine = this.getOutgoingLine();
if (outgoingLine) { if (outgoingLine) {
outgoingLine.setVisibility(value); outgoingLine.setVisibility(value, fade);
} }
} }
@ -986,7 +948,7 @@ abstract class Topic extends NodeGraph {
return elem.isVisible(); return elem.isVisible();
} }
private _setRelationshipLinesVisibility(value: boolean): void { private _setRelationshipLinesVisibility(value: boolean, fade = 0): void {
this._relationships.forEach((relationship) => { this._relationships.forEach((relationship) => {
const sourceTopic = relationship.getSourceTopic(); const sourceTopic = relationship.getSourceTopic();
const targetTopic = relationship.getTargetTopic(); const targetTopic = relationship.getTargetTopic();
@ -997,27 +959,28 @@ abstract class Topic extends NodeGraph {
value value
&& (targetParent == null || !targetParent.areChildrenShrunken()) && (targetParent == null || !targetParent.areChildrenShrunken())
&& (sourceParent == null || !sourceParent.areChildrenShrunken()), && (sourceParent == null || !sourceParent.areChildrenShrunken()),
fade,
); );
}); });
} }
private _setTopicVisibility(value: boolean) { private _setTopicVisibility(value: boolean, fade = 0) {
const elem = this.get2DElement(); const elem = this.get2DElement();
elem.setVisibility(value); elem.setVisibility(value, fade);
if (this.getIncomingLines().length > 0) { if (this.getIncomingLines().length > 0) {
const connector = this.getShrinkConnector(); const connector = this.getShrinkConnector();
if ($defined(connector)) { if ($defined(connector)) {
connector.setVisibility(value); connector.setVisibility(value, fade);
} }
} }
// Hide inner shape ... // Hide inner shape ...
this.getInnerShape().setVisibility(value); this.getInnerShape().setVisibility(value, fade);
// Hide text shape ... // Hide text shape ...
const textShape = this.getTextShape(); const textShape = this.getTextShape();
textShape.setVisibility(this.getShapeType() !== TopicShape.IMAGE ? value : false); textShape.setVisibility(this.getShapeType() !== TopicShape.IMAGE ? value : false, fade);
} }
/** */ /** */
@ -1033,14 +996,14 @@ abstract class Topic extends NodeGraph {
textShape.setOpacity(opacity); textShape.setOpacity(opacity);
} }
private _setChildrenVisibility(isVisible: boolean) { private _setChildrenVisibility(value: boolean, fade = 0) {
// Hide all children. // Hide all children.
const children = this.getChildren(); const children = this.getChildren();
const model = this.getModel(); const model = this.getModel();
const visibility = isVisible ? !model.areChildrenShrunken() : isVisible; const visibility = value ? !model.areChildrenShrunken() : value;
children.forEach((child) => { children.forEach((child) => {
child.setVisibility(visibility); child.setVisibility(visibility, fade);
const outgoingLine = child.getOutgoingLine(); const outgoingLine = child.getOutgoingLine();
outgoingLine.setVisibility(visibility); outgoingLine.setVisibility(visibility);
}); });
@ -1081,7 +1044,7 @@ abstract class Topic extends NodeGraph {
this._updatePositionOnChangeSize(oldSize, roundedSize); this._updatePositionOnChangeSize(oldSize, roundedSize);
if (hasSizeChanged) { if (hasSizeChanged) {
EventBus.instance.fireEvent(EventBus.events.NodeResizeEvent, { EventBus.instance.fireEvent('topicResize', {
node: this.getModel(), node: this.getModel(),
size: roundedSize, size: roundedSize,
}); });
@ -1112,7 +1075,7 @@ abstract class Topic extends NodeGraph {
outgoingLine.removeFromWorkspace(workspace); outgoingLine.removeFromWorkspace(workspace);
// Remove from workspace. // Remove from workspace.
EventBus.instance.fireEvent(EventBus.events.NodeDisconnectEvent, this.getModel()); EventBus.instance.fireEvent('topicDisconect', this.getModel());
// Change text based on the current connection ... // Change text based on the current connection ...
const model = this.getModel(); const model = this.getModel();
@ -1195,7 +1158,7 @@ abstract class Topic extends NodeGraph {
// Fire connection event ... // Fire connection event ...
if (this.isInWorkspace()) { if (this.isInWorkspace()) {
EventBus.instance.fireEvent(EventBus.events.NodeConnectEvent, { EventBus.instance.fireEvent('topicConnected', {
parentNode: targetTopic.getModel(), parentNode: targetTopic.getModel(),
childNode: this.getModel(), childNode: this.getModel(),
}); });
@ -1233,7 +1196,7 @@ abstract class Topic extends NodeGraph {
workspace.removeChild(line); workspace.removeChild(line);
} }
this._isInWorkspace = false; this._isInWorkspace = false;
EventBus.instance.fireEvent(EventBus.events.NodeRemoved, this.getModel()); EventBus.instance.fireEvent('topicRemoved', this.getModel());
} }
addToWorkspace(workspace: Workspace) { addToWorkspace(workspace: Workspace) {
@ -1241,11 +1204,11 @@ abstract class Topic extends NodeGraph {
workspace.append(elem); workspace.append(elem);
if (!this.isInWorkspace()) { if (!this.isInWorkspace()) {
if (!this.isCentralTopic()) { if (!this.isCentralTopic()) {
EventBus.instance.fireEvent(EventBus.events.NodeAdded, this.getModel()); EventBus.instance.fireEvent('topicAdded', this.getModel());
} }
if (this.getModel().isConnected()) { if (this.getModel().isConnected()) {
EventBus.instance.fireEvent(EventBus.events.NodeConnectEvent, { EventBus.instance.fireEvent('topicConnected', {
parentNode: this.getOutgoingConnectedTopic().getModel(), parentNode: this.getOutgoingConnectedTopic().getModel(),
childNode: this.getModel(), childNode: this.getModel(),
}); });
@ -1315,7 +1278,7 @@ abstract class Topic extends NodeGraph {
} }
} }
private _flatten2DElements(topic: Topic) { private _flatten2DElements(topic: Topic): (Topic | Relationship)[] {
let result = []; let result = [];
const children = topic.getChildren(); const children = topic.getChildren();

View File

@ -0,0 +1,27 @@
import { $assert } from '@wisemapping/core-js';
import CentralTopic from './CentralTopic';
import MainTopic from './MainTopic';
import NodeModel from './model/NodeModel';
import Topic from './Topic';
class TopicFactory {
static create(nodeModel: NodeModel, options: object): Topic {
$assert(nodeModel, 'Model can not be null');
const type = nodeModel.getType();
$assert(type, 'Node model type can not be null');
let result: Topic;
if (type === 'CentralTopic') {
result = new CentralTopic(nodeModel, options);
} else if (type === 'MainTopic') {
result = new MainTopic(nodeModel, options);
} else {
$assert(false, `unsupported node type:${type}`);
}
return result;
}
}
export default TopicFactory;

View File

@ -40,14 +40,6 @@ const TopicFeatureFactory = {
icon: NoteIcon, icon: NoteIcon,
}, },
/**
* @param {mindplot.Topic} topic
* @param {mindplot.model.FeatureModel} model
* @param {Boolean} readOnly true if the editor is running in read-only mode
* @throws will throw an error if topic is null or undefined
* @throws will throw an error if model is null or undefined
* @return {mindplot.Icon} a new instance of the icon subclass matching the topic feature
*/
createIcon(topic, model, readOnly) { createIcon(topic, model, readOnly) {
$assert(topic, 'topic can not be null'); $assert(topic, 'topic can not be null');
$assert(model, 'model can not be null'); $assert(model, 'model can not be null');

View File

@ -52,9 +52,6 @@ class AddFeatureToTopicCommand extends Command {
this._featureModel = null; this._featureModel = null;
} }
/**
* Overrides abstract parent method
*/
execute(commandContext: CommandContext) { execute(commandContext: CommandContext) {
const topic = commandContext.findTopics([this._topicId])[0]; const topic = commandContext.findTopics([this._topicId])[0];
@ -66,10 +63,6 @@ class AddFeatureToTopicCommand extends Command {
topic.addFeature(this._featureModel); topic.addFeature(this._featureModel);
} }
/**
* Overrides abstract parent method
* @see {@link mindplot.Command.undoExecute}
*/
undoExecute(commandContext: CommandContext) { undoExecute(commandContext: CommandContext) {
const topic = commandContext.findTopics([this._topicId])[0]; const topic = commandContext.findTopics([this._topicId])[0];
topic.removeFeature(this._featureModel); topic.removeFeature(this._featureModel);

View File

@ -25,7 +25,7 @@ type CommandTypes = string | object | boolean | number;
class GenericFunctionCommand extends Command { class GenericFunctionCommand extends Command {
private _value: CommandTypes; private _value: CommandTypes;
private _topicsId: number[]; private _topicsIds: number[];
private _commandFunc: (topic: Topic, value: CommandTypes) => CommandTypes; private _commandFunc: (topic: Topic, value: CommandTypes) => CommandTypes;
@ -39,7 +39,7 @@ class GenericFunctionCommand extends Command {
super(); super();
this._value = value; this._value = value;
this._topicsId = topicsIds; this._topicsIds = topicsIds;
this._commandFunc = commandFunc; this._commandFunc = commandFunc;
this._oldValues = []; this._oldValues = [];
} }
@ -49,7 +49,7 @@ class GenericFunctionCommand extends Command {
*/ */
execute(commandContext: CommandContext) { execute(commandContext: CommandContext) {
if (!this._applied) { if (!this._applied) {
const topics = commandContext.findTopics(this._topicsId); const topics = commandContext.findTopics(this._topicsIds);
if (topics != null) { if (topics != null) {
const me = this; const me = this;
@ -66,7 +66,7 @@ class GenericFunctionCommand extends Command {
undoExecute(commandContext: CommandContext): void { undoExecute(commandContext: CommandContext): void {
if (this._applied) { if (this._applied) {
const topics = commandContext.findTopics(this._topicsId); const topics = commandContext.findTopics(this._topicsIds);
topics.forEach(((topic: Topic, index: number) => { topics.forEach(((topic: Topic, index: number) => {
this._commandFunc(topic, this._oldValues[index]); this._commandFunc(topic, this._oldValues[index]);

View File

@ -1,6 +1,4 @@
/* eslint-disable no-unused-vars */ /**
/* eslint-disable class-methods-use-this */
/*
* Copyright [2021] [wisemapping] * Copyright [2021] [wisemapping]
* *
* Licensed under WiseMapping Public License, Version 1.0 (the "License"). * Licensed under WiseMapping Public License, Version 1.0 (the "License").
@ -17,22 +15,26 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import RootedTreeSet from './RootedTreeSet';
import Node from './Node';
import PositionType from '../PositionType';
abstract class ChildrenSorterStrategy { abstract class ChildrenSorterStrategy {
abstract computeChildrenIdByHeights(treeSet, node); abstract computeChildrenIdByHeights(treeSet: RootedTreeSet, node: Node);
abstract computeOffsets(treeSet, node); abstract computeOffsets(treeSet: RootedTreeSet, node: Node);
abstract insert(treeSet, parent, child, order); abstract insert(treeSet: RootedTreeSet, parent: Node, child: Node, order: number);
abstract detach(treeSet, node); abstract detach(treeSet: RootedTreeSet, node: Node);
abstract predict(treeSet, parent, node, position, free); abstract predict(treeSet: RootedTreeSet, parent, node: Node, position: PositionType);
abstract verify(treeSet, node); abstract verify(treeSet: RootedTreeSet, node: Node);
abstract getChildDirection(treeSet, node); abstract getChildDirection(treeSet: RootedTreeSet, node: Node);
abstract toString(); abstract toString(): string;
} }
export default ChildrenSorterStrategy; export default ChildrenSorterStrategy;

View File

@ -17,25 +17,22 @@
*/ */
import Events from '../Events'; import Events from '../Events';
export type EventType = 'topicResize' | 'topicMoved' | 'childShrinked' | 'topicConnected' | 'topicAdded' | 'topicRemoved' | 'forceLayout' | 'topicDisconect';
class EventBus extends Events { class EventBus extends Events {
// eslint-disable-next-line no-use-before-define
static _instance: EventBus = new EventBus();
static get instance(): EventBus {
return this._instance;
}
fireEvent(type: EventType, eventArgs?: unknown[] | unknown): Events {
return super.fireEvent(type, eventArgs);
}
addEvent(type: EventType, fn?, internal?: boolean): Events {
return super.addEvent(type, fn, internal);
}
} }
/**
* Enum for events
* @enum {String}
*/
EventBus.events = {
NodeResizeEvent: 'NodeResizeEvent',
NodeMoveEvent: 'NodeMoveEvent',
NodeShrinkEvent: 'NodeShrinkEvent',
NodeConnectEvent: 'NodeConnectEvent',
NodeDisconnectEvent: 'NodeDisconnectEvent',
NodeAdded: 'NodeAdded',
NodeRemoved: 'NodeRemoved',
DoLayout: 'DoLayout',
};
/** instance */
EventBus.instance = new EventBus();
export default EventBus; export default EventBus;

View File

@ -15,59 +15,57 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import PositionType from '../PositionType';
import SizeType from '../SizeType';
import Topic from '../Topic';
import EventBus from './EventBus'; import EventBus from './EventBus';
import LayoutManager from './LayoutManager';
class EventBusDispatcher { class EventBusDispatcher {
private _layoutManager: LayoutManager;
constructor() { constructor() {
this.registerBusEvents(); this.registerBusEvents();
} }
/** setLayoutManager(layoutManager: LayoutManager) {
* @param {mindplot.layout.LayoutManager} layoutManager
*/
setLayoutManager(layoutManager) {
this._layoutManager = layoutManager; this._layoutManager = layoutManager;
} }
/**
* register bus events
*/
registerBusEvents() { registerBusEvents() {
EventBus.instance.addEvent(EventBus.events.NodeAdded, this._nodeAdded.bind(this)); EventBus.instance.addEvent('topicAdded', this._topicAdded.bind(this));
EventBus.instance.addEvent(EventBus.events.NodeRemoved, this._nodeRemoved.bind(this)); EventBus.instance.addEvent('topicRemoved', this._topicRemoved.bind(this));
EventBus.instance.addEvent(EventBus.events.NodeResizeEvent, this._nodeResizeEvent.bind(this)); EventBus.instance.addEvent('topicResize', this._topicResizeEvent.bind(this));
EventBus.instance.addEvent(EventBus.events.NodeMoveEvent, this._nodeMoveEvent.bind(this)); EventBus.instance.addEvent('topicMoved', this._topicMoved.bind(this));
EventBus.instance.addEvent( EventBus.instance.addEvent('topicDisconect', this._topicDisconect.bind(this));
EventBus.events.NodeDisconnectEvent, this._nodeDisconnectEvent.bind(this), EventBus.instance.addEvent('topicConnected', this._topicConnected.bind(this));
); EventBus.instance.addEvent('childShrinked', this._childShrinked.bind(this));
EventBus.instance.addEvent(EventBus.events.NodeConnectEvent, this._nodeConnectEvent.bind(this)); EventBus.instance.addEvent('forceLayout', this._forceLayout.bind(this));
EventBus.instance.addEvent(EventBus.events.NodeShrinkEvent, this._nodeShrinkEvent.bind(this));
EventBus.instance.addEvent(EventBus.events.DoLayout, this._doLayout.bind(this));
} }
_nodeResizeEvent(args) { private _topicResizeEvent(args: { node: Topic, size: SizeType }) {
this._layoutManager.updateNodeSize(args.node.getId(), args.size); this._layoutManager.updateNodeSize(args.node.getId(), args.size);
} }
_nodeMoveEvent(args) { private _topicMoved(args: { node: Topic, position: PositionType }) {
this._layoutManager.moveNode(args.node.getId(), args.position); this._layoutManager.moveNode(args.node.getId(), args.position);
} }
_nodeDisconnectEvent(node) { private _topicDisconect(node: Topic) {
this._layoutManager.disconnectNode(node.getId()); this._layoutManager.disconnectNode(node.getId());
} }
_nodeConnectEvent(args) { private _topicConnected(args: { parentNode: Topic, childNode: Topic }) {
this._layoutManager.connectNode( this._layoutManager.connectNode(
args.parentNode.getId(), args.childNode.getId(), args.childNode.getOrder(), args.parentNode.getId(), args.childNode.getId(), args.childNode.getOrder(),
); );
} }
_nodeShrinkEvent(node) { private _childShrinked(node: Topic) {
this._layoutManager.updateShrinkState(node.getId(), node.areChildrenShrunken()); this._layoutManager.updateShrinkState(node.getId(), node.areChildrenShrunken());
} }
_nodeAdded(node) { private _topicAdded(node: Topic) {
// Central topic must not be added twice ... // Central topic must not be added twice ...
if (node.getId() !== 0) { if (node.getId() !== 0) {
this._layoutManager.addNode(node.getId(), { width: 10, height: 10 }, node.getPosition()); this._layoutManager.addNode(node.getId(), { width: 10, height: 10 }, node.getPosition());
@ -75,21 +73,14 @@ class EventBusDispatcher {
} }
} }
_nodeRemoved(node) { private _topicRemoved(node: Topic) {
this._layoutManager.removeNode(node.getId()); this._layoutManager.removeNode(node.getId());
} }
_doLayout() { private _forceLayout() {
// (function() {
this._layoutManager.layout(true); this._layoutManager.layout(true);
// console.log("---------");
// this._layoutManager.dump();
// console.log("---------");
// console.log("---------");
// }).delay(0, this);
} }
/** @return layout manager */
getLayoutManager() { getLayoutManager() {
return this._layoutManager; return this._layoutManager;
} }

View File

@ -15,15 +15,23 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
import $ from 'jquery';
import { $assert, $defined } from '@wisemapping/core-js'; import { $assert, $defined } from '@wisemapping/core-js';
import Events from '../Events'; import Events from '../Events';
import RootedTreeSet from './RootedTreeSet'; import RootedTreeSet from './RootedTreeSet';
import OriginalLayout from './OriginalLayout'; import OriginalLayout from './OriginalLayout';
import ChangeEvent from './ChangeEvent'; import ChangeEvent from './ChangeEvent';
import SizeType from '../SizeType';
import Node from './Node';
import PositionType from '../PositionType';
class LayoutManager extends Events { class LayoutManager extends Events {
constructor(rootNodeId, rootSize) { private _treeSet: RootedTreeSet;
private _layout: OriginalLayout;
private _events: ChangeEvent[];
constructor(rootNodeId: number, rootSize: SizeType) {
super(); super();
$assert($defined(rootNodeId), 'rootNodeId can not be null'); $assert($defined(rootNodeId), 'rootNodeId can not be null');
$assert(rootSize, 'rootSize can not be null'); $assert(rootSize, 'rootSize can not be null');
@ -36,40 +44,22 @@ class LayoutManager extends Events {
this._events = []; this._events = [];
} }
/** updateNodeSize(id: number, size: SizeType): void {
* @param id
* @param size
* @throws will throw an error if id is null or undefined
*/
updateNodeSize(id, size) {
$assert($defined(id), 'id can not be null'); $assert($defined(id), 'id can not be null');
const node = this._treeSet.find(id); const node = this._treeSet.find(id);
node.setSize(size); node.setSize(size);
} }
/** updateShrinkState(id: number, value: boolean): void {
* @param id
* @param value
* @throws will throw an error if id is null or undefined
* @throws will throw an error if value is null or undefined
* @return this
*/
updateShrinkState(id, value) {
$assert($defined(id), 'id can not be null'); $assert($defined(id), 'id can not be null');
$assert($defined(value), 'value can not be null'); $assert($defined(value), 'value can not be null');
const node = this._treeSet.find(id); const node = this._treeSet.find(id);
node.setShrunken(value); node.setShrunken(value);
return this;
} }
/** find(id: number): Node {
* @param id
* @return {@link RootedTreeSet}.find(id)
*/
find(id) {
return this._treeSet.find(id); return this._treeSet.find(id);
} }
@ -81,31 +71,17 @@ class LayoutManager extends Events {
* @throws will throw an error if the position's x property is null or undefined * @throws will throw an error if the position's x property is null or undefined
* @throws will throw an error if the position's y property is null or undefined * @throws will throw an error if the position's y property is null or undefined
*/ */
moveNode(id, position) { moveNode(id: number, position: PositionType) {
$assert($defined(id), 'id cannot be null'); $assert($defined(id), 'id cannot be null');
$assert($defined(position), 'position cannot be null'); $assert($defined(position), 'position cannot be null');
$assert($defined(position.x), 'x can not be null'); $assert($defined(position.x), 'x can not be null');
$assert($defined(position.y), 'y can not be null'); $assert($defined(position.y), 'y can not be null');
const node = this._treeSet.find(id); const node = this._treeSet.find(id);
// @Todo: this should not be here. This is broking the isolated node support...
// node.setFree(true);
// node.setFreeDisplacement(
// {x:position.x - node.getPosition().x, y:position.y - node.getPosition().y}
// );
node.setPosition(position); node.setPosition(position);
} }
/** connectNode(parentId: number, childId: number, order: number) {
* @param parentId
* @param childId
* @param order
* @throws will throw an error if parentId is null or undefined
* @throws will throw an error if childId is null or undefined
* @throws will throw an error if order is null or undefined
* @return this
*/
connectNode(parentId, childId, order) {
$assert($defined(parentId), 'parentId cannot be null'); $assert($defined(parentId), 'parentId cannot be null');
$assert($defined(childId), 'childId cannot be null'); $assert($defined(childId), 'childId cannot be null');
$assert($defined(order), 'order cannot be null'); $assert($defined(order), 'order cannot be null');
@ -115,16 +91,9 @@ class LayoutManager extends Events {
return this; return this;
} }
/** disconnectNode(id: number): void {
* @param id
* @throws will throw an error if id is null or undefined
* @return this
*/
disconnectNode(id) {
$assert($defined(id), 'id can not be null'); $assert($defined(id), 'id can not be null');
this._layout.disconnectNode(id); this._layout.disconnectNode(id);
return this;
} }
/** /**
@ -134,7 +103,7 @@ class LayoutManager extends Events {
* @throws will throw an error if id is null or undefined * @throws will throw an error if id is null or undefined
* @return this * @return this
*/ */
addNode(id, size, position) { addNode(id: number, size: SizeType, position: PositionType) {
$assert($defined(id), 'id can not be null'); $assert($defined(id), 'id can not be null');
const result = this._layout.createNode(id, size, position, 'topic'); const result = this._layout.createNode(id, size, position, 'topic');
this._treeSet.add(result); this._treeSet.add(result);
@ -142,13 +111,7 @@ class LayoutManager extends Events {
return this; return this;
} }
/** removeNode(id: number) {
* removes a node and its connection to parent if existing
* @param id
* @throws will throw an error if id is null or undefined
* @return this
*/
removeNode(id) {
$assert($defined(id), 'id can not be null'); $assert($defined(id), 'id can not be null');
const node = this._treeSet.find(id); const node = this._treeSet.find(id);
@ -163,47 +126,31 @@ class LayoutManager extends Events {
return this; return this;
} }
/** predict(parentId: number, nodeId: number, position: PositionType): { order: number, position: PositionType } {
* @param {Number} parentId
* @param {Number=} nodeId
* @param {String=} position the position to use as mindplot.layout.Node.properties position
* property as '(x,y)'
* @param {Boolean=} free true specifies free node positioning
* @throws will throw an error if parentId is null or undefined
*/
predict(parentId, nodeId, position, free) {
$assert($defined(parentId), 'parentId can not be null'); $assert($defined(parentId), 'parentId can not be null');
const parent = this._treeSet.find(parentId); const parent = this._treeSet.find(parentId);
const node = nodeId ? this._treeSet.find(nodeId) : null; const node = nodeId ? this._treeSet.find(nodeId) : null;
const sorter = parent.getSorter(); const sorter = parent.getSorter();
const result = sorter.predict(this._treeSet, parent, node, position, free); const result = sorter.predict(this._treeSet, parent, node, position);
return { order: result[0], position: result[1] }; return { order: result[0], position: result[1] };
} }
/**
* logs dump to console
*/
dump() { dump() {
console.log(this._treeSet.dump()); console.log(this._treeSet.dump());
} }
/** plot(containerId: string, size = { width: 200, height: 200 }) {
* @param containerId
* @param {width:Number, height:Number} size
* @throws will throw an error if containerId is null or undefined
* @return canvas
*/
plot(containerId, size = { width: 200, height: 200 }) {
// this method is only used from tests that include Raphael // this method is only used from tests that include Raphael
if (!global.Raphael) {
if (!globalThis.Raphael) {
console.warn('Raphael.js not found, exiting plot()'); console.warn('Raphael.js not found, exiting plot()');
return null; return null;
} }
$assert(containerId, 'containerId cannot be null'); $assert(containerId, 'containerId cannot be null');
const squaresize = 10; const squaresize = 10;
const canvas = global.Raphael(containerId, size.width, size.height); const canvas = globalThis.Raphael(containerId, size.width, size.height);
canvas.drawGrid( canvas.drawGrid(
0, 0,
0, 0,
@ -217,40 +164,33 @@ class LayoutManager extends Events {
return canvas; return canvas;
} }
/** layout(flush: boolean): LayoutManager {
* initializes the layout to be updated
* @param fireEvents
* @return this
*/
layout(fireEvents) {
// File repositioning ... // File repositioning ...
this._layout.layout(); this._layout.layout();
// Collect changes ... // Collect changes ...
this._collectChanges(); this._collectChanges(this._treeSet.getTreeRoots());
if ($(fireEvents).length > 0 || fireEvents) { if (flush) {
this._flushEvents(); this._flushEvents();
} }
return this; return this;
} }
_flushEvents() { private _flushEvents() {
this._events.forEach(((event) => { this._events.forEach(((event) => {
this.fireEvent('change', event); this.fireEvent('change', event);
})); }));
this._events = []; this._events = [];
} }
_collectChanges(nodes) { private _collectChanges(nodes: Node[]) {
const nodesToCollect = nodes || this._treeSet.getTreeRoots(); nodes.forEach(((node) => {
nodesToCollect.forEach(((node) => {
if (node.hasOrderChanged() || node.hasPositionChanged()) { if (node.hasOrderChanged() || node.hasPositionChanged()) {
// Find or create a event ... // Find or create a event ...
const id = node.getId(); const id = node.getId();
let event = this._events.some((e) => e.id === id); let event: ChangeEvent = this._events.find((e) => e.getId() === id);
if (!event) { if (!event) {
event = new ChangeEvent(id); event = new ChangeEvent(id);
} }

View File

@ -28,31 +28,20 @@ class RootedTreeSet {
this._rootNodes = []; this._rootNodes = [];
} }
/**
* @param root
* @throws will throw an error if root is null or undefined
*/
setRoot(root: Node) { setRoot(root: Node) {
$assert(root, 'root can not be null'); $assert(root, 'root can not be null');
this._rootNodes.push(this._decodate(root)); this._rootNodes.push(this._decodate(root));
} }
/** getter */ getTreeRoots(): Node[] {
getTreeRoots() {
return this._rootNodes; return this._rootNodes;
} }
_decodate(node: Node) { _decodate(node: Node): Node {
node._children = []; node._children = [];
return node; return node;
} }
/**
* @param {mindplot.model.NodeModel} node
* @throws will throw an error if node is null or undefined
* @throws will throw an error if node with id already exists
* @throws will throw an error if node has been added already
*/
add(node: Node) { add(node: Node) {
$assert(node, 'node can not be null'); $assert(node, 'node can not be null');
if (this.find(node.getId(), false)) { if (this.find(node.getId(), false)) {

View File

@ -59,6 +59,8 @@ class BootstrapDialog extends Options {
$(this).remove(); $(this).remove();
}); });
this._native.on('shown.bs.modal', this.onDialogShown); this._native.on('shown.bs.modal', this.onDialogShown);
this._native.appendTo('#mindplot-tooltips');
} }
_buildFooter() { _buildFooter() {

View File

@ -19,7 +19,7 @@ import { $assert } from '@wisemapping/core-js';
import FeatureType from './FeatureType'; import FeatureType from './FeatureType';
class FeatureModel { class FeatureModel {
static _next_id = 0; static _nextId = 0;
private _id: number; private _id: number;
@ -85,8 +85,8 @@ class FeatureModel {
} }
static _nextUUID(): number { static _nextUUID(): number {
const result = FeatureModel._next_id + 1; const result = FeatureModel._nextId + 1;
FeatureModel._next_id = result; FeatureModel._nextId = result;
return result; return result;
} }
} }

View File

@ -29,7 +29,7 @@ const parseJsObject = (str: string) => JSON.parse(str.replace(/(['"])?([a-z0-9A-
abstract class INodeModel { abstract class INodeModel {
static MAIN_TOPIC_TO_MAIN_TOPIC_DISTANCE = 220; static MAIN_TOPIC_TO_MAIN_TOPIC_DISTANCE = 220;
private static _next_uuid = 0; private static _nextUuid = 0;
protected _mindmap: Mindmap; protected _mindmap: Mindmap;
@ -49,9 +49,9 @@ abstract class INodeModel {
const newId = INodeModel._nextUUID(); const newId = INodeModel._nextUUID();
this.putProperty('id', newId); this.putProperty('id', newId);
} else { } else {
if (id > INodeModel._next_uuid) { if (id > INodeModel._nextUuid) {
$assert(Number.isFinite(id)); $assert(Number.isFinite(id));
INodeModel._next_uuid = id; INodeModel._nextUuid = id;
} }
this.putProperty('id', id); this.putProperty('id', id);
} }
@ -287,7 +287,7 @@ abstract class INodeModel {
abstract getPropertiesKeys(): string[]; abstract getPropertiesKeys(): string[];
abstract getProperty(key: string): number | string | boolean; abstract getProperty(key: string): number | string | boolean | undefined;
abstract putProperty(key: string, value: number | string | boolean): void; abstract putProperty(key: string, value: number | string | boolean): void;
@ -358,8 +358,8 @@ abstract class INodeModel {
abstract removeChild(child: INodeModel); abstract removeChild(child: INodeModel);
static _nextUUID(): number { static _nextUUID(): number {
INodeModel._next_uuid += 1; INodeModel._nextUuid += 1;
return INodeModel._next_uuid; return INodeModel._nextUuid;
} }
} }

View File

@ -20,7 +20,7 @@ import Point from '@wisemapping/web2d';
import ConnectionLine from '../ConnectionLine'; import ConnectionLine from '../ConnectionLine';
class RelationshipModel { class RelationshipModel {
static _next_uuid = 0; static _nextUuid = 0;
private _id: number; private _id: number;
@ -133,8 +133,8 @@ class RelationshipModel {
} }
static _nextUUID() { static _nextUUID() {
RelationshipModel._next_uuid += 1; RelationshipModel._nextUuid += 1;
return RelationshipModel._next_uuid; return RelationshipModel._nextUuid;
} }
} }

View File

@ -1,7 +0,0 @@
import fadeEffect from './FadeEffect';
import shape from './Shape';
export default {
FadeEffect: fadeEffect,
Shape: shape,
};

View File

@ -27,7 +27,7 @@ const defaultOptions = {
title: '', title: '',
content: '', content: '',
delay: 0, delay: 0,
container: false, container: '#mindplot-tooltips',
destroyOnExit: false, destroyOnExit: false,
}; };

View File

@ -31,7 +31,6 @@ class LinkIconTooltip extends FloatingTip {
}, },
html: true, html: true,
placement: 'bottom', placement: 'bottom',
container: 'body',
title: $msg('LINK'), title: $msg('LINK'),
trigger: 'manual', trigger: 'manual',
template: '<div id="linkPopover" class="popover" onmouseover="jQuery(this).mouseleave(function() {jQuery(this).fadeOut(200); });" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>', template: '<div id="linkPopover" class="popover" onmouseover="jQuery(this).mouseleave(function() {jQuery(this).fadeOut(200); });" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',

View File

@ -39,9 +39,6 @@ class Menu extends IMenu {
// Create panels ... // Create panels ...
const designerModel = designer.getModel(); const designerModel = designer.getModel();
const fontFamilyBtn = $('#fontFamily');
if (fontFamilyBtn) {
const fontFamilyModel = { const fontFamilyModel = {
getValue() { getValue() {
const nodes = designerModel.filterSelectedTopics(); const nodes = designerModel.filterSelectedTopics();
@ -63,10 +60,7 @@ class Menu extends IMenu {
}; };
this._toolbarElems.push(new FontFamilyPanel('fontFamily', fontFamilyModel)); this._toolbarElems.push(new FontFamilyPanel('fontFamily', fontFamilyModel));
Menu._registerTooltip('fontFamily', $msg('FONT_FAMILY')); Menu._registerTooltip('fontFamily', $msg('FONT_FAMILY'));
}
const fontSizeBtn = $('#fontSize');
if (fontSizeBtn) {
const fontSizeModel = { const fontSizeModel = {
getValue(): number { getValue(): number {
const nodes = designerModel.filterSelectedTopics(); const nodes = designerModel.filterSelectedTopics();
@ -88,10 +82,7 @@ class Menu extends IMenu {
}; };
this._toolbarElems.push(new FontSizePanel('fontSize', fontSizeModel)); this._toolbarElems.push(new FontSizePanel('fontSize', fontSizeModel));
Menu._registerTooltip('fontSize', $msg('FONT_SIZE')); Menu._registerTooltip('fontSize', $msg('FONT_SIZE'));
}
const topicShapeBtn = $('#topicShape');
if (topicShapeBtn) {
const topicShapeModel = { const topicShapeModel = {
getValue() { getValue() {
const nodes = designerModel.filterSelectedTopics(); const nodes = designerModel.filterSelectedTopics();
@ -112,10 +103,7 @@ class Menu extends IMenu {
}; };
this._toolbarElems.push(new TopicShapePanel('topicShape', topicShapeModel)); this._toolbarElems.push(new TopicShapePanel('topicShape', topicShapeModel));
Menu._registerTooltip('topicShape', $msg('TOPIC_SHAPE')); Menu._registerTooltip('topicShape', $msg('TOPIC_SHAPE'));
}
const topicIconBtn = $('#topicIcon');
if (topicIconBtn) {
// Create icon panel dialog ... // Create icon panel dialog ...
const topicIconModel = { const topicIconModel = {
getValue() { getValue() {
@ -127,11 +115,7 @@ class Menu extends IMenu {
}; };
this._toolbarElems.push(new IconPanel('topicIcon', topicIconModel)); this._toolbarElems.push(new IconPanel('topicIcon', topicIconModel));
Menu._registerTooltip('topicIcon', $msg('TOPIC_ICON')); Menu._registerTooltip('topicIcon', $msg('TOPIC_ICON'));
}
// Topic color item ...
const topicColorBtn = $('#topicColor');
if (topicColorBtn) {
const topicColorModel = { const topicColorModel = {
getValue() { getValue() {
const nodes = designerModel.filterSelectedTopics(); const nodes = designerModel.filterSelectedTopics();
@ -152,11 +136,7 @@ class Menu extends IMenu {
}; };
this._toolbarElems.push(new ColorPalettePanel('topicColor', topicColorModel, widgetsBaseUrl)); this._toolbarElems.push(new ColorPalettePanel('topicColor', topicColorModel, widgetsBaseUrl));
Menu._registerTooltip('topicColor', $msg('TOPIC_COLOR')); Menu._registerTooltip('topicColor', $msg('TOPIC_COLOR'));
}
// Border color item ...
const topicBorderBtn = $('#topicBorder');
if (topicBorderBtn) {
const borderColorModel = { const borderColorModel = {
getValue() { getValue() {
const nodes = designerModel.filterSelectedTopics(); const nodes = designerModel.filterSelectedTopics();
@ -177,11 +157,7 @@ class Menu extends IMenu {
}; };
this._toolbarElems.push(new ColorPalettePanel('topicBorder', borderColorModel, widgetsBaseUrl)); this._toolbarElems.push(new ColorPalettePanel('topicBorder', borderColorModel, widgetsBaseUrl));
Menu._registerTooltip('topicBorder', $msg('TOPIC_BORDER_COLOR')); Menu._registerTooltip('topicBorder', $msg('TOPIC_BORDER_COLOR'));
}
// Font color item ...
const fontColorBtn = $('#fontColor');
if (fontColorBtn) {
const fontColorModel = { const fontColorModel = {
getValue() { getValue() {
let result = null; let result = null;
@ -202,7 +178,6 @@ class Menu extends IMenu {
}; };
this._toolbarElems.push(new ColorPalettePanel('fontColor', fontColorModel, baseUrl)); this._toolbarElems.push(new ColorPalettePanel('fontColor', fontColorModel, baseUrl));
Menu._registerTooltip('fontColor', $msg('FONT_COLOR')); Menu._registerTooltip('fontColor', $msg('FONT_COLOR'));
}
Menu._registerTooltip('export', $msg('EXPORT')); Menu._registerTooltip('export', $msg('EXPORT'));
@ -315,14 +290,6 @@ class Menu extends IMenu {
} }
} }
const discardElem = $('#discard');
if (discardElem.length !== 0) {
this._addButton('discard', false, false, () => {
this.discardChanges(designer);
});
Menu._registerTooltip('discard', $msg('DISCARD_CHANGES'));
}
const shareElem = $('#shareIt'); const shareElem = $('#shareIt');
if (shareElem.length !== 0) { if (shareElem.length !== 0) {
Menu._registerTooltip('shareIt', $msg('COLLABORATE')); Menu._registerTooltip('shareIt', $msg('COLLABORATE'));
@ -350,14 +317,12 @@ class Menu extends IMenu {
} }
const backTolist = $('#backToList'); const backTolist = $('#backToList');
if (backTolist.length !== 0) {
backTolist.bind('click', (event) => { backTolist.bind('click', (event) => {
event.stopPropagation(); event.stopPropagation();
window.location.href = '/c/maps/'; window.location.href = '/c/maps/';
return false; return false;
}); });
Menu._registerTooltip('backToList', $msg('BACK_TO_MAP_LIST')); Menu._registerTooltip('backToList', $msg('BACK_TO_MAP_LIST'));
}
// Account dialog ... // Account dialog ...
const accountSettings = $('#account'); const accountSettings = $('#account');
@ -367,10 +332,10 @@ class Menu extends IMenu {
}); });
this._toolbarElems.push(new AccountSettingsPanel('account')); this._toolbarElems.push(new AccountSettingsPanel('account'));
Menu._registerTooltip('account', `${global.accountEmail}`); Menu._registerTooltip('account', `${global.accountEmail}`);
}
this._registerEvents(designer); this._registerEvents(designer);
} }
}
private _registerEvents(designer: Designer) { private _registerEvents(designer: Designer) {
// Register on close events ... // Register on close events ...

View File

@ -255,8 +255,8 @@ class ElementClass {
this.peer.setFill(null, opacity); this.peer.setFill(null, opacity);
} }
setVisibility(isVisible) { setVisibility(value, fade) {
this.peer.setVisibility(isVisible); this.peer.setVisibility(value, fade);
} }
isVisible() { isVisible() {

View File

@ -181,11 +181,14 @@ class ElementPeer {
} }
} }
/* setVisibility(value, fade) {
* style='visibility: visible' this._native.setAttribute('visibility', value ? 'visible' : 'hidden');
*/ this._native.style.opacity = value ? 1 : 0;
setVisibility(isVisible) { if (fade) {
this._native.setAttribute('visibility', isVisible ? 'visible' : 'hidden'); this._native.style.transition = `visibility ${fade}ms, opacity ${fade}ms`;
} else {
this._native.style.transition = null;
}
} }
isVisible() { isVisible() {

View File

@ -66,7 +66,7 @@
</tr> </tr>
<tr> <tr>
<td> <td>
Visibility. Visibility with CSS Transition
</td> </td>
<td> <td>
<div id="visibility"></div> <div id="visibility"></div>

View File

@ -225,14 +225,13 @@ const visibilityTest = () => {
rect.setPosition(120, 20); rect.setPosition(120, 20);
workspace.append(rect); workspace.append(rect);
rect.addEvent('mouseover', () => { rect.addEvent('mouseover', () => {
alert('Mouse Over'); rect.setVisibility(false, 500);
});
rect.addEvent('mouseout', () => {
rect.setVisibility(true, 500);
}); });
let isVisible = true;
const executer = function () {
isVisible = !isVisible;
rect.setVisibility(isVisible);
};
// executer.periodical(100); // executer.periodical(100);
workspace.addItAsChildTo($('#visibility')); workspace.addItAsChildTo($('#visibility'));
}; };

View File

@ -81,7 +81,7 @@ const App = (): ReactElement => {
component={withSessionExpirationHandling(MapsPage)} component={withSessionExpirationHandling(MapsPage)}
/> />
<Route exact path="/c/maps/:id/edit"> <Route exact path="/c/maps/:id/edit">
<EnhacedEditorPage isTryMode={false} /> <EnhacedEditorPage isTryMode={false}/>
</Route> </Route>
<Route exact path="/c/maps/:id/try"> <Route exact path="/c/maps/:id/try">
<EnhacedEditorPage isTryMode={true} /> <EnhacedEditorPage isTryMode={true} />

View File

@ -611,12 +611,12 @@ export default class RestClient implements Client {
} }
} }
buildPersistenceManager(editorMode: EditorRenderMode ): PersistenceManager { buildPersistenceManager(editorMode: EditorRenderMode): PersistenceManager {
if (this.persistenceManager) { if (this.persistenceManager) {
return this.persistenceManager; return this.persistenceManager;
} }
let persistence: PersistenceManager; let persistence: PersistenceManager;
if (editorMode === 'edition') { if (editorMode === 'edition-owner' || editorMode === 'edition-editor') {
persistence = new RESTPersistenceManager({ persistence = new RESTPersistenceManager({
documentUrl: '/c/restful/maps/{id}/document', documentUrl: '/c/restful/maps/{id}/document',
revertUrl: '/c/restful/maps/{id}/history/latest', revertUrl: '/c/restful/maps/{id}/history/latest',
@ -645,13 +645,15 @@ export default class RestClient implements Client {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
private parseResponseOnError = (response: any): ErrorInfo => { private parseResponseOnError = (response: any): ErrorInfo => {
console.error("Backend error=>"); console.error(`Performing backend action error: ${JSON.stringify(response)}`);
console.error(response.data);
let result: ErrorInfo | undefined; let result: ErrorInfo | undefined;
if (response) { if (response) {
const status: number = response.status; const status: number = response.status;
const data = response.data; const data = response.data;
console.error(`Status Code: ${status}`);
console.error(`Status Data: ${response.data}`);
console.error(`Status Message: ${response.message}`);
switch (status) { switch (status) {
case 401: case 401:

View File

@ -1,25 +1,16 @@
import { EditorOptions } from '@wisemapping/editor'; import { EditorOptions } from '@wisemapping/editor';
import { EditorRenderMode } from '@wisemapping/mindplot';
import AppConfig from '../../classes/app-config'; import AppConfig from '../../classes/app-config';
export default class EditorOptionsBulder { export default class EditorOptionsBulder {
static build(locale: string, hotkeys: boolean, isTryMode: boolean): { options: EditorOptions, mapId: number } { static build(locale: string, mode: EditorRenderMode, hotkeys: boolean): EditorOptions {
let options: EditorOptions = { let options: EditorOptions = {
enableKeyboardEvents: hotkeys, enableKeyboardEvents: hotkeys,
locale: locale, locale: locale,
mode: mode,
}; };
if (isTryMode) {
// Sent to try mode ...
options.mode = 'showcase';
} else if (global.mindmapLocked) {
// Map locked, open for view mode ...
options.mode = 'viewonly';
} else {
options.mode = 'edition';
}
let mapId: number;
if (!AppConfig.isDevelopEnv()) { if (!AppConfig.isDevelopEnv()) {
options = { options = {
zoom: (global.userOptions?.zoom != undefined zoom: (global.userOptions?.zoom != undefined
@ -30,7 +21,6 @@ export default class EditorOptionsBulder {
mapTitle: global.mapTitle, mapTitle: global.mapTitle,
...options ...options
} }
mapId = global.mapId;
} else { } else {
// Running in a development mode. // Running in a development mode.
console.log('Running editor in development mode'); console.log('Running editor in development mode');
@ -40,8 +30,11 @@ export default class EditorOptionsBulder {
mapTitle: "Develop Mindnap", mapTitle: "Develop Mindnap",
...options ...options
} }
mapId = 666;
} }
return { options, mapId }; return options;
}
static loadMapId(): number {
return !AppConfig.isDevelopEnv() ? global.mapId : 555;
} }
} }

View File

@ -2,13 +2,14 @@ import React, { useEffect } from 'react';
import ActionDispatcher from '../maps-page/action-dispatcher'; import ActionDispatcher from '../maps-page/action-dispatcher';
import { ActionType } from '../maps-page/action-chooser'; import { ActionType } from '../maps-page/action-chooser';
import Editor from '@wisemapping/editor'; import Editor from '@wisemapping/editor';
import { EditorRenderMode, PersistenceManager } from '@wisemapping/mindplot';
import AppI18n from '../../classes/app-i18n'; import AppI18n from '../../classes/app-i18n';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { hotkeysEnabled } from '../../redux/editorSlice'; import { hotkeysEnabled } from '../../redux/editorSlice';
import ReactGA from 'react-ga'; import ReactGA from 'react-ga';
import Client from '../../classes/client'; import Client from '../../classes/client';
import { activeInstance, fetchAccount } from '../../redux/clientSlice'; import { activeInstance, fetchAccount, fetchMapById } from '../../redux/clientSlice';
import { PersistenceManager } from '@wisemapping/mindplot';
import EditorOptionsBulder from './EditorOptionsBuider'; import EditorOptionsBulder from './EditorOptionsBuider';
export type EditorPropsType = { export type EditorPropsType = {
@ -20,16 +21,43 @@ const EditorPage = ({ isTryMode }: EditorPropsType): React.ReactElement => {
const hotkey = useSelector(hotkeysEnabled); const hotkey = useSelector(hotkeysEnabled);
const userLocale = AppI18n.getUserLocale(); const userLocale = AppI18n.getUserLocale();
const client: Client = useSelector(activeInstance); const client: Client = useSelector(activeInstance);
const { mapId, options } = EditorOptionsBulder.build(userLocale.code, hotkey, isTryMode);
useEffect(() => { useEffect(() => {
ReactGA.pageview(window.location.pathname + window.location.search); ReactGA.pageview(window.location.pathname + window.location.search);
}, []); }, []);
const findEditorMode = (isTryMode: boolean, mapId: number): EditorRenderMode | null => {
let result: EditorRenderMode = null;
if (isTryMode) {
result = 'showcase';
} else if (global.mindmapLocked) {
result = 'viewonly';
} else {
const fetchResult = fetchMapById(mapId);
if (!fetchResult.isLoading) {
if (fetchResult.error) {
throw new Error(`User coild not be loaded: ${JSON.stringify(fetchResult.error)}`);
}
result = fetchResult.map.role === 'owner' ? 'edition-owner' : 'edition-editor';
}
}
return result;
}
// What is the role ?
const mapId = EditorOptionsBulder.loadMapId();
const mode = findEditorMode(isTryMode, mapId);
// Account settings can be null and editor cannot be initilized multiple times. This creates problems // Account settings can be null and editor cannot be initilized multiple times. This creates problems
// at the i18n resource loading. // at the i18n resource loading.
const persistence = client.buildPersistenceManager(options.mode); const isAccountLoaded = mode === 'showcase' || fetchAccount;
const loadCompleted = persistence && (options.mode === 'showcase' || fetchAccount()); const loadCompleted = mode && isAccountLoaded;
let options, persistence: PersistenceManager;
if (loadCompleted) {
options = EditorOptionsBulder.build(userLocale.code, mode, hotkey);
persistence = client.buildPersistenceManager(mode);
}
return loadCompleted ? ( return loadCompleted ? (
<> <>