diff --git a/.gitignore b/.gitignore
index 21a52a85..a30550ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,4 +51,7 @@ Thumbs.db
**/build/**/*
.vscode
-*/test/playground/dist
\ No newline at end of file
+*/test/playground/dist
+
+# visual code local workspaces
+wisemapping-frontend.code-workspace
diff --git a/.prettierrc.json b/.prettierrc.json
index 0c45137e..2c9ca384 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -1,6 +1,6 @@
{
- "trailingComma": "es5",
- "tabWidth": 4,
+ "trailingComma": "all",
+ "tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 100
diff --git a/docker-compose.snapshots.update.yml b/docker-compose.snapshots.update.yml
index 3571ffd9..93880046 100644
--- a/docker-compose.snapshots.update.yml
+++ b/docker-compose.snapshots.update.yml
@@ -1,7 +1,7 @@
version: '3'
services:
e2e:
- image: cypress/included:9.7.0
+ image: cypress/included:10.2.0
container_name: wisemapping-integration-tests
entrypoint: '/bin/sh -c "yarn install && yarn bootstrap && yarn build && yarn test:integration"'
working_dir: /e2e
diff --git a/docker-compose.snapshots.yml b/docker-compose.snapshots.yml
index 35a8429d..8f7a613f 100644
--- a/docker-compose.snapshots.yml
+++ b/docker-compose.snapshots.yml
@@ -1,7 +1,7 @@
version: '3'
services:
e2e:
- image: cypress/included:9.7.0
+ image: cypress/included:10.2.0
container_name: wisemapping-integration-tests
entrypoint: '/bin/sh -c "yarn bootstrap && yarn build && yarn test:integration"'
working_dir: /e2e
diff --git a/lerna.json b/lerna.json
index dabb023c..d9f270f3 100644
--- a/lerna.json
+++ b/lerna.json
@@ -4,5 +4,6 @@
],
"version": "1.0.0",
"npmClient": "yarn",
- "useWorkspaces": true
+ "useWorkspaces": true,
+ "useNx": false
}
diff --git a/package.json b/package.json
index 0a6cd91b..0c75a662 100644
--- a/package.json
+++ b/package.json
@@ -45,5 +45,20 @@
"homepage": "http://localhost:8080/react",
"license": "https://wisemapping.atlassian.net/wiki/spaces/WS/pages/524357/WiseMapping+Public+License+Version+1.0+WPL",
"version": "0.4.0",
- "dependencies": {}
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged",
+ "pre-push": "yarn lint && yarn test:unit"
+ }
+ },
+ "lint-staged": {
+ "**/*.{ts,tsx}": [
+ "prettier --write"
+ ]
+ },
+ "eslintConfig": {
+ "rules": {
+ "implicit-arrow-linebreak": "off"
+ }
+ }
}
diff --git a/packages/editor/images/close-dialog-icon.svg b/packages/editor/images/close-dialog-icon.svg
new file mode 100644
index 00000000..3b57389c
--- /dev/null
+++ b/packages/editor/images/close-dialog-icon.svg
@@ -0,0 +1,43 @@
+
+
+
diff --git a/packages/editor/lang/de.json b/packages/editor/lang/de.json
index bf777ed4..928a15bf 100644
--- a/packages/editor/lang/de.json
+++ b/packages/editor/lang/de.json
@@ -4,6 +4,18 @@
},
"editor.try-welcome-description": {
"defaultMessage": "Melden Sie sich an, um kostenlos eine unbegrenzte Anzahl von Mindmaps zu erstellen, zu teilen und zu veröffentlichen."
+ },
+ "editor.try-welcome-mobile": {
+ "defaultMessage": "Diese Edition zeigt einige der Mindmap-Funktionen!"
+ },
+ "editor.try-welcome-description-mobile": {
+ "defaultMessage": "Melden Sie sich an, um kostenlos eine unbegrenzte Anzahl von Mindmaps zu erstellen, zu teilen und zu veröffentlichen. Eingeschränkte Funktionen der Mindmap-Edition werden auf Mobilgeräten unterstützt. Verwenden Sie den Desktop-Browser für vollständige Editorfunktionen."
+ },
+ "editor.edit-mobile": {
+ "defaultMessage": "Hinweis für Mobilgeräte."
+ },
+ "editor.edit-description-mobile": {
+ "defaultMessage": "Eingeschränkte Funktionen der Mindmap-Edition werden auf Mobilgeräten unterstützt. Verwenden Sie den Desktop-Browser für vollständige Editorfunktionen."
},
"login.signup": {
"defaultMessage": "Anmeldung"
diff --git a/packages/editor/lang/en.json b/packages/editor/lang/en.json
index c1eaa0bd..233007e1 100644
--- a/packages/editor/lang/en.json
+++ b/packages/editor/lang/en.json
@@ -1,9 +1,21 @@
{
"editor.try-welcome": {
- "defaultMessage": "This edition space showcases some of the mindmap editor capabilities !"
+ "defaultMessage": "This edition space showcases some of the mindmap editor capabilities!"
},
"editor.try-welcome-description": {
- "defaultMessage": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free."
+ "defaultMessage": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free."
+ },
+ "editor.try-welcome-mobile": {
+ "defaultMessage": "This edition space showcases some of the mindmap capabilities!"
+ },
+ "editor.try-welcome-description-mobile": {
+ "defaultMessage": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free. Limited mindmap edition capabilties are supported in Mobile devices. Use Desktop browser for full editor capabilies."
+ },
+ "editor.edit-mobile": {
+ "defaultMessage": "Note for mobile devices."
+ },
+ "editor.edit-description-mobile": {
+ "defaultMessage": "Limited mindmap edition capabilities are supported in Mobile devices. Use Desktop browser for full editor capabilities."
},
"login.signup": {
"defaultMessage": "Sign Up"
diff --git a/packages/editor/lang/es.json b/packages/editor/lang/es.json
index df4a128a..0707cc4e 100644
--- a/packages/editor/lang/es.json
+++ b/packages/editor/lang/es.json
@@ -4,6 +4,18 @@
},
"editor.try-welcome-description": {
"defaultMessage": "Registrate para comenzar a crear, compartir y publicar una cantidad ilimitada de mapas mentales de forma gratuita."
+ },
+ "editor.try-welcome-mobile": {
+ "defaultMessage": "¡Este espacio de edición muestra algunas de las capacidades de mapas mentales!"
+ },
+ "editor.try-welcome-description-mobile": {
+ "defaultMessage": "Registrate para comenzar a crear, compartir y publicar una cantidad ilimitada de mapas mentales de forma gratuita. En dispositivos móbiles las funciones son limitadas. Use la versión de escritorio para tener las funciones completas."
+ },
+ "editor.edit-mobile": {
+ "defaultMessage": "Nota para dispositivos móbiles."
+ },
+ "editor.edit-description-mobile": {
+ "defaultMessage": "En dispositivos móbiles las funciones son limitadas. Use la versión de escritorio para tener las funciones completas."
},
"login.signup": {
"defaultMessage": "Crear cuenta"
diff --git a/packages/editor/lang/fr.json b/packages/editor/lang/fr.json
index ce7af6f5..4832052b 100644
--- a/packages/editor/lang/fr.json
+++ b/packages/editor/lang/fr.json
@@ -1,9 +1,21 @@
{
"editor.try-welcome": {
- "defaultMessage": "Cet espace d'édition présente certaines des fonctionnalités de l'éditeur de cartes mentales !"
+ "defaultMessage": "Cet espace d'édition présente certaines des fonctionnalités de l'éditeur de cartes mentales!"
},
"editor.try-welcome-description": {
"defaultMessage": "Inscrivez-vous pour commencer à créer, partager et publier gratuitement un nombre illimité de cartes mentales."
+ },
+ "editor.try-welcome-mobile": {
+ "defaultMessage": "Cet espace d'édition présente certaines des fonctionnalités des cartes mentales!"
+ },
+ "editor.try-welcome-description-mobile": {
+ "defaultMessage": "Inscrivez-vous pour commencer à créer, partager et publier gratuitement un nombre illimité de cartes mentales. Les capacités d'édition limitées de mindmap sont prises en charge dans les appareils mobiles. Utilisez le navigateur de bureau pour bénéficier de toutes les fonctionnalités de l'éditeur."
+ },
+ "editor.edit-mobile": {
+ "defaultMessage": "Remarque pour les appareils mobiles."
+ },
+ "editor.edit-description-mobile": {
+ "defaultMessage": "Les capacités d'édition limitées de mindmap sont prises en charge dans les appareils mobiles. Utilisez le navigateur de bureau pour bénéficier de toutes les fonctionnalités de l'éditeur."
},
"login.signup": {
"defaultMessage": "S'inscrire"
diff --git a/packages/editor/lang/ru.json b/packages/editor/lang/ru.json
index e954afb0..72e549b5 100644
--- a/packages/editor/lang/ru.json
+++ b/packages/editor/lang/ru.json
@@ -4,6 +4,18 @@
},
"editor.try-welcome-description": {
"defaultMessage": "Чтобы получить бесплатный неограниченный доступ — нужна только регистрация."
+ },
+ "editor.try-welcome-mobile": {
+ "defaultMessage": "В этом издании демонстрируются некоторые возможности ментальных карт!"
+ },
+ "editor.try-welcome-description-mobile": {
+ "defaultMessage": "Зарегистрируйтесь, чтобы начать создавать, делиться и публиковать неограниченное количество ментальных карт бесплатно. Возможности ограниченной версии Mindmap поддерживаются на мобильных устройствах. Используйте настольный браузер для полных возможностей редактора."
+ },
+ "editor.edit-mobile": {
+ "defaultMessage": "Примечание для мобильных устройств."
+ },
+ "editor.edit-description-mobile": {
+ "defaultMessage": "Возможности ограниченной версии Mindmap поддерживаются на мобильных устройствах. Используйте настольный браузер для полных возможностей редактора."
},
"login.signup": {
"defaultMessage": "Регистрация"
diff --git a/packages/editor/lang/zh.json b/packages/editor/lang/zh.json
index ecea81dc..cfc1a00f 100644
--- a/packages/editor/lang/zh.json
+++ b/packages/editor/lang/zh.json
@@ -1,14 +1,26 @@
{
- "editor.try-welcome": {
- "defaultMessage": "这个编辑区域展示了一些思维导图编辑器的功能!"
- },
- "editor.try-welcome-description": {
- "defaultMessage": "注册后可以免费创建、分享和发布无限数量的思维导图。"
- },
- "login.signup": {
- "defaultMessage": "注册"
- },
- "action.share": {
- "defaultMessage": "分享"
- }
+ "editor.try-welcome": {
+ "defaultMessage": "这个编辑区域展示了一些思维导图编辑器的功能!"
+ },
+ "editor.try-welcome-description": {
+ "defaultMessage": "注册后可以免费创建、分享和发布无限数量的思维导图。"
+ },
+ "editor.try-welcome-mobile": {
+ "defaultMessage": "这个版本空间展示了一些思维导图功能!"
+ },
+ "editor.try-welcome-description-mobile": {
+ "defaultMessage": "注册以开始免费创建、共享和发布无限数量的思维导图。 移动设备支持有限的思维导图编辑功能。 使用桌面浏览器获得完整的编辑器功能。"
+ },
+ "editor.edit-mobile": {
+ "defaultMessage": "移动设备注意事项."
+ },
+ "editor.edit-description-mobile": {
+ "defaultMessage": "移动设备支持有限的思维导图编辑功能。 使用桌面浏览器获得完整的编辑器功能。"
+ },
+ "login.signup": {
+ "defaultMessage": "注册"
+ },
+ "action.share": {
+ "defaultMessage": "分享"
+ }
}
\ No newline at end of file
diff --git a/packages/editor/src/classes/menu/Menu.ts b/packages/editor/src/classes/menu/Menu.ts
index 40d20db3..1cd16a93 100644
--- a/packages/editor/src/classes/menu/Menu.ts
+++ b/packages/editor/src/classes/menu/Menu.ts
@@ -61,7 +61,9 @@ class Menu extends IMenu {
this._addButton('position', false, false, () => {
designer.zoomToFit();
});
- Menu._registerTooltip('position', $msg('CENTER_POSITION'));
+ // Disabled because this tooltip overflows the screen and makes the button un-clickeable
+ // This should be enabled when migrating to material-ui
+ //Menu._registerTooltip('position', $msg('CENTER_POSITION'));
// Edition actions ...
if (!readOnly) {
@@ -181,7 +183,9 @@ class Menu extends IMenu {
designer.changeBorderColor(hex);
},
};
- this._toolbarElems.push(new ColorPalettePanel('topicBorder', borderColorModel, widgetsBaseUrl));
+ this._toolbarElems.push(
+ new ColorPalettePanel('topicBorder', borderColorModel, widgetsBaseUrl),
+ );
Menu._registerTooltip('topicBorder', $msg('TOPIC_BORDER_COLOR'));
const fontColorModel = {
@@ -271,14 +275,12 @@ class Menu extends IMenu {
});
Menu._registerTooltip('fontItalic', $msg('FONT_ITALIC'), $msg('CTRL') + ' + I');
-
if (!readOnly) {
// Register action on save ...
const saveElem = $('#save');
- this._addButton('save', false, false,
- () => {
- this.save(saveElem, designer, true);
- });
+ this._addButton('save', false, false, () => {
+ this.save(saveElem, designer, true);
+ });
Menu._registerTooltip('save', $msg('SAVE'), $msg('CTRL') + ' + S');
// Register unload save ...
@@ -290,13 +292,11 @@ class Menu extends IMenu {
});
// Autosave on a fixed period of time ...
- setInterval(
- () => {
- if (this.isSaveRequired()) {
- this.save(saveElem, designer, false);
- }
- }, 10000,
- );
+ setInterval(() => {
+ if (this.isSaveRequired()) {
+ this.save(saveElem, designer, false);
+ }
+ }, 10000);
}
}
@@ -394,10 +394,14 @@ class Menu extends IMenu {
// Register Events ...
let result = null;
if ($(`#${buttonId}`)) {
- const button = new ToolbarItem(buttonId, ((event) => {
- fn(event);
- this.clear();
- }), { topicAction: isTopic, relAction: isRelationship });
+ const button = new ToolbarItem(
+ buttonId,
+ (event) => {
+ fn(event);
+ this.clear();
+ },
+ { topicAction: isTopic, relAction: isRelationship },
+ );
this._toolbarElems.push(button);
result = button;
@@ -409,9 +413,10 @@ class Menu extends IMenu {
if ($(`#${buttonId}`)) {
let tooltip = text;
if (shortcut) {
- const platformedShortcut = navigator.appVersion.indexOf('Mac') !== -1
- ? shortcut.replace('meta+', '⌘')
- : shortcut.replace('meta+', 'ctrl+');
+ const platformedShortcut =
+ navigator.appVersion.indexOf('Mac') !== -1
+ ? shortcut.replace('meta+', '⌘')
+ : shortcut.replace('meta+', 'ctrl+');
tooltip = `${tooltip} (${platformedShortcut})`;
}
return new KeyboardShortcutTooltip($(`#${buttonId}`), tooltip);
diff --git a/packages/editor/src/compiled-lang/de.json b/packages/editor/src/compiled-lang/de.json
index ac8ea4ac..a455e2a8 100644
--- a/packages/editor/src/compiled-lang/de.json
+++ b/packages/editor/src/compiled-lang/de.json
@@ -5,6 +5,18 @@
"value": "Teilen"
}
],
+ "editor.edit-description-mobile": [
+ {
+ "type": 0,
+ "value": "Eingeschränkte Funktionen der Mindmap-Edition werden auf Mobilgeräten unterstützt. Verwenden Sie den Desktop-Browser für vollständige Editorfunktionen."
+ }
+ ],
+ "editor.edit-mobile": [
+ {
+ "type": 0,
+ "value": "Hinweis für Mobilgeräte."
+ }
+ ],
"editor.try-welcome": [
{
"type": 0,
@@ -17,6 +29,18 @@
"value": "Melden Sie sich an, um kostenlos eine unbegrenzte Anzahl von Mindmaps zu erstellen, zu teilen und zu veröffentlichen."
}
],
+ "editor.try-welcome-description-mobile": [
+ {
+ "type": 0,
+ "value": "Melden Sie sich an, um kostenlos eine unbegrenzte Anzahl von Mindmaps zu erstellen, zu teilen und zu veröffentlichen. Eingeschränkte Funktionen der Mindmap-Edition werden auf Mobilgeräten unterstützt. Verwenden Sie den Desktop-Browser für vollständige Editorfunktionen."
+ }
+ ],
+ "editor.try-welcome-mobile": [
+ {
+ "type": 0,
+ "value": "Diese Edition zeigt einige der Mindmap-Funktionen!"
+ }
+ ],
"login.signup": [
{
"type": 0,
diff --git a/packages/editor/src/compiled-lang/en.json b/packages/editor/src/compiled-lang/en.json
index ab5c90b6..f466aad6 100644
--- a/packages/editor/src/compiled-lang/en.json
+++ b/packages/editor/src/compiled-lang/en.json
@@ -5,10 +5,22 @@
"value": "Share"
}
],
+ "editor.edit-description-mobile": [
+ {
+ "type": 0,
+ "value": "Limited mindmap edition capabilities are supported in Mobile devices. Use Desktop browser for full editor capabilities."
+ }
+ ],
+ "editor.edit-mobile": [
+ {
+ "type": 0,
+ "value": "Note for mobile devices."
+ }
+ ],
"editor.try-welcome": [
{
"type": 0,
- "value": "This edition space showcases some of the mindmap editor capabilities !"
+ "value": "This edition space showcases some of the mindmap editor capabilities!"
}
],
"editor.try-welcome-description": [
@@ -17,6 +29,18 @@
"value": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free."
}
],
+ "editor.try-welcome-description-mobile": [
+ {
+ "type": 0,
+ "value": "Sign Up to start creating, sharing and publishing unlimited number of mindmaps for free. Limited mindmap edition capabilties are supported in Mobile devices. Use Desktop browser for full editor capabilies."
+ }
+ ],
+ "editor.try-welcome-mobile": [
+ {
+ "type": 0,
+ "value": "This edition space showcases some of the mindmap capabilities!"
+ }
+ ],
"login.signup": [
{
"type": 0,
diff --git a/packages/editor/src/compiled-lang/es.json b/packages/editor/src/compiled-lang/es.json
index 19ff2e8a..65e44628 100644
--- a/packages/editor/src/compiled-lang/es.json
+++ b/packages/editor/src/compiled-lang/es.json
@@ -5,6 +5,18 @@
"value": "Compartir"
}
],
+ "editor.edit-description-mobile": [
+ {
+ "type": 0,
+ "value": "En dispositivos móbiles las funciones son limitadas. Use la versión de escritorio para tener las funciones completas."
+ }
+ ],
+ "editor.edit-mobile": [
+ {
+ "type": 0,
+ "value": "Nota para dispositivos móbiles."
+ }
+ ],
"editor.try-welcome": [
{
"type": 0,
@@ -17,6 +29,18 @@
"value": "Registrate para comenzar a crear, compartir y publicar una cantidad ilimitada de mapas mentales de forma gratuita."
}
],
+ "editor.try-welcome-description-mobile": [
+ {
+ "type": 0,
+ "value": "Registrate para comenzar a crear, compartir y publicar una cantidad ilimitada de mapas mentales de forma gratuita. En dispositivos móbiles las funciones son limitadas. Use la versión de escritorio para tener las funciones completas."
+ }
+ ],
+ "editor.try-welcome-mobile": [
+ {
+ "type": 0,
+ "value": "¡Este espacio de edición muestra algunas de las capacidades de mapas mentales!"
+ }
+ ],
"login.signup": [
{
"type": 0,
diff --git a/packages/editor/src/compiled-lang/fr.json b/packages/editor/src/compiled-lang/fr.json
index a7f74b02..beab84d3 100644
--- a/packages/editor/src/compiled-lang/fr.json
+++ b/packages/editor/src/compiled-lang/fr.json
@@ -5,10 +5,22 @@
"value": "Partager"
}
],
+ "editor.edit-description-mobile": [
+ {
+ "type": 0,
+ "value": "Les capacités d'édition limitées de mindmap sont prises en charge dans les appareils mobiles. Utilisez le navigateur de bureau pour bénéficier de toutes les fonctionnalités de l'éditeur."
+ }
+ ],
+ "editor.edit-mobile": [
+ {
+ "type": 0,
+ "value": "Remarque pour les appareils mobiles."
+ }
+ ],
"editor.try-welcome": [
{
"type": 0,
- "value": "Cet espace d'édition présente certaines des fonctionnalités de l'éditeur de cartes mentales !"
+ "value": "Cet espace d'édition présente certaines des fonctionnalités de l'éditeur de cartes mentales!"
}
],
"editor.try-welcome-description": [
@@ -17,6 +29,18 @@
"value": "Inscrivez-vous pour commencer à créer, partager et publier gratuitement un nombre illimité de cartes mentales."
}
],
+ "editor.try-welcome-description-mobile": [
+ {
+ "type": 0,
+ "value": "Inscrivez-vous pour commencer à créer, partager et publier gratuitement un nombre illimité de cartes mentales. Les capacités d'édition limitées de mindmap sont prises en charge dans les appareils mobiles. Utilisez le navigateur de bureau pour bénéficier de toutes les fonctionnalités de l'éditeur."
+ }
+ ],
+ "editor.try-welcome-mobile": [
+ {
+ "type": 0,
+ "value": "Cet espace d'édition présente certaines des fonctionnalités des cartes mentales!"
+ }
+ ],
"login.signup": [
{
"type": 0,
diff --git a/packages/editor/src/compiled-lang/ru.json b/packages/editor/src/compiled-lang/ru.json
index 6a9236c3..716d0b29 100644
--- a/packages/editor/src/compiled-lang/ru.json
+++ b/packages/editor/src/compiled-lang/ru.json
@@ -5,6 +5,18 @@
"value": "Поделиться"
}
],
+ "editor.edit-description-mobile": [
+ {
+ "type": 0,
+ "value": "Возможности ограниченной версии Mindmap поддерживаются на мобильных устройствах. Используйте настольный браузер для полных возможностей редактора."
+ }
+ ],
+ "editor.edit-mobile": [
+ {
+ "type": 0,
+ "value": "Примечание для мобильных устройств."
+ }
+ ],
"editor.try-welcome": [
{
"type": 0,
@@ -17,6 +29,18 @@
"value": "Чтобы получить бесплатный неограниченный доступ — нужна только регистрация."
}
],
+ "editor.try-welcome-description-mobile": [
+ {
+ "type": 0,
+ "value": "Зарегистрируйтесь, чтобы начать создавать, делиться и публиковать неограниченное количество ментальных карт бесплатно. Возможности ограниченной версии Mindmap поддерживаются на мобильных устройствах. Используйте настольный браузер для полных возможностей редактора."
+ }
+ ],
+ "editor.try-welcome-mobile": [
+ {
+ "type": 0,
+ "value": "В этом издании демонстрируются некоторые возможности ментальных карт!"
+ }
+ ],
"login.signup": [
{
"type": 0,
diff --git a/packages/editor/src/compiled-lang/zh.json b/packages/editor/src/compiled-lang/zh.json
index e0ab537c..69e858e6 100644
--- a/packages/editor/src/compiled-lang/zh.json
+++ b/packages/editor/src/compiled-lang/zh.json
@@ -5,6 +5,18 @@
"value": "分享"
}
],
+ "editor.edit-description-mobile": [
+ {
+ "type": 0,
+ "value": "移动设备支持有限的思维导图编辑功能。 使用桌面浏览器获得完整的编辑器功能。"
+ }
+ ],
+ "editor.edit-mobile": [
+ {
+ "type": 0,
+ "value": "移动设备注意事项."
+ }
+ ],
"editor.try-welcome": [
{
"type": 0,
@@ -17,6 +29,18 @@
"value": "注册后可以免费创建、分享和发布无限数量的思维导图。"
}
],
+ "editor.try-welcome-description-mobile": [
+ {
+ "type": 0,
+ "value": "注册以开始免费创建、共享和发布无限数量的思维导图。 移动设备支持有限的思维导图编辑功能。 使用桌面浏览器获得完整的编辑器功能。"
+ }
+ ],
+ "editor.try-welcome-mobile": [
+ {
+ "type": 0,
+ "value": "这个版本空间展示了一些思维导图功能!"
+ }
+ ],
"login.signup": [
{
"type": 0,
diff --git a/packages/editor/src/components/footer/index.tsx b/packages/editor/src/components/footer/index.tsx
index 1b10f9b0..dae9ba4d 100644
--- a/packages/editor/src/components/footer/index.tsx
+++ b/packages/editor/src/components/footer/index.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useState } from 'react';
import { StyledLogo, Notifier } from './styled';
import { useIntl } from 'react-intl';
@@ -6,51 +6,99 @@ import KeyboardSvg from '../../../images/keyboard.svg';
import AddSvg from '../../../images/add.svg';
import MinusSvg from '../../../images/minus.svg';
import CenterFocusSvg from '../../../images/center_focus.svg';
+import CloseDialogSvg from '../../../images/close-dialog-icon.svg';
+
import ActionButton from '../action-button';
import { EditorRenderMode } from '@wisemapping/mindplot';
export type FooterPropsType = {
- editorMode: EditorRenderMode;
+ editorMode: EditorRenderMode;
+ isMobile: boolean;
};
-const Footer = ({ editorMode }: FooterPropsType): React.ReactElement => {
- const intl = useIntl();
+const Footer = ({ editorMode, isMobile }: FooterPropsType): React.ReactElement => {
+ const intl = useIntl();
+ const [dialogClass, setDialogClass] = useState('tryInfoPanel');
- return (
- <>
-
-
-
-
-
-
-
-
+ var titleKey = undefined;
+ var descriptionKey = undefined;
+ var showSignupButton = undefined;
+
+ if (editorMode !== 'viewonly' && editorMode !== 'showcase' && isMobile) {
+ titleKey = 'editor.edit-mobile';
+ descriptionKey = 'editor.edit-description-mobile';
+ showSignupButton = false;
+ }
+ if (editorMode === 'showcase' && isMobile) {
+ titleKey = 'editor.try-welcome-mobile';
+ descriptionKey = 'editor.edit-description-mobile';
+ showSignupButton = true;
+ }
+ if (editorMode === 'showcase' && !isMobile) {
+ titleKey = 'editor.try-welcome';
+ descriptionKey = 'editor.try-welcome-description';
+ showSignupButton = true;
+ }
+
+ // if the toolbar is present, the alert must not overlap
+ var alertTopAdjustmentStyle =
+ editorMode !== 'viewonly' && !isMobile
+ ? 'tryInfoPanelWithToolbar'
+ : 'tryInfoPanelWithoutToolbar';
+
+ return (
+ <>
+
+ {!isMobile && (
+
+
+
+ )}
+
+
+
+
+
+
+
+ {titleKey && (
+
+ )}
+ >
+ );
};
export default Footer;
diff --git a/packages/editor/src/global-styled.css b/packages/editor/src/global-styled.css
index 06d15195..632406d9 100644
--- a/packages/editor/src/global-styled.css
+++ b/packages/editor/src/global-styled.css
@@ -9,6 +9,19 @@ html {
font-size: initial;
}
+body {
+ width: 100vw;
+ height: 100vh;
+ min-width: 100vw;
+ min-height: 100vh;
+ margin: 0px;
+}
+
+.mindplot-root {
+ width: 100%;
+ height: 100%;
+}
+
div#mindplot {
position: relative;
top: 50px;
@@ -201,21 +214,69 @@ div#shotcuts > img{
color: #ffffff;
}
-div#tryInfoPanel {
- position: absolute;
- margin: auto;
- text-align: center;
- top: 80px;
- left: 20px;
- width: 200px;
- padding: 20px;
- font-size: 15px;
- border-radius: 9px;
- background-color: white;
- border: solid 2px #ffa800;
+.tryInfoPanel {
+ position: absolute;
+ text-align: center;
+ left: 0;
+ right: 0;
+ background-color: white;
+ border: solid 2px #ffa800;
+ margin: auto;
+ width: 99%;
+ border-radius: 9px;
+ width: 96%;
}
-#tryInfoPanel > p {
+@media (min-width: 600px) {
+ .tryInfoPanel {
+ font-size: 15px;
+ }
+}
+
+@media (max-width: 600px) {
+ .tryInfoPanel {
+ font-size: 13px;
+ }
+}
+
+.tryInfoPanel .tryInfoPanelInner {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+.tryInfoPanel .tryInfoPanelInner .closeButton {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+}
+
+.tryInfoPanel .tryInfoPanelInner .closeButton button {
+ cursor: pointer;
+ border-style: hidden;
+ background-color: transparent;
+ padding: 0px;
+}
+
+.tryInfoPanel .tryInfoPanelInner .closeButton button img {
+ width: 18px;
+ height: 18px;
+ filter: invert(73%) sepia(21%) saturate(4699%) hue-rotate(357deg) brightness(98%) contrast(108%);
+}
+
+.tryInfoPanelWithToolbar {
+ top: 55px;
+}
+
+.tryInfoPanelWithoutToolbar {
+ top: 5px;
+}
+
+.tryInfoPanelClosed {
+ display: none;
+}
+
+.tryInfoPanel > p {
justify-content: center;
- padding-bottom: 20px;
}
\ No newline at end of file
diff --git a/packages/editor/src/index.tsx b/packages/editor/src/index.tsx
index eed9732f..37e4fd41 100644
--- a/packages/editor/src/index.tsx
+++ b/packages/editor/src/index.tsx
@@ -1,122 +1,141 @@
-import React, { useEffect } from 'react';
+import React, { useEffect, useState } from 'react';
import Toolbar, { ToolbarActionType } from './components/toolbar';
import Footer from './components/footer';
import { IntlProvider } from 'react-intl';
import {
- $notify,
- buildDesigner,
- PersistenceManager,
- DesignerOptionsBuilder,
- Designer,
- DesignerKeyboard,
- EditorRenderMode,
+ $notify,
+ buildDesigner,
+ PersistenceManager,
+ DesignerOptionsBuilder,
+ Designer,
+ DesignerKeyboard,
+ EditorRenderMode,
} from '@wisemapping/mindplot';
import './global-styled.css';
import I18nMsg from './classes/i18n-msg';
import Menu from './classes/menu/Menu';
declare global {
- // used in mindplot
- var designer: Designer;
- var accountEmail: string;
+ // used in mindplot
+ var designer: Designer;
+ var accountEmail: string;
}
export type EditorOptions = {
- mode: EditorRenderMode,
- locale: string,
- zoom?: number,
- locked?: boolean,
- lockedMsg?: string;
- mapTitle: string;
- enableKeyboardEvents: boolean;
-}
-
-export type EditorProps = {
- mapId: string;
- options: EditorOptions;
- persistenceManager: PersistenceManager;
- onAction: (action: ToolbarActionType) => void;
- onLoad?: (designer: Designer) => void;
+ mode: EditorRenderMode;
+ locale: string;
+ zoom?: number;
+ locked?: boolean;
+ lockedMsg?: string;
+ mapTitle: string;
+ enableKeyboardEvents: boolean;
};
-const Editor = ({
- mapId,
- options,
- persistenceManager,
- onAction,
- onLoad,
-}: EditorProps) => {
+export type EditorProps = {
+ mapId: string;
+ options: EditorOptions;
+ persistenceManager: PersistenceManager;
+ onAction: (action: ToolbarActionType) => void;
+ onLoad?: (designer: Designer) => void;
+};
- useEffect(() => {
- // Change page title ...
- document.title = `${options.mapTitle} | WiseMapping `;
+const Editor = ({ mapId, options, persistenceManager, onAction, onLoad }: EditorProps) => {
+ const [isMobile, setIsMobile] = useState(undefined);
- // Load mindmap ...
- const designer = onLoadDesigner(mapId, options, persistenceManager);
- // Has extended actions been customized ...
- if (onLoad) {
- onLoad(designer);
- }
+ useEffect(() => {
+ // Change page title ...
+ document.title = `${options.mapTitle} | WiseMapping `;
- // Load mindmap ...
- const instance = PersistenceManager.getInstance();
- const mindmap = instance.load(mapId);
- designer.loadMap(mindmap);
+ // Load mindmap ...
+ const designer = onLoadDesigner(mapId, options, persistenceManager);
+ // Has extended actions been customized ...
+ if (onLoad) {
+ onLoad(designer);
+ }
- if (options.locked) {
- $notify(options.lockedMsg, false);
- }
- }, []);
+ // Load mindmap ...
+ const instance = PersistenceManager.getInstance();
+ const mindmap = instance.load(mapId);
+ designer.loadMap(mindmap);
- useEffect(() => {
- if (options.enableKeyboardEvents) {
- DesignerKeyboard.resume();
- } else {
- DesignerKeyboard.pause();
- }
- }, [options.enableKeyboardEvents]);
+ setIsMobile(checkMobile());
- const onLoadDesigner = (mapId: string, options: EditorOptions, persistenceManager: PersistenceManager): Designer => {
- const buildOptions = DesignerOptionsBuilder.buildOptions({
- persistenceManager,
- mode: options.mode,
- mapId: mapId,
- container: 'mindplot',
- zoom: options.zoom,
- locale: options.locale,
- });
+ if (options.locked) {
+ $notify(options.lockedMsg, false);
+ }
+ }, []);
- // Build designer ...
- const result = buildDesigner(buildOptions);
+ useEffect(() => {
+ if (options.enableKeyboardEvents) {
+ DesignerKeyboard.resume();
+ } else {
+ DesignerKeyboard.pause();
+ }
+ }, [options.enableKeyboardEvents]);
- // Register toolbar event ...
- if (options.mode === 'edition-owner' || options.mode === 'edition-editor' || options.mode === 'edition-viewer' || options.mode === 'showcase') {
- const menu = new Menu(designer, 'toolbar');
+ const checkMobile = () => {
+ const check =
+ /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(
+ navigator.userAgent.toLowerCase(),
+ ) ||
+ /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
+ navigator.userAgent.toLowerCase().substring(0, 4),
+ );
+ return check;
+ };
- // If a node has focus, focus can be move to another node using the keys.
- designer.cleanScreen = () => {
- menu.clear();
- };
- }
- return result;
+ const onLoadDesigner = (
+ mapId: string,
+ options: EditorOptions,
+ persistenceManager: PersistenceManager,
+ ): Designer => {
+ const buildOptions = DesignerOptionsBuilder.buildOptions({
+ persistenceManager,
+ mode: options.mode,
+ mapId: mapId,
+ container: 'mindplot',
+ zoom: options.zoom,
+ locale: options.locale,
+ });
- };
+ // Build designer ...
+ const result = buildDesigner(buildOptions);
- const locale = options.locale;
- const msg = I18nMsg.loadLocaleData(locale);
- const mindplotStyle = (options.mode === 'viewonly') ? { top: 0 } : { top: 'inherit' };
- return (
-
- {(options.mode !== 'viewonly') &&
-
- }
-
-
-
-
- );
-}
+ // Register toolbar event ...
+ if (
+ options.mode === 'edition-owner' ||
+ options.mode === 'edition-editor' ||
+ options.mode === 'edition-viewer' ||
+ options.mode === 'showcase'
+ ) {
+ const menu = new Menu(designer, 'toolbar');
+
+ // If a node has focus, focus can be move to another node using the keys.
+ designer.cleanScreen = () => {
+ menu.clear();
+ };
+ }
+ return result;
+ };
+
+ const locale = options.locale;
+ const msg = I18nMsg.loadLocaleData(locale);
+ const mindplotStyle = options.mode === 'viewonly' ? { top: 0 } : { top: 'inherit' };
+ // if the Toolbar is not hidden before the variable 'isMobile' is defined, it appears intermittently when the page loads
+ // if the Toolbar is not rendered, Menu.ts cant find buttons for create event listeners
+ // so, with this hack the Toolbar is rendered but no visible until the variable 'isMobile' is defined
+ const toolbarContainerStyle = isMobile === undefined ? { display: 'none' } : { display: 'block' };
+ return (
+
+
+ {options.mode !== 'viewonly' && !isMobile && (
+
+ )}
+
+
+
+
+
+ );
+};
export default Editor;
diff --git a/packages/editor/test/playground/index.html b/packages/editor/test/playground/index.html
index ebe99995..bde08e82 100644
--- a/packages/editor/test/playground/index.html
+++ b/packages/editor/test/playground/index.html
@@ -2,31 +2,55 @@
-
-
-
@wisemapping/editor - Playground
-
+ .section {
+ font-weight: bold;
+ }
+
+ tbody tr td:first-child {
+ width: 20%;
+ }
+
-
@wisemapping/editor - Playground
-
You will find here a set of examples that shows how you can use integrate WiseMapping Editor
-
-
- - View mode: Simple integration to load and render mindaps in read
- only mode
- - Editor mode: Example on how mindplot can be used for mindmap edition. Browser local storage is used for persistance.
-
-
+
@wisemapping/editor - Playground
+
You will find here a set of examples that shows how you can use integrate WiseMapping Editor
+
+
View Mode:Simple integration to load and render mindaps in read only mode.
+
+
Editor Mode:Example on how mindplot can be used for mindmap edition. Browser local storage is used for persistance.
+
+
Showcase Mode:When an user wants to try the editor without creating an account.
+
+
\ No newline at end of file
diff --git a/packages/editor/test/playground/map-render/css/viewmode.css b/packages/editor/test/playground/map-render/css/viewmode.css
index a4f63a31..5680a89a 100644
--- a/packages/editor/test/playground/map-render/css/viewmode.css
+++ b/packages/editor/test/playground/map-render/css/viewmode.css
@@ -20,7 +20,7 @@ div#footer-logo {
}
#floating-panel {
- bottom: 80px;
+ bottom: 20px;
align-items: stretch;
}
diff --git a/packages/editor/test/playground/map-render/html/editor.html b/packages/editor/test/playground/map-render/html/editor.html
index 5f9fa974..dd6bd63d 100644
--- a/packages/editor/test/playground/map-render/html/editor.html
+++ b/packages/editor/test/playground/map-render/html/editor.html
@@ -10,8 +10,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/packages/editor/test/playground/map-render/html/showcase.html b/packages/editor/test/playground/map-render/html/showcase.html
new file mode 100644
index 00000000..dd6bd63d
--- /dev/null
+++ b/packages/editor/test/playground/map-render/html/showcase.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
WiseMapping - Editor
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/editor/test/playground/map-render/html/viewmode.html b/packages/editor/test/playground/map-render/html/viewmode.html
index d2351e79..6a1a2341 100644
--- a/packages/editor/test/playground/map-render/html/viewmode.html
+++ b/packages/editor/test/playground/map-render/html/viewmode.html
@@ -4,35 +4,14 @@
WiseMapping - View Mode
+
-
-
diff --git a/packages/editor/test/playground/map-render/js/showcase.tsx b/packages/editor/test/playground/map-render/js/showcase.tsx
new file mode 100644
index 00000000..7b862a66
--- /dev/null
+++ b/packages/editor/test/playground/map-render/js/showcase.tsx
@@ -0,0 +1,52 @@
+/*
+ * 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 React from 'react';
+import ReactDOM from 'react-dom';
+import Editor, { EditorOptions } from '../../../../src/index';
+import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
+
+const initialization = (designer: Designer) => {
+ designer.addEvent('loadSuccess', () => {
+ const elem = document.getElementById('mindplot');
+ if (elem) {
+ elem.classList.add('ready');
+ }
+ });
+};
+
+const persistence = new LocalStorageManager('samples/{id}.wxml', false, false);
+const mapId = 'welcome';
+const options: EditorOptions = {
+ zoom: 0.8,
+ locked: false,
+ mapTitle: 'Develop WiseMapping',
+ mode: 'showcase',
+ locale: 'en',
+ enableKeyboardEvents: true,
+};
+
+ReactDOM.render(
+
console.log('action called:', action)}
+ onLoad={initialization}
+ />,
+ document.getElementById('root'),
+);
diff --git a/packages/editor/webpack.playground.js b/packages/editor/webpack.playground.js
index 3f4acdf5..003cfd50 100644
--- a/packages/editor/webpack.playground.js
+++ b/packages/editor/webpack.playground.js
@@ -10,6 +10,7 @@ const playgroundConfig = {
entry: {
viewmode: path.resolve(__dirname, './test/playground/map-render/js/viewmode'),
editor: path.resolve(__dirname, './test/playground/map-render/js/editor'),
+ showcase: path.resolve(__dirname, './test/playground/map-render/js/showcase'),
},
output: {
path: path.resolve(__dirname, 'test/playground/dist'),
@@ -43,11 +44,16 @@ const playgroundConfig = {
template: 'test/playground/map-render/html/viewmode.html',
}),
new HtmlWebpackPlugin({
- chunks: ['editor'],
- filename: 'editor.html',
- template: 'test/playground/map-render/html/editor.html',
- }),
- ],
+ chunks: ['editor'],
+ filename: 'editor.html',
+ template: 'test/playground/map-render/html/editor.html',
+ }),
+ new HtmlWebpackPlugin({
+ chunks: ['showcase'],
+ filename: 'showcase.html',
+ template: 'test/playground/map-render/html/showcase.html',
+ }),
+ ],
};
module.exports = merge(common, playgroundConfig);
diff --git a/packages/mindplot/.eslintrc.json b/packages/mindplot/.eslintrc.json
index d812e4f8..09f182eb 100644
--- a/packages/mindplot/.eslintrc.json
+++ b/packages/mindplot/.eslintrc.json
@@ -22,6 +22,21 @@
"@typescript-eslint"
],
"rules": {
+ // ignore errores when a line finishes with (setting this value to 0 ignores all errors)
+ "operator-linebreak": [
+ "error", "after", {
+ "overrides": {
+ "+": "ignore",
+ "-": "ignore",
+ ":": "ignore",
+ "*": "ignore",
+ "?": "ignore",
+ ">": "ignore",
+ "||": "ignore",
+ "&&": "ignore"
+ }
+ }
+ ],
"no-underscore-dangle": "off",
"no-plusplus": "off",
"no-param-reassign": "off",
@@ -44,7 +59,8 @@
"ts": "never",
"tsx": "never"
}
- ]
+ ],
+ "implicit-arrow-linebreak": "off"
},
"settings": {
"import/resolver": {
@@ -59,4 +75,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/mindplot/src/@types/custom.d.ts b/packages/mindplot/src/@types/custom.d.ts
index 60bd434c..091d25e2 100644
--- a/packages/mindplot/src/@types/custom.d.ts
+++ b/packages/mindplot/src/@types/custom.d.ts
@@ -1,4 +1,4 @@
declare module '*.svg' {
- const content: any;
- export default content;
+ const content: any;
+ export default content;
}
diff --git a/packages/mindplot/src/components/Designer.ts b/packages/mindplot/src/components/Designer.ts
index a17b0189..b812d77a 100644
--- a/packages/mindplot/src/components/Designer.ts
+++ b/packages/mindplot/src/components/Designer.ts
@@ -82,7 +82,6 @@ class Designer extends Events {
super();
$assert(options, 'options must be defined');
$assert(options.zoom, 'zoom must be defined');
- $assert(options.containerSize, 'size must be defined');
$assert(divElement, 'divElement must be defined');
// Set up i18n location ...
@@ -91,10 +90,10 @@ class Designer extends Events {
this._options = options;
- // Set full div elem render area ...
- if (options.containerSize) {
- divElement.css(options.containerSize);
- }
+ // Set full div elem render area.The component must fill container size
+ // container is responsible for location and size
+ divElement.css('width', '100%');
+ divElement.css('height', '100%');
// Dispatcher manager ...
const commandContext = new CommandContext(this);
@@ -138,14 +137,18 @@ class Designer extends Events {
private _registerWheelEvents(): void {
const zoomFactor = 1.02;
- document.addEventListener('wheel', (event: WheelEvent) => {
- if (event.deltaX > 0 || event.deltaY > 0) {
- this.zoomOut(zoomFactor);
- } else {
- this.zoomIn(zoomFactor);
- }
- event.preventDefault();
- }, { passive: false });
+ document.addEventListener(
+ 'wheel',
+ (event: WheelEvent) => {
+ if (event.deltaX > 0 || event.deltaY > 0) {
+ this.zoomOut(zoomFactor);
+ } else {
+ this.zoomIn(zoomFactor);
+ }
+ event.preventDefault();
+ },
+ { passive: false },
+ );
}
getActionDispatcher(): StandaloneActionDispatcher {
@@ -187,8 +190,7 @@ class Designer extends Events {
screenManager.addEvent('dblclick', (event: MouseEvent) => {
if (workspace.isWorkspaceEventsEnabled()) {
const mousePos = screenManager.getWorkspaceMousePosition(event);
- const centralTopic: CentralTopic = me.getModel()
- .getCentralTopic();
+ const centralTopic: CentralTopic = me.getModel().getCentralTopic();
const model = me._createChildModel(centralTopic, mousePos);
this._actionDispatcher.addTopics([model], [centralTopic.getId()]);
@@ -568,9 +570,9 @@ class Designer extends Events {
}
/**
- * @param {mindplot.Mindmap} mindmap
- * @throws will throw an error if mindmapModel is null or undefined
- */
+ * @param {mindplot.Mindmap} mindmap
+ * @throws will throw an error if mindmapModel is null or undefined
+ */
loadMap(mindmap: Mindmap): void {
$assert(mindmap, 'mindmapModel can not be null');
this._mindmap = mindmap;
@@ -644,11 +646,11 @@ class Designer extends Events {
}
/**
- * @private
- * @param {mindplot.model.RelationshipModel} model
- * @return {mindplot.Relationship} the relationship created to the model
- * @throws will throw an error if model is null or undefined
- */
+ * @private
+ * @param {mindplot.model.RelationshipModel} model
+ * @return {mindplot.Relationship} the relationship created to the model
+ * @throws will throw an error if model is null or undefined
+ */
private _relationshipModelToRelationship(model: RelationshipModel): Relationship {
$assert(model, 'Node model can not be null');
@@ -667,9 +669,9 @@ class Designer extends Events {
}
/**
- * @param {mindplot.model.RelationshipModel} model
- * @return {mindplot.Relationship} the relationship added to the mindmap
- */
+ * @param {mindplot.model.RelationshipModel} model
+ * @return {mindplot.Relationship} the relationship added to the mindmap
+ */
addRelationship(model: RelationshipModel): Relationship {
const mindmap = this.getMindmap();
mindmap.addRelationship(model);
@@ -677,9 +679,9 @@ class Designer extends Events {
}
/**
- * deletes the relationship from the linked topics, DesignerModel, Workspace and Mindmap
- * @param {mindplot.Relationship} rel the relationship to delete
- */
+ * deletes the relationship from the linked topics, DesignerModel, Workspace and Mindmap
+ * @param {mindplot.Relationship} rel the relationship to delete
+ */
deleteRelationship(rel: Relationship): void {
const sourceTopic = rel.getSourceTopic();
sourceTopic.deleteRelationship(rel);
@@ -704,9 +706,7 @@ class Designer extends Events {
const targetTopic = dmodel.findTopicById(targetTopicId);
$assert(
targetTopic,
- `targetTopic could not be found:${targetTopicId},${dmodel
- .getTopics()
- .map((e) => e.getId())}`,
+ `targetTopic could not be found:${targetTopicId},${dmodel.getTopics().map((e) => e.getId())}`,
);
// Build relationship line ....
@@ -797,16 +797,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();
+ const topicsIds = this.getModel().filterTopicsIds();
if (topicsIds.length > 0) {
this._actionDispatcher.changeFontStyleToTopic(topicsIds);
}
@@ -815,8 +813,7 @@ class Designer extends Events {
changeFontColor(color: string) {
$assert(color, 'color can not be null');
- const topicsIds = this.getModel()
- .filterTopicsIds();
+ const topicsIds = this.getModel().filterTopicsIds();
if (topicsIds.length > 0) {
this._actionDispatcher.changeFontColorToTopic(topicsIds, color);
@@ -851,9 +848,8 @@ class Designer extends Events {
}
changeTopicShape(shape: string) {
- const validateFunc = (topic: Topic) => !(
- topic.getType() === 'CentralTopic' && shape === TopicShape.LINE
- );
+ const validateFunc = (topic: Topic) =>
+ !(topic.getType() === 'CentralTopic' && shape === TopicShape.LINE);
const validateError = 'Central Topic shape can not be changed to line figure.';
const topicsIds = this.getModel().filterTopicsIds(validateFunc, validateError);
@@ -872,9 +868,13 @@ class Designer extends Events {
addIconType(iconType: string): void {
const topicsIds = this.getModel().filterTopicsIds();
if (topicsIds.length > 0) {
- this._actionDispatcher.addFeatureToTopic(topicsIds[0], TopicFeatureFactory.Icon.id as FeatureType, {
- id: iconType,
- });
+ this._actionDispatcher.addFeatureToTopic(
+ topicsIds[0],
+ TopicFeatureFactory.Icon.id as FeatureType,
+ {
+ id: iconType,
+ },
+ );
}
}
diff --git a/packages/mindplot/src/components/DesignerOptionsBuilder.ts b/packages/mindplot/src/components/DesignerOptionsBuilder.ts
index a4e0ac53..26a2396c 100644
--- a/packages/mindplot/src/components/DesignerOptionsBuilder.ts
+++ b/packages/mindplot/src/components/DesignerOptionsBuilder.ts
@@ -18,37 +18,25 @@
import { $assert } from '@wisemapping/core-js';
import EditorRenderMode from './EditorRenderMode';
import PersistenceManager from './PersistenceManager';
-import SizeType from './SizeType';
export type DesignerOptions = {
- zoom: number,
- containerSize?: SizeType,
- mode: EditorRenderMode,
- mapId?: string,
- container: string,
- persistenceManager?: PersistenceManager,
- saveOnLoad?: boolean,
- locale?: string,
+ zoom: number;
+ mode: EditorRenderMode;
+ mapId?: string;
+ container: string;
+ persistenceManager?: PersistenceManager;
+ saveOnLoad?: boolean;
+ locale?: string;
};
class OptionsBuilder {
static buildOptions(options: DesignerOptions): DesignerOptions {
$assert(options.persistenceManager, 'persistence must be defined');
- let { containerSize } = options;
- if (options.containerSize == null) {
- // If it has not been defined, use browser size ...
- containerSize = {
- width: window.screen.width,
- height: window.screen.height,
- };
- }
-
const defaultOptions: DesignerOptions = {
mode: 'edition-owner',
zoom: 0.85,
saveOnLoad: true,
- containerSize,
container: 'mindplot',
locale: 'en',
};
diff --git a/packages/mindplot/src/components/IconGroup.ts b/packages/mindplot/src/components/IconGroup.ts
index 3f0c506d..d7c62b12 100644
--- a/packages/mindplot/src/components/IconGroup.ts
+++ b/packages/mindplot/src/components/IconGroup.ts
@@ -16,15 +16,8 @@
* limitations under the License.
*/
-import {
- $assert,
- $defined,
-} from '@wisemapping/core-js';
-import {
- Group,
- ElementClass,
- Point,
-} from '@wisemapping/web2d';
+import { $assert, $defined } from '@wisemapping/core-js';
+import { Group, ElementClass, Point } from '@wisemapping/web2d';
import IconGroupRemoveTip from './IconGroupRemoveTip';
import Icon from './Icon';
import SizeType from './SizeType';
@@ -102,7 +95,10 @@ class IconGroup {
icon.setGroup(this);
icons.push(icon);
- this._icons = icons.sort((a, b) => ORDER_BY_TYPE.get(a.getModel().getType()) - ORDER_BY_TYPE.get(b.getModel().getType()));
+ this._icons = icons.sort(
+ (a, b) =>
+ ORDER_BY_TYPE.get(a.getModel().getType()) - ORDER_BY_TYPE.get(b.getModel().getType()),
+ );
// Add all the nodes back ...
this._resize(this._icons.length);
@@ -185,10 +181,7 @@ class IconGroup {
private _positionIcon(icon: Icon, order: number) {
const iconSize = Icon.SIZE + IconGroup.ICON_PADDING * 2;
- icon.getImage().setPosition(
- iconSize * order + IconGroup.ICON_PADDING,
- IconGroup.ICON_PADDING,
- );
+ icon.getImage().setPosition(iconSize * order + IconGroup.ICON_PADDING, IconGroup.ICON_PADDING);
}
static ICON_PADDING = 5;
diff --git a/packages/mindplot/src/components/ScreenManager.ts b/packages/mindplot/src/components/ScreenManager.ts
index 88e80d0c..f1f4cce6 100644
--- a/packages/mindplot/src/components/ScreenManager.ts
+++ b/packages/mindplot/src/components/ScreenManager.ts
@@ -21,7 +21,7 @@ import { Point } from '@wisemapping/web2d';
class ScreenManager {
private _divContainer: JQuery;
- private _padding: { x: number; y: number; };
+ private _padding: { x: number; y: number };
private _clickEvents;
@@ -34,20 +34,23 @@ class ScreenManager {
// Ignore default click event propagation. Prevent 'click' event on drag.
this._clickEvents = [];
- this._divContainer.bind('click', (event: { stopPropagation: () => void; }) => {
+ this._divContainer.bind('click', (event: { stopPropagation: () => void }) => {
event.stopPropagation();
});
- this._divContainer.bind('dblclick', (event: { stopPropagation: () => void; preventDefault: () => void; }) => {
- event.stopPropagation();
- event.preventDefault();
- });
+ this._divContainer.bind(
+ 'dblclick',
+ (event: { stopPropagation: () => void; preventDefault: () => void }) => {
+ event.stopPropagation();
+ event.preventDefault();
+ },
+ );
}
/**
* Return the current visibile area in the browser.
*/
- getVisibleBrowserSize(): { width: number, height: number } {
+ getVisibleBrowserSize(): { width: number; height: number } {
return {
width: window.innerWidth,
height: window.innerHeight - Number.parseInt(this._divContainer.css('top'), 10),
@@ -82,10 +85,31 @@ class ScreenManager {
}
}
- getWorkspaceMousePosition(event: MouseEvent) {
- // Retrieve current mouse position.
- let x = event.clientX;
- let y = event.clientY;
+ private mouseEvents = ['mousedown', 'mouseup', 'mousemove', 'dblclick', 'click'];
+
+ private tocuchEvents = ['touchstart', 'touchend', 'touchmove'];
+
+ // the received type was changed from MouseEvent to "any", because we must support touch events
+ getWorkspaceMousePosition(event: any) {
+ let x;
+ let y;
+
+ if (this.mouseEvents.includes(event.type)) {
+ // Retrieve current mouse position.
+ x = event.clientX;
+ y = event.clientY;
+ } else if (this.tocuchEvents.includes(event.type)) {
+ x = event.touches[0].clientX;
+ y = event.touches[0].clientY;
+ }
+
+ // if value is zero assert throws error
+ if (x !== 0) {
+ $assert(x, `clientX can not be null, eventType= ${event.type}`);
+ }
+ if (y !== 0) {
+ $assert(y, `clientY can not be null, eventType= ${event.type}`);
+ }
// Adjust the deviation of the container positioning ...
const containerPosition = this.getContainer().position();
diff --git a/packages/mindplot/src/components/Workspace.ts b/packages/mindplot/src/components/Workspace.ts
index b764c1f9..74b4a131 100644
--- a/packages/mindplot/src/components/Workspace.ts
+++ b/packages/mindplot/src/components/Workspace.ts
@@ -130,11 +130,19 @@ class Workspace {
setZoom(zoom: number, center = false): void {
this._zoom = zoom;
const workspace = this._workspace;
- const newVisibleAreaSize = this._screenManager.getVisibleBrowserSize();
- // Update coord scale...
- const newCoordWidth = zoom * this._containerSize.width;
- const newCoordHeight = zoom * this._containerSize.height;
+ const divContainer = this._screenManager.getContainer();
+ const containerWidth = divContainer.width();
+ const containerHeight = divContainer.height();
+ const newVisibleAreaSize = { width: containerWidth, height: containerHeight };
+
+ // - svg must fit container size
+ const svgElement = divContainer.find('svg');
+ svgElement.attr('width', containerWidth);
+ svgElement.attr('height', containerHeight);
+ // - svg viewPort must fit container size with zoom adjustment
+ const newCoordWidth = containerWidth * this._zoom;
+ const newCoordHeight = containerHeight * this._zoom;
let coordOriginX: number;
let coordOriginY: number;
@@ -222,11 +230,14 @@ class Workspace {
wasDragged = true;
};
screenManager.addEvent('mousemove', workspace._mouseMoveListener);
+ screenManager.addEvent('touchmove', workspace._mouseMoveListener);
// Register mouse up listeners ...
workspace._mouseUpListener = () => {
screenManager.removeEvent('mousemove', workspace._mouseMoveListener);
screenManager.removeEvent('mouseup', workspace._mouseUpListener);
+ screenManager.removeEvent('touchmove', workspace._mouseUpListener);
+ screenManager.removeEvent('touchend', workspace._mouseMoveListener);
workspace._mouseUpListener = null;
workspace._mouseMoveListener = null;
window.document.body.style.cursor = 'default';
@@ -241,12 +252,14 @@ class Workspace {
}
};
screenManager.addEvent('mouseup', workspace._mouseUpListener);
+ screenManager.addEvent('touchend', workspace._mouseUpListener);
}
} else {
workspace._mouseUpListener();
}
};
screenManager.addEvent('mousedown', mouseDownListener);
+ screenManager.addEvent('touchstart', mouseDownListener);
}
}
diff --git a/packages/mindplot/src/components/commands/MoveControlPointCommand.ts b/packages/mindplot/src/components/commands/MoveControlPointCommand.ts
index 2856c6e5..33523cf9 100644
--- a/packages/mindplot/src/components/commands/MoveControlPointCommand.ts
+++ b/packages/mindplot/src/components/commands/MoveControlPointCommand.ts
@@ -42,8 +42,8 @@ class MoveControlPointCommand extends Command {
* @classdesc This command handles do/undo of changing the control points of a relationship
* arrow. These are the two points that appear when the relationship is on focus. They
* influence how the arrow is drawn (not the source or the destination topic nor the arrow
- * direction)
- */
+ * direction)
+ */
constructor(ctrlPointController: ControlPoint, point: number) {
$assert(ctrlPointController, 'line can not be null');
$assert($defined(point), 'point can not be null');
diff --git a/packages/mindplot/src/components/layout/AbstractBasicSorter.ts b/packages/mindplot/src/components/layout/AbstractBasicSorter.ts
index 8bab9ffa..8ccce86d 100644
--- a/packages/mindplot/src/components/layout/AbstractBasicSorter.ts
+++ b/packages/mindplot/src/components/layout/AbstractBasicSorter.ts
@@ -45,9 +45,9 @@ abstract class AbstractBasicSorter extends ChildrenSorterStrategy {
} else {
let childrenHeight = 0;
- children.forEach(((child) => {
+ children.forEach((child) => {
childrenHeight += this._computeChildrenHeight(treeSet, child, heightCache);
- }));
+ });
result = Math.max(height, childrenHeight);
}
diff --git a/packages/mindplot/src/components/layout/BalancedSorter.ts b/packages/mindplot/src/components/layout/BalancedSorter.ts
index 04b0ef20..2e67db7b 100644
--- a/packages/mindplot/src/components/layout/BalancedSorter.ts
+++ b/packages/mindplot/src/components/layout/BalancedSorter.ts
@@ -34,14 +34,8 @@ class BalancedSorter extends AbstractBasicSorter {
// If it is a dragged node...
if (node) {
$assert($defined(position), 'position cannot be null for predict in dragging');
- const nodeDirection = this._getRelativeDirection(
- rootNode.getPosition(),
- node.getPosition(),
- );
- const positionDirection = this._getRelativeDirection(
- rootNode.getPosition(),
- position,
- );
+ const nodeDirection = this._getRelativeDirection(rootNode.getPosition(), node.getPosition());
+ const positionDirection = this._getRelativeDirection(rootNode.getPosition(), position);
const siblings = graph.getSiblings(node);
const sameParent = parent === graph.getParent(node);
@@ -50,7 +44,8 @@ class BalancedSorter extends AbstractBasicSorter {
}
}
- let right; let left;
+ let right;
+ let left;
if (!position) {
right = this._getChildrenForOrder(parent, graph, 0);
left = this._getChildrenForOrder(parent, graph, 1);
@@ -58,19 +53,16 @@ class BalancedSorter extends AbstractBasicSorter {
// Filter nodes on one side..
let order;
if (position) {
- order = position.x > rootNode.getPosition().x
- ? 0
- : 1;
+ order = position.x > rootNode.getPosition().x ? 0 : 1;
} else {
- order = right.length - left.length > 0
- ? 1
- : 0;
+ order = right.length - left.length > 0 ? 1 : 0;
}
const direction = order % 2 === 0 ? 1 : -1;
// Exclude the dragged node (if set)
- const children = this._getChildrenForOrder(parent, graph, order)
- .filter((child) => child !== node);
+ const children = this._getChildrenForOrder(parent, graph, order).filter(
+ (child) => child !== node,
+ );
// No children?
if (children.length === 0) {
@@ -78,10 +70,9 @@ class BalancedSorter extends AbstractBasicSorter {
order,
{
x:
- parent.getPosition().x
- + direction
- * (parent.getSize().width / 2
- + BalancedSorter.INTERNODE_HORIZONTAL_PADDING * 2),
+ parent.getPosition().x +
+ direction *
+ (parent.getSize().width / 2 + BalancedSorter.INTERNODE_HORIZONTAL_PADDING * 2),
y: parent.getPosition().y,
},
];
@@ -94,9 +85,10 @@ class BalancedSorter extends AbstractBasicSorter {
children.forEach((child, index) => {
const cpos = child.getPosition();
if (newestPosition.y > cpos.y) {
- const yOffset = child === last
- ? child.getSize().height + BalancedSorter.INTERNODE_VERTICAL_PADDING * 2
- : (children[index + 1].getPosition().y - child.getPosition().y) / 2;
+ const yOffset =
+ child === last
+ ? child.getSize().height + BalancedSorter.INTERNODE_VERTICAL_PADDING * 2
+ : (children[index + 1].getPosition().y - child.getPosition().y) / 2;
result = [child.getOrder() + 2, { x: cpos.x, y: cpos.y + yOffset }];
}
});
@@ -109,9 +101,9 @@ class BalancedSorter extends AbstractBasicSorter {
{
x: first.getPosition().x,
y:
- first.getPosition().y
- - first.getSize().height
- - BalancedSorter.INTERNODE_VERTICAL_PADDING * 2,
+ first.getPosition().y -
+ first.getSize().height -
+ BalancedSorter.INTERNODE_VERTICAL_PADDING * 2,
},
];
}
@@ -202,10 +194,11 @@ class BalancedSorter extends AbstractBasicSorter {
}
const yOffset = ysum + heights[i].height / 2;
- const xOffset = direction
- * (node.getSize().width / 2
- + heights[i].width / 2
- + +BalancedSorter.INTERNODE_HORIZONTAL_PADDING);
+ const xOffset =
+ direction *
+ (node.getSize().width / 2 +
+ heights[i].width / 2 +
+ +BalancedSorter.INTERNODE_HORIZONTAL_PADDING);
$assert(!Number.isNaN(xOffset), 'xOffset can not be null');
$assert(!Number.isNaN(yOffset), 'yOffset can not be null');
@@ -226,9 +219,9 @@ class BalancedSorter extends AbstractBasicSorter {
const order = i === 0 && factor === 1 ? 1 : factor * i;
$assert(
children[i].getOrder() === order,
- `Missing order elements. Missing order: ${i * factor
- }. Parent:${node.getId()
- },Node:${children[i].getId()}`,
+ `Missing order elements. Missing order: ${
+ i * factor
+ }. Parent:${node.getId()},Node:${children[i].getId()}`,
);
}
}
@@ -242,8 +235,9 @@ class BalancedSorter extends AbstractBasicSorter {
}
protected _getChildrenForOrder(parent: Node, graph: RootedTreeSet, order: number) {
- return this._getSortedChildren(graph, parent)
- .filter((child) => child.getOrder() % 2 === order % 2);
+ return this._getSortedChildren(graph, parent).filter(
+ (child) => child.getOrder() % 2 === order % 2,
+ );
}
protected _getVerticalPadding(): number {
diff --git a/packages/mindplot/src/components/widget/LinkEditor.ts b/packages/mindplot/src/components/widget/LinkEditor.ts
index a9b278e0..06c70e95 100644
--- a/packages/mindplot/src/components/widget/LinkEditor.ts
+++ b/packages/mindplot/src/components/widget/LinkEditor.ts
@@ -119,8 +119,9 @@ class LinkEditor extends BootstrapDialog {
* @return {Boolean} true if the url is valid
*/
private checkURL(url: string): boolean {
- const regex = /^(http|https):\/\/[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/i;
- return (regex.test(url));
+ const regex =
+ /^(http|https):\/\/[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/i;
+ return regex.test(url);
}
/**
@@ -129,7 +130,7 @@ class LinkEditor extends BootstrapDialog {
*/
private hasProtocol(url: string): boolean {
const regex = /^(http|https):\/\//i;
- return (regex.test(url));
+ return regex.test(url);
}
/**
diff --git a/packages/mindplot/src/components/widget/LinkIconTooltip.ts b/packages/mindplot/src/components/widget/LinkIconTooltip.ts
index 440ac955..31c1a215 100644
--- a/packages/mindplot/src/components/widget/LinkIconTooltip.ts
+++ b/packages/mindplot/src/components/widget/LinkIconTooltip.ts
@@ -35,7 +35,8 @@ class LinkIconTooltip extends FloatingTip {
placement: 'bottom',
title: $msg('LINK'),
trigger: 'manual',
- template: '',
+ template:
+ '',
destroyOnExit: true,
});
}
diff --git a/packages/mindplot/src/index.ts b/packages/mindplot/src/index.ts
index 8cd70ae2..c1e95477 100644
--- a/packages/mindplot/src/index.ts
+++ b/packages/mindplot/src/index.ts
@@ -33,17 +33,11 @@ import DesignerKeyboard from './components/DesignerKeyboard';
import EditorRenderMode from './components/EditorRenderMode';
import ImageIcon from './components/ImageIcon';
-import {
- buildDesigner,
-} from './components/DesignerBuilder';
+import { buildDesigner } from './components/DesignerBuilder';
-import {
- $notify,
-} from './components/widget/ToolbarNotifier';
+import { $notify } from './components/widget/ToolbarNotifier';
-import {
- $msg,
-} from './components/Messages';
+import { $msg } from './components/Messages';
// This hack is required to initialize Bootstrap. In future, this should be removed.
const globalAny: any = global;
diff --git a/packages/mindplot/src/indexLoader.ts b/packages/mindplot/src/indexLoader.ts
index ec6d48ed..57452fd0 100644
--- a/packages/mindplot/src/indexLoader.ts
+++ b/packages/mindplot/src/indexLoader.ts
@@ -16,11 +16,8 @@
* limitations under the License.
*/
import jquery from 'jquery';
-import {
-} from './components/widget/ToolbarNotifier';
-import {
- buildDesigner,
-} from './components/DesignerBuilder';
+import {} from './components/widget/ToolbarNotifier';
+import { buildDesigner } from './components/DesignerBuilder';
import PersistenceManager from './components/PersistenceManager';
import LocalStorageManager from './components/LocalStorageManager';
import DesignerOptionsBuilder from './components/DesignerOptionsBuilder';
@@ -43,16 +40,14 @@ const persistence: PersistenceManager = new LocalStorageManager(
const params = new URLSearchParams(window.location.search.substring(1));
const zoomParam = Number.parseFloat(params.get('zoom'));
-const options = DesignerOptionsBuilder.buildOptions(
- {
- persistenceManager: persistence,
- mode: 'viewonly',
- mapId: global.mapId,
- container: 'mindplot',
- zoom: zoomParam || global.userOptions.zoom,
- locale: global.locale,
- },
-);
+const options = DesignerOptionsBuilder.buildOptions({
+ persistenceManager: persistence,
+ mode: 'viewonly',
+ mapId: global.mapId,
+ container: 'mindplot',
+ zoom: zoomParam || global.userOptions.zoom,
+ locale: global.locale,
+});
// Build designer ...
const designer = buildDesigner(options);
diff --git a/packages/webapp/src/app.tsx b/packages/webapp/src/app.tsx
index b1b905f9..1c52ba9d 100644
--- a/packages/webapp/src/app.tsx
+++ b/packages/webapp/src/app.tsx
@@ -19,88 +19,73 @@ import EditorPage from './components/editor-page';
import AppConfig from './classes/app-config';
import withSessionExpirationHandling from './components/HOCs/withSessionExpirationHandling';
-
declare module '@mui/styles/defaultTheme' {
- // eslint-disable-next-line @typescript-eslint/no-empty-interface
- interface DefaultTheme extends Theme { }
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
+ interface DefaultTheme extends Theme {}
}
// Google Analytics Initialization.
ReactGA.initialize([
- {
- trackingId: AppConfig.getGoogleAnalyticsAccount(),
- }
+ {
+ trackingId: AppConfig.getGoogleAnalyticsAccount(),
+ },
]);
-
const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- refetchIntervalInBackground: false,
- staleTime: 5 * 1000 * 60, // 10 minutes
- },
+ defaultOptions: {
+ queries: {
+ refetchIntervalInBackground: false,
+ staleTime: 5 * 1000 * 60, // 10 minutes
},
+ },
});
const App = (): ReactElement => {
- const locale = AppI18n.getDefaultLocale();
- const EnhacedEditorPage = withSessionExpirationHandling(EditorPage);
+ const locale = AppI18n.getDefaultLocale();
+ const EnhacedEditorPage = withSessionExpirationHandling(EditorPage);
- return locale.message ? (
-
-
- }
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ) : (
- Loading ...
- );
+ return locale.message ? (
+
+
+ }
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) : (
+ Loading ...
+ );
};
export default App;
diff --git a/packages/webapp/src/components/HOCs/withSessionExpirationHandling.tsx b/packages/webapp/src/components/HOCs/withSessionExpirationHandling.tsx
index 2e2fbda9..28f5c03e 100644
--- a/packages/webapp/src/components/HOCs/withSessionExpirationHandling.tsx
+++ b/packages/webapp/src/components/HOCs/withSessionExpirationHandling.tsx
@@ -6,27 +6,27 @@ import ClientHealthSentinel from '../../classes/client/client-health-sentinel';
import { activeInstance, sessionExpired } from '../../redux/clientSlice';
function withSessionExpirationHandling(Component: ComponentType) {
- return (hocProps: T): React.ReactElement => {
- const client: Client = useSelector(activeInstance);
- const dispatch = useDispatch();
+ return (hocProps: T): React.ReactElement => {
+ const client: Client = useSelector(activeInstance);
+ const dispatch = useDispatch();
- useEffect(() => {
- if (client) {
- client.onSessionExpired(() => {
- dispatch(sessionExpired());
- });
- } else {
- console.warn('Session expiration wont be handled because could not find client');
- }
- }, []);
+ useEffect(() => {
+ if (client) {
+ client.onSessionExpired(() => {
+ dispatch(sessionExpired());
+ });
+ } else {
+ console.warn('Session expiration wont be handled because could not find client');
+ }
+ }, []);
- return (
- <>
-
- ;
- >
- );
- };
+ return (
+ <>
+
+
+ >
+ );
+ };
}
export default withSessionExpirationHandling;
diff --git a/packages/webapp/src/components/maps-page/index.tsx b/packages/webapp/src/components/maps-page/index.tsx
index 71d58dca..683c432a 100644
--- a/packages/webapp/src/components/maps-page/index.tsx
+++ b/packages/webapp/src/components/maps-page/index.tsx
@@ -43,313 +43,315 @@ import LabelDeleteConfirm from './maps-list/label-delete-confirm';
import ReactGA from 'react-ga4';
import { withStyles } from '@mui/styles';
-
export type Filter = GenericFilter | LabelFilter;
export interface GenericFilter {
- type: 'public' | 'all' | 'starred' | 'shared' | 'label' | 'owned';
+ type: 'public' | 'all' | 'starred' | 'shared' | 'label' | 'owned';
}
export interface LabelFilter {
- type: 'label';
- label: Label;
+ type: 'label';
+ label: Label;
}
interface ToolbarButtonInfo {
- filter: GenericFilter | LabelFilter;
- label: string;
- icon: React.ReactElement;
+ filter: GenericFilter | LabelFilter;
+ label: string;
+ icon: React.ReactElement;
}
const MapsPage = (): ReactElement => {
- const classes = useStyles();
- const [filter, setFilter] = React.useState({ type: 'all' });
- const client: Client = useSelector(activeInstance);
- const queryClient = useQueryClient();
- const [activeDialog, setActiveDialog] = React.useState(undefined);
- const [labelToDelete, setLabelToDelete] = React.useState(null);
- // Reload based on user preference ...
- const userLocale = AppI18n.getUserLocale();
+ const classes = useStyles();
+ const [filter, setFilter] = React.useState({ type: 'all' });
+ const client: Client = useSelector(activeInstance);
+ const queryClient = useQueryClient();
+ const [activeDialog, setActiveDialog] = React.useState(undefined);
+ const [labelToDelete, setLabelToDelete] = React.useState(null);
+ // Reload based on user preference ...
+ const userLocale = AppI18n.getUserLocale();
- const cache = createIntlCache();
- const intl = createIntl({
- defaultLocale: userLocale.code,
- locale: Locales.EN.code,
- messages: userLocale.message
- }, cache)
+ const cache = createIntlCache();
+ const intl = createIntl(
+ {
+ defaultLocale: userLocale.code,
+ locale: Locales.EN.code,
+ messages: userLocale.message,
+ },
+ cache,
+ );
- useEffect(() => {
- document.title = intl.formatMessage({
- id: 'maps.page-title',
- defaultMessage: 'My Maps | WiseMapping',
- });
- ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Maps List' });
- }, []);
-
- const mutation = useMutation((id: number) => client.deleteLabel(id), {
- onSuccess: () => {
- queryClient.invalidateQueries('labels');
- queryClient.invalidateQueries('maps');
- },
- onError: (error) => {
- console.error(`Unexpected error ${error}`);
- },
+ useEffect(() => {
+ document.title = intl.formatMessage({
+ id: 'maps.page-title',
+ defaultMessage: 'My Maps | WiseMapping',
});
+ ReactGA.send({ hitType: 'pageview', page: window.location.pathname, title: 'Maps List' });
+ }, []);
- const handleMenuClick = (filter: Filter) => {
- queryClient.invalidateQueries('maps');
- setFilter(filter);
- };
+ const mutation = useMutation((id: number) => client.deleteLabel(id), {
+ onSuccess: () => {
+ queryClient.invalidateQueries('labels');
+ queryClient.invalidateQueries('maps');
+ },
+ onError: (error) => {
+ console.error(`Unexpected error ${error}`);
+ },
+ });
- const handleLabelDelete = (id: number) => {
- mutation.mutate(id);
- };
+ const handleMenuClick = (filter: Filter) => {
+ queryClient.invalidateQueries('maps');
+ setFilter(filter);
+ };
- const { data } = useQuery('labels', () => {
- return client.fetchLabels();
- });
+ const handleLabelDelete = (id: number) => {
+ mutation.mutate(id);
+ };
- const labels: Label[] = data ? data : [];
- const filterButtons: ToolbarButtonInfo[] = [
- {
- filter: { type: 'all' },
- label: intl.formatMessage({ id: 'maps.nav-all', defaultMessage: 'All' }),
- icon: ,
- },
- {
- filter: { type: 'owned' },
- label: intl.formatMessage({ id: 'maps.nav-onwned', defaultMessage: 'Owned' }),
- icon: ,
- },
- {
- filter: { type: 'starred' },
- label: intl.formatMessage({ id: 'maps.nav-starred', defaultMessage: 'Starred' }),
- icon: ,
- },
- {
- filter: { type: 'shared' },
- label: intl.formatMessage({ id: 'maps.nav-shared', defaultMessage: 'Shared with me' }),
- icon: ,
- },
- {
- filter: { type: 'public' },
- label: intl.formatMessage({ id: 'maps.nav-public', defaultMessage: 'Public' }),
- icon: ,
- },
- ];
+ const { data } = useQuery('labels', () => {
+ return client.fetchLabels();
+ });
- labels.forEach((l) =>
- filterButtons.push({
- filter: { type: 'label', label: l },
- label: l.title,
- icon: ,
- })
- );
+ const labels: Label[] = data ? data : [];
+ const filterButtons: ToolbarButtonInfo[] = [
+ {
+ filter: { type: 'all' },
+ label: intl.formatMessage({ id: 'maps.nav-all', defaultMessage: 'All' }),
+ icon: ,
+ },
+ {
+ filter: { type: 'owned' },
+ label: intl.formatMessage({ id: 'maps.nav-onwned', defaultMessage: 'Owned' }),
+ icon: ,
+ },
+ {
+ filter: { type: 'starred' },
+ label: intl.formatMessage({ id: 'maps.nav-starred', defaultMessage: 'Starred' }),
+ icon: ,
+ },
+ {
+ filter: { type: 'shared' },
+ label: intl.formatMessage({ id: 'maps.nav-shared', defaultMessage: 'Shared with me' }),
+ icon: ,
+ },
+ {
+ filter: { type: 'public' },
+ label: intl.formatMessage({ id: 'maps.nav-public', defaultMessage: 'Public' }),
+ icon: ,
+ },
+ ];
- return (
-
+ filterButtons.push({
+ filter: { type: 'label', label: l },
+ label: l.title,
+ icon: ,
+ }),
+ );
+
+ return (
+
+
+
-
-
-
-
- }
- className={classes.newMapButton}
- onClick={() => setActiveDialog('create')}
- >
-
-
-
+
+
+ }
+ className={classes.newMapButton}
+ onClick={() => setActiveDialog('create')}
+ >
+
+
+
-
- }
- className={classes.importButton}
- onClick={() => setActiveDialog('import')}
- >
-
-
-
- setActiveDialog(undefined)}
- mapsId={[]}
- fromEditor
- />
+
+ }
+ className={classes.importButton}
+ onClick={() => setActiveDialog('import')}
+ >
+
+
+
+ setActiveDialog(undefined)}
+ mapsId={[]}
+ fromEditor
+ />
-
-
-
-
-
-
-
-
-
- {filterButtons.map((buttonInfo) => {
- return (
-
- );
- })}
-
-
-
-
-
-
-
-
-
-
-
-
+
- {labelToDelete &&
setLabelToDelete(null)}
- onConfirm={() => {
- handleLabelDelete(labelToDelete);
- setLabelToDelete(null);
- }}
- label={labels.find(l => l.id === labelToDelete)}
- />}
-
- );
+
+
+
+
+
+
+
+
+ {filterButtons.map((buttonInfo) => {
+ return (
+
+ );
+ })}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {labelToDelete && (
+ setLabelToDelete(null)}
+ onConfirm={() => {
+ handleLabelDelete(labelToDelete);
+ setLabelToDelete(null);
+ }}
+ label={labels.find((l) => l.id === labelToDelete)}
+ />
+ )}
+
+ );
};
interface ListItemProps {
- icon: React.ReactElement;
- label: string;
- filter: Filter;
- active?: Filter;
- onClick: (filter: Filter) => void;
- onDelete?: (id: number) => void;
+ icon: React.ReactElement;
+ label: string;
+ filter: Filter;
+ active?: Filter;
+ onClick: (filter: Filter) => void;
+ onDelete?: (id: number) => void;
}
// https://stackoverflow.com/questions/61486061/how-to-set-selected-and-hover-color-of-listitem-in-mui
const CustomListItem = withStyles({
- root: {
- "&$selected": {
- backgroundColor: "rgb(210, 140, 5)",
- color: "white",
- "& .MuiListItemIcon-root": {
- color: "white"
- }
- },
- "&$selected:hover": {
- backgroundColor: "rgb(210, 140, 5)",
- color: "white",
- "& .MuiListItemIcon-root": {
- color: "white"
- }
- },
+ root: {
+ '&$selected': {
+ backgroundColor: 'rgb(210, 140, 5)',
+ color: 'white',
+ '& .MuiListItemIcon-root': {
+ color: 'white',
+ },
},
- selected: {}
+ '&$selected:hover': {
+ backgroundColor: 'rgb(210, 140, 5)',
+ color: 'white',
+ '& .MuiListItemIcon-root': {
+ color: 'white',
+ },
+ },
+ },
+ selected: {},
})(ListItemButton);
const StyleListItem = (props: ListItemProps) => {
- const icon = props.icon;
- const label = props.label;
- const filter = props.filter;
- const activeFilter = props.active;
- const onClick = props.onClick;
- const onDeleteLabel = props.onDelete;
- const isSelected =
- activeFilter &&
- activeFilter.type == filter.type &&
- (activeFilter.type != 'label' ||
- (activeFilter as LabelFilter).label == (filter as LabelFilter).label);
- const handleOnClick = (event: React.MouseEvent, filter: Filter) => {
- event.stopPropagation();
- onClick(filter);
- };
+ const icon = props.icon;
+ const label = props.label;
+ const filter = props.filter;
+ const activeFilter = props.active;
+ const onClick = props.onClick;
+ const onDeleteLabel = props.onDelete;
+ const isSelected =
+ activeFilter &&
+ activeFilter.type == filter.type &&
+ (activeFilter.type != 'label' ||
+ (activeFilter as LabelFilter).label == (filter as LabelFilter).label);
+ const handleOnClick = (event: React.MouseEvent, filter: Filter) => {
+ event.stopPropagation();
+ onClick(filter);
+ };
- const handleOnDelete = (
- event: React.MouseEvent,
- filter: Filter
- ) => {
- event.stopPropagation();
- if (!onDeleteLabel) {
- throw 'Illegal state exeption';
- }
- onDeleteLabel((filter as LabelFilter).label.id);
- };
+ const handleOnDelete = (
+ event: React.MouseEvent,
+ filter: Filter,
+ ) => {
+ event.stopPropagation();
+ if (!onDeleteLabel) {
+ throw 'Illegal state exeption';
+ }
+ onDeleteLabel((filter as LabelFilter).label.id);
+ };
- return (
- handleOnClick(e, filter)}>
- {icon}
-
- {filter.type == 'label' && (
-
- handleOnDelete(e, filter)}
- size="large">
-
-
-
- )}
-
- );
+ return (
+ handleOnClick(e, filter)}>
+ {icon}
+
+ {filter.type == 'label' && (
+
+ handleOnDelete(e, filter)}
+ size="large"
+ >
+
+
+
+ )}
+
+ );
};
export default MapsPage;