Add theme support.

This commit is contained in:
Paulo Gustavo Veiga 2023-02-13 20:17:27 -08:00
parent 9fad52a04d
commit f28e23490e
16 changed files with 145 additions and 34 deletions

View File

@ -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');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 107 KiB

View File

@ -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

View File

@ -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;

View File

@ -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,
];
}

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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),

View File

@ -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;

View File

@ -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

View File

@ -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()],

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 78 KiB