Add theme support.
@ -48,7 +48,7 @@ describe('Topic Shape Suite', () => {
|
||||
.invoke('attr', 'rx')
|
||||
.then(parseInt)
|
||||
.should('be.a', 'number')
|
||||
.should('be.lte', 8);
|
||||
.should('be.eq', 9);
|
||||
|
||||
cy.focusTopicByText('Mind Mapping');
|
||||
cy.matchImageSnapshot('changeToRoundedRectangle');
|
||||
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 108 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 107 KiB |
@ -9,6 +9,7 @@ import {
|
||||
} from '../../../components/toolbar/ToolbarValueModelBuilder';
|
||||
import { LineType } from '@wisemapping/mindplot/src/components/ConnectionLine';
|
||||
import { TopicShapeType } from '@wisemapping/mindplot/src/components/model/INodeModel';
|
||||
import ThemeType from '@wisemapping/mindplot/src/components/model/ThemeType';
|
||||
|
||||
class NodePropertyBuilder {
|
||||
designer: Designer;
|
||||
@ -25,6 +26,7 @@ class NodePropertyBuilder {
|
||||
private connectionColoreModel: NodeProperty<string>;
|
||||
private noteModel: NodeProperty<string>;
|
||||
private linkModel: NodeProperty<string>;
|
||||
private _themeModel: NodeProperty<ThemeType>;
|
||||
|
||||
constructor(designer: Designer) {
|
||||
this.designer = designer;
|
||||
@ -92,10 +94,6 @@ class NodePropertyBuilder {
|
||||
return this.selectedTopicColorModel;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns model to get and set the node link
|
||||
*/
|
||||
getLinkModel(): NodeProperty<string> {
|
||||
// const selected = this.selectedTopic();
|
||||
if (!this.linkModel)
|
||||
@ -112,6 +110,18 @@ class NodePropertyBuilder {
|
||||
return this.linkModel;
|
||||
}
|
||||
|
||||
getThemeModel(): NodeProperty<ThemeType> {
|
||||
// const selected = this.selectedTopic();
|
||||
if (!this._themeModel)
|
||||
this._themeModel = {
|
||||
getValue: (): ThemeType => this.designer.getMindmap().getTheme(),
|
||||
setValue: (value: ThemeType) => {
|
||||
this.designer.changeTheme(value);
|
||||
},
|
||||
};
|
||||
return this._themeModel;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns model to get and set topic border color
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 Box from '@mui/material/Box';
|
||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||
import Radio from '@mui/material/Radio';
|
||||
import RadioGroup from '@mui/material/RadioGroup';
|
||||
import { SelectChangeEvent } from '@mui/material/Select';
|
||||
import React, { ReactElement, useState } from 'react';
|
||||
import NodeProperty from '../../../../classes/model/node-property';
|
||||
|
||||
const ThemeEditor = (props: {
|
||||
closeModal: () => void;
|
||||
themeModel: NodeProperty<string> | null;
|
||||
}): ReactElement => {
|
||||
const [theme, setTheme] = useState(props.themeModel.getValue());
|
||||
const handleOnChange = (event: SelectChangeEvent) => {
|
||||
setTheme(event.target.value);
|
||||
props.themeModel.setValue(event.target.value);
|
||||
props.closeModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ px: 2, pb: 2, width: '300px' }}>
|
||||
<RadioGroup row value={theme} onChange={handleOnChange}>
|
||||
<FormControlLabel value="classic" control={<Radio />} label="Classic" />
|
||||
<FormControlLabel value="prism" control={<Radio />} label="Summer" />
|
||||
</RadioGroup>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeEditor;
|
@ -39,6 +39,7 @@ import ShareOutlined from '@mui/icons-material/ShareOutlined';
|
||||
import SwapCallsOutlined from '@mui/icons-material/SwapCallsOutlined';
|
||||
import NotInterestedOutlined from '@mui/icons-material/NotInterestedOutlined';
|
||||
import ShortcutIconOutlined from '@mui/icons-material/ShortcutOutlined';
|
||||
import ColorLensOutlined from '@mui/icons-material/ColorLensOutlined';
|
||||
|
||||
import Palette from '@mui/icons-material/Square';
|
||||
import SquareOutlined from '@mui/icons-material/SquareOutlined';
|
||||
@ -53,6 +54,7 @@ import FontFamilySelector from '../action-widget/button/font-family-selector';
|
||||
import Editor from '../../classes/model/editor';
|
||||
import { IntlShape } from 'react-intl';
|
||||
import { LineType } from '@wisemapping/mindplot/src/components/ConnectionLine';
|
||||
import ThemeEditor from '../action-widget/pane/theme-editor';
|
||||
|
||||
const keyTooltip = (msg: string, key: string): string => {
|
||||
const isMac = window.navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
||||
@ -381,6 +383,24 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
|
||||
disabled: () => model.getDesignerModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* tool for node link edition
|
||||
*/
|
||||
const editThemeConfiguration: ActionConfig = {
|
||||
icon: <ColorLensOutlined />,
|
||||
tooltip: intl.formatMessage({ id: 'editor-panel.tooltip-theme', defaultMessage: 'Theme' }),
|
||||
useClickToClose: true,
|
||||
title: intl.formatMessage({ id: 'editor-panel.theme-title', defaultMessage: 'Theme' }),
|
||||
options: [
|
||||
{
|
||||
render: (closeModal) => (
|
||||
<ThemeEditor closeModal={closeModal} themeModel={modelBuilder.getThemeModel()} />
|
||||
),
|
||||
},
|
||||
],
|
||||
disabled: () => model.getDesignerModel().filterSelectedTopics().length === 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* tool for node note edition
|
||||
*/
|
||||
@ -461,5 +481,6 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
|
||||
editNoteConfiguration,
|
||||
editLinkUrlConfiguration,
|
||||
addRelationConfiguration,
|
||||
editThemeConfiguration,
|
||||
];
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import { LineType } from './ConnectionLine';
|
||||
import XMLSerializerFactory from './persistence/XMLSerializerFactory';
|
||||
import ImageExpoterFactory from './export/ImageExporterFactory';
|
||||
import PositionType from './PositionType';
|
||||
import ThemeType from './model/ThemeType';
|
||||
|
||||
class Designer extends Events {
|
||||
private _mindmap: Mindmap | null;
|
||||
@ -70,7 +71,7 @@ class Designer extends Events {
|
||||
|
||||
private _model: DesignerModel;
|
||||
|
||||
private _workspace: Canvas;
|
||||
private _canvas: Canvas;
|
||||
|
||||
_eventBussDispatcher: EventBusDispatcher;
|
||||
|
||||
@ -108,7 +109,7 @@ class Designer extends Events {
|
||||
|
||||
// Init Screen manager..
|
||||
const screenManager = new ScreenManager(divElem);
|
||||
this._workspace = new Canvas(screenManager, this._model.getZoom(), this.isReadOnly());
|
||||
this._canvas = new Canvas(screenManager, this._model.getZoom(), this.isReadOnly());
|
||||
|
||||
// Init layout manager ...
|
||||
this._eventBussDispatcher = new EventBusDispatcher();
|
||||
@ -121,11 +122,11 @@ class Designer extends Events {
|
||||
// Register keyboard events ...
|
||||
DesignerKeyboard.register(this);
|
||||
|
||||
this._dragManager = this._buildDragManager(this._workspace);
|
||||
this._dragManager = this._buildDragManager(this._canvas);
|
||||
}
|
||||
this._registerWheelEvents();
|
||||
|
||||
this._relPivot = new RelationshipPivot(this._workspace, this);
|
||||
this._relPivot = new RelationshipPivot(this._canvas, this);
|
||||
|
||||
TopicEventDispatcher.configure(this.isReadOnly());
|
||||
|
||||
@ -168,7 +169,7 @@ class Designer extends Events {
|
||||
}
|
||||
|
||||
private _registerMouseEvents() {
|
||||
const workspace = this._workspace;
|
||||
const workspace = this._canvas;
|
||||
const screenManager = workspace.getScreenManager();
|
||||
const me = this;
|
||||
// Initialize workspace event listeners.
|
||||
@ -202,7 +203,7 @@ class Designer extends Events {
|
||||
|
||||
private _buildDragManager(workspace: Canvas): DragManager {
|
||||
const designerModel = this.getModel();
|
||||
const dragConnector = new DragConnector(designerModel, this._workspace);
|
||||
const dragConnector = new DragConnector(designerModel, this._canvas);
|
||||
const dragManager = new DragManager(workspace, this._eventBussDispatcher);
|
||||
const topics = designerModel.getTopics();
|
||||
|
||||
@ -263,7 +264,7 @@ class Designer extends Events {
|
||||
}
|
||||
|
||||
if (targetTopic) {
|
||||
topic.connectTo(targetTopic, this._workspace);
|
||||
topic.connectTo(targetTopic, this._canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,12 +333,12 @@ class Designer extends Events {
|
||||
return;
|
||||
}
|
||||
this.getModel().setZoom(zoom);
|
||||
this._workspace.setZoom(zoom);
|
||||
this._canvas.setZoom(zoom);
|
||||
}
|
||||
|
||||
zoomToFit(): void {
|
||||
this.getModel().setZoom(1);
|
||||
this._workspace.setZoom(1, true);
|
||||
this._canvas.setZoom(1, true);
|
||||
}
|
||||
|
||||
zoomOut(factor = 1.2) {
|
||||
@ -345,7 +346,7 @@ class Designer extends Events {
|
||||
const scale = model.getZoom() * factor;
|
||||
if (scale <= 7.0) {
|
||||
model.setZoom(scale);
|
||||
this._workspace.setZoom(scale);
|
||||
this._canvas.setZoom(scale);
|
||||
} else {
|
||||
$notify($msg('ZOOM_ERROR'));
|
||||
}
|
||||
@ -357,7 +358,7 @@ class Designer extends Events {
|
||||
|
||||
if (scale >= 0.3) {
|
||||
model.setZoom(scale);
|
||||
this._workspace.setZoom(scale);
|
||||
this._canvas.setZoom(scale);
|
||||
} else {
|
||||
$notify($msg('ZOOM_ERROR'));
|
||||
}
|
||||
@ -590,7 +591,7 @@ class Designer extends Events {
|
||||
}
|
||||
|
||||
// Current mouse position ....
|
||||
const screen = this._workspace.getScreenManager();
|
||||
const screen = this._canvas.getScreenManager();
|
||||
const pos = screen.getWorkspaceMousePosition(event);
|
||||
|
||||
// create a connection ...
|
||||
@ -605,7 +606,7 @@ class Designer extends Events {
|
||||
loadMap(mindmap: Mindmap): Promise<void> {
|
||||
this._mindmap = mindmap;
|
||||
|
||||
this._workspace.enableQueueRender(true);
|
||||
this._canvas.enableQueueRender(true);
|
||||
|
||||
// Init layout manager ...
|
||||
const size = { width: 25, height: 25 };
|
||||
@ -633,7 +634,7 @@ class Designer extends Events {
|
||||
const centralTopic = this.getModel().getCentralTopic();
|
||||
this.goToNode(centralTopic);
|
||||
|
||||
return this._workspace.enableQueueRender(false).then(() => {
|
||||
return this._canvas.enableQueueRender(false).then(() => {
|
||||
// Connect relationships ...
|
||||
const relationships = mindmap.getRelationships();
|
||||
relationships.forEach((relationship) => this._relationshipModelToRelationship(relationship));
|
||||
@ -642,7 +643,7 @@ class Designer extends Events {
|
||||
nodesGraph.forEach((topic) => topic.setVisibility(true));
|
||||
|
||||
// Enable workspace drag events ...
|
||||
this._workspace.registerEvents();
|
||||
this._canvas.registerEvents();
|
||||
// Finally, sort the map ...
|
||||
EventBus.instance.fireEvent('forceLayout');
|
||||
this.fireEvent('loadSuccess');
|
||||
@ -666,14 +667,13 @@ 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()!);
|
||||
|
||||
const result = this._buildNodeGraph(nodeModel, this.isReadOnly());
|
||||
result.setVisibility(false);
|
||||
|
||||
this._workspace.append(result);
|
||||
this._canvas.append(result);
|
||||
children.forEach((child) => {
|
||||
if (child) {
|
||||
this.nodeModelToTopic(child);
|
||||
@ -682,6 +682,13 @@ class Designer extends Events {
|
||||
return result;
|
||||
}
|
||||
|
||||
changeTheme(theme: ThemeType): void {
|
||||
console.log(`theme:${theme}`);
|
||||
this.getMindmap().setTheme(theme);
|
||||
const centralTopic = this.getModel().getCentralTopic();
|
||||
centralTopic.redraw(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {mindplot.model.RelationshipModel} model
|
||||
@ -701,7 +708,7 @@ class Designer extends Events {
|
||||
|
||||
result.setVisibility(sourceTopic.isVisible() && targetTopic.isVisible());
|
||||
|
||||
this._workspace.append(result);
|
||||
this._canvas.append(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -723,7 +730,7 @@ class Designer extends Events {
|
||||
targetTopic.deleteRelationship(rel);
|
||||
|
||||
this.getModel().removeRelationship(rel);
|
||||
this._workspace.removeChild(rel);
|
||||
this._canvas.removeChild(rel);
|
||||
|
||||
const mindmap = this.getMindmap();
|
||||
mindmap.deleteRelationship(rel.getModel());
|
||||
@ -771,14 +778,14 @@ class Designer extends Events {
|
||||
removeTopic(node: Topic): void {
|
||||
if (!node.isCentralTopic()) {
|
||||
const parent = node.getParent();
|
||||
node.disconnect(this._workspace);
|
||||
node.disconnect(this._canvas);
|
||||
|
||||
// remove children
|
||||
while (node.getChildren().length > 0) {
|
||||
this.removeTopic(node.getChildren()[0]);
|
||||
}
|
||||
|
||||
this._workspace.removeChild(node);
|
||||
this._canvas.removeChild(node);
|
||||
this.getModel().removeTopic(node);
|
||||
|
||||
// Delete this node from the model...
|
||||
@ -792,7 +799,7 @@ class Designer extends Events {
|
||||
}
|
||||
|
||||
private _resetEdition() {
|
||||
const screenManager = this._workspace.getScreenManager();
|
||||
const screenManager = this._canvas.getScreenManager();
|
||||
screenManager.fireEvent('update');
|
||||
screenManager.fireEvent('mouseup');
|
||||
this._relPivot.dispose();
|
||||
@ -946,7 +953,7 @@ class Designer extends Events {
|
||||
}
|
||||
|
||||
getWorkSpace(): Canvas {
|
||||
return this._workspace;
|
||||
return this._canvas;
|
||||
}
|
||||
|
||||
public get cleanScreen(): () => void {
|
||||
|
@ -64,12 +64,12 @@ class MainTopic extends Topic {
|
||||
return group;
|
||||
}
|
||||
|
||||
updateTopicShape(_targetTopic: Topic) {
|
||||
updateTopicShape() {
|
||||
this.redrawShapeType();
|
||||
}
|
||||
|
||||
disconnect(workspace: Canvas) {
|
||||
super.disconnect(workspace);
|
||||
disconnect(canvas: Canvas) {
|
||||
super.disconnect(canvas);
|
||||
|
||||
const innerShape = this.getInnerShape();
|
||||
innerShape.setVisibility(true);
|
||||
|
@ -50,6 +50,8 @@ const ICON_SCALING_FACTOR = 1.3;
|
||||
abstract class Topic extends NodeGraph {
|
||||
private _innerShape: LineTopicShape | Rect | LineTopicShape | null;
|
||||
|
||||
private _innerShapeType: TopicShapeType | undefined;
|
||||
|
||||
private _relationships: Relationship[];
|
||||
|
||||
private _isInWorkspace: boolean;
|
||||
@ -109,7 +111,6 @@ abstract class Topic extends NodeGraph {
|
||||
const model = this.getModel();
|
||||
model.setShapeType(type);
|
||||
|
||||
this.redrawShapeType();
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
@ -212,6 +213,7 @@ abstract class Topic extends NodeGraph {
|
||||
throw new Error(exhaustiveCheck);
|
||||
}
|
||||
}
|
||||
this._innerShapeType = shapeType;
|
||||
result.setPosition(0, 0);
|
||||
return result;
|
||||
}
|
||||
@ -970,7 +972,6 @@ abstract class Topic extends NodeGraph {
|
||||
// Remove from workspace.
|
||||
EventBus.instance.fireEvent('topicDisconect', this.getModel());
|
||||
|
||||
this.redrawShapeType();
|
||||
this.redraw(true);
|
||||
}
|
||||
}
|
||||
@ -1133,6 +1134,13 @@ abstract class Topic extends NodeGraph {
|
||||
if (this._isInWorkspace) {
|
||||
const theme = ThemeFactory.create(this.getModel());
|
||||
const textShape = this.getOrBuildTextShape();
|
||||
|
||||
// Needs to update inner shape ...
|
||||
const shapeType = this.getShapeType();
|
||||
if (shapeType !== this._innerShapeType) {
|
||||
this.redrawShapeType();
|
||||
}
|
||||
|
||||
// Update font ...
|
||||
const fontColor = this.getFontColor();
|
||||
textShape.setColor(fontColor);
|
||||
|
@ -114,7 +114,6 @@ 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),
|
||||
|
@ -49,6 +49,10 @@ class Mindmap extends IMindmap {
|
||||
return this._theme ? this._theme : 'classic';
|
||||
}
|
||||
|
||||
setTheme(value: ThemeType): void {
|
||||
this._theme = value;
|
||||
}
|
||||
|
||||
/** */
|
||||
getDescription(): string {
|
||||
return this._description;
|
||||
|
@ -28,6 +28,7 @@ import { LineType } from '../ConnectionLine';
|
||||
import { FontWeightType } from '../FontWeightType';
|
||||
import { FontStyleType } from '../FontStyleType';
|
||||
import { TopicShapeType } from '../model/INodeModel';
|
||||
import ThemeType from '../model/ThemeType';
|
||||
|
||||
class XMLSerializerTango implements XMLMindmapSerializer {
|
||||
private static MAP_ROOT_NODE = 'map';
|
||||
@ -49,6 +50,13 @@ class XMLSerializerTango implements XMLMindmapSerializer {
|
||||
if (name) {
|
||||
mapElem.setAttribute('name', this._rmXmlInv(name));
|
||||
}
|
||||
|
||||
// Add theme ...
|
||||
const theme = mindmap.getTheme();
|
||||
if (theme && theme !== 'classic') {
|
||||
mapElem.setAttribute('theme', theme);
|
||||
}
|
||||
|
||||
const version = mindmap.getVersion();
|
||||
if ($defined(version)) {
|
||||
mapElem.setAttribute('version', version);
|
||||
@ -257,6 +265,11 @@ class XMLSerializerTango implements XMLMindmapSerializer {
|
||||
const version = rootElem.getAttribute('version') || 'pela';
|
||||
const mindmap = new Mindmap(mapId, version);
|
||||
|
||||
const theme = rootElem.getAttribute('theme');
|
||||
if (theme) {
|
||||
mindmap.setTheme(theme as ThemeType);
|
||||
}
|
||||
|
||||
// Add all the topics nodes ...
|
||||
const childNodes = Array.from(rootElem.childNodes);
|
||||
const topicsNodes = childNodes
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable func-call-spacing */
|
||||
/*
|
||||
* Copyright [2021] [wisemapping]
|
||||
*
|
||||
@ -44,6 +45,7 @@ export type TopicStyleType = {
|
||||
|
||||
type StyleType = string | string[] | number | undefined | LineType;
|
||||
|
||||
// eslint-disable-next-line no-spaced-func
|
||||
const keyToModel = new Map<keyof TopicStyleType, (model: NodeModel) => StyleType>([
|
||||
['borderColor', (m: NodeModel) => m.getBorderColor()],
|
||||
['backgroundColor', (m: NodeModel) => m.getBackgroundColor()],
|
||||
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 78 KiB |