diff --git a/packages/editor/cypress/e2e/topicShape.cy.ts b/packages/editor/cypress/e2e/topicShape.cy.ts index fc012153..0ee796a5 100644 --- a/packages/editor/cypress/e2e/topicShape.cy.ts +++ b/packages/editor/cypress/e2e/topicShape.cy.ts @@ -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'); diff --git a/packages/editor/cypress/snapshots/topicFontChange.cy.ts/changeFontColor.snap.png b/packages/editor/cypress/snapshots/topicFontChange.cy.ts/changeFontColor.snap.png index 551f7962..bf15847d 100644 Binary files a/packages/editor/cypress/snapshots/topicFontChange.cy.ts/changeFontColor.snap.png and b/packages/editor/cypress/snapshots/topicFontChange.cy.ts/changeFontColor.snap.png differ diff --git a/packages/editor/cypress/snapshots/topicFontChange.cy.ts/changeFontItalic.snap.png b/packages/editor/cypress/snapshots/topicFontChange.cy.ts/changeFontItalic.snap.png index 47e77489..f132121a 100644 Binary files a/packages/editor/cypress/snapshots/topicFontChange.cy.ts/changeFontItalic.snap.png and b/packages/editor/cypress/snapshots/topicFontChange.cy.ts/changeFontItalic.snap.png differ diff --git a/packages/editor/cypress/snapshots/topicShape.cy.ts/changeToRoundedRectangle.snap.png b/packages/editor/cypress/snapshots/topicShape.cy.ts/changeToRoundedRectangle.snap.png index 9e97510f..89438808 100644 Binary files a/packages/editor/cypress/snapshots/topicShape.cy.ts/changeToRoundedRectangle.snap.png and b/packages/editor/cypress/snapshots/topicShape.cy.ts/changeToRoundedRectangle.snap.png differ diff --git a/packages/editor/src/classes/model/node-property-builder/index.ts b/packages/editor/src/classes/model/node-property-builder/index.ts index a93fd351..0826182b 100644 --- a/packages/editor/src/classes/model/node-property-builder/index.ts +++ b/packages/editor/src/classes/model/node-property-builder/index.ts @@ -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; private noteModel: NodeProperty; private linkModel: NodeProperty; + private _themeModel: NodeProperty; 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 { // const selected = this.selectedTopic(); if (!this.linkModel) @@ -112,6 +110,18 @@ class NodePropertyBuilder { return this.linkModel; } + getThemeModel(): NodeProperty { + // 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 diff --git a/packages/editor/src/components/action-widget/pane/theme-editor/index.tsx b/packages/editor/src/components/action-widget/pane/theme-editor/index.tsx new file mode 100644 index 00000000..2c4bc8ba --- /dev/null +++ b/packages/editor/src/components/action-widget/pane/theme-editor/index.tsx @@ -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 | 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 ( + + + } label="Classic" /> + } label="Summer" /> + + + ); +}; + +export default ThemeEditor; diff --git a/packages/editor/src/components/editor-toolbar/configBuilder.tsx b/packages/editor/src/components/editor-toolbar/configBuilder.tsx index 63e26d37..4febfaef 100644 --- a/packages/editor/src/components/editor-toolbar/configBuilder.tsx +++ b/packages/editor/src/components/editor-toolbar/configBuilder.tsx @@ -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: , + 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) => ( + + ), + }, + ], + 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, ]; } diff --git a/packages/mindplot/src/components/Designer.ts b/packages/mindplot/src/components/Designer.ts index 45791373..dc80af23 100644 --- a/packages/mindplot/src/components/Designer.ts +++ b/packages/mindplot/src/components/Designer.ts @@ -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 { 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 { diff --git a/packages/mindplot/src/components/MainTopic.ts b/packages/mindplot/src/components/MainTopic.ts index ee6b5956..3b17f768 100644 --- a/packages/mindplot/src/components/MainTopic.ts +++ b/packages/mindplot/src/components/MainTopic.ts @@ -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); diff --git a/packages/mindplot/src/components/Topic.ts b/packages/mindplot/src/components/Topic.ts index d2f204b5..aa8ed502 100644 --- a/packages/mindplot/src/components/Topic.ts +++ b/packages/mindplot/src/components/Topic.ts @@ -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); diff --git a/packages/mindplot/src/components/layout/OriginalLayout.ts b/packages/mindplot/src/components/layout/OriginalLayout.ts index dc2ae3e4..6a7ba47a 100644 --- a/packages/mindplot/src/components/layout/OriginalLayout.ts +++ b/packages/mindplot/src/components/layout/OriginalLayout.ts @@ -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), diff --git a/packages/mindplot/src/components/model/Mindmap.ts b/packages/mindplot/src/components/model/Mindmap.ts index 74f60379..77191e45 100644 --- a/packages/mindplot/src/components/model/Mindmap.ts +++ b/packages/mindplot/src/components/model/Mindmap.ts @@ -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; diff --git a/packages/mindplot/src/components/persistence/XMLSerializerTango.ts b/packages/mindplot/src/components/persistence/XMLSerializerTango.ts index 71d662a6..1c650ab5 100644 --- a/packages/mindplot/src/components/persistence/XMLSerializerTango.ts +++ b/packages/mindplot/src/components/persistence/XMLSerializerTango.ts @@ -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 diff --git a/packages/mindplot/src/components/theme/DefaultTheme.ts b/packages/mindplot/src/components/theme/DefaultTheme.ts index 4680ea01..0f47e911 100644 --- a/packages/mindplot/src/components/theme/DefaultTheme.ts +++ b/packages/mindplot/src/components/theme/DefaultTheme.ts @@ -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 StyleType>([ ['borderColor', (m: NodeModel) => m.getBorderColor()], ['backgroundColor', (m: NodeModel) => m.getBackgroundColor()], diff --git a/packages/webapp/cypress/snapshots/editor.cy.ts/editor-page.snap.png b/packages/webapp/cypress/snapshots/editor.cy.ts/editor-page.snap.png index 47c173d1..a4a06552 100644 Binary files a/packages/webapp/cypress/snapshots/editor.cy.ts/editor-page.snap.png and b/packages/webapp/cypress/snapshots/editor.cy.ts/editor-page.snap.png differ diff --git a/packages/webapp/cypress/snapshots/registration.cy.ts/registration-page.snap.png b/packages/webapp/cypress/snapshots/registration.cy.ts/registration-page.snap.png index 44481325..d266a1d6 100644 Binary files a/packages/webapp/cypress/snapshots/registration.cy.ts/registration-page.snap.png and b/packages/webapp/cypress/snapshots/registration.cy.ts/registration-page.snap.png differ