Add storybook theme tests

This commit is contained in:
Paulo Gustavo Veiga 2023-02-17 13:25:57 -08:00
parent c1d81cd074
commit 4fc015c6dc
25 changed files with 400 additions and 146 deletions

View File

@ -0,0 +1,11 @@
context('Connection suite', () => {
it('classic theme', () => {
cy.visit('/iframe.html?args=&id=mindplot-connection--classic&viewMode=story');
cy.matchImageSnapshot('connection-classic');
});
it('classic prism', () => {
cy.visit('/iframe.html?args=&id=mindplot-connection--prism&viewMode=story');
cy.matchImageSnapshot('connection-prism');
});
});

View File

@ -33,4 +33,8 @@ context('Topic suite', () => {
cy.visit('/iframe.html?args=&id=mindplot-topic--shape-ellipse&viewMode=story');
cy.matchImageSnapshot('topic-shape-ellipse');
});
it('topic none line', () => {
cy.visit('/iframe.html?args=&id=mindplot-topic--shape-none&viewMode=story');
cy.matchImageSnapshot('topic-shape-none');
});
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -21,13 +21,14 @@ import { $assert } from '@wisemapping/core-js';
import { Mindmap } from '..';
import CommandContext from './CommandContext';
import { PivotType } from './RelationshipControlPoints';
import Events from './Events';
import EventDispispatcher from './EventDispatcher';
import NodeModel from './model/NodeModel';
import RelationshipModel from './model/RelationshipModel';
import Topic from './Topic';
import PositionType from './PositionType';
import EventBusType from './EventBusType';
abstract class ActionDispatcher extends Events {
abstract class ActionDispatcher extends EventDispispatcher<EventBusType> {
private static _instance: ActionDispatcher;
private _commandContext: CommandContext;

View File

@ -20,7 +20,7 @@ import $ from 'jquery';
import { $assert, $defined } from '@wisemapping/core-js';
import Messages, { $msg } from './Messages';
import Events from './Events';
import EventDispispatcher from './EventDispatcher';
import StandaloneActionDispatcher from './StandaloneActionDispatcher';
import CommandContext from './CommandContext';
@ -37,7 +37,7 @@ import DragManager from './DragManager';
import RelationshipPivot from './RelationshipPivot';
import Relationship from './Relationship';
import TopicEventDispatcher, { TopicEvent } from './TopicEventDispatcher';
import TopicEventDispatcher from './TopicEventDispatcher';
import TopicFactory from './TopicFactory';
import EventBus from './layout/EventBus';
@ -62,8 +62,11 @@ import ImageExpoterFactory from './export/ImageExporterFactory';
import PositionType from './PositionType';
import ThemeType from './model/ThemeType';
import ThemeFactory from './theme/ThemeFactory';
import ChangeEvent from './layout/ChangeEvent';
class Designer extends Events {
type DesignerEventType = 'modelUpdate' | 'onfocus' | 'onblur' | 'loadSuccess';
class Designer extends EventDispispatcher<DesignerEventType> {
private _mindmap: Mindmap | null;
private _options: DesignerOptions;
@ -159,15 +162,10 @@ class Designer extends Events {
return this._actionDispatcher;
}
addEvent(type: string, listener): Events {
if (type === TopicEvent.EDIT || type === TopicEvent.CLICK) {
const editor = TopicEventDispatcher.getInstance();
editor.addEvent(type, listener);
} else {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
addEvent(type: DesignerEventType, listener: (event: (args?: any) => void) => void): void {
super.addEvent(type, listener);
}
return this;
}
private _registerMouseEvents() {
const workspace = this._canvas;
@ -619,7 +617,8 @@ class Designer extends Events {
// Init layout manager ...
const size = { width: 25, height: 25 };
const layoutManager = new LayoutManager(mindmap.getCentralTopic().getId(), size);
layoutManager.addEvent('change', (event) => {
layoutManager.addEvent('change', (event: ChangeEvent) => {
const id = event.getId();
const topic = this.getModel().findTopicById(id);
if (topic) {
@ -627,6 +626,7 @@ class Designer extends Events {
topic.setOrder(event.getOrder());
}
});
this._eventBussDispatcher.setLayoutManager(layoutManager);
// Building node graph ...
@ -652,6 +652,7 @@ class Designer extends Events {
// Enable workspace drag events ...
this._canvas.registerEvents();
// Finally, sort the map ...
EventBus.instance.fireEvent('forceLayout');
this.fireEvent('loadSuccess');

View File

@ -18,12 +18,11 @@
import { $assert } from '@wisemapping/core-js';
import CentralTopic from './CentralTopic';
import { DesignerOptions } from './DesignerOptionsBuilder';
import Events from './Events';
import Relationship from './Relationship';
import Topic from './Topic';
import { $notify } from './model/ToolbarNotifier';
class DesignerModel extends Events {
class DesignerModel {
private _zoom: number;
private _topics: Topic[];
@ -31,7 +30,6 @@ class DesignerModel extends Events {
private _relationships: Relationship[];
constructor(options: DesignerOptions) {
super();
this._zoom = options.zoom;
this._topics = [];
this._relationships = [];

View File

@ -0,0 +1,30 @@
/*
* 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.
*/
export type EventBusType =
| 'topicResize'
| 'topicMoved'
| 'forceLayout'
| 'childShrinked'
| 'topicConnected'
| 'topicAdded'
| 'topicRemoved'
| 'topicDisconect'
| 'modelUpdate';
export default EventBusType;

View File

@ -0,0 +1,73 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/*
* 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.
*/
class EventDispispatcher<T> {
private _handlerByType: Map<T, ((args?: any) => void)[]>;
constructor() {
this._handlerByType = new Map();
}
private static _normalizeEventName<K>(value: K): K {
return String(value).replace(/^on([A-Z])/, (_full, first) => first.toLowerCase()) as K;
}
addEvent(typeName: T, fn: (args?: any) => void, internal?: boolean): void {
const type = EventDispispatcher._normalizeEventName(typeName);
let events = this._handlerByType.get(type);
// Add function had not been added yet
events = events || [];
if (events && !events.includes(fn)) {
events.push(fn);
this._handlerByType.set(type, events);
}
// Mark reference ...
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
fn.internal = Boolean(internal);
}
fireEvent(typeName: T, arg?: any): void {
const type = EventDispispatcher._normalizeEventName(typeName);
const events = this._handlerByType.get(type);
if (events) {
const args: any = arg ? [arg] : [];
events.forEach((fn) => {
fn.apply(this, args);
});
}
}
removeEvent(typeName: T, fn: (...args: any) => void): void {
const type = EventDispispatcher._normalizeEventName(typeName);
const events = this._handlerByType.get(type);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (events && !fn.internal) {
const index = events.indexOf(fn);
if (index !== -1) {
events.splice(index, 1);
}
}
}
}
export default EventDispispatcher;

View File

@ -1,69 +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.
*/
class Events {
protected _handlerByType;
constructor() {
this._handlerByType = {};
}
static _normalizeEventName(string: string) {
return string.replace(/^on([A-Z])/, (_full, first) => first.toLowerCase());
}
addEvent(typeName: string, fn?, internal?: boolean): Events {
const type = Events._normalizeEventName(typeName);
// Add function had not been added yet
const funByType = this._handlerByType[type] ? this._handlerByType[type] : [];
if (!funByType.includes(fn)) {
funByType.push(fn);
this._handlerByType[type] = funByType;
}
// Mark reference ...
fn.internal = Boolean(internal);
return this;
}
fireEvent(typeName: string, eventArgs?): Events {
const type = Events._normalizeEventName(typeName);
const events = this._handlerByType[type];
if (!events) return this;
const args = Array.isArray(eventArgs) ? eventArgs : [eventArgs];
events.forEach((fn) => {
// Execute our of the main thread...
fn.apply(this, args);
});
return this;
}
removeEvent(typeName: string, fn?): Events {
const type = Events._normalizeEventName(typeName);
const events = this._handlerByType[type];
if (events && !fn.internal) {
const index = events.indexOf(fn);
if (index !== -1) events.splice(index, 1);
}
return this;
}
}
export default Events;

View File

@ -20,11 +20,13 @@ import { FontStyle } from '@wisemapping/web2d/src/components/peer/svg/FontPeer';
import $ from 'jquery';
import ActionDispatcher from './ActionDispatcher';
import Events from './Events';
import EventDispatcher from './EventDispatcher';
import EventBus from './layout/EventBus';
import Topic from './Topic';
class EditorComponent extends Events {
type EditorEventType = 'input';
class EditorComponent extends EventDispatcher<EditorEventType> {
private _topic: Topic;
private _oldText: string | undefined;

View File

@ -55,7 +55,7 @@ class StandaloneActionDispatcher extends ActionDispatcher {
this._actionRunner = new DesignerActionRunner(commandContext, this);
}
addTopics(models: NodeModel[], parentTopicsId: number[] | null) {
addTopics(models: NodeModel[], parentTopicsId: number[] | null): void {
const command = new AddTopicCommand(models, parentTopicsId);
this.execute(command);
}

View File

@ -27,7 +27,7 @@ import EventBus from './layout/EventBus';
import ShirinkConnector from './ShrinkConnector';
import ActionDispatcher from './ActionDispatcher';
import TopicEventDispatcher, { TopicEvent } from './TopicEventDispatcher';
import TopicEventDispatcher from './TopicEventDispatcher';
import { TopicShapeType } from './model/INodeModel';
import NodeModel from './model/NodeModel';
import Relationship from './Relationship';
@ -528,7 +528,7 @@ abstract class Topic extends NodeGraph {
}
const eventDispatcher = me._getTopicEventDispatcher();
eventDispatcher.process(TopicEvent.CLICK, me);
eventDispatcher.process('clicknode', me);
event.stopPropagation();
});
}
@ -594,7 +594,7 @@ abstract class Topic extends NodeGraph {
getShrinkConnector(): ShirinkConnector | null {
let result = this._connector;
if (this._connector == null) {
if (!this._connector) {
this._connector = new ShirinkConnector(this);
this._connector.setVisibility(false);
result = this._connector;
@ -693,11 +693,8 @@ abstract class Topic extends NodeGraph {
* Point: references the center of the rect shape.!!!
*/
setPosition(point: PositionType): void {
$assert(point, 'position can not be null');
// allowed param reassign to avoid risks of existing code relying in this side-effect
// eslint-disable-next-line no-param-reassign
point.x = Math.ceil(point.x);
// eslint-disable-next-line no-param-reassign
point.y = Math.ceil(point.y);
// Update model's position ...
@ -721,7 +718,6 @@ abstract class Topic extends NodeGraph {
this.invariant();
}
/** */
getOutgoingLine(): ConnectionLine | null {
return this._outgoingLine;
}
@ -959,7 +955,7 @@ abstract class Topic extends NodeGraph {
this.redraw();
}
connectTo(targetTopic: Topic, workspace: Canvas): void {
connectTo(targetTopic: Topic, canvas: Canvas): void {
// Connect Graphical Nodes ...
targetTopic.append(this);
this._parent = targetTopic;
@ -971,10 +967,10 @@ abstract class Topic extends NodeGraph {
// Create a connection line ...
const outgoingLine = this.createConnectionLine(targetTopic);
outgoingLine.setVisibility(false);
// outgoingLine.setVisibility(false);
this._outgoingLine = outgoingLine;
workspace.append(outgoingLine);
canvas.append(outgoingLine);
// Update figure is necessary.
this.updateTopicShape(targetTopic);

View File

@ -15,20 +15,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Events from './Events';
import EventDispispatcher from './EventDispatcher';
import Topic from './Topic';
import MultitTextEditor from './MultilineTextEditor';
const TopicEvent = {
EDIT: 'editnode',
CLICK: 'clicknode',
};
type TopicEventType = 'editnode' | 'clicknode';
class TopicEventDispatcher extends Events {
class TopicEventDispatcher extends EventDispispatcher<TopicEventType> {
private _readOnly: boolean;
// eslint-disable-next-line no-use-before-define
static _instance: TopicEventDispatcher;
private static _instance: TopicEventDispatcher;
constructor(readOnly: boolean) {
super();
@ -43,10 +40,10 @@ class TopicEventDispatcher extends Events {
}
show(topic: Topic, textOverwrite?: string): void {
this.process(TopicEvent.EDIT, topic, textOverwrite);
this.process('editnode', topic, textOverwrite);
}
process(eventType: string, topic: Topic, textOverwrite?: string): void {
process(eventType: TopicEventType, topic: Topic, textOverwrite?: string): void {
// Close all previous open editor ....
const editor = MultitTextEditor.getInstance();
if (editor.isActive()) {
@ -55,7 +52,7 @@ class TopicEventDispatcher extends Events {
// Open the new editor ...
const model = topic.getModel();
if (!this._readOnly && eventType === TopicEvent.EDIT) {
if (!this._readOnly && eventType === 'editnode') {
editor.show(topic, textOverwrite);
} else {
this.fireEvent(eventType, { model, readOnly: this._readOnly });
@ -66,8 +63,13 @@ class TopicEventDispatcher extends Events {
return MultitTextEditor.getInstance().isActive();
}
static configure(readOnly: boolean): void {
static configure(readOnly: boolean): TopicEventDispatcher {
if (this._instance) {
throw new Error('events already initialized');
}
this._instance = new TopicEventDispatcher(readOnly);
return this._instance;
}
static getInstance(): TopicEventDispatcher {
@ -77,6 +79,4 @@ class TopicEventDispatcher extends Events {
return this._instance;
}
}
export { TopicEvent };
export default TopicEventDispatcher;

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/*
* Copyright [2021] [wisemapping]
*
@ -15,18 +16,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Events from '../Events';
import EventDispispatcher from '../EventDispatcher';
import { EventBusType } from '../EventBusType';
export type EventType =
| 'topicResize'
| 'topicMoved'
| 'childShrinked'
| 'topicConnected'
| 'topicAdded'
| 'topicRemoved'
| 'forceLayout'
| 'topicDisconect';
class EventBus extends Events {
class EventBus extends EventDispispatcher<EventBusType> {
// eslint-disable-next-line no-use-before-define
static _instance: EventBus = new EventBus();
@ -34,11 +27,11 @@ class EventBus extends Events {
return this._instance;
}
fireEvent(type: EventType, eventArgs?: unknown[] | unknown): Events {
return super.fireEvent(type, eventArgs);
fireEvent(type: EventBusType, arg?: any): void {
return super.fireEvent(type, arg);
}
addEvent(type: EventType, fn?, internal?: boolean): Events {
addEvent(type: EventBusType, fn: (arg?: any) => void, internal?: boolean): void {
return super.addEvent(type, fn, internal);
}
}

View File

@ -45,15 +45,15 @@ class EventBusDispatcher {
}
private _topicResizeEvent(args: { node: Topic; size: SizeType }) {
this._layoutManager!.updateNodeSize(args.node.getId(), args.size);
this.getLayoutManager().updateNodeSize(args.node.getId(), args.size);
}
private _topicMoved(args: { node: Topic; position: PositionType }) {
this._layoutManager!.moveNode(args.node.getId(), args.position);
this.getLayoutManager().moveNode(args.node.getId(), args.position);
}
private _topicDisconect(node: Topic) {
this._layoutManager!.disconnectNode(node.getId());
this.getLayoutManager().disconnectNode(node.getId());
}
private _topicConnected(args: { parentNode: Topic; childNode: Topic }) {
@ -64,28 +64,31 @@ class EventBusDispatcher {
);
}
getLayoutManager(): LayoutManager {
if (!this._layoutManager) {
throw new Error('Layout not initialized');
}
return this._layoutManager;
}
private _childShrinked(node: Topic) {
this._layoutManager!.updateShrinkState(node.getId(), node.areChildrenShrunken());
this.getLayoutManager().updateShrinkState(node.getId(), node.areChildrenShrunken());
}
private _topicAdded(node: Topic) {
// Central topic must not be added twice ...
if (node.getId() !== 0) {
this._layoutManager!.addNode(node.getId(), { width: 10, height: 10 }, node.getPosition());
this._layoutManager!.updateShrinkState(node.getId(), node.areChildrenShrunken());
this.getLayoutManager().addNode(node.getId(), { width: 10, height: 10 }, node.getPosition());
this.getLayoutManager().updateShrinkState(node.getId(), node.areChildrenShrunken());
}
}
private _topicRemoved(node: Topic) {
this._layoutManager!.removeNode(node.getId());
this.getLayoutManager().removeNode(node.getId());
}
private _forceLayout() {
this._layoutManager!.layout(true);
}
getLayoutManager(): LayoutManager {
return this._layoutManager!;
private _forceLayout(): void {
this.getLayoutManager().layout(true);
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
type LayoutEventType = 'change' | 'forceLayout';
export default LayoutEventType;

View File

@ -16,15 +16,16 @@
* limitations under the License.
*/
import { $assert, $defined } from '@wisemapping/core-js';
import Events from '../Events';
import EventDispispatcher from '../EventDispatcher';
import RootedTreeSet from './RootedTreeSet';
import OriginalLayout from './OriginalLayout';
import ChangeEvent from './ChangeEvent';
import SizeType from '../SizeType';
import Node from './Node';
import PositionType from '../PositionType';
import LayoutEventType from './LayoutEventType';
class LayoutManager extends Events {
class LayoutManager extends EventDispispatcher<LayoutEventType> {
private _treeSet: RootedTreeSet;
private _layout: OriginalLayout;

View File

@ -15,6 +15,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
type ThemeType = 'classic' | 'prism';
type ThemeType = 'classic' | 'prism' | 'dark-prism';
export default ThemeType;

View File

@ -1,11 +1,10 @@
import NodeModel from '../model/NodeModel';
import ThemeType from '../model/ThemeType';
import ClassicTheme from './ClassicTheme';
import DarkPrismTheme from './DarkPrismTheme';
import PrismTheme from './PrismTheme';
import Theme from './Theme';
type ThemeId = 'prism' | 'classic' | 'dark-prism';
class ThemeFactory {
private static prismTheme = new PrismTheme();
@ -13,7 +12,7 @@ class ThemeFactory {
private static classicTheme = new ClassicTheme();
static createById(id: ThemeId): Theme {
static createById(id: ThemeType): Theme {
let result: Theme;
switch (id) {
case 'classic':

View File

@ -0,0 +1,30 @@
import { Story, Meta } from '@storybook/html';
import createConnection, { TopicArgs } from './Connection';
export default {
title: 'Mindplot/Connection',
// More on argTypes: https://storybook.js.org/docs/html/api/argtypes
argTypes: {
shapeType: {
options: ['none', 'rectangle', 'rounded rectangle', 'elipse', 'line'],
control: { type: 'select' },
},
},
} as Meta;
const Template: Story<TopicArgs> = (args: TopicArgs) => createConnection(args);
export const Classic = Template.bind({});
Classic.args = {
theme: 'classic',
};
export const Prism = Template.bind({});
Prism.args = {
theme: 'prism',
};
export const DarkPrism = Template.bind({});
DarkPrism.args = {
theme: 'dark-prism',
};

View File

@ -0,0 +1,136 @@
import $ from 'jquery';
import { Mindmap, Topic } from '../../../src';
import NodeModel from '../../../src/components/model/NodeModel';
import CentralTopic from '../../../src/components/CentralTopic';
import Canvas from '../../../src/components/Canvas';
import ScreenManager from '../../../src/components/ScreenManager';
import TopicEventDispatcher from '../../../src/components/TopicEventDispatcher';
import ThemeType from '../../../src/components/model/ThemeType';
import MainTopic from '../../../src/components/MainTopic';
import EventBusDispatcher from '../../../src/components/layout/EventBusDispatcher';
import LayoutManager from '../../../src/components/layout/LayoutManager';
import ChangeEvent from '../../../src/components/layout/ChangeEvent';
import EventBus from '../../../src/components/layout/EventBus';
const registerRefreshHook = (topics: Topic[]) => {
// Trigger a redraw after the node is added ...
if (globalThis.observer) {
globalThis.observer.disconnect();
}
globalThis.observer = new MutationObserver(() => {
// Relayout...
topics.forEach((t) => t.redraw());
EventBus.instance.fireEvent('forceLayout');
});
globalThis.observer.observe(document.getElementById('root')!, { childList: true });
};
export type TopicArgs = {
readOnly?: boolean;
theme?: ThemeType;
};
const createConnection = ({ theme = undefined, readOnly = true }: TopicArgs) => {
// Build basic container ...
const divElem = document.createElement('div');
const jqueryDiv = $(divElem);
jqueryDiv.css({
height: '600px',
width: '800px',
backgroundColor: 'gray',
});
// Initialize designer helpers ...
const screenManager = new ScreenManager(divElem);
const canvas = new Canvas(screenManager, 0.7, readOnly);
TopicEventDispatcher.configure(readOnly);
// Register event propagation ..
const mindmap = new Mindmap();
const central = new NodeModel('CentralTopic', mindmap);
central.setText('Central Topic');
mindmap.addBranch(central);
// Add Children ...
const child1 = new NodeModel('MainTopic', mindmap);
child1.setOrder(0);
child1.setText('This is child one !\nwith other line');
child1.setPosition(100, 100);
const child2 = new NodeModel('MainTopic', mindmap);
child2.setOrder(1);
child2.setPosition(100, -100);
const child3 = new NodeModel('MainTopic', mindmap);
child3.setOrder(0);
child3.setPosition(-100, 100);
const child4 = new NodeModel('MainTopic', mindmap);
child4.setOrder(1);
child4.setPosition(-100, -100);
const subchild1 = new NodeModel('MainTopic', mindmap);
subchild1.setOrder(0);
subchild1.setPosition(300, 80);
const subchild2 = new NodeModel('MainTopic', mindmap);
subchild2.setOrder(1);
subchild2.setPosition(300, 120);
// Theme ...
if (theme) {
mindmap.setTheme(theme);
}
// Create and add to canvas..
const centralTopic = new CentralTopic(central, { readOnly });
const child1Topic = new MainTopic(child1, { readOnly });
const child2Topic = new MainTopic(child2, { readOnly });
const child3Topic = new MainTopic(child3, { readOnly });
const child4Topic = new MainTopic(child4, { readOnly });
const subchild1Topic = new MainTopic(subchild1, { readOnly });
const subchild2Topic = new MainTopic(subchild2, { readOnly });
const topics = [
child1Topic,
child2Topic,
child3Topic,
child4Topic,
centralTopic,
subchild1Topic,
subchild2Topic,
];
// Configure event dispatcher ...
const dispatcher = new EventBusDispatcher();
const size = { width: 25, height: 25 };
const layoutManager = new LayoutManager(mindmap.getCentralTopic().getId(), size);
dispatcher.setLayoutManager(layoutManager);
layoutManager.addEvent('change', (event: ChangeEvent) => {
const id = event.getId();
const topic = topics.filter((t) => t.getModel().getId() === id)[0];
topic.setPosition(event.getPosition());
topic.setOrder(event.getOrder());
});
// Add to canvas ...
topics.forEach((t) => canvas.append(t));
// Connect nodes ...
child1Topic.connectTo(centralTopic, canvas);
child2Topic.connectTo(centralTopic, canvas);
child3Topic.connectTo(centralTopic, canvas);
child4Topic.connectTo(centralTopic, canvas);
subchild1Topic.connectTo(child1Topic, canvas);
subchild2Topic.connectTo(child1Topic, canvas);
// Register refresh hook ..
registerRefreshHook(topics);
return divElem;
};
export default createConnection;

View File

@ -21,6 +21,7 @@ export default {
noteText: { control: 'text' },
linkText: { control: 'text' },
eicon: { control: 'multi-select', options: ['❤️', '🌈', '🖇️'] },
theme: { control: 'select', options: ['classic', 'prism', 'dark-prism'] },
},
} as Meta;
@ -83,3 +84,19 @@ ShapeNone.args = {
eicon: ['🌈'],
shapeType: 'none',
};
export const ThemeClassic = Template.bind({});
ThemeClassic.args = {
text: 'Theme Classic',
eicon: ['🌈'],
shapeType: 'none',
theme: 'classic',
};
export const ThemePrime = Template.bind({});
ThemePrime.args = {
text: 'Theme Prime',
eicon: ['🌈'],
shapeType: 'none',
theme: 'prism',
};

View File

@ -8,6 +8,7 @@ import ScreenManager from '../../../src/components/ScreenManager';
import EmojiIconModel from '../../../src/components/model/EmojiIconModel';
import TopicEventDispatcher from '../../../src/components/TopicEventDispatcher';
import { TopicShapeType } from '../../../src/components/model/INodeModel';
import ThemeType from '../../../src/components/model/ThemeType';
const registerRefreshHook = (topic: Topic) => {
// Trigger a redraw after the node is added ...
@ -34,6 +35,7 @@ export type TopicArgs = {
noteText?: string;
linkText?: string;
eicon?: string[];
theme?: ThemeType;
};
const createTopic = ({
@ -47,6 +49,7 @@ const createTopic = ({
noteText = undefined,
linkText = undefined,
eicon = undefined,
theme = undefined,
readOnly = true,
}: TopicArgs) => {
// Build basic container ...
@ -92,6 +95,11 @@ const createTopic = ({
});
}
// Theme ...
if (theme) {
mindmap.setTheme(theme);
}
// Create topic UI element ...
mindmap.addBranch(model);
const centralTopic = new CentralTopic(model, { readOnly });