mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-10 17:33:24 +01:00
Merge branch 'develop' of https://bitbucket.org/wisemapping/wisemapping-frontend into feature/testing-editor
This commit is contained in:
commit
d81b68bf33
@ -43,7 +43,10 @@
|
||||
"import/resolver": {
|
||||
"webpack": {
|
||||
"config": "./webpack.common.js"
|
||||
}
|
||||
},
|
||||
"node": {
|
||||
"extensions": [".js",".ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,8 +48,6 @@ import LayoutManager from './layout/LayoutManager';
|
||||
|
||||
import { TopicShape } from './model/INodeModel';
|
||||
import { $notify } from './widget/ToolbarNotifier';
|
||||
import ImageExpoterFactory from './export/ImageExporterFactory';
|
||||
import TextExporterFactory from './export/TextExporterFactory';
|
||||
import RelationshipModel from './model/RelationshipModel';
|
||||
import Mindmap from './model/Mindmap';
|
||||
import NodeModel from './model/NodeModel';
|
||||
@ -57,8 +55,7 @@ import Topic from './Topic';
|
||||
import { DesignerOptions } from './DesignerOptionsBuilder';
|
||||
import MainTopic from './MainTopic';
|
||||
import DragTopic from './DragTopic';
|
||||
|
||||
export type ExportFormat = 'png' | 'svg' | 'jpg' | 'wxml';
|
||||
import CentralTopic from './CentralTopic';
|
||||
|
||||
class Designer extends Events {
|
||||
private _mindmap: Mindmap;
|
||||
@ -94,7 +91,9 @@ class Designer extends Events {
|
||||
this._options = options;
|
||||
|
||||
// Set full div elem render area ...
|
||||
divElement.css(options.containerSize);
|
||||
if (options.containerSize) {
|
||||
divElement.css(options.containerSize);
|
||||
}
|
||||
|
||||
// Dispatcher manager ...
|
||||
const commandContext = new CommandContext(this);
|
||||
@ -180,14 +179,16 @@ class Designer extends Events {
|
||||
|
||||
// Deselect on click ...
|
||||
screenManager.addEvent('click', (event: UIEvent) => {
|
||||
me.onObjectFocusEvent(null, event);
|
||||
me.onObjectFocusEvent(undefined, event);
|
||||
});
|
||||
|
||||
// Create nodes on double click...
|
||||
screenManager.addEvent('dblclick', (event: MouseEvent) => {
|
||||
if (workspace.isWorkspaceEventsEnabled()) {
|
||||
const mousePos = screenManager.getWorkspaceMousePosition(event);
|
||||
const centralTopic = me.getModel().getCentralTopic();
|
||||
const centralTopic:CentralTopic = me.getModel()
|
||||
.getCentralTopic();
|
||||
|
||||
const model = me._createChildModel(centralTopic, mousePos);
|
||||
this._actionDispatcher.addTopics([model], [centralTopic.getId()]);
|
||||
}
|
||||
@ -282,13 +283,7 @@ class Designer extends Events {
|
||||
return topic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?mindplot.Topic} currentObject
|
||||
* @param {Event=} event
|
||||
* sets focus to the given currentObject and removes it from any other objects if not
|
||||
* triggered with Ctrl pressed
|
||||
*/
|
||||
onObjectFocusEvent(currentObject: Topic = null, event = null): void {
|
||||
onObjectFocusEvent(currentObject?: Topic, event?): void {
|
||||
// Close node editors ..
|
||||
const topics = this.getModel().getTopics();
|
||||
topics.forEach((topic) => topic.closeEditors());
|
||||
@ -347,32 +342,6 @@ class Designer extends Events {
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SUPPORTED_FORMATS: ExportFormat[] = ['png', 'svg', 'jpg', 'wxml'];
|
||||
|
||||
export(formatType: ExportFormat): Promise<string> {
|
||||
const workspace = this._workspace;
|
||||
const svgElement = workspace.getSVGElement();
|
||||
const size = workspace.getSize();
|
||||
|
||||
let exporter;
|
||||
switch (formatType) {
|
||||
case 'png':
|
||||
case 'jpg':
|
||||
case 'svg': {
|
||||
exporter = ImageExpoterFactory.create(formatType, this._mindmap, svgElement, size.width, size.height);
|
||||
break;
|
||||
}
|
||||
case 'wxml': {
|
||||
exporter = TextExporterFactory.create(formatType, this._mindmap);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported encoding');
|
||||
}
|
||||
|
||||
return exporter.export();
|
||||
}
|
||||
|
||||
zoomIn(factor = 1.2): void {
|
||||
const model = this.getModel();
|
||||
const scale = model.getZoom() / factor;
|
||||
@ -530,21 +499,24 @@ class Designer extends Events {
|
||||
const parentTopic = topic.getOutgoingConnectedTopic();
|
||||
const siblingModel = this._createSiblingModel(topic);
|
||||
|
||||
// Hack: if parent is central topic, add node below not on opposite side.
|
||||
// This should be done in the layout
|
||||
if (parentTopic.getType() === 'CentralTopic') {
|
||||
siblingModel.setOrder(topic.getOrder() + 2);
|
||||
}
|
||||
if (siblingModel) {
|
||||
// Hack: if parent is central topic, add node below not on opposite side.
|
||||
// This should be done in the layout
|
||||
if (parentTopic.getType() === 'CentralTopic') {
|
||||
siblingModel.setOrder(topic.getOrder() + 2);
|
||||
}
|
||||
|
||||
const parentTopicId = parentTopic.getId();
|
||||
this._actionDispatcher.addTopics([siblingModel], [parentTopicId]);
|
||||
const parentTopicId = parentTopic.getId();
|
||||
this._actionDispatcher.addTopics([siblingModel], [parentTopicId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _createSiblingModel(topic: Topic): NodeModel {
|
||||
let result = null;
|
||||
let model = null;
|
||||
private _createSiblingModel(topic: Topic): NodeModel | undefined {
|
||||
let result: NodeModel | undefined;
|
||||
let model: NodeModel;
|
||||
const parentTopic = topic.getOutgoingConnectedTopic();
|
||||
|
||||
if (parentTopic != null) {
|
||||
// Create a new node ...
|
||||
model = topic.getModel();
|
||||
@ -555,9 +527,9 @@ class Designer extends Events {
|
||||
const order = topic.getOrder() + 1;
|
||||
result.setOrder(order);
|
||||
result.setPosition(10, 10); // Set a dummy position ...
|
||||
}
|
||||
|
||||
this._copyNodeProps(model, result);
|
||||
this._copyNodeProps(model, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -598,8 +570,10 @@ class Designer extends Events {
|
||||
layoutManager.addEvent('change', (event) => {
|
||||
const id = event.getId();
|
||||
const topic = me.getModel().findTopicById(id);
|
||||
topic.setPosition(event.getPosition());
|
||||
topic.setOrder(event.getOrder());
|
||||
if (topic) {
|
||||
topic.setPosition(event.getPosition());
|
||||
topic.setOrder(event.getOrder());
|
||||
}
|
||||
});
|
||||
this._eventBussDispatcher.setLayoutManager(layoutManager);
|
||||
|
||||
@ -636,9 +610,8 @@ class Designer extends Events {
|
||||
this._actionDispatcher.actionRunner.redo();
|
||||
}
|
||||
|
||||
/** */
|
||||
isReadOnly(): boolean {
|
||||
return this._options.readOnly;
|
||||
return Boolean(this._options?.readOnly);
|
||||
}
|
||||
|
||||
nodeModelToTopic(nodeModel: NodeModel): Topic {
|
||||
@ -825,15 +798,14 @@ class Designer extends Events {
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
changeFontFamily(font: string) {
|
||||
const topicsIds = this.getModel().filterTopicsIds();
|
||||
const topicsIds = this.getModel()
|
||||
.filterTopicsIds();
|
||||
if (topicsIds.length > 0) {
|
||||
this._actionDispatcher.changeFontFamilyToTopic(topicsIds, font);
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
changeFontStyle(): void {
|
||||
const topicsIds = this.getModel()
|
||||
.filterTopicsIds();
|
||||
@ -842,7 +814,6 @@ class Designer extends Events {
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
changeFontColor(color: string) {
|
||||
$assert(color, 'color can not be null');
|
||||
|
||||
@ -883,9 +854,8 @@ class Designer extends Events {
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
changeTopicShape(shape) {
|
||||
const validateFunc = (topic) => !(
|
||||
changeTopicShape(shape: string) {
|
||||
const validateFunc = (topic: Topic) => !(
|
||||
topic.getType() === 'CentralTopic' && shape === TopicShape.LINE
|
||||
);
|
||||
|
||||
@ -896,16 +866,14 @@ class Designer extends Events {
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
changeFontWeight() {
|
||||
changeFontWeight(): void {
|
||||
const topicsIds = this.getModel().filterTopicsIds();
|
||||
if (topicsIds.length > 0) {
|
||||
this._actionDispatcher.changeFontWeightToTopic(topicsIds);
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
addIconType(iconType) {
|
||||
addIconType(iconType: string): void {
|
||||
const topicsIds = this.getModel().filterTopicsIds();
|
||||
if (topicsIds.length > 0) {
|
||||
this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeatureFactory.Icon.id, {
|
||||
@ -915,9 +883,9 @@ class Designer extends Events {
|
||||
}
|
||||
|
||||
/**
|
||||
* lets the selected topic open the link editor where the user can define or modify an
|
||||
* existing link
|
||||
*/
|
||||
* lets the selected topic open the link editor where the user can define or modify an
|
||||
* existing link
|
||||
*/
|
||||
addLink() {
|
||||
const model = this.getModel();
|
||||
const topic = model.selectedTopic();
|
||||
@ -937,19 +905,22 @@ class Designer extends Events {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {mindplot.Topic} node
|
||||
* sets the focus to the given node
|
||||
*/
|
||||
goToNode(node) {
|
||||
goToNode(node: Topic): void {
|
||||
node.setOnFocus(true);
|
||||
this.onObjectFocusEvent(node);
|
||||
}
|
||||
|
||||
/** @return {mindplot.Workspace} */
|
||||
getWorkSpace() {
|
||||
getWorkSpace(): Workspace {
|
||||
return this._workspace;
|
||||
}
|
||||
|
||||
public get cleanScreen(): () => void {
|
||||
return this._cleanScreen;
|
||||
}
|
||||
|
||||
public set cleanScreen(value: () => void) {
|
||||
this._cleanScreen = value;
|
||||
}
|
||||
}
|
||||
|
||||
export default Designer;
|
||||
|
@ -24,7 +24,7 @@ import { $notifyModal } from './widget/ModalDialogNotifier';
|
||||
import { $msg } from './Messages';
|
||||
import { DesignerOptions } from './DesignerOptionsBuilder';
|
||||
|
||||
let designer = null;
|
||||
let designer: Designer;
|
||||
|
||||
export function buildDesigner(options: DesignerOptions): Designer {
|
||||
const divContainer = $(`#${options.container}`);
|
||||
@ -38,7 +38,7 @@ export function buildDesigner(options: DesignerOptions): Designer {
|
||||
console.log('Map loadded successfully');
|
||||
});
|
||||
|
||||
const onerrorFn = (message:string, url, lineNo) => {
|
||||
const onerrorFn = (message: string, url, lineNo) => {
|
||||
// Close loading dialog ...
|
||||
// @ts-ignore
|
||||
if (window.waitDialog) {
|
||||
@ -64,10 +64,10 @@ export function buildDesigner(options: DesignerOptions): Designer {
|
||||
|
||||
// Register toolbar event ...
|
||||
if ($('#toolbar').length) {
|
||||
const menu = new Menu(designer, 'toolbar', options.mapId);
|
||||
const menu = new Menu(designer, 'toolbar', options.mapId ? options.mapId : 'unknown');
|
||||
|
||||
// If a node has focus, focus can be move to another node using the keys.
|
||||
designer._cleanScreen = function _cleanScreen() {
|
||||
designer.cleanScreen = () => {
|
||||
menu.clear();
|
||||
};
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { $assert, $defined } from '@wisemapping/core-js';
|
||||
import { $assert } from '@wisemapping/core-js';
|
||||
import CentralTopic from './CentralTopic';
|
||||
import { DesignerOptions } from './DesignerOptionsBuilder';
|
||||
import Events from './Events';
|
||||
@ -24,11 +24,11 @@ import Topic from './Topic';
|
||||
import { $notify } from './widget/ToolbarNotifier';
|
||||
|
||||
class DesignerModel extends Events {
|
||||
_zoom: number;
|
||||
private _zoom: number;
|
||||
|
||||
_topics: Topic[];
|
||||
private _topics: Topic[];
|
||||
|
||||
_relationships: Relationship[];
|
||||
private _relationships: Relationship[];
|
||||
|
||||
constructor(options: DesignerOptions) {
|
||||
super();
|
||||
@ -37,11 +37,11 @@ class DesignerModel extends Events {
|
||||
this._relationships = [];
|
||||
}
|
||||
|
||||
getZoom():number {
|
||||
getZoom(): number {
|
||||
return this._zoom;
|
||||
}
|
||||
|
||||
setZoom(zoom: number):void {
|
||||
setZoom(zoom: number): void {
|
||||
this._zoom = zoom;
|
||||
}
|
||||
|
||||
@ -55,12 +55,11 @@ class DesignerModel extends Events {
|
||||
|
||||
getCentralTopic(): CentralTopic {
|
||||
const topics = this.getTopics();
|
||||
return topics[0] as CentralTopic;
|
||||
return topics[0] as unknown as CentralTopic;
|
||||
}
|
||||
|
||||
/** @return {mindplot.Topic[]} selected topics */
|
||||
filterSelectedTopics(): Topic[] {
|
||||
const result = [];
|
||||
const result: Topic[] = [];
|
||||
for (let i = 0; i < this._topics.length; i++) {
|
||||
if (this._topics[i].isOnFocus()) {
|
||||
result.push(this._topics[i]);
|
||||
@ -70,7 +69,7 @@ class DesignerModel extends Events {
|
||||
}
|
||||
|
||||
filterSelectedRelationships(): Relationship[] {
|
||||
const result = [];
|
||||
const result:Relationship[] = [];
|
||||
for (let i = 0; i < this._relationships.length; i++) {
|
||||
if (this._relationships[i].isOnFocus()) {
|
||||
result.push(this._relationships[i]);
|
||||
@ -80,17 +79,18 @@ class DesignerModel extends Events {
|
||||
}
|
||||
|
||||
getEntities(): (Relationship | Topic)[] {
|
||||
let result = [].concat(this._topics);
|
||||
let result:(Relationship|Topic)[] = [];
|
||||
result = result.concat(this._topics);
|
||||
result = result.concat(this._relationships);
|
||||
return result;
|
||||
}
|
||||
|
||||
removeTopic(topic:Topic):void {
|
||||
removeTopic(topic: Topic): void {
|
||||
$assert(topic, 'topic can not be null');
|
||||
this._topics = this._topics.filter((t) => t !== topic);
|
||||
}
|
||||
|
||||
removeRelationship(rel:Relationship):void {
|
||||
removeRelationship(rel: Relationship): void {
|
||||
$assert(rel, 'rel can not be null');
|
||||
this._relationships = this._relationships.filter((r) => r !== rel);
|
||||
}
|
||||
@ -106,13 +106,13 @@ class DesignerModel extends Events {
|
||||
this._relationships.push(rel);
|
||||
}
|
||||
|
||||
filterTopicsIds(validate: (topic: Topic) => boolean = null, errorMsg = null): number[] {
|
||||
const result = [];
|
||||
filterTopicsIds(validate?: (topic: Topic) => boolean, errorMsg?: string): number[] {
|
||||
const result: number[] = [];
|
||||
const topics = this.filterSelectedTopics();
|
||||
|
||||
let isValid = true;
|
||||
topics.forEach((topic) => {
|
||||
if ($defined(validate)) {
|
||||
if (validate) {
|
||||
isValid = validate(topic);
|
||||
}
|
||||
|
||||
@ -127,13 +127,13 @@ class DesignerModel extends Events {
|
||||
return result;
|
||||
}
|
||||
|
||||
selectedTopic(): Topic {
|
||||
selectedTopic(): Topic | undefined {
|
||||
const topics = this.filterSelectedTopics();
|
||||
return (topics.length > 0) ? topics[0] : null;
|
||||
return (topics.length > 0) ? topics[0] : undefined;
|
||||
}
|
||||
|
||||
findTopicById(id: number): Topic {
|
||||
let result = null;
|
||||
findTopicById(id: number): Topic | undefined {
|
||||
let result: Topic | undefined;
|
||||
for (let i = 0; i < this._topics.length; i++) {
|
||||
const topic = this._topics[i];
|
||||
if (topic.getId() === id) {
|
||||
|
@ -38,13 +38,13 @@ abstract class IMindmap {
|
||||
|
||||
abstract setVersion(version: string): void;
|
||||
|
||||
abstract addBranch(nodeModel: INodeModel): void;
|
||||
abstract addBranch(nodeModel): void;
|
||||
|
||||
abstract getBranches(): Array<INodeModel>;
|
||||
abstract getBranches();
|
||||
|
||||
abstract removeBranch(node: INodeModel): void;
|
||||
abstract removeBranch(node): void;
|
||||
|
||||
abstract getRelationships(): Array<RelationshipModel>;
|
||||
abstract getRelationships(): RelationshipModel[];
|
||||
|
||||
connect(parent: INodeModel, child: INodeModel): void {
|
||||
// Child already has a parent ?
|
||||
@ -71,9 +71,9 @@ abstract class IMindmap {
|
||||
this.addBranch(child);
|
||||
}
|
||||
|
||||
abstract hasAlreadyAdded(node: INodeModel): boolean;
|
||||
abstract hasAlreadyAdded(node): boolean;
|
||||
|
||||
abstract createNode(type: NodeType, id: number): INodeModel
|
||||
abstract createNode(type: NodeType, id: number)
|
||||
|
||||
abstract createRelationship(fromNodeId: number, toNodeId: number): void;
|
||||
|
||||
|
@ -28,7 +28,7 @@ const parseJsObject = (str: string) => JSON.parse(str.replace(/(['"])?([a-z0-9A-
|
||||
abstract class INodeModel {
|
||||
static MAIN_TOPIC_TO_MAIN_TOPIC_DISTANCE = 220;
|
||||
|
||||
static _next_uuid = 0;
|
||||
private static _next_uuid = 0;
|
||||
|
||||
protected _mindmap: Mindmap;
|
||||
|
||||
@ -43,8 +43,7 @@ abstract class INodeModel {
|
||||
|
||||
abstract getFeatures(): FeatureModel[];
|
||||
|
||||
/** */
|
||||
setId(id: number): void {
|
||||
setId(id?: number): void {
|
||||
if (!$defined(id)) {
|
||||
const newId = INodeModel._nextUUID();
|
||||
this.putProperty('id', newId);
|
||||
@ -79,7 +78,7 @@ abstract class INodeModel {
|
||||
|
||||
getPosition(): { x: number, y: number } {
|
||||
const value = this.getProperty('position') as string;
|
||||
let result = null;
|
||||
let result;
|
||||
if (value != null) {
|
||||
result = parseJsObject(value);
|
||||
}
|
||||
@ -92,7 +91,7 @@ abstract class INodeModel {
|
||||
|
||||
getImageSize(): {width: number, height: number} {
|
||||
const value = this.getProperty('imageSize') as string;
|
||||
let result = null;
|
||||
let result;
|
||||
if (value != null) {
|
||||
result = parseJsObject(value);
|
||||
}
|
||||
@ -261,7 +260,7 @@ abstract class INodeModel {
|
||||
const tmindmap = target.getMindmap();
|
||||
|
||||
children.forEach((snode) => {
|
||||
const tnode = tmindmap.createNode(snode.getType(), snode.getId());
|
||||
const tnode:INodeModel = tmindmap.createNode(snode.getType(), snode.getId());
|
||||
snode.copyTo(tnode);
|
||||
target.append(tnode);
|
||||
});
|
||||
@ -276,16 +275,14 @@ abstract class INodeModel {
|
||||
deleteNode(): void {
|
||||
const mindmap = this.getMindmap();
|
||||
|
||||
// console.log("Before:" + mindmap.inspect());
|
||||
const parent = this.getParent();
|
||||
if ($defined(parent)) {
|
||||
if (parent) {
|
||||
parent.removeChild(this);
|
||||
} else {
|
||||
// If it has not parent, it must be an isolate topic ...
|
||||
// @ts-ignore
|
||||
mindmap.removeBranch(this);
|
||||
}
|
||||
// It's an isolated node. It must be a hole branch ...
|
||||
// console.log("After:" + mindmap.inspect());
|
||||
}
|
||||
|
||||
abstract getPropertiesKeys(): string[];
|
||||
@ -298,7 +295,7 @@ abstract class INodeModel {
|
||||
|
||||
abstract getChildren(): INodeModel[];
|
||||
|
||||
abstract getParent(): INodeModel;
|
||||
abstract getParent(): INodeModel | null;
|
||||
|
||||
abstract clone(): INodeModel;
|
||||
|
||||
@ -321,7 +318,7 @@ abstract class INodeModel {
|
||||
|
||||
findNodeById(id: number): INodeModel {
|
||||
$assert(Number.isFinite(id));
|
||||
let result = null;
|
||||
let result;
|
||||
if (this.getId() === id) {
|
||||
result = this;
|
||||
} else {
|
||||
|
@ -38,7 +38,7 @@ class Mindmap extends IMindmap {
|
||||
$assert(id, 'Id can not be null');
|
||||
|
||||
this._branches = [];
|
||||
this._description = null;
|
||||
this._description = '';
|
||||
this._relationships = [];
|
||||
this._version = version;
|
||||
this._id = id;
|
||||
@ -79,7 +79,7 @@ class Mindmap extends IMindmap {
|
||||
* @throws will throw an error if nodeModel is null, undefined or not a node model object
|
||||
* @throws will throw an error if
|
||||
*/
|
||||
addBranch(nodeModel: NodeModel): void {
|
||||
addBranch(nodeModel: INodeModel): void {
|
||||
$assert(nodeModel && nodeModel.isNodeModel(), 'Add node must be invoked with model objects');
|
||||
const branches = this.getBranches();
|
||||
if (branches.length === 0) {
|
||||
@ -89,18 +89,18 @@ class Mindmap extends IMindmap {
|
||||
$assert(nodeModel.getType() !== 'CentralTopic', 'Mindmaps only have one cental topic');
|
||||
}
|
||||
|
||||
this._branches.push(nodeModel);
|
||||
this._branches.push(nodeModel as NodeModel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodeModel
|
||||
*/
|
||||
removeBranch(nodeModel: INodeModel): void {
|
||||
removeBranch(nodeModel: NodeModel): void {
|
||||
$assert(nodeModel && nodeModel.isNodeModel(), 'Remove node must be invoked with model objects');
|
||||
this._branches = this._branches.filter((b) => b !== nodeModel);
|
||||
}
|
||||
|
||||
getBranches() {
|
||||
getBranches():NodeModel[] {
|
||||
return this._branches;
|
||||
}
|
||||
|
||||
@ -122,11 +122,11 @@ class Mindmap extends IMindmap {
|
||||
return result;
|
||||
}
|
||||
|
||||
createNode(type: NodeModelType = 'MainTopic', id: number) {
|
||||
createNode(type: NodeModelType = 'MainTopic', id?: number):NodeModel {
|
||||
return new NodeModel(type, this, id);
|
||||
}
|
||||
|
||||
createRelationship(sourceNodeId: number, targetNodeId: number):RelationshipModel {
|
||||
createRelationship(sourceNodeId: number, targetNodeId: number): RelationshipModel {
|
||||
$assert($defined(sourceNodeId), 'from node cannot be null');
|
||||
$assert($defined(targetNodeId), 'to node cannot be null');
|
||||
|
||||
@ -148,7 +148,7 @@ class Mindmap extends IMindmap {
|
||||
}
|
||||
|
||||
findNodeById(id: number) {
|
||||
let result = null;
|
||||
let result;
|
||||
for (let i = 0; i < this._branches.length; i++) {
|
||||
const branch = this._branches[i];
|
||||
result = branch.findNodeById(id);
|
||||
|
@ -31,9 +31,9 @@ class NodeModel extends INodeModel {
|
||||
private _features: FeatureModel[];
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
private _parent: NodeModel;
|
||||
private _parent: NodeModel | null;
|
||||
|
||||
constructor(type: NodeModelType, mindmap: Mindmap, id: number) {
|
||||
constructor(type: NodeModelType, mindmap: Mindmap, id?: number) {
|
||||
$assert(type, 'Node type can not be null');
|
||||
$assert(mindmap, 'mindmap can not be null');
|
||||
super(mindmap);
|
||||
@ -121,10 +121,9 @@ class NodeModel extends INodeModel {
|
||||
return this._properties;
|
||||
}
|
||||
|
||||
getProperty(key: string) {
|
||||
getProperty(key: string): number | string | boolean {
|
||||
$defined(key, 'key can not be null');
|
||||
const result = this._properties[key];
|
||||
return !$defined(result) ? null : result;
|
||||
return this._properties[key];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,7 +186,7 @@ class NodeModel extends INodeModel {
|
||||
return this._children;
|
||||
}
|
||||
|
||||
getParent(): NodeModel {
|
||||
getParent(): NodeModel | null {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
|
@ -106,7 +106,7 @@ class Menu extends IMenu {
|
||||
}
|
||||
return result;
|
||||
},
|
||||
setValue(value) {
|
||||
setValue(value:string) {
|
||||
designer.changeTopicShape(value);
|
||||
},
|
||||
};
|
||||
@ -121,7 +121,7 @@ class Menu extends IMenu {
|
||||
getValue() {
|
||||
return null;
|
||||
},
|
||||
setValue(value) {
|
||||
setValue(value: string) {
|
||||
designer.addIconType(value);
|
||||
},
|
||||
};
|
||||
@ -146,7 +146,7 @@ class Menu extends IMenu {
|
||||
}
|
||||
return result;
|
||||
},
|
||||
setValue(hex) {
|
||||
setValue(hex:string) {
|
||||
designer.changeBackgroundColor(hex);
|
||||
},
|
||||
};
|
||||
|
@ -24,6 +24,10 @@ import LocalStorageManager from './components/LocalStorageManager';
|
||||
import RESTPersistenceManager from './components/RestPersistenceManager';
|
||||
import Menu from './components/widget/Menu';
|
||||
import DesignerOptionsBuilder from './components/DesignerOptionsBuilder';
|
||||
import ImageExporterFactory from './components/export/ImageExporterFactory';
|
||||
import TextExporterFactory from './components/export/TextExporterFactory';
|
||||
import Exporter from './components/export/Exporter';
|
||||
|
||||
import {
|
||||
buildDesigner,
|
||||
} from './components/DesignerBuilder';
|
||||
@ -32,6 +36,7 @@ import {
|
||||
} from './components/widget/ToolbarNotifier';
|
||||
|
||||
// This hack is required to initialize Bootstrap. In future, this should be removed.
|
||||
// @ts-ignore
|
||||
global.jQuery = jquery;
|
||||
require('@libraries/bootstrap/js/bootstrap');
|
||||
|
||||
@ -45,5 +50,8 @@ export {
|
||||
LocalStorageManager,
|
||||
DesignerOptionsBuilder,
|
||||
buildDesigner,
|
||||
TextExporterFactory,
|
||||
ImageExporterFactory,
|
||||
Exporter,
|
||||
$notify,
|
||||
};
|
@ -7,7 +7,10 @@
|
||||
"moduleResolution": "Node",
|
||||
"target": "es6",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true
|
||||
"esModuleInterop": true,
|
||||
"rootDirs": [
|
||||
"src",
|
||||
]
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"files": ["mindplot.d.ts"]
|
||||
|
@ -10,7 +10,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
entry: {
|
||||
mindplot: './src/index.js',
|
||||
mindplot: './src/index.ts',
|
||||
loader: './src/indexLoader.ts',
|
||||
},
|
||||
mode: 'production',
|
||||
|
@ -60,7 +60,7 @@
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.57",
|
||||
"@reduxjs/toolkit": "^1.5.0",
|
||||
"@wisemapping/editor": "^0.1.0",
|
||||
"@wisemapping/editor": "^0.4.0",
|
||||
"axios": "^0.21.0",
|
||||
"dayjs": "^1.10.4",
|
||||
"react": "^17.0.0",
|
||||
|
@ -10,8 +10,9 @@ import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
import Radio from '@material-ui/core/Radio';
|
||||
import Select from '@material-ui/core/Select';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import { Designer, TextExporterFactory, ImageExpoterFactory, Exporter, MindMap, RESTPersistenceManager } from '@wisemapping/mindplot';
|
||||
|
||||
type ExportFormat = 'pdf' | 'svg' | 'jpg' | 'png' | 'txt' | 'mm' | 'wxml' | 'xls' | 'txt';
|
||||
type ExportFormat = 'svg' | 'jpg' | 'png' | 'txt' | 'mm' | 'wxml' | 'xls' | 'md';
|
||||
type ExportGroup = 'image' | 'document' | 'mindmap-tool';
|
||||
|
||||
type ExportDialogProps = {
|
||||
@ -24,15 +25,11 @@ type ExportDialogProps = {
|
||||
const ExportDialog = ({
|
||||
mapId,
|
||||
onClose,
|
||||
enableImgExport,
|
||||
svgXml,
|
||||
enableImgExport
|
||||
}: ExportDialogProps): React.ReactElement => {
|
||||
const intl = useIntl();
|
||||
const [submit, setSubmit] = React.useState<boolean>(false);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const [formExportRef, setExportFormRef] = React.useState<any>();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const [formTransformtRef, setTransformFormRef] = React.useState<any>();
|
||||
|
||||
const [exportGroup, setExportGroup] = React.useState<ExportGroup>(
|
||||
enableImgExport ? 'image' : 'document'
|
||||
);
|
||||
@ -52,7 +49,7 @@ const ExportDialog = ({
|
||||
let defaultFormat: ExportFormat;
|
||||
switch (value) {
|
||||
case 'document':
|
||||
defaultFormat = 'pdf';
|
||||
defaultFormat = 'txt';
|
||||
break;
|
||||
case 'image':
|
||||
defaultFormat = 'svg';
|
||||
@ -72,41 +69,75 @@ const ExportDialog = ({
|
||||
setSubmit(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (submit) {
|
||||
// TODO: Remove usage of global "designer"
|
||||
const designer = global.designer;
|
||||
const exporter = (formatType: ExportFormat) => {
|
||||
let svgElement: Element | null = null;
|
||||
let size;
|
||||
let mindmap: MindMap;
|
||||
|
||||
const designer: Designer = global.designer;
|
||||
if (designer != null) {
|
||||
// Depending on the type of export. It will require differt POST.
|
||||
if (
|
||||
designer &&
|
||||
designer.EXPORT_SUPPORTED_FORMATS.includes(exportFormat)
|
||||
) {
|
||||
designer.export(exportFormat)
|
||||
.then((url: string) => {
|
||||
// Create hidden anchor to force download ...
|
||||
const anchor: HTMLAnchorElement = document.createElement('a');
|
||||
anchor.style.display = 'display: none';
|
||||
anchor.download = `${mapId}.${exportFormat}`;
|
||||
anchor.href = url;
|
||||
document.body.appendChild(anchor);
|
||||
const workspace = designer.getWorkspace();
|
||||
svgElement = workspace.getSVGElement();
|
||||
size = workspace.getSize();
|
||||
mindmap = designer.getMindmap();
|
||||
} else {
|
||||
// Load mindmap ...
|
||||
const persistence = new RESTPersistenceManager({
|
||||
documentUrl: '/c/restful/maps/{id}/document',
|
||||
revertUrl: '/c/restful/maps/{id}/history/latest',
|
||||
lockUrl: '/c/restful/maps/{id}/lock',
|
||||
timestamp: global.lockTimestamp,
|
||||
session: global.lockSession,
|
||||
});
|
||||
mindmap = persistence.load(global.mapId)
|
||||
|
||||
}
|
||||
|
||||
// Trigger click ...
|
||||
anchor.click();
|
||||
|
||||
// Clean up ...
|
||||
URL.revokeObjectURL(url);
|
||||
document.body.removeChild(anchor);
|
||||
});
|
||||
} else if (exportFormat === 'pdf') {
|
||||
formTransformtRef?.submit();
|
||||
} else {
|
||||
formExportRef?.submit();
|
||||
let exporter: Exporter;
|
||||
switch (formatType) {
|
||||
case 'png':
|
||||
case 'jpg':
|
||||
case 'svg': {
|
||||
exporter = ImageExpoterFactory.create(formatType, mindmap, svgElement, size.width, size.height);
|
||||
break;
|
||||
}
|
||||
case 'wxml':
|
||||
case 'md':
|
||||
case 'txt': {
|
||||
exporter = TextExporterFactory.create(formatType, mindmap);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error('Unsupported encoding');
|
||||
}
|
||||
|
||||
return exporter.export();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const { map } = fetchMapById(mapId);
|
||||
if (submit) {
|
||||
exporter(exportFormat)
|
||||
.then((url: string) => {
|
||||
// Create hidden anchor to force download ...
|
||||
const anchor: HTMLAnchorElement = document.createElement('a');
|
||||
anchor.style.display = 'display: none';
|
||||
anchor.download = `${map?.title}.${exportFormat}`;
|
||||
anchor.href = url;
|
||||
document.body.appendChild(anchor);
|
||||
|
||||
// Trigger click ...
|
||||
anchor.click();
|
||||
|
||||
// Clean up ...
|
||||
URL.revokeObjectURL(url);
|
||||
document.body.removeChild(anchor);
|
||||
});
|
||||
onClose();
|
||||
}
|
||||
}, [submit]);
|
||||
|
||||
const { map } = fetchMapById(mapId);
|
||||
return (
|
||||
<div>
|
||||
<BaseDialog
|
||||
@ -117,7 +148,7 @@ const ExportDialog = ({
|
||||
submitButton={intl.formatMessage({ id: 'export.title', defaultMessage: 'Export' })}
|
||||
>
|
||||
{
|
||||
!enableImgExport &&
|
||||
!enableImgExport &&
|
||||
<Alert severity="info">
|
||||
<FormattedMessage
|
||||
id="export.warning"
|
||||
@ -151,9 +182,6 @@ const ExportDialog = ({
|
||||
<MenuItem value="svg" className={classes.menu}>
|
||||
Scalable Vector Graphics (SVG)
|
||||
</MenuItem>
|
||||
<MenuItem value="pdf" className={classes.menu}>
|
||||
Portable Document Format (PDF)
|
||||
</MenuItem>
|
||||
<MenuItem value="png" className={classes.menu}>
|
||||
Portable Network Graphics (PNG)
|
||||
</MenuItem>
|
||||
@ -189,6 +217,9 @@ const ExportDialog = ({
|
||||
<MenuItem className={classes.select} value="txt">
|
||||
Plain Text File (TXT)
|
||||
</MenuItem>
|
||||
<MenuItem className={classes.select} value="md">
|
||||
Markdown (MD)
|
||||
</MenuItem>
|
||||
</Select>
|
||||
)}
|
||||
</FormControl>
|
||||
@ -227,26 +258,6 @@ const ExportDialog = ({
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</BaseDialog>
|
||||
|
||||
{/* Hidden form for the purpose of summit */}
|
||||
<form
|
||||
action={`/c/restful/maps/${mapId}.${exportFormat}`}
|
||||
ref={setExportFormRef}
|
||||
method="GET"
|
||||
>
|
||||
<input name="download" type="hidden" value={exportFormat} />
|
||||
<input name="filename" type="hidden" value={map?.title} />
|
||||
</form>
|
||||
|
||||
<form
|
||||
action={`/c/restful/transform.${exportFormat}`}
|
||||
ref={setTransformFormRef}
|
||||
method="POST"
|
||||
>
|
||||
<input name="download" type="hidden" value={exportFormat} />
|
||||
<input name="filename" type="hidden" value={map?.title} />
|
||||
<input name="svgXml" id="svgXml" value={svgXml} type="hidden" />
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
12
yarn.lock
12
yarn.lock
@ -3294,6 +3294,18 @@
|
||||
jquery "3.6.0"
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@wisemapping/mindplot@^0.4.15":
|
||||
version "0.4.15"
|
||||
resolved "https://registry.yarnpkg.com/@wisemapping/mindplot/-/mindplot-0.4.15.tgz#d4a7aa3a96bd5a91ec7f800eb392be820d97510b"
|
||||
integrity sha512-4buCwA9VezQylHkZ4c7JB+MBqGAOzOnBUZEjeqkg5hZMSt0mobMD3inp+tStJbZtQQbt7p6KJ/04+ewHmGlBag==
|
||||
dependencies:
|
||||
"@types/jquery" "^3.5.11"
|
||||
"@wisemapping/core-js" "^0.4.0"
|
||||
"@wisemapping/web2d" "^0.4.0"
|
||||
jest "^27.4.5"
|
||||
jquery "3.6.0"
|
||||
lodash "^4.17.21"
|
||||
|
||||
"@xtuc/ieee754@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
|
||||
|
Loading…
Reference in New Issue
Block a user