diff --git a/packages/mindplot/.eslintrc.json b/packages/mindplot/.eslintrc.json index 3473120e..4506f905 100644 --- a/packages/mindplot/.eslintrc.json +++ b/packages/mindplot/.eslintrc.json @@ -43,7 +43,10 @@ "import/resolver": { "webpack": { "config": "./webpack.common.js" - } + }, + "node": { + "extensions": [".js",".ts"] + } } } } diff --git a/packages/mindplot/src/components/Designer.ts b/packages/mindplot/src/components/Designer.ts index 10da20ea..86d30599 100644 --- a/packages/mindplot/src/components/Designer.ts +++ b/packages/mindplot/src/components/Designer.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 { - 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; diff --git a/packages/mindplot/src/components/DesignerBuilder.ts b/packages/mindplot/src/components/DesignerBuilder.ts index d569dce6..76571701 100644 --- a/packages/mindplot/src/components/DesignerBuilder.ts +++ b/packages/mindplot/src/components/DesignerBuilder.ts @@ -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(); }; } diff --git a/packages/mindplot/src/components/DesignerModel.ts b/packages/mindplot/src/components/DesignerModel.ts index e280d340..8a2642b8 100644 --- a/packages/mindplot/src/components/DesignerModel.ts +++ b/packages/mindplot/src/components/DesignerModel.ts @@ -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) { diff --git a/packages/mindplot/src/components/model/IMindmap.ts b/packages/mindplot/src/components/model/IMindmap.ts index ebffe5f5..fa119af0 100644 --- a/packages/mindplot/src/components/model/IMindmap.ts +++ b/packages/mindplot/src/components/model/IMindmap.ts @@ -38,13 +38,13 @@ abstract class IMindmap { abstract setVersion(version: string): void; - abstract addBranch(nodeModel: INodeModel): void; + abstract addBranch(nodeModel): void; - abstract getBranches(): Array; + abstract getBranches(); - abstract removeBranch(node: INodeModel): void; + abstract removeBranch(node): void; - abstract getRelationships(): Array; + 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; diff --git a/packages/mindplot/src/components/model/INodeModel.ts b/packages/mindplot/src/components/model/INodeModel.ts index 0603d2b6..91d2367e 100644 --- a/packages/mindplot/src/components/model/INodeModel.ts +++ b/packages/mindplot/src/components/model/INodeModel.ts @@ -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 { diff --git a/packages/mindplot/src/components/model/Mindmap.ts b/packages/mindplot/src/components/model/Mindmap.ts index 1c92b580..2fe3ec5d 100644 --- a/packages/mindplot/src/components/model/Mindmap.ts +++ b/packages/mindplot/src/components/model/Mindmap.ts @@ -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); diff --git a/packages/mindplot/src/components/model/NodeModel.ts b/packages/mindplot/src/components/model/NodeModel.ts index 39c23670..f1c1cea9 100644 --- a/packages/mindplot/src/components/model/NodeModel.ts +++ b/packages/mindplot/src/components/model/NodeModel.ts @@ -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; } diff --git a/packages/mindplot/src/components/widget/Menu.ts b/packages/mindplot/src/components/widget/Menu.ts index 4bc2f225..bfd83810 100644 --- a/packages/mindplot/src/components/widget/Menu.ts +++ b/packages/mindplot/src/components/widget/Menu.ts @@ -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); }, }; diff --git a/packages/mindplot/src/index.js b/packages/mindplot/src/index.ts similarity index 86% rename from packages/mindplot/src/index.js rename to packages/mindplot/src/index.ts index c3d6a070..bd778752 100644 --- a/packages/mindplot/src/index.js +++ b/packages/mindplot/src/index.ts @@ -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, }; diff --git a/packages/mindplot/tsconfig.json b/packages/mindplot/tsconfig.json index 4a1b8b6a..a17d9247 100644 --- a/packages/mindplot/tsconfig.json +++ b/packages/mindplot/tsconfig.json @@ -7,7 +7,10 @@ "moduleResolution": "Node", "target": "es6", "allowJs": true, - "esModuleInterop": true + "esModuleInterop": true, + "rootDirs": [ + "src", + ] }, "exclude": ["node_modules"], "files": ["mindplot.d.ts"] diff --git a/packages/mindplot/webpack.common.js b/packages/mindplot/webpack.common.js index da5cb401..4a64bba7 100644 --- a/packages/mindplot/webpack.common.js +++ b/packages/mindplot/webpack.common.js @@ -10,7 +10,7 @@ module.exports = { }, }, entry: { - mindplot: './src/index.js', + mindplot: './src/index.ts', loader: './src/indexLoader.ts', }, mode: 'production', diff --git a/packages/webapp/package.json b/packages/webapp/package.json index d51d12e1..5fc9d6a5 100644 --- a/packages/webapp/package.json +++ b/packages/webapp/package.json @@ -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", diff --git a/packages/webapp/src/components/maps-page/action-dispatcher/export-dialog/index.tsx b/packages/webapp/src/components/maps-page/action-dispatcher/export-dialog/index.tsx index 04563aa7..18aef68f 100644 --- a/packages/webapp/src/components/maps-page/action-dispatcher/export-dialog/index.tsx +++ b/packages/webapp/src/components/maps-page/action-dispatcher/export-dialog/index.tsx @@ -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 } 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(false); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const [formExportRef, setExportFormRef] = React.useState(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const [formTransformtRef, setTransformFormRef] = React.useState(); + const [exportGroup, setExportGroup] = React.useState( 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,64 @@ const ExportDialog = ({ setSubmit(true); }; + const exporter = (formatType: ExportFormat) => { + let svgElement: Element | null = null; + let size; + let mindmpap: MindMap; + + const designer: Designer = global.designer; + if (designer != null) { + // Depending on the type of export. It will require differt POST. + const workspace = designer.getWorkspace(); + svgElement = workspace.getSVGElement(); + size = workspace.getSize(); + mindmpap = designer.getMindmap(); + } + + let exporter: Exporter; + switch (formatType) { + case 'png': + case 'jpg': + case 'svg': { + exporter = ImageExpoterFactory.create(formatType, mindmpap, svgElement, size.width, size.height); + break; + } + case 'wxml': + case 'md': + case 'txt': { + exporter = TextExporterFactory.create(formatType, mindmpap); + break; + } + default: + throw new Error('Unsupported encoding'); + } + + return exporter.export(); + }; + useEffect(() => { if (submit) { - // TODO: Remove usage of global "designer" - const designer = global.designer; - // 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 { map } = fetchMapById(mapId); + 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(); + // Trigger click ... + anchor.click(); - // Clean up ... - URL.revokeObjectURL(url); - document.body.removeChild(anchor); - }); - } else if (exportFormat === 'pdf') { - formTransformtRef?.submit(); - } else { - formExportRef?.submit(); - } + // Clean up ... + URL.revokeObjectURL(url); + document.body.removeChild(anchor); + }); onClose(); } }, [submit]); - const { map } = fetchMapById(mapId); return (
{ - !enableImgExport && + !enableImgExport && Scalable Vector Graphics (SVG) - - Portable Document Format (PDF) - Portable Network Graphics (PNG) @@ -189,6 +206,9 @@ const ExportDialog = ({ Plain Text File (TXT) + + Markdown (MD) + )} @@ -227,26 +247,6 @@ const ExportDialog = ({ - - {/* Hidden form for the purpose of summit */} -
- - -
- -
- - - -
); }; diff --git a/yarn.lock b/yarn.lock index a02eb8e1..17101e9d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4010,6 +4010,18 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.0.tgz#2c275aa05c895eccebbfc34cfb223c6e8bd591a2" integrity sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA== +"@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"