diff --git a/packages/editor/src/classes/bootstrap/BootstrapWidgetManager.ts b/packages/editor/src/classes/bootstrap/BootstrapWidgetManager.ts
new file mode 100644
index 00000000..d3ce35a1
--- /dev/null
+++ b/packages/editor/src/classes/bootstrap/BootstrapWidgetManager.ts
@@ -0,0 +1,90 @@
+import {
+ WidgetManager,
+ Topic,
+ LinkModel,
+ LinkIcon,
+ NoteModel,
+ NoteIcon,
+ $msg,
+} from '@wisemapping/mindplot';
+import LinkIconTooltip from './LinkIconTooltip';
+import LinkEditor from './LinkEditor';
+import FloatingTip from './FloatingTip';
+import NoteEditor from './NoteEditor';
+import $ from 'jquery';
+
+export default class BootstrapWidgetManager extends WidgetManager {
+ createTooltipForLink(topic: Topic, linkModel: LinkModel, linkIcon: LinkIcon) {
+ const htmlImage = linkIcon.getImage().peer;
+ const toolTip = new LinkIconTooltip(linkIcon);
+ linkIcon.addEvent('mouseleave', (event) => {
+ setTimeout(() => {
+ if (!$('#linkPopover:hover').length) {
+ toolTip.hide();
+ }
+ event.stopPropagation();
+ }, 100);
+ });
+ $(htmlImage._native).mouseenter(() => {
+ toolTip.show();
+ });
+ }
+
+ showEditorForLink(topic: Topic, linkModel: LinkModel, linkIcon: LinkIcon) {
+ const editorModel = {
+ getValue(): string {
+ return topic.getLinkValue();
+ },
+ setValue(value: string) {
+ topic.setLinkValue(value);
+ },
+ };
+ topic.closeEditors();
+ const editor = new LinkEditor(editorModel);
+ editor.show();
+ }
+
+ private _buildTooltipContentForNote(noteModel: NoteModel): JQuery {
+ if ($('body').find('#textPopoverNote').length === 1) {
+ const text = $('body').find('#textPopoverNote');
+ text.text(noteModel.getText());
+ return text;
+ }
+ const result = $('
').css({ padding: '5px' });
+
+ const text = $('').text(noteModel.getText()).css({
+ 'white-space': 'pre-wrap',
+ 'word-wrap': 'break-word',
+ });
+ result.append(text);
+ return result;
+ }
+
+ createTooltipForNote(topic: Topic, noteModel: NoteModel, noteIcon: NoteIcon) {
+ const htmlImage = noteIcon.getImage().peer;
+ const me = this;
+ const toolTip = new FloatingTip($(htmlImage._native), {
+ title: $msg('NOTE'),
+ content() {
+ return me._buildTooltipContentForNote(noteModel);
+ },
+ html: true,
+ placement: 'bottom',
+ destroyOnExit: true,
+ });
+ }
+
+ showEditorForNote(topic: Topic, noteModel: NoteModel, noteIcon: NoteIcon) {
+ const editorModel = {
+ getValue(): string {
+ return topic.getNoteValue();
+ },
+ setValue(value: string) {
+ topic.setNoteValue(value);
+ },
+ };
+ topic.closeEditors();
+ const editor = new NoteEditor(editorModel);
+ editor.show();
+ }
+}
diff --git a/packages/mindplot/src/components/widget/FloatingTip.ts b/packages/editor/src/classes/bootstrap/FloatingTip.ts
similarity index 98%
rename from packages/mindplot/src/components/widget/FloatingTip.ts
rename to packages/editor/src/classes/bootstrap/FloatingTip.ts
index e613737b..54abfd01 100644
--- a/packages/mindplot/src/components/widget/FloatingTip.ts
+++ b/packages/editor/src/classes/bootstrap/FloatingTip.ts
@@ -16,7 +16,7 @@
* limitations under the License.
*/
import merge from 'lodash/merge';
-import Events from '../Events';
+import Events from '../menu/Events';
const defaultOptions = {
animation: true,
diff --git a/packages/mindplot/src/components/widget/LinkEditor.ts b/packages/editor/src/classes/bootstrap/LinkEditor.ts
similarity index 98%
rename from packages/mindplot/src/components/widget/LinkEditor.ts
rename to packages/editor/src/classes/bootstrap/LinkEditor.ts
index 06c70e95..8316e532 100644
--- a/packages/mindplot/src/components/widget/LinkEditor.ts
+++ b/packages/editor/src/classes/bootstrap/LinkEditor.ts
@@ -17,8 +17,8 @@
*/
import $ from 'jquery';
import { $assert } from '@wisemapping/core-js';
-import { $msg } from '../Messages';
-import BootstrapDialog from './bootstrap/BootstrapDialog';
+import { $msg } from '@wisemapping/mindplot';
+import BootstrapDialog from './BootstrapDialog';
interface LinkEditorModel {
getValue(): string;
diff --git a/packages/mindplot/src/components/widget/LinkIconTooltip.ts b/packages/editor/src/classes/bootstrap/LinkIconTooltip.ts
similarity index 92%
rename from packages/mindplot/src/components/widget/LinkIconTooltip.ts
rename to packages/editor/src/classes/bootstrap/LinkIconTooltip.ts
index 31c1a215..047adcca 100644
--- a/packages/mindplot/src/components/widget/LinkIconTooltip.ts
+++ b/packages/editor/src/classes/bootstrap/LinkIconTooltip.ts
@@ -17,9 +17,9 @@
*/
import { $assert } from '@wisemapping/core-js';
import $ from 'jquery';
-import LinkIcon from '../LinkIcon';
-import LinkModel from '../model/LinkModel';
-import { $msg } from '../Messages';
+import { LinkIcon } from '@wisemapping/mindplot';
+import { LinkModel } from '@wisemapping/mindplot';
+import { $msg } from '@wisemapping/mindplot';
import FloatingTip from './FloatingTip';
class LinkIconTooltip extends FloatingTip {
@@ -56,7 +56,7 @@ class LinkIconTooltip extends FloatingTip {
target: '_blank',
});
- link.append(linkText);
+ link.html(linkText);
result.append(link);
return result;
}
diff --git a/packages/mindplot/src/components/widget/NoteEditor.ts b/packages/editor/src/classes/bootstrap/NoteEditor.ts
similarity index 98%
rename from packages/mindplot/src/components/widget/NoteEditor.ts
rename to packages/editor/src/classes/bootstrap/NoteEditor.ts
index b7691e49..765ce26d 100644
--- a/packages/mindplot/src/components/widget/NoteEditor.ts
+++ b/packages/editor/src/classes/bootstrap/NoteEditor.ts
@@ -18,7 +18,7 @@
import { $assert } from '@wisemapping/core-js';
import $ from 'jquery';
import BootstrapDialog from '../../../../editor/src/classes/bootstrap/BootstrapDialog';
-import { $msg } from '../Messages';
+import { $msg } from '@wisemapping/mindplot';
interface NoteEditorModel {
getValue(): string;
diff --git a/packages/editor/src/classes/menu/KeyboardShortcutTooltip.js b/packages/editor/src/classes/menu/KeyboardShortcutTooltip.js
index 58cdd83d..7c3ce051 100644
--- a/packages/editor/src/classes/menu/KeyboardShortcutTooltip.js
+++ b/packages/editor/src/classes/menu/KeyboardShortcutTooltip.js
@@ -17,7 +17,7 @@
*/
import $ from 'jquery';
import { $assert } from '@wisemapping/core-js';
-import FloatingTip from '@wisemapping/mindplot/src/components/widget/FloatingTip';
+import FloatingTip from '../bootstrap/FloatingTip';
class KeyboardShortcutTooltip extends FloatingTip {
constructor(buttonElem, text) {
diff --git a/packages/editor/src/classes/menu/ToolbarPaneItem.js b/packages/editor/src/classes/menu/ToolbarPaneItem.js
index 97c0ba37..4dbb54f7 100644
--- a/packages/editor/src/classes/menu/ToolbarPaneItem.js
+++ b/packages/editor/src/classes/menu/ToolbarPaneItem.js
@@ -17,7 +17,7 @@
*/
import { $assert } from '@wisemapping/core-js';
import ToolbarItem from './ToolbarItem';
-import FloatingTip from '@wisemapping/mindplot/src/components/widget/FloatingTip';
+import FloatingTip from '../bootstrap/FloatingTip';
class ToolbarPaneItem extends ToolbarItem {
constructor(buttonId, model, delayInit) {
diff --git a/packages/editor/src/global-styled.css b/packages/editor/src/global-styled.css
index 3d7950fb..81ba233a 100644
--- a/packages/editor/src/global-styled.css
+++ b/packages/editor/src/global-styled.css
@@ -22,21 +22,6 @@ body {
height: 100%;
}
-div#mindplot {
- position: relative;
- top: 50px;
- left: 0;
- width: 100%;
- height: 100%;
- border: 0;
- overflow: hidden;
- opacity: 1;
- background-color: #f2f2f2;
- background-image: linear-gradient(#ebe9e7 1px, transparent 1px),
- linear-gradient(to right, #ebe9e7 1px, #f2f2f2 1px);
- background-size: 50px 50px;
-}
-
.notesTip {
background-color: #dfcf3c;
padding: 5px 15px;
diff --git a/packages/editor/src/index.tsx b/packages/editor/src/index.tsx
index 37e4fd41..84b9f4ac 100644
--- a/packages/editor/src/index.tsx
+++ b/packages/editor/src/index.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
import Toolbar, { ToolbarActionType } from './components/toolbar';
import Footer from './components/footer';
import { IntlProvider } from 'react-intl';
@@ -10,10 +10,23 @@ import {
Designer,
DesignerKeyboard,
EditorRenderMode,
+ MindplotWebComponentInterface,
+ Mindmap,
+ MockPersistenceManager,
+ LocalStorageManager,
+ RESTPersistenceManager,
+ TextExporterFactory,
+ ImageExporterFactory,
+ Exporter,
+ Importer,
+ TextImporterFactory,
} from '@wisemapping/mindplot';
import './global-styled.css';
import I18nMsg from './classes/i18n-msg';
import Menu from './classes/menu/Menu';
+import BootstrapWidgetManager from './classes/bootstrap/BootstrapWidgetManager';
+
+require('../../../libraries/bootstrap/js/bootstrap.min');
declare global {
// used in mindplot
@@ -21,6 +34,14 @@ declare global {
var accountEmail: string;
}
+declare global {
+ namespace JSX {
+ interface IntrinsicElements {
+ ['mindplot-component']: MindplotWebComponentInterface;
+ }
+ }
+}
+
export type EditorOptions = {
mode: EditorRenderMode;
locale: string;
@@ -31,6 +52,23 @@ export type EditorOptions = {
enableKeyboardEvents: boolean;
};
+export {
+ PersistenceManager,
+ DesignerOptionsBuilder,
+ Designer,
+ DesignerKeyboard,
+ EditorRenderMode,
+ Mindmap,
+ MockPersistenceManager,
+ LocalStorageManager,
+ RESTPersistenceManager,
+ TextExporterFactory,
+ ImageExporterFactory,
+ Exporter,
+ Importer,
+ TextImporterFactory,
+};
+
export type EditorProps = {
mapId: string;
options: EditorOptions;
@@ -41,22 +79,21 @@ export type EditorProps = {
const Editor = ({ mapId, options, persistenceManager, onAction, onLoad }: EditorProps) => {
const [isMobile, setIsMobile] = useState(undefined);
+ const mindplotComponent: any = useRef();
useEffect(() => {
// Change page title ...
document.title = `${options.mapTitle} | WiseMapping `;
// Load mindmap ...
+
const designer = onLoadDesigner(mapId, options, persistenceManager);
// Has extended actions been customized ...
if (onLoad) {
onLoad(designer);
}
- // Load mindmap ...
- const instance = PersistenceManager.getInstance();
- const mindmap = instance.load(mapId);
- designer.loadMap(mindmap);
+ mindplotComponent.current.loadMap(mapId);
setIsMobile(checkMobile());
@@ -89,17 +126,10 @@ const Editor = ({ mapId, options, persistenceManager, onAction, onLoad }: Editor
options: EditorOptions,
persistenceManager: PersistenceManager,
): Designer => {
- const buildOptions = DesignerOptionsBuilder.buildOptions({
- persistenceManager,
- mode: options.mode,
- mapId: mapId,
- container: 'mindplot',
- zoom: options.zoom,
- locale: options.locale,
- });
+ mindplotComponent.current.buildDesigner(persistenceManager, new BootstrapWidgetManager());
// Build designer ...
- const result = buildDesigner(buildOptions);
+ const result = mindplotComponent.current && mindplotComponent.current.getDesigner();
// Register toolbar event ...
if (
@@ -132,7 +162,11 @@ const Editor = ({ mapId, options, persistenceManager, onAction, onLoad }: Editor
)}
-
+
diff --git a/packages/editor/test/playground/map-render/js/editor.tsx b/packages/editor/test/playground/map-render/js/editor.tsx
index 97f0ff52..4dc1037c 100644
--- a/packages/editor/test/playground/map-render/js/editor.tsx
+++ b/packages/editor/test/playground/map-render/js/editor.tsx
@@ -22,7 +22,7 @@ import { LocalStorageManager, Designer } from '@wisemapping/mindplot';
const initialization = (designer: Designer) => {
designer.addEvent('loadSuccess', () => {
- const elem = document.getElementById('mindplot');
+ const elem = document.getElementById('mindmap-comp');
if (elem) {
elem.classList.add('ready');
}
diff --git a/packages/mindplot/README.md b/packages/mindplot/README.md
index 28f360cb..fa6289d1 100644
--- a/packages/mindplot/README.md
+++ b/packages/mindplot/README.md
@@ -1,7 +1,78 @@
# WiseMapping Mindplot
-WiseMapping Mindplot module is the core mind map rerendering of WiseMapping. This lighway library allows eithe edition and visualization of saves mindmaps.
+WiseMapping Mindplot module is the core mind map rerendering of WiseMapping. This lighway library allows eithe edition and visualization of saved mindmaps.
## Usage
+A WebComponent implementation for mindplot designer is available.
+This component is registered as mindplot-component in customElements API. (see https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define)
+For use it you need to import minplot.js and put in your DOM a tag. In order to create a Designer on it you need to call its buildDesigner method. Maps can be loaded through loadMap method.
+
+#### Code example
+
+```
+
+
+
+
+
+
+
+
+
+
+```
+
+Optionaly you can use your own presistence manager and widget manager.
+If you don't have special requirements you can use the defaults.
+
+```
+var persistence = new LocalStorageManager(
+ 'map.xml',
+ false, false
+);
+var widget = new MyAwesomeWidgetManager();
+// then build the designer with these params
+webComponent.buildDesigner(persistence, widget);
+```
+
+## Usage with React framework
+
+To use the web component in your JSX code, first you need to register it in the IntrinsicElements interface using provided MindplotWebComponentInterface
+
+#### TypeScript example
+```
+import { MindplotWebComponentInterface } from '@wisemapping/mindplot';
+
+declare global {
+ namespace JSX {
+ interface IntrinsicElements {
+ ['mindplot-component']: MindplotWebComponentInterface;
+ }
+ }
+}
+
+const App = ()=>{
+ const mindplotComponent: any = useRef();
+
+ useEffect(()=>{
+ mindplotComponent.current.buildDesigner();
+ mindplotComponent.current.loadMap("map_id");
+ }, [])
+
+ return (
+
+
);
+}
+```
+
+
Check out the examples located in `test/playground/map-render/js` for some hints on high level usage. You can browse them by running `yarn playground`.
diff --git a/packages/mindplot/src/components/Designer.ts b/packages/mindplot/src/components/Designer.ts
index b812d77a..262307c8 100644
--- a/packages/mindplot/src/components/Designer.ts
+++ b/packages/mindplot/src/components/Designer.ts
@@ -56,6 +56,7 @@ import { DesignerOptions } from './DesignerOptionsBuilder';
import DragTopic from './DragTopic';
import CentralTopic from './CentralTopic';
import FeatureType from './model/FeatureType';
+import WidgetManager from './WidgetManager';
class Designer extends Events {
private _mindmap: Mindmap;
@@ -882,7 +883,8 @@ class Designer extends Events {
const model = this.getModel();
const topic = model.selectedTopic();
if (topic) {
- topic.showLinkEditor();
+ const manager = WidgetManager.getInstance();
+ manager.showEditorForLink(topic, null, null);
this.onObjectFocusEvent();
}
}
@@ -891,7 +893,8 @@ class Designer extends Events {
const model = this.getModel();
const topic = model.selectedTopic();
if (topic) {
- topic.showNoteEditor();
+ const manager = WidgetManager.getInstance();
+ manager.showEditorForNote(topic, null, null);
this.onObjectFocusEvent();
}
}
diff --git a/packages/mindplot/src/components/DesignerBuilder.ts b/packages/mindplot/src/components/DesignerBuilder.ts
index c5ce43aa..e8160a61 100644
--- a/packages/mindplot/src/components/DesignerBuilder.ts
+++ b/packages/mindplot/src/components/DesignerBuilder.ts
@@ -20,11 +20,12 @@ import $ from 'jquery';
import PersistenceManager from './PersistenceManager';
import Designer from './Designer';
import { DesignerOptions } from './DesignerOptionsBuilder';
+import WidgetManager from './WidgetManager';
let designer: Designer;
export function buildDesigner(options: DesignerOptions): Designer {
- const divContainer = $(`#${options.container}`);
+ const divContainer = options.divContainer ? $(options.divContainer) : $(`#${options.container}`);
$assert(divContainer, 'container could not be null');
// Register load events ...
@@ -34,7 +35,8 @@ export function buildDesigner(options: DesignerOptions): Designer {
const persistence = options.persistenceManager;
$assert(persistence, 'persistence must be defined');
PersistenceManager.init(persistence);
-
+ const widgetManager = options.widgetManager ? options.widgetManager : new WidgetManager();
+ WidgetManager.init(widgetManager);
return designer;
}
diff --git a/packages/mindplot/src/components/DesignerOptionsBuilder.ts b/packages/mindplot/src/components/DesignerOptionsBuilder.ts
index 26a2396c..41fde211 100644
--- a/packages/mindplot/src/components/DesignerOptionsBuilder.ts
+++ b/packages/mindplot/src/components/DesignerOptionsBuilder.ts
@@ -17,14 +17,17 @@
*/
import { $assert } from '@wisemapping/core-js';
import EditorRenderMode from './EditorRenderMode';
+import WidgetManager from './WidgetManager';
import PersistenceManager from './PersistenceManager';
export type DesignerOptions = {
zoom: number;
mode: EditorRenderMode;
mapId?: string;
+ divContainer?: HTMLElement;
container: string;
persistenceManager?: PersistenceManager;
+ widgetManager?: WidgetManager;
saveOnLoad?: boolean;
locale?: string;
};
diff --git a/packages/mindplot/src/components/LinkIcon.ts b/packages/mindplot/src/components/LinkIcon.ts
index c47bba87..1765f93b 100644
--- a/packages/mindplot/src/components/LinkIcon.ts
+++ b/packages/mindplot/src/components/LinkIcon.ts
@@ -16,13 +16,12 @@
* limitations under the License.
*/
import { $assert } from '@wisemapping/core-js';
-import $ from 'jquery';
import Icon from './Icon';
-import LinkIconTooltip from './widget/LinkIconTooltip';
import LinksImage from '../../assets/icons/links.svg';
import LinkModel from './model/LinkModel';
import Topic from './Topic';
import FeatureModel from './model/FeatureModel';
+import WidgetManager from './WidgetManager';
class LinkIcon extends Icon {
private _linksModel: FeatureModel;
@@ -31,8 +30,6 @@ class LinkIcon extends Icon {
private _readOnly: boolean;
- private _tip: LinkIconTooltip;
-
constructor(topic: Topic, linkModel: LinkModel, readOnly: boolean) {
$assert(topic, 'topic can not be null');
$assert(linkModel, 'linkModel can not be null');
@@ -47,30 +44,12 @@ class LinkIcon extends Icon {
private _registerEvents() {
this._image.setCursor('pointer');
- this._tip = new LinkIconTooltip(this);
- const me = this;
+ const manager = WidgetManager.getInstance();
+ manager.createTooltipForLink(this._topic, this._linksModel as LinkModel, this);
if (!this._readOnly) {
- // Add on click event to open the editor ...
- this.addEvent('click', (event) => {
- me._tip.hide();
- me._topic.showLinkEditor();
- event.stopPropagation();
- });
- // FIXME: we shouldn't have timeout of that..
- this.addEvent('mouseleave', (event) => {
- setTimeout(() => {
- if (!$('#linkPopover:hover').length) {
- me._tip.hide();
- }
- event.stopPropagation();
- }, 100);
- });
+ manager.configureEditorForLink(this._topic, this._linksModel as LinkModel, this);
}
-
- $(this.getImage().peer._native).mouseenter(() => {
- me._tip.show();
- });
}
getModel(): FeatureModel {
diff --git a/packages/mindplot/src/components/MindplotWebComponent.ts b/packages/mindplot/src/components/MindplotWebComponent.ts
new file mode 100644
index 00000000..3d03b000
--- /dev/null
+++ b/packages/mindplot/src/components/MindplotWebComponent.ts
@@ -0,0 +1,81 @@
+import Designer from './Designer';
+import buildDesigner from './DesignerBuilder';
+import DesignerOptionsBuilder from './DesignerOptionsBuilder';
+import EditorRenderMode from './EditorRenderMode';
+import LocalStorageManager from './LocalStorageManager';
+import Mindmap from './model/Mindmap';
+import PersistenceManager from './PersistenceManager';
+import WidgetManager from './WidgetManager';
+import mindplotStyles from './styles/mindplot-styles';
+
+const defaultPersistenceManager = () => new LocalStorageManager('map.xml', false, false);
+
+export type MindplotWebComponentInterface = {
+ id: string;
+ mode: string;
+ ref: any;
+};
+/**
+ * WebComponent implementation for minplot designer.
+ * This component is registered as mindplot-component in customElements api. (see https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define)
+ * For use it you need to import minplot.js and put in your DOM a tag. In order to create a Designer on it you need to call its buildDesigner method. Maps can be loaded throught loadMap method.
+ */
+class MindplotWebComponent extends HTMLElement {
+ private _shadowRoot: ShadowRoot;
+
+ private _mindmap: Mindmap;
+
+ private _designer: Designer;
+
+ constructor() {
+ super();
+ this._shadowRoot = this.attachShadow({ mode: 'open' });
+ const mindplotStylesElement = document.createElement('style');
+ mindplotStylesElement.innerHTML = mindplotStyles;
+ this._shadowRoot.appendChild(mindplotStylesElement);
+ const wrapper = document.createElement('div');
+ wrapper.setAttribute('class', 'wise-editor');
+ wrapper.setAttribute('id', 'mindplot');
+ this._shadowRoot.appendChild(wrapper);
+ }
+
+ /**
+ * @returns the designer
+ */
+ getDesigner(): Designer {
+ return this._designer;
+ }
+
+ /**
+ * Build the designer of the component
+ * @param {PersistenceManager} persistence the persistence manager to be used. By default a LocalStorageManager is created
+ * @param {UIManager} widgetManager an UI Manager to override default Designer option.
+ */
+ buildDesigner(persistence?: PersistenceManager, widgetManager?: WidgetManager) {
+ const editorRenderMode = this.getAttribute('mode') as EditorRenderMode;
+ const persistenceManager = persistence || defaultPersistenceManager();
+ const mode = editorRenderMode || 'viewonly';
+ const options = DesignerOptionsBuilder.buildOptions({
+ persistenceManager,
+ mode,
+ widgetManager,
+ divContainer: this._shadowRoot.getElementById('mindplot'),
+ container: 'mindplot',
+ zoom: 0.85,
+ locale: 'en',
+ });
+ this._designer = buildDesigner(options);
+ }
+
+ /**
+ * Load map in designer throught persistence manager instance
+ * @param id the map id to be loaded.
+ */
+ loadMap(id: string) {
+ const instance = PersistenceManager.getInstance();
+ this._mindmap = instance.load(id);
+ this._designer.loadMap(this._mindmap);
+ }
+}
+
+export default MindplotWebComponent;
diff --git a/packages/mindplot/src/components/NoteIcon.ts b/packages/mindplot/src/components/NoteIcon.ts
index 173e82ff..9d030714 100644
--- a/packages/mindplot/src/components/NoteIcon.ts
+++ b/packages/mindplot/src/components/NoteIcon.ts
@@ -16,14 +16,12 @@
* limitations under the License.
*/
import { $assert } from '@wisemapping/core-js';
-import $ from 'jquery';
-import { $msg } from './Messages';
import Icon from './Icon';
import NotesImage from '../../assets/icons/notes.svg';
import Topic from './Topic';
import NoteModel from './model/NoteModel';
import FeatureModel from './model/FeatureModel';
-import FloatingTip from './widget/FloatingTip';
+import WidgetManager from './WidgetManager';
class NoteIcon extends Icon {
private _linksModel: NoteModel;
@@ -32,8 +30,6 @@ class NoteIcon extends Icon {
private _readOnly: boolean;
- private _tip: FloatingTip;
-
constructor(topic: Topic, noteModel: NoteModel, readOnly: boolean) {
$assert(topic, 'topic can not be null');
@@ -47,42 +43,12 @@ class NoteIcon extends Icon {
private _registerEvents(): void {
this._image.setCursor('pointer');
- const me = this;
+ const manager = WidgetManager.getInstance();
+ manager.createTooltipForNote(this._topic, this._linksModel as NoteModel, this);
if (!this._readOnly) {
- // Add on click event to open the editor ...
- this.addEvent('click', (event) => {
- me._topic.showNoteEditor();
- event.stopPropagation();
- });
+ manager.configureEditorForNote(this._topic, this._linksModel as NoteModel, this);
}
-
- this._tip = new FloatingTip($(me.getImage().peer._native), {
- title: $msg('NOTE'),
- // Content can also be a function of the target element!
- content() {
- return me._buildTooltipContent();
- },
- html: true,
- placement: 'bottom',
- destroyOnExit: true,
- });
- }
-
- private _buildTooltipContent(): JQuery {
- if ($('body').find('#textPopoverNote').length === 1) {
- const text = $('body').find('#textPopoverNote');
- text.text(this._linksModel.getText());
- return text;
- }
- const result = $('').css({ padding: '5px' });
-
- const text = $('').text(this._linksModel.getText()).css({
- 'white-space': 'pre-wrap',
- 'word-wrap': 'break-word',
- });
- result.append(text);
- return result;
}
getModel(): FeatureModel {
diff --git a/packages/mindplot/src/components/Topic.ts b/packages/mindplot/src/components/Topic.ts
index 5ea3db1e..6ee96713 100644
--- a/packages/mindplot/src/components/Topic.ts
+++ b/packages/mindplot/src/components/Topic.ts
@@ -28,9 +28,7 @@ import ConnectionLine from './ConnectionLine';
import IconGroup from './IconGroup';
import EventBus from './layout/EventBus';
import ShirinkConnector from './ShrinkConnector';
-import NoteEditor from './widget/NoteEditor';
import ActionDispatcher from './ActionDispatcher';
-import LinkEditor from './widget/LinkEditor';
import TopicEventDispatcher, { TopicEvent } from './TopicEventDispatcher';
import { TopicShape } from './model/INodeModel';
@@ -732,79 +730,65 @@ abstract class Topic extends NodeGraph {
});
}
- showNoteEditor(): void {
- const topicId = this.getId();
+ getNoteValue(): string {
const model = this.getModel();
- const editorModel = {
- getValue(): string {
- const notes = model.findFeatureByType(TopicFeatureFactory.Note.id);
- let result;
- if (notes.length > 0) {
- result = (notes[0] as NoteModel).getText();
- }
+ const notes = model.findFeatureByType(TopicFeatureFactory.Note.id);
+ let result;
+ if (notes.length > 0) {
+ result = (notes[0] as NoteModel).getText();
+ }
- return result;
- },
-
- setValue(value: string) {
- const dispatcher = ActionDispatcher.getInstance();
- const notes = model.findFeatureByType(TopicFeatureFactory.Note.id);
- if (!$defined(value)) {
- const featureId = notes[0].getId();
- dispatcher.removeFeatureFromTopic(topicId, featureId);
- } else if (notes.length > 0) {
- dispatcher.changeFeatureToTopic(topicId, notes[0].getId(), {
- text: value,
- });
- } else {
- dispatcher.addFeatureToTopic(topicId, TopicFeatureFactory.Note.id, {
- text: value,
- });
- }
- },
- };
- const editor = new NoteEditor(editorModel);
- this.closeEditors();
- editor.show();
+ return result;
}
- /** opens a dialog where the user can enter or edit an existing link associated with this topic */
- showLinkEditor() {
+ setNoteValue(value: string) {
const topicId = this.getId();
const model = this.getModel();
- const editorModel = {
- getValue(): string {
- // @param {mindplot.model.LinkModel[]} links
- const links = model.findFeatureByType(TopicFeatureFactory.Link.id);
- let result;
- if (links.length > 0) {
- result = (links[0] as LinkModel).getUrl();
- }
+ const dispatcher = ActionDispatcher.getInstance();
+ const notes = model.findFeatureByType(TopicFeatureFactory.Note.id);
+ if (!$defined(value)) {
+ const featureId = notes[0].getId();
+ dispatcher.removeFeatureFromTopic(topicId, featureId);
+ } else if (notes.length > 0) {
+ dispatcher.changeFeatureToTopic(topicId, notes[0].getId(), {
+ text: value,
+ });
+ } else {
+ dispatcher.addFeatureToTopic(topicId, TopicFeatureFactory.Note.id, {
+ text: value,
+ });
+ }
+ }
- return result;
- },
+ getLinkValue(): string {
+ const model = this.getModel();
+ // @param {mindplot.model.LinkModel[]} links
+ const links = model.findFeatureByType(TopicFeatureFactory.Link.id);
+ let result;
+ if (links.length > 0) {
+ result = (links[0] as LinkModel).getUrl();
+ }
- setValue(value: string) {
- const dispatcher = ActionDispatcher.getInstance();
- const links = model.findFeatureByType(TopicFeatureFactory.Link.id);
- if (!$defined(value)) {
- const featureId = links[0].getId();
- dispatcher.removeFeatureFromTopic(topicId, featureId);
- } else if (links.length > 0) {
- dispatcher.changeFeatureToTopic(topicId, links[0].getId(), {
- url: value,
- });
- } else {
- dispatcher.addFeatureToTopic(topicId, TopicFeatureFactory.Link.id, {
- url: value,
- });
- }
- },
- };
+ return result;
+ }
- this.closeEditors();
- const editor = new LinkEditor(editorModel);
- editor.show();
+ setLinkValue(value: string) {
+ const topicId = this.getId();
+ const model = this.getModel();
+ const dispatcher = ActionDispatcher.getInstance();
+ const links = model.findFeatureByType(TopicFeatureFactory.Link.id);
+ if (!$defined(value)) {
+ const featureId = links[0].getId();
+ dispatcher.removeFeatureFromTopic(topicId, featureId);
+ } else if (links.length > 0) {
+ dispatcher.changeFeatureToTopic(topicId, links[0].getId(), {
+ url: value,
+ });
+ } else {
+ dispatcher.addFeatureToTopic(topicId, TopicFeatureFactory.Link.id, {
+ url: value,
+ });
+ }
}
closeEditors() {
diff --git a/packages/mindplot/src/components/WidgetManager.ts b/packages/mindplot/src/components/WidgetManager.ts
new file mode 100644
index 00000000..983240b2
--- /dev/null
+++ b/packages/mindplot/src/components/WidgetManager.ts
@@ -0,0 +1,107 @@
+import $ from 'jquery';
+import LinkIcon from './LinkIcon';
+import LinkModel from './model/LinkModel';
+import NoteModel from './model/NoteModel';
+import NoteIcon from './NoteIcon';
+import Topic from './Topic';
+import { $msg } from './Messages';
+
+class WidgetManager {
+ // eslint-disable-next-line no-use-before-define
+ static _instance: WidgetManager;
+
+ static init = (instance: WidgetManager) => {
+ this._instance = instance;
+ };
+
+ static getInstance(): WidgetManager {
+ return this._instance;
+ }
+
+ private createTooltip(mindmapElement, title, linkModel: LinkModel, noteModel: NoteModel) {
+ const webcomponentShadowRoot = $($('#mindmap-comp')[0].shadowRoot);
+ let tooltip = webcomponentShadowRoot.find('#mindplot-svg-tooltip');
+ if (!tooltip.length) {
+ webcomponentShadowRoot.append(
+ '',
+ );
+ tooltip = webcomponentShadowRoot.find('#mindplot-svg-tooltip');
+
+ tooltip.on('mouseover', (evt) => {
+ tooltip.css({ display: 'block' });
+ evt.stopPropagation();
+ });
+ tooltip.on('mouseleave', (evt) => {
+ tooltip.css({ display: 'none' });
+ evt.stopPropagation();
+ });
+ }
+
+ mindmapElement.addEvent('mouseenter', (evt) => {
+ webcomponentShadowRoot.find('#mindplot-svg-tooltip-title').html(title);
+ if (linkModel) {
+ webcomponentShadowRoot
+ .find('#mindplot-svg-tooltip-content-link')
+ .attr('href', linkModel.getUrl());
+ webcomponentShadowRoot.find('#mindplot-svg-tooltip-content-link').html(linkModel.getUrl());
+ webcomponentShadowRoot.find('#mindplot-svg-tooltip-content-link').css({ display: 'block' });
+ webcomponentShadowRoot.find('#mindplot-svg-tooltip-content-note').css({ display: 'none' });
+ }
+ if (noteModel) {
+ webcomponentShadowRoot.find('#mindplot-svg-tooltip-content-note').html(noteModel.getText());
+ webcomponentShadowRoot.find('#mindplot-svg-tooltip-content-note').css({ display: 'block' });
+ webcomponentShadowRoot.find('#mindplot-svg-tooltip-content-link').css({ display: 'none' });
+ }
+ const targetRect = evt.target.getBoundingClientRect();
+ const newX = Math.max(0, targetRect.left + targetRect.width / 2 - tooltip.width() / 2);
+ const newY = Math.max(0, targetRect.bottom + 10);
+ tooltip.css({ top: newY, left: newX, position: 'absolute' });
+ tooltip.css({ display: 'block' });
+ evt.stopPropagation();
+ });
+ mindmapElement.addEvent('mouseleave', (evt) => {
+ tooltip.css({ display: 'none' });
+ evt.stopPropagation();
+ });
+ }
+
+ createTooltipForLink(topic: Topic, linkModel: LinkModel, linkIcon: LinkIcon) {
+ this.createTooltip(linkIcon.getImage().peer, $msg('LINK'), linkModel, undefined);
+ }
+
+ createTooltipForNote(topic: Topic, noteModel: NoteModel, noteIcon: NoteIcon) {
+ this.createTooltip(noteIcon.getImage().peer, $msg('NOTE'), undefined, noteModel);
+ }
+
+ configureEditorForLink(topic: Topic, linkModel: LinkModel, linkIcon: LinkIcon) {
+ const htmlImage = linkIcon.getImage().peer;
+ htmlImage.addEvent('click', (evt) => {
+ this.showEditorForLink(topic, linkModel, linkIcon);
+ evt.stopPropagation();
+ });
+ }
+
+ configureEditorForNote(topic: Topic, noteModel: NoteModel, noteIcon: NoteIcon) {
+ const htmlImage = noteIcon.getImage().peer;
+ htmlImage.addEvent('click', (evt) => {
+ this.showEditorForNote(topic, noteModel, noteIcon);
+ evt.stopPropagation();
+ });
+ }
+
+ showEditorForLink(topic: Topic, linkModel: LinkModel, linkIcon: LinkIcon) {
+ console.log('Show link editor not yet implemented');
+ }
+
+ showEditorForNote(topic: Topic, noteModel: NoteModel, noteIcon: NoteIcon) {
+ console.log('Show note editor not yet implemented');
+ }
+}
+
+export default WidgetManager;
diff --git a/packages/mindplot/src/components/Workspace.ts b/packages/mindplot/src/components/Workspace.ts
index 74b4a131..3d1d3c31 100644
--- a/packages/mindplot/src/components/Workspace.ts
+++ b/packages/mindplot/src/components/Workspace.ts
@@ -223,7 +223,9 @@ class Workspace {
// Change cursor.
window.document.body.style.cursor = 'move';
- mouseMoveEvent.preventDefault();
+ // If I dont ignore touchmove events, browser console shows a lot of errors:
+ // Unable to preventDefault inside passive event listener invocation.
+ if (mouseMoveEvent.type !== 'touchmove') mouseMoveEvent.preventDefault();
// Fire drag event ...
screenManager.fireEvent('update');
diff --git a/packages/mindplot/src/components/styles/mindplot-styles.js b/packages/mindplot/src/components/styles/mindplot-styles.js
new file mode 100644
index 00000000..d5e26f73
--- /dev/null
+++ b/packages/mindplot/src/components/styles/mindplot-styles.js
@@ -0,0 +1,132 @@
+const mindplotStyles = `
+
+ div#mindplot {
+ position: relative;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ border: 0;
+ overflow: hidden;
+ opacity: 1;
+ background-color: #f2f2f2;
+ background-image: linear-gradient(#ebe9e7 1px, transparent 1px),
+ linear-gradient(to right, #ebe9e7 1px, #f2f2f2 1px);
+ background-size: 50px 50px;
+ }
+
+ .mindplot-svg-tooltip {
+ display: none;
+ color: rgb(51, 51, 51);
+ text-align: center;
+ padding: 1px;
+ border-radius: 6px;
+ position: absolute;
+ z-index: 999;
+ background-color: rgb(255, 255, 255);
+ animation: fadeIn 0.4s;
+ }
+
+ .fade-in {
+ animation: fadeIn ease 0.4s;
+ -webkit-animation: fadeIn ease 0.4s;
+ -moz-animation: fadeIn ease 0.4s;
+ -o-animation: fadeIn ease 0.4s;
+ -ms-animation: fadeIn ease 0.4s;
+ }
+
+ @keyframes fadeIn {
+ 0% { opacity: 0; }
+ 100% { opacity: 1; }
+ }
+
+ @-moz-keyframes fadeIn {
+ 0% { opacity: 0; }
+ 100% { opacity: 1; }
+ }
+
+ @-webkit-keyframes fadeIn {
+ 0% { opacity: 0; }
+ 100% { opacity: 1; }
+ }
+
+ @-o-keyframes fadeIn {
+ 0% { opacity: 0; }
+ 100% { opacity: 1; }
+ }
+
+ @-ms-keyframes fadeIn {
+ 0% { opacity: 0; }
+ 100% { opacity: 1; }
+ }
+
+.mindplot-svg-tooltip-title {
+ background-color: rgb(247, 247, 247);
+ border-bottom-color: rgb(235, 235, 235);
+ border-bottom-left-radius: 0px;
+ border-bottom-right-radius: 0px;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ border-top-left-radius: 5px;
+ border-top-right-radius: 5px;
+ box-sizing: border-box;
+ color: rgb(51, 51, 51);
+ cursor: default;
+ display: block;
+ padding: 8px 14px;
+ text-align: left;
+ font-family: Arial;
+ font-size: small;
+}
+
+.mindplot-svg-tooltip-content {
+ background-color: rgb(255, 255, 255);
+ padding: 6px 4px;
+ max-width: 250px;
+}
+
+.mindplot-svg-tooltip-content-link {
+ padding: 3px 5px;
+ overflow: hidden;
+ font-size: smaller;
+ text-decoration: none;
+ font-family: Arial;
+ font-size: small;
+ color: #428bca;
+}
+
+.mindplot-svg-tooltip-content-note {
+ text-align: left;
+ font-family: Arial;
+ font-size: small;
+}
+
+.mindplot-svg-tooltip:before {
+ content: "";
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-left: 10px solid transparent;
+ border-right: 10px solid transparent;
+ border-bottom: 10px solid #fff;
+ bottom: 100%;
+ right: 50%;
+ transform: translateX(50%);
+ z-index: 5;
+ }
+
+ .mindplot-svg-tooltip:after {
+ content: "";
+ display: block;
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-left: 10px solid transparent;
+ border-right: 10px solid transparent;
+ border-bottom: 10px solid rgb(247, 247, 247);
+ bottom: calc(1px + 100%);
+ right: 50%;
+ transform: translateX(50%);
+ }
+`;
+
+export default mindplotStyles;
diff --git a/packages/mindplot/src/components/widget/bootstrap/BootstrapDialog.js b/packages/mindplot/src/components/widget/bootstrap/BootstrapDialog.js
deleted file mode 100644
index 693915cc..00000000
--- a/packages/mindplot/src/components/widget/bootstrap/BootstrapDialog.js
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright [2021] [wisemapping]
- *
- * Licensed under WiseMapping Public License, Version 1.0 (the "License").
- * It is basically the Apache License, Version 2.0 (the "License") plus the
- * "powered by wisemapping" text requirement on every single page;
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the license at
- *
- * http://www.wisemapping.org/license
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import $ from 'jquery';
-import Options from '../../Options';
-import { $msg } from '../../Messages';
-
-class BootstrapDialog extends Options {
- constructor(title, options) {
- super();
- this.options = {
- cancelButton: false,
- closeButton: false,
- acceptButton: true,
- removeButton: false,
- errorMessage: false,
- onEventData: {},
- };
-
- this.setOptions(options);
- this.options.onEventData.dialog = this;
- this._native = $('').append(
- '',
- );
- const content = $('');
- const header = this._buildHeader(title);
-
- if (header) {
- content.append(header);
- }
- const body = $('');
- if (this.options.errorMessage) {
- const error = $('');
- error.hide();
- body.append(error);
- }
- content.append(body);
- const footer = this._buildFooter();
- if (footer) {
- content.append(footer);
- }
- this._native.find('.modal-dialog').append(content);
- this._native.on('hidden.bs.modal', function remove() {
- $(this).remove();
- });
- this._native.on('shown.bs.modal', this.onDialogShown);
-
- this._native.appendTo('#mindplot-tooltips');
- }
-
- _buildFooter() {
- let footer = null;
- if (this.options.acceptButton || this.options.removeButton || this.options.cancelButton) {
- footer = $('