Merge branch 'develop' of https://bitbucket.org/wisemapping/wisemapping-frontend into feature/testing-editor

This commit is contained in:
Ezequiel-Vega 2022-01-26 12:38:21 -03:00
commit d81b68bf33
15 changed files with 204 additions and 200 deletions

View File

@ -43,7 +43,10 @@
"import/resolver": {
"webpack": {
"config": "./webpack.common.js"
}
},
"node": {
"extensions": [".js",".ts"]
}
}
}
}

View File

@ -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;

View File

@ -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();
};
}

View File

@ -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) {

View File

@ -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;

View File

@ -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 {

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
},
};

View File

@ -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,
};

View File

@ -7,7 +7,10 @@
"moduleResolution": "Node",
"target": "es6",
"allowJs": true,
"esModuleInterop": true
"esModuleInterop": true,
"rootDirs": [
"src",
]
},
"exclude": ["node_modules"],
"files": ["mindplot.d.ts"]

View File

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

View File

@ -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",

View File

@ -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>
);
};

View File

@ -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"