Introduce Theme framework.

This commit is contained in:
Paulo Gustavo Veiga 2023-02-12 09:57:33 -08:00
parent 1eb6f45bae
commit 9fad52a04d
78 changed files with 806 additions and 437 deletions

View File

@ -48,7 +48,7 @@ describe('Topic Shape Suite', () => {
.invoke('attr', 'rx')
.then(parseInt)
.should('be.a', 'number')
.should('be.lt', 5);
.should('be.lte', 8);
cy.focusTopicByText('Mind Mapping');
cy.matchImageSnapshot('changeToRoundedRectangle');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -96,10 +96,7 @@ class ConnectionLine {
private updateColor(): string {
// In case that the main topic has changed the color, overwrite the main topic definiton.
let color = this._targetTopic.getConnectionColor();
if (this._targetTopic.isCentralTopic()) {
color = this._sourceTopic.getModel().getConnectionColor() || color;
}
const color = this._sourceTopic.getConnectionColor();
this._color = color;
switch (this._type) {

View File

@ -523,8 +523,6 @@ class Designer extends Events {
const { position } = result;
childModel.setPosition(position.x, position.y);
childModel.copy(parentModel);
return childModel;
}
@ -554,7 +552,7 @@ class Designer extends Events {
// Hack: if parent is central topic, add node below not on opposite side.
// This should be done in the layout
if (parentTopic.getType() === 'CentralTopic') {
siblingModel.setOrder(topic.getOrder() + 2);
siblingModel.setOrder(topic.getOrder()! + 2);
}
const parentTopicId = parentTopic.getId();
@ -575,10 +573,9 @@ class Designer extends Events {
result = mindmap.createNode();
// Create a new node ...
const order = topic.getOrder() + 1;
const order = topic.getOrder()! + 1;
result.setOrder(order);
result.setPosition(10, 10); // Set a dummy position ...
result.copy(model);
}
return result;
@ -671,7 +668,7 @@ class Designer extends Events {
nodeModelToTopic(nodeModel: NodeModel): Topic {
$assert(nodeModel, 'Node model can not be null');
let children = nodeModel.getChildren().slice();
children = children.sort((a, b) => a.getOrder() - b.getOrder());
children = children.sort((a, b) => a.getOrder()! - b.getOrder()!);
const result = this._buildNodeGraph(nodeModel, this.isReadOnly());
result.setVisibility(false);

View File

@ -26,7 +26,8 @@ import PositionType from './PositionType';
class MainTopic extends Topic {
buildDragShape(): ElementClass<ElementPeer> {
const innerShape = this._buildShape(this.getShapeType());
const shapeType = this.getShapeType();
const innerShape = this._buildShape(shapeType);
const size = this.getSize();
innerShape.setSize(size.width, size.height);
innerShape.setPosition(0, 0);
@ -50,27 +51,21 @@ class MainTopic extends Topic {
const group = new Group(groupAttributes);
group.append(innerShape);
// Add Text ...
if (this.getShapeType() !== 'image') {
const textShape = this._buildTextShape(true);
const text = this.getText();
textShape.setText(text);
textShape.setOpacity(0.5);
const textShape = this._buildTextShape(true);
const text = this.getText();
textShape.setText(text);
textShape.setOpacity(0.5);
// Copy text position of the topic element ...
const textPosition = this.getOrBuildTextShape().getPosition();
textShape.setPosition(textPosition.x, textPosition.y);
// Copy text position of the topic element ...
const textPosition = this.getOrBuildTextShape().getPosition();
textShape.setPosition(textPosition.x, textPosition.y);
group.append(textShape);
}
group.append(textShape);
return group;
}
updateTopicShape(targetTopic: Topic) {
if (!targetTopic.isCentralTopic()) {
// Get the real shape type ...
this.redrawShapeType();
}
updateTopicShape(_targetTopic: Topic) {
this.redrawShapeType();
}
disconnect(workspace: Canvas) {

View File

@ -27,7 +27,7 @@ import { $notify } from './widget/ToolbarNotifier';
import { $msg } from './Messages';
import DesignerKeyboard from './DesignerKeyboard';
import LocalStorageManager from './LocalStorageManager';
import TopicStyle from './TopicStyle';
import ThemeFactory from './theme/ThemeFactory';
/**
* WebComponent implementation for minplot designer.
@ -54,7 +54,9 @@ class MindplotWebComponent extends HTMLElement {
const wrapper = document.createElement('div');
wrapper.setAttribute('class', 'wise-editor');
wrapper.setAttribute('id', 'mindplot-canvas');
wrapper.setAttribute('style', TopicStyle.defaultCanvasCssStyle());
const theme = ThemeFactory.createById('classic');
wrapper.setAttribute('style', theme.getCanvasCssStyle());
this._shadowRoot.appendChild(wrapper);
this._isLoaded = false;

View File

@ -21,7 +21,7 @@ import { Ellipse, Group } from '@wisemapping/web2d';
import TopicConfig from './TopicConfig';
import ActionDispatcher from './ActionDispatcher';
import Topic from './Topic';
import ColorUtil from './render/ColorUtil';
import ColorUtil from './theme/ColorUtil';
class ShirinkConnector {
private _isShrink: boolean;

View File

@ -20,7 +20,6 @@ import { $assert, $defined } from '@wisemapping/core-js';
import { Rect, Text, Group, ElementClass, ElementPeer } from '@wisemapping/web2d';
import NodeGraph, { NodeOption } from './NodeGraph';
import TopicStyle from './TopicStyle';
import TopicFeatureFactory from './TopicFeature';
import ConnectionLine, { LineType } from './ConnectionLine';
import IconGroup from './IconGroup';
@ -40,11 +39,11 @@ import SizeType from './SizeType';
import FeatureModel from './model/FeatureModel';
import PositionType from './PositionType';
import LineTopicShape from './widget/LineTopicShape';
import ColorUtil from './render/ColorUtil';
import Icon from './Icon';
import { FontStyleType } from './FontStyleType';
import { FontWeightType } from './FontWeightType';
import DragTopic from './DragTopic';
import ThemeFactory from './theme/ThemeFactory';
const ICON_SCALING_FACTOR = 1.3;
@ -119,83 +118,51 @@ abstract class Topic extends NodeGraph {
}
protected redrawShapeType() {
const oldInnerShape = this.getInnerShape();
if (oldInnerShape) {
this._removeInnerShape();
this._removeInnerShape();
// Create a new one ...
const innerShape = this.getInnerShape();
// Create a new one ...
const innerShape = this.getInnerShape();
// Update figure size ...
const size = this.getSize();
this.setSize(size, true);
// Update figure size ...
const size = this.getSize();
this.setSize(size, true);
const group = this.get2DElement();
group.append(innerShape);
const group = this.get2DElement();
group.append(innerShape);
// Move text to the front ...
const text = this.getOrBuildTextShape();
text.moveToFront();
// Move text to the front ...
const text = this.getOrBuildTextShape();
text.moveToFront();
// Move iconGroup to front ...
const iconGroup = this.getIconGroup();
if (iconGroup) {
iconGroup.moveToFront();
}
// Move iconGroup to front ...
const iconGroup = this.getIconGroup();
if (iconGroup) {
iconGroup.moveToFront();
}
// Move connector to front
const connector = this.getShrinkConnector();
if (connector) {
connector.moveToFront();
}
// Move connector to front
const connector = this.getShrinkConnector();
if (connector) {
connector.moveToFront();
}
}
getShapeType(): TopicShapeType {
const model = this.getModel();
let result = model.getShapeType();
if (!result) {
result = TopicStyle.defaultShapeType(this);
}
return result;
const theme = ThemeFactory.create(model);
return theme.getShapeType(this);
}
getConnectionStyle(): LineType {
const model = this.getModel();
let result: LineType | undefined = model.getConnectionStyle();
// Style is infered looking recursivelly on the parent nodes.
if (result === undefined) {
const parent = this.getParent();
if (parent) {
result = parent.getConnectionStyle();
} else {
result = TopicStyle.defaultConnectionType(this);
}
}
return result!;
const theme = ThemeFactory.create(model);
return theme.getConnectionType(this);
}
getConnectionColor(): string {
const model = this.getModel();
let result: string | undefined = model.getConnectionColor();
// Style is infered looking recursivelly on the parent nodes.
if (!result) {
const parent = this.getParent();
if (parent && parent.isCentralTopic()) {
// This means that this is central main node, in this case, I will overwrite with the main color if it was defined.
result = this.getModel().getConnectionColor() || parent.getModel().getConnectionColor();
} else {
result = parent?.getConnectionColor();
}
}
if (!result) {
result = TopicStyle.defaultConnectionColor(this);
}
return result!;
const theme = ThemeFactory.create(model);
return theme.getConnectionColor(this);
}
private _removeInnerShape(): ElementClass<ElementPeer> {
@ -232,7 +199,7 @@ abstract class Topic extends NodeGraph {
result = new Rect(0.9, { strokeWidth: 2 });
break;
case 'rounded rectangle':
result = new Rect(0.3, { strokeWidth: 2 });
result = new Rect(0.6, { strokeWidth: 2 });
break;
case 'line':
result = new LineTopicShape(this, { strokeWidth: 2 });
@ -300,14 +267,16 @@ abstract class Topic extends NodeGraph {
}
private _buildIconGroup(): IconGroup {
const model = this.getModel();
const theme = ThemeFactory.create(model);
const textHeight = this.getOrBuildTextShape().getFontHeight();
const iconSize = textHeight * ICON_SCALING_FACTOR;
const result = new IconGroup(this.getId(), iconSize);
const padding = TopicStyle.getInnerPadding(this);
const padding = theme.getInnerPadding(this);
result.setPosition(padding, padding);
// Load topic features ...
const model = this.getModel();
const featuresModel = model.getFeatures();
featuresModel.forEach((f) => {
@ -397,78 +366,58 @@ abstract class Topic extends NodeGraph {
const model = this.getModel();
model.setFontFamily(value);
this.redraw();
this.redraw(true);
}
setFontSize(value: number): void {
const model = this.getModel();
model.setFontSize(value);
this.redraw();
this.redraw(true);
}
setFontStyle(value: FontStyleType): void {
const model = this.getModel();
model.setFontStyle(value);
this.redraw();
this.redraw(true);
}
setFontWeight(value: FontWeightType): void {
const model = this.getModel();
model.setFontWeight(value);
this.redraw();
this.redraw(true);
}
getFontWeight(): FontWeightType {
const model = this.getModel();
let result = model.getFontWeight();
if (!result) {
const font = TopicStyle.defaultFontStyle(this);
result = font.weight;
}
return result;
const theme = ThemeFactory.create(model);
return theme.getFontWeight(this);
}
getFontFamily(): string {
const model = this.getModel();
let result = model.getFontFamily();
if (!result) {
const font = TopicStyle.defaultFontStyle(this);
result = font.font;
}
return result;
const theme = ThemeFactory.create(model);
return theme.getFontFamily(this);
}
getFontColor(): string {
const model = this.getModel();
let result = model.getFontColor();
if (!result) {
const font = TopicStyle.defaultFontStyle(this);
result = font.color;
}
return result;
const theme = ThemeFactory.create(model);
return theme.getFontColor(this);
}
getFontStyle(): string {
getFontStyle(): FontStyleType {
const model = this.getModel();
let result = model.getFontStyle();
if (!result) {
const font = TopicStyle.defaultFontStyle(this);
result = font.style;
}
return result;
const theme = ThemeFactory.create(model);
return theme.getFontStyle(this);
}
getFontSize(): number {
const model = this.getModel();
let result = model.getFontSize();
if (!result) {
const font = TopicStyle.defaultFontStyle(this);
result = font.size;
}
return result;
const theme = ThemeFactory.create(model);
return theme.getFontSize(this);
}
setFontColor(value: string | undefined) {
@ -485,20 +434,22 @@ abstract class Topic extends NodeGraph {
const model = this.getModel();
model.setText(modelText);
this.redraw();
this.redraw(true);
}
getText(): string {
const model = this.getModel();
const theme = ThemeFactory.create(model);
const text = model.getText();
return text || TopicStyle.defaultText(this);
return text || theme.getText(this);
}
setBackgroundColor(color: string | undefined): void {
const model = this.getModel();
model.setBackgroundColor(color);
this.redraw();
this.redraw(true);
}
setConnectionStyle(type: LineType): void {
@ -517,19 +468,8 @@ abstract class Topic extends NodeGraph {
getBackgroundColor(): string {
const model = this.getModel();
let result = model.getBackgroundColor();
if (!result && !this.isCentralTopic()) {
// Be sure that not overwride default background color ...
const borderColor = model.getBorderColor();
if (borderColor) {
result = ColorUtil.lightenColor(borderColor, 40);
}
}
if (!result) {
result = TopicStyle.defaultBackgroundColor(this);
}
return result;
const theme = ThemeFactory.create(model);
return theme.getBackgroundColor(this);
}
setBorderColor(color: string | undefined): void {
@ -541,25 +481,8 @@ abstract class Topic extends NodeGraph {
getBorderColor(): string {
const model = this.getModel();
let result = model.getBorderColor();
// If the the style is a line, the color is alward the connection one.
if (this.getShapeType() === 'line') {
result = this.getConnectionColor();
}
if (result === undefined) {
const parent = this.getParent();
if (parent) {
result = parent.getBorderColor();
}
}
// If border color has not been defined, use the connection color for the border ...
if (!result) {
result = this.getConnectionColor();
}
return result;
const theme = ThemeFactory.create(model);
return theme.getBorderColor(this);
}
_buildTopicShape(): void {
@ -638,11 +561,12 @@ abstract class Topic extends NodeGraph {
setOnFocus(focus: boolean) {
if (this.isOnFocus() !== focus) {
const theme = ThemeFactory.create(this.getModel());
this._onFocus = focus;
const outerShape = this.getOuterShape();
const fillColor = TopicStyle.defaultOuterBackgroundColor(this, focus);
const borderColor = TopicStyle.defaultOuterBorderColor(this);
const fillColor = theme.getOuterBackgroundColor(this, focus);
const borderColor = theme.getOuterBorderColor(this);
outerShape.setFill(fillColor);
outerShape.setStroke(1, 'solid', borderColor);
@ -1051,7 +975,7 @@ abstract class Topic extends NodeGraph {
}
}
getOrder(): number {
getOrder(): number | undefined {
const model = this.getModel();
return model.getOrder();
}
@ -1184,6 +1108,7 @@ abstract class Topic extends NodeGraph {
// Has the style change ?
const connStyleChanged =
this._outgoingLine.getLineType() !== this.getParent()!.getConnectionStyle();
if (connStyleChanged) {
// Todo: Review static reference ...
const workspace = designer.getWorkSpace();
@ -1197,13 +1122,8 @@ abstract class Topic extends NodeGraph {
result = true;
}
// Has the color changed ?
const color = this._outgoingLine.getStrokeColor();
const connColorChanged = color !== this.getParent()!.getConnectionColor();
if (connColorChanged) {
this._outgoingLine.redraw();
result = true;
}
this._outgoingLine.redraw();
result = true;
}
}
return result;
@ -1211,6 +1131,7 @@ abstract class Topic extends NodeGraph {
redraw(redrawChildren = false): void {
if (this._isInWorkspace) {
const theme = ThemeFactory.create(this.getModel());
const textShape = this.getOrBuildTextShape();
// Update font ...
const fontColor = this.getFontColor();
@ -1233,8 +1154,8 @@ abstract class Topic extends NodeGraph {
// Update outer shape style ...
const outerShape = this.getOuterShape();
const outerFillColor = TopicStyle.defaultOuterBackgroundColor(this, this.isOnFocus());
const outerBorderColor = TopicStyle.defaultOuterBorderColor(this);
const outerFillColor = theme.getOuterBackgroundColor(this, this.isOnFocus());
const outerBorderColor = theme.getOuterBorderColor(this);
outerShape.setFill(outerFillColor);
outerShape.setStroke(1, 'solid', outerBorderColor);
@ -1242,7 +1163,7 @@ abstract class Topic extends NodeGraph {
// Calculate topic size and adjust elements ...
const textWidth = textShape.getShapeWidth();
const textHeight = textShape.getShapeHeight();
const padding = TopicStyle.getInnerPadding(this);
const padding = theme.getInnerPadding(this);
// Adjust icons group based on the font size ...
const iconGroup = this.getOrBuildIconGroup();

View File

@ -1,205 +0,0 @@
/*
* Copyright [2011] [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 { $assert } from '@wisemapping/core-js';
import { LineType } from './ConnectionLine';
import { FontStyleType } from './FontStyleType';
import { FontWeightType } from './FontWeightType';
import { $msg } from './Messages';
import { TopicShapeType } from './model/INodeModel';
import ColorUtil from './render/ColorUtil';
import Topic from './Topic';
type FontStyle = {
font: string;
size: number;
style: FontStyleType;
weight: FontWeightType;
color: string;
};
type TopicStyleType = {
borderColor: string;
backgroundColor: string;
connectionStyle: LineType;
connectionColor: string;
fontStyle: FontStyle;
msgKey: string;
shapeType: TopicShapeType;
};
const TopicDefaultStyles = {
CENTRAL_TOPIC: {
borderColor: '#3971B1',
backgroundColor: '#509DC0',
fontStyle: {
font: 'Verdana',
size: 10,
style: 'normal' as FontStyleType,
weight: 'bold' as FontWeightType,
color: '#ffffff',
},
connectionStyle: LineType.THICK_CURVED,
connectionColor: '#345780',
msgKey: 'CENTRAL_TOPIC',
shapeType: 'rounded rectangle' as TopicShapeType,
},
MAIN_TOPIC: {
borderColor: '#023BB9',
backgroundColor: '#E0E5EF',
fontStyle: {
font: 'Verdana',
size: 8,
style: 'normal' as FontStyleType,
weight: 'normal' as FontWeightType,
color: '#525C61',
},
connectionStyle: LineType.ARC,
connectionColor: '#345780',
msgKey: 'MAIN_TOPIC',
shapeType: 'line' as TopicShapeType,
},
SUB_TOPIC: {
borderColor: '#96e3ff',
backgroundColor: '#96e3ff',
fontStyle: {
font: 'Verdana',
size: 8,
style: 'normal' as FontStyleType,
weight: 'normal' as FontWeightType,
color: '#525C61',
},
connectionStyle: LineType.ARC,
connectionColor: '#345780',
msgKey: 'SUB_TOPIC',
shapeType: 'line' as TopicShapeType,
},
ISOLATED_TOPIC: {
borderColor: '#023BB9',
backgroundColor: '#96e3ff',
fontStyle: {
font: 'Verdana',
size: 8,
style: 'normal' as FontStyleType,
weight: 'normal' as FontWeightType,
color: '#525C61',
},
msgKey: 'ISOLATED_TOPIC',
connectionStyle: LineType.THICK_CURVED,
connectionColor: '#345780',
shapeType: 'line' as TopicShapeType,
},
};
class TopicStyle {
static _getStyles(topic: Topic): TopicStyleType {
$assert(topic, 'topic can not be null');
let result: TopicStyleType;
if (topic.isCentralTopic()) {
result = TopicDefaultStyles.CENTRAL_TOPIC;
} else {
const targetTopic = topic.getOutgoingConnectedTopic();
if (targetTopic) {
if (targetTopic.isCentralTopic()) {
result = TopicDefaultStyles.MAIN_TOPIC;
} else {
result = TopicDefaultStyles.SUB_TOPIC;
}
} else {
result = TopicDefaultStyles.ISOLATED_TOPIC;
}
}
return result;
}
static defaultText(topic: Topic): string {
const { msgKey } = this._getStyles(topic);
return $msg(msgKey);
}
static defaultFontStyle(topic: Topic): FontStyle {
return this._getStyles(topic).fontStyle;
}
static defaultCanvasCssStyle(): string {
return `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;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;`;
}
static defaultOuterBorderColor(topic: Topic): string {
let result: string;
if (topic.getShapeType() === 'line') {
result = '#F4B82D';
} else {
const innerBorderColor = topic.getBorderColor();
result = ColorUtil.lightenColor(innerBorderColor, 70);
}
return result;
}
static defaultOuterBackgroundColor(topic: Topic, onFocus: boolean): string {
let result: string;
if (topic.getShapeType() === 'line') {
result = onFocus ? '#F4B82D' : '#FCEBC0';
} else {
const innerBgColor = topic.getBackgroundColor();
result = ColorUtil.lightenColor(innerBgColor, 70);
}
return result;
}
static defaultBackgroundColor(topic: Topic): string {
return this._getStyles(topic).backgroundColor;
}
static defaultBorderColor(topic: Topic): string {
return this._getStyles(topic).borderColor;
}
static getInnerPadding(topic: Topic): number {
return topic.getOrBuildTextShape().getFontHeight() * 0.5;
}
static defaultShapeType(topic: Topic): TopicShapeType {
return this._getStyles(topic).shapeType;
}
static defaultConnectionType(topic: Topic): LineType {
return this._getStyles(topic).connectionStyle;
}
static defaultConnectionColor(topic: Topic): string {
return this._getStyles(topic).connectionColor;
}
}
export default TopicStyle;

View File

@ -15,6 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { $defined } from '@wisemapping/core-js';
import Command from '../Command';
import CommandContext from '../CommandContext';
import PositionType from '../PositionType';
@ -27,7 +28,7 @@ class DragTopicCommand extends Command {
private _position: PositionType;
private _order: number;
private _order: number | undefined;
constructor(topicId: number, position: PositionType, order: number, parentTopic: Topic) {
super();
@ -55,8 +56,8 @@ class DragTopicCommand extends Command {
}
// Set topic order ...
if (this._order != null) {
topic.setOrder(this._order);
if ($defined(this._order)) {
topic.setOrder(this._order!);
} else if (this._position != null) {
commandContext.moveTopic(topic, this._position);
} else {

View File

@ -124,7 +124,8 @@ export default class FreemindImporter extends Importer {
relationship.setSrcCtrlPoint({ x, y: coords.y });
// Fix coord
if (srcTopic.getOrder() && srcTopic.getOrder() % 2 !== 0) {
const order = srcTopic.getOrder();
if (order !== undefined && order % 2 !== 0) {
const y = coords.y * -1;
relationship.setSrcCtrlPoint({ x: coords.x, y });
}
@ -140,7 +141,8 @@ export default class FreemindImporter extends Importer {
relationship.setDestCtrlPoint({ x, y: coords.y });
}
if (destNode.getOrder() && destNode.getOrder() % 2 !== 0) {
const order = destNode.getOrder();
if (order !== undefined && order % 2 !== 0) {
const y = coords.y * -1;
relationship.setDestCtrlPoint({ x: coords.x, y });
}

View File

@ -24,7 +24,7 @@ abstract class ChildrenSorterStrategy {
abstract computeOffsets(treeSet: RootedTreeSet, node: Node): Map<number, PositionType>;
abstract insert(treeSet: RootedTreeSet, parent: Node, child: Node, order: number): void;
abstract insert(treeSet: RootedTreeSet, parent: Node, child: Node, order?: number): void;
abstract detach(treeSet: RootedTreeSet, node: Node): void;

View File

@ -60,7 +60,7 @@ class EventBusDispatcher {
this._layoutManager!.connectNode(
args.parentNode.getId(),
args.childNode.getId(),
args.childNode.getOrder(),
args.childNode.getOrder()!, // @todo: This can be a issue ...
);
}

View File

@ -114,6 +114,7 @@ class OriginalLayout {
const parentX = parentPosition.x;
const parentY = parentPosition.y;
console.log(`${parent?.getId()}:${offset.x}`);
const newPos = {
x: parentX + offset.x,
y: parentY + offset.y + this.calculateAlignOffset(node, child, heightById),
@ -174,6 +175,9 @@ class OriginalLayout {
private fixOverlapping(node: Node, heightById: Map<number, number>): void {
const children = this._treeSet.getChildren(node);
if (node.isFree()) {
this._shiftBranches(node, heightById);
}
children.forEach((child) => {
this.fixOverlapping(child, heightById);
});

View File

@ -137,7 +137,8 @@ abstract class INodeModel {
}
getShapeType(): TopicShapeType {
return this.getProperty('shapeType') as TopicShapeType;
const result = this.getProperty('shapeType') as TopicShapeType;
return result;
}
setShapeType(type: TopicShapeType | undefined) {
@ -152,7 +153,7 @@ abstract class INodeModel {
this.putProperty('order', value);
}
getOrder(): number {
getOrder(): number | undefined {
return this.getProperty('order') as number;
}

View File

@ -21,6 +21,7 @@ import INodeModel, { NodeModelType } from './INodeModel';
import NodeModel from './NodeModel';
import RelationshipModel from './RelationshipModel';
import ModelCodeName from '../persistence/ModelCodeName';
import ThemeType from './ThemeType';
class Mindmap extends IMindmap {
private _description: string;
@ -33,6 +34,8 @@ class Mindmap extends IMindmap {
private _relationships: Array<RelationshipModel>;
private _theme: ThemeType | undefined;
constructor(id?: string, version: string = ModelCodeName.TANGO) {
super();
this._branches = [];
@ -42,6 +45,10 @@ class Mindmap extends IMindmap {
this._id = id;
}
getTheme(): ThemeType {
return this._theme ? this._theme : 'classic';
}
/** */
getDescription(): string {
return this._description;

View File

@ -132,46 +132,6 @@ class NodeModel extends INodeModel {
return result;
}
copy(value: NodeModel) {
// I don't copy the font size if the target is the source is the central topic.
if (value.getType() !== 'CentralTopic') {
const fontSize = value.getFontSize();
if (fontSize) {
this.setFontSize(fontSize);
}
}
const fontFamily = value.getFontFamily();
if (fontFamily) {
this.setFontFamily(fontFamily);
}
const fontColor = value.getFontColor();
if (fontColor) {
this.setFontColor(fontColor);
}
const fontWeight = value.getFontWeight();
if (fontWeight) {
this.setFontWeight(fontWeight);
}
const fontStyle = value.getFontStyle();
if (fontStyle) {
this.setFontStyle(fontStyle);
}
const shape = value.getShapeType();
if (shape) {
this.setShapeType(shape);
}
const backgroundColor = value.getBackgroundColor();
if (backgroundColor) {
this.setBackgroundColor(backgroundColor);
}
}
deepCopy(): NodeModel {
const result = new NodeModel(this.getType(), this._mindmap);
result._children = this._children.map((node) => {

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 ThemeType = 'classic' | 'prism';
export default ThemeType;

View File

@ -59,8 +59,8 @@ class Pela2TangoMigrator implements XMLMindmapSerializer {
} else {
rightNodes.push(child);
}
rightNodes.sort((a, b) => a.getOrder() - b.getOrder());
leftNodes.sort((a, b) => a.getOrder() - b.getOrder());
rightNodes.sort((a, b) => a.getOrder()! - b.getOrder()!);
leftNodes.sort((a, b) => a.getOrder()! - b.getOrder()!);
});
for (let i = 0; i < rightNodes.length; i++) {

View File

@ -60,7 +60,9 @@ class XMLSerializerBeta implements XMLMindmapSerializer {
parentTopic.setAttribute('position', `${pos.x},${pos.y}`);
} else {
const order = topic.getOrder();
parentTopic.setAttribute('order', order.toString());
if (order !== undefined) {
parentTopic.setAttribute('order', order.toString());
}
}
}

View File

@ -452,7 +452,7 @@ class XMLSerializerTango implements XMLMindmapSerializer {
if (topic.getType() !== 'CentralTopic') {
topic
.getChildren()
.sort((a, b) => a.getOrder() - b.getOrder())
.sort((a, b) => a.getOrder()! - b.getOrder()!)
.forEach((child, index) => {
if (child.getOrder() !== index) {
child.setOrder(index);

View File

@ -0,0 +1,124 @@
/*
* Copyright [2011] [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 { LineType } from '../ConnectionLine';
import { FontStyleType } from '../FontStyleType';
import { FontWeightType } from '../FontWeightType';
import { TopicShapeType } from '../model/INodeModel';
import DefaultTheme, { TopicStyleType } from './DefaultTheme';
import { TopicType } from './Theme';
const defaultStyles = new Map<TopicType, TopicStyleType>([
[
'CentralTopic',
{
msgKey: 'CENTRAL_TOPIC',
borderColor: '#3971B1',
backgroundColor: '#509DC0',
fontFamily: 'Verdana',
fontSize: 10,
fontStyle: 'normal' as FontStyleType,
fontWeight: 'bold' as FontWeightType,
fontColor: '#ffffff',
connectionStyle: LineType.THICK_CURVED,
connectionColor: '#345780',
shapeType: 'rounded rectangle' as TopicShapeType,
outerBackgroundColor: '#F4B82D',
outerBorderColor: '#F4B82D',
},
],
[
'MainTopic',
{
msgKey: 'MAIN_TOPIC',
borderColor: '#023BB9',
backgroundColor: '#E0E5EF',
fontFamily: 'Verdana',
fontSize: 9,
fontStyle: 'normal' as FontStyleType,
fontWeight: 'normal' as FontWeightType,
fontColor: '#525C61',
connectionStyle: LineType.THICK_CURVED,
connectionColor: '#345780',
shapeType: 'line' as TopicShapeType,
outerBackgroundColor: '#F4B82D',
outerBorderColor: '#F4B82D',
},
],
[
'SubTopic',
{
msgKey: 'SUB_TOPIC',
borderColor: '#96e3ff',
backgroundColor: '#96e3ff',
fontFamily: 'Verdana',
fontSize: 8,
fontStyle: 'normal' as FontStyleType,
fontWeight: 'normal' as FontWeightType,
fontColor: '#525C61',
connectionStyle: LineType.THICK_CURVED,
connectionColor: '#345780',
shapeType: 'line' as TopicShapeType,
outerBackgroundColor: '#F4B82D',
outerBorderColor: '#F4B82D',
},
],
[
'IsolatedTopic',
{
msgKey: 'ISOLATED_TOPIC',
borderColor: '#023BB9',
backgroundColor: '#96e3ff',
fontFamily: 'Verdana',
fontSize: 8,
fontStyle: 'normal' as FontStyleType,
fontWeight: 'normal' as FontWeightType,
fontColor: '#525C61',
connectionStyle: LineType.THICK_CURVED,
connectionColor: '#345780',
shapeType: 'line' as TopicShapeType,
outerBackgroundColor: '#F4B82D',
outerBorderColor: '#F4B82D',
},
],
]);
class ClassicTheme extends DefaultTheme {
constructor() {
super(defaultStyles);
}
getCanvasCssStyle(): string {
return `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;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;`;
}
}
export default ClassicTheme;

View File

@ -0,0 +1,247 @@
/*
* 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 { LineType } from '../ConnectionLine';
import { FontStyleType } from '../FontStyleType';
import { FontWeightType } from '../FontWeightType';
import { TopicShapeType } from '../model/INodeModel';
import NodeModel from '../model/NodeModel';
import ColorUtil from './ColorUtil';
import Topic from '../Topic';
import Theme, { TopicType } from './Theme';
import { $msg } from '../Messages';
export type TopicStyleType = {
borderColor: string | string[];
backgroundColor: string | string[];
connectionColor: string | string[];
connectionStyle: LineType;
fontFamily: string;
fontSize: number;
fontStyle: FontStyleType;
fontWeight: FontWeightType;
fontColor: string;
msgKey: string;
shapeType: TopicShapeType;
outerBackgroundColor: string;
outerBorderColor: string;
};
type StyleType = string | string[] | number | undefined | LineType;
const keyToModel = new Map<keyof TopicStyleType, (model: NodeModel) => StyleType>([
['borderColor', (m: NodeModel) => m.getBorderColor()],
['backgroundColor', (m: NodeModel) => m.getBackgroundColor()],
['shapeType', (m: NodeModel) => m.getShapeType()],
['connectionStyle', (m: NodeModel) => m.getConnectionStyle()],
['connectionColor', (m: NodeModel) => m.getConnectionColor()],
['fontFamily', (m: NodeModel) => m.getFontFamily()],
['fontColor', (m: NodeModel) => m.getFontColor()],
['fontWeight', (m: NodeModel) => m.getFontWeight()],
['fontSize', (m: NodeModel) => m.getFontSize()],
['fontStyle', (m: NodeModel) => m.getFontStyle()],
]);
abstract class DefaultTheme implements Theme {
private _style: Map<TopicType, TopicStyleType>;
constructor(style: Map<TopicType, TopicStyleType>) {
this._style = style;
}
abstract getCanvasCssStyle(): string;
protected resolve(key: keyof TopicStyleType, topic: Topic, resolveDefault = true): StyleType {
// Search parent value ...
const recurviveModelStrategy = (value: keyof TopicStyleType, t: Topic): StyleType => {
const model = t.getModel();
let result: StyleType = keyToModel.get(key)!(model);
const parent = t.getParent();
if (!result && parent) {
result = recurviveModelStrategy(value, parent);
}
return result;
};
// Can be found in the model or parent ?
let result = recurviveModelStrategy(key, topic);
if (!result && resolveDefault) {
result = this.getStyles(topic)[key];
}
return result;
}
protected getStyles(topic: Topic): TopicStyleType {
let result: TopicStyleType;
if (topic.isCentralTopic()) {
result = this._style.get('CentralTopic')!;
} else {
const targetTopic = topic.getOutgoingConnectedTopic();
if (targetTopic) {
if (targetTopic.isCentralTopic()) {
result = this._style.get('MainTopic')!;
} else {
result = this._style.get('SubTopic')!;
}
} else {
result = this._style.get('IsolatedTopic')!;
}
}
return result;
}
getBackgroundColor(topic: Topic): string {
const model = topic.getModel();
let result = model.getBackgroundColor();
if (!result && !topic.isCentralTopic()) {
// Be sure that not overwride default background color ...
const borderColor = model.getBorderColor();
if (borderColor) {
result = ColorUtil.lightenColor(borderColor, 40);
}
}
if (!result) {
let colors: string[] = [];
colors = colors.concat(this.resolve('backgroundColor', topic) as string[] | string);
// if the element is an array, use topic order to decide color ..
let order = topic.getOrder();
order = order || 0;
const index = order % colors.length;
result = colors[index];
}
return result;
}
getShapeType(topic: Topic): TopicShapeType {
const result = this.resolve('shapeType', topic) as TopicShapeType;
return result;
}
getConnectionType(topic: Topic): LineType {
return this.resolve('connectionStyle', topic) as LineType;
}
getFontFamily(topic: Topic): string {
return this.resolve('fontFamily', topic) as string;
}
getFontSize(topic: Topic): number {
return this.resolve('fontSize', topic) as number;
}
getFontStyle(topic: Topic): FontStyleType {
return this.resolve('fontStyle', topic) as FontStyleType;
}
getFontWeight(topic: Topic): FontWeightType {
return this.resolve('fontWeight', topic) as FontWeightType;
}
getFontColor(topic: Topic): string {
return this.resolve('fontColor', topic) as string;
}
getBorderColor(topic: Topic): string {
const model = topic.getModel();
let result = model.getBorderColor();
// If the the style is a line, the color is alward the connection one.
if (topic.getShapeType() === 'line') {
result = topic.getConnectionColor();
}
if (result === undefined) {
const parent = topic.getParent();
if (parent) {
result = parent.getBorderColor();
}
}
// If border color has not been defined, use the connection color for the border ...
if (!result) {
result = topic.getConnectionColor();
}
return result;
}
getOuterBorderColor(topic: Topic): string {
let result: string;
if (topic.getShapeType() === 'line') {
result = this.getStyles(topic).outerBorderColor;
} else {
const innerBorderColor = topic.getBorderColor();
result = ColorUtil.lightenColor(innerBorderColor, 70);
}
return result;
}
getOuterBackgroundColor(topic: Topic, onFocus: boolean): string {
let result: string;
if (topic.getShapeType() === 'line') {
const color = this.getStyles(topic).outerBackgroundColor;
result = onFocus ? color : ColorUtil.lightenColor(color, 30);
} else {
const innerBgColor = topic.getBackgroundColor();
result = ColorUtil.lightenColor(innerBgColor, 70);
}
return result;
}
getInnerPadding(topic: Topic): number {
return topic.getOrBuildTextShape().getFontHeight() * 0.5;
}
getText(topic: Topic): string {
const { msgKey } = this.getStyles(topic);
return $msg(msgKey);
}
getConnectionColor(topic: Topic): string {
const model = topic.getModel();
let result: string | undefined = model.getConnectionColor();
// Style is infered looking recursivelly on the parent nodes.
if (!result) {
const parent = topic.getParent();
if (parent && parent.isCentralTopic()) {
// This means that this is central main node, in this case, I will overwrite with the main color if it was defined.
result = topic.getModel().getConnectionColor() || parent.getModel().getConnectionColor();
} else {
result = parent?.getConnectionColor();
}
}
if (!result) {
let colors: string[] = [];
colors = colors.concat(this.resolve('connectionColor', topic) as string[] | string);
// if the element is an array, use topic order to decide color ..
let order = topic.getOrder();
order = order || 0;
const index = order % colors.length;
result = colors[index];
}
return result!;
}
}
export default DefaultTheme;

View File

@ -0,0 +1,201 @@
/*
* Copyright [2011] [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 { LineType } from '../ConnectionLine';
import { FontStyleType } from '../FontStyleType';
import { FontWeightType } from '../FontWeightType';
import { TopicShapeType } from '../model/INodeModel';
import Topic from '../Topic';
import DefaultTheme, { TopicStyleType } from './DefaultTheme';
import { TopicType } from './Theme';
const defaultStyles = new Map<TopicType, TopicStyleType>([
[
'CentralTopic',
{
msgKey: 'CENTRAL_TOPIC',
borderColor: '#FFFFFF',
backgroundColor: '#FFFFFF',
fontFamily: 'Verdana',
fontSize: 10,
fontStyle: 'normal' as FontStyleType,
fontWeight: 'bold' as FontWeightType,
fontColor: '#000000',
connectionStyle: LineType.ARC,
connectionColor: '#345780',
shapeType: 'rounded rectangle' as TopicShapeType,
outerBackgroundColor: '#F4B82D',
outerBorderColor: '#F4B82D',
},
],
[
'MainTopic',
{
msgKey: 'MAIN_TOPIC',
borderColor: [
'#9B7BEB',
'#E5628C',
'#EB5130',
'#F3AE3D',
'#F8D651',
'#A5D945',
'#6BC953',
'#6AD6D7',
'#4CA6F7',
'#4B6FF6',
],
backgroundColor: [
'#9B7BEB',
'#E5628C',
'#EB5130',
'#F3AE3D',
'#F8D651',
'#A5D945',
'#6BC953',
'#6AD6D7',
'#4CA6F7',
'#4B6FF6',
],
connectionColor: [
'#9B7BEB',
'#E5628C',
'#EB5130',
'#F3AE3D',
'#F8D651',
'#A5D945',
'#6BC953',
'#6AD6D7',
'#4CA6F7',
'#4B6FF6',
],
fontFamily: 'Verdana',
fontSize: 9,
fontStyle: 'normal' as FontStyleType,
fontWeight: 'normal' as FontWeightType,
fontColor: '#000000',
connectionStyle: LineType.ARC,
shapeType: 'rounded rectangle' as TopicShapeType,
outerBackgroundColor: '#F4B82D',
outerBorderColor: '#F4B82D',
},
],
[
'SubTopic',
{
msgKey: 'SUB_TOPIC',
borderColor: '#96e3ff',
backgroundColor: '#96e3ff',
fontFamily: 'Verdana',
fontSize: 8,
fontStyle: 'normal' as FontStyleType,
fontWeight: 'normal' as FontWeightType,
fontColor: '#000000',
connectionStyle: LineType.THICK_CURVED,
connectionColor: '#345780',
shapeType: 'line' as TopicShapeType,
outerBackgroundColor: '#F4B82D',
outerBorderColor: '#F4B82D',
},
],
[
'IsolatedTopic',
{
msgKey: 'ISOLATED_TOPIC',
borderColor: '#023BB9',
backgroundColor: '#96e3ff',
fontFamily: 'Verdana',
fontSize: 8,
fontStyle: 'normal' as FontStyleType,
fontWeight: 'normal' as FontWeightType,
fontColor: '#000000',
connectionStyle: LineType.THICK_CURVED,
connectionColor: '#345780',
shapeType: 'line' as TopicShapeType,
outerBackgroundColor: '#F4B82D',
outerBorderColor: '#F4B82D',
},
],
]);
class PrismTheme extends DefaultTheme {
constructor() {
super(defaultStyles);
}
getCanvasCssStyle(): string {
return `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;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;`;
}
getConnectionColor(topic: Topic): string {
const model = topic.getModel();
let result: string | undefined = model.getConnectionColor();
if (!result) {
let colors: string[] = [];
colors = colors.concat(this.resolve('connectionColor', topic) as string[] | string);
// if the element is an array, use topic order to decide color ..
let order = topic.getOrder();
order = order || 0;
const index = order % colors.length;
result = colors[index];
}
return result!;
}
getBorderColor(topic: Topic): string {
const model = topic.getModel();
let result = model.getBorderColor();
// If the the style is a line, the color is alward the connection one.
// If border color has not been defined, use the connection color for the border ...
if (!result) {
if (topic.getShapeType() === 'line') {
result = 'none';
} else {
let colors: string[] = [];
colors = colors.concat(this.resolve('borderColor', topic) as string[] | string);
// if the element is an array, use topic order to decide color ..
let order = topic.getOrder();
order = order || 0;
const index = order % colors.length;
result = colors[index];
}
}
return result;
}
}
export default PrismTheme;

View File

@ -0,0 +1,57 @@
/*
* 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 { LineType } from '../ConnectionLine';
import { FontStyleType } from '../FontStyleType';
import { FontWeightType } from '../FontWeightType';
import { TopicShapeType } from '../model/INodeModel';
import Topic from '../Topic';
export type TopicType = 'CentralTopic' | 'MainTopic' | 'SubTopic' | 'IsolatedTopic';
interface Theme {
getText(topic: Topic): string;
getFontFamily(topic: Topic): string;
getFontSize(topic: Topic): number;
getFontStyle(topic: Topic): FontStyleType;
getFontWeight(topic: Topic): FontWeightType;
getFontColor(topic: Topic): string;
getCanvasCssStyle(): string;
getOuterBorderColor(topic: Topic): string;
getOuterBackgroundColor(topic: Topic, onFocus: boolean): string;
getBackgroundColor(topic: Topic): string;
getBorderColor(topic: Topic): string;
getInnerPadding(topic: Topic): number;
getShapeType(topic: Topic): TopicShapeType;
getConnectionType(topic: Topic): LineType;
getConnectionColor(topic: Topic): string;
}
export default Theme;

View File

@ -0,0 +1,36 @@
import NodeModel from '../model/NodeModel';
import ClassicTheme from './ClassicTheme';
import PrismTheme from './PrismTheme';
import Theme from './Theme';
type ThemeId = 'prism' | 'classic';
class ThemeFactory {
private static prismTheme = new PrismTheme();
private static classicTheme = new ClassicTheme();
static createById(id: ThemeId): Theme {
let result: Theme;
switch (id) {
case 'classic':
result = ThemeFactory.classicTheme;
break;
case 'prism':
result = ThemeFactory.prismTheme;
break;
default: {
const exhaustiveCheck: never = id;
throw new Error(exhaustiveCheck);
}
}
return result;
}
static create(model: NodeModel): Theme {
const mindmap = model.getMindmap();
const theme = mindmap.getTheme();
return ThemeFactory.createById(theme);
}
}
export default ThemeFactory;

View File

@ -35,7 +35,7 @@ class ArcLine extends ArcLine2d {
let xOffset = x;
if (this._targetTopic.isCentralTopic()) {
const sourceX = this._sourceTopic.getPosition().x;
xOffset = Math.sign(sourceX) * (this._targetTopic.getSize().width / 3);
xOffset = Math.sign(sourceX) * 10;
} else {
xOffset = x + 3 * Math.sign(x);
}

View File

@ -23,7 +23,7 @@ class LineTopicShape extends StraightLine {
private _size: SizeType | null;
constructor(topic: Topic, attributes?: StyleAttributes) {
const stokeColor = topic.getConnectionColor();
const stokeColor = topic.getBorderColor();
super({ ...attributes, strokeColor: stokeColor });
this._size = null;
}