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 8329c901..e708bdfe 100644 --- a/packages/editor/src/classes/model/node-property-builder/index.ts +++ b/packages/editor/src/classes/model/node-property-builder/index.ts @@ -22,6 +22,7 @@ class NodePropertyBuilder { private topicShapeModel: NodeProperty; private topicIconModel: NodeProperty; private connetionStyleModel: NodeProperty; + private connectionColoreModel: NodeProperty; private noteModel: NodeProperty; private linkModel: NodeProperty; @@ -137,10 +138,6 @@ class NodePropertyBuilder { return this.fontColorModel; } - /** - * - * @returns model to get and set topic icon - */ getTopicIconModel(): NodeProperty { if (!this.topicIconModel) this.topicIconModel = { @@ -204,6 +201,15 @@ class NodePropertyBuilder { return this.connetionStyleModel; } + getConnectionColorModel(): NodeProperty { + if (!this.connectionColoreModel) + this.connectionColoreModel = { + getValue: () => this.selectedTopic()?.getConnectionColor(), + setValue: (value: string) => this.designer.changeConnectionColor(value), + }; + return this.connectionColoreModel; + } + getTopicShapeModel(): NodeProperty { if (!this.topicShapeModel) this.topicShapeModel = { diff --git a/packages/editor/src/components/editor-toolbar/configBuilder.tsx b/packages/editor/src/components/editor-toolbar/configBuilder.tsx index d3728d38..116df37d 100644 --- a/packages/editor/src/components/editor-toolbar/configBuilder.tsx +++ b/packages/editor/src/components/editor-toolbar/configBuilder.tsx @@ -36,6 +36,7 @@ import PolylineOutlined from '@mui/icons-material/PolylineOutlined'; import GestureOutlined from '@mui/icons-material/GestureOutlined'; import TimelineOutined from '@mui/icons-material/TimelineOutlined'; import ShareOutlined from '@mui/icons-material/ShareOutlined'; +import SwapCallsOutlined from '@mui/icons-material/SwapCallsOutlined'; import Palette from '@mui/icons-material/Square'; import SquareOutlined from '@mui/icons-material/SquareOutlined'; @@ -104,11 +105,7 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo }, null, { - icon: () => ( - - ), + icon: () => , tooltip: intl.formatMessage({ id: 'editor-panel.tooltip-topic-fill-color', defaultMessage: 'Fill color', @@ -120,16 +117,14 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo + /> ); }, }, ], }, { - icon: () => ( - - ), + icon: () => , tooltip: intl.formatMessage({ id: 'editor-panel.tooltip-topic-border-color', defaultMessage: 'Border color', @@ -141,7 +136,7 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo + /> ); }, }, @@ -158,15 +153,6 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo defaultMessage: 'Connection Style', }), options: [ - { - icon: , - tooltip: intl.formatMessage({ - id: 'editor-panel.tooltip-connection-style-curved-thin', - defaultMessage: 'Thin Curved', - }), - onClick: () => valueBulder.getConnectionStyleModel().setValue(LineType.THICK_CURVED), - selected: () => valueBulder.getConnectionStyleModel().getValue() === LineType.THICK_CURVED, - }, { icon: , tooltip: intl.formatMessage({ @@ -176,6 +162,15 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo onClick: () => valueBulder.getConnectionStyleModel().setValue(LineType.THICK_CURVED), selected: () => valueBulder.getConnectionStyleModel().getValue() === LineType.THICK_CURVED, }, + { + icon: , + tooltip: intl.formatMessage({ + id: 'editor-panel.tooltip-connection-style-curved-thin', + defaultMessage: 'Thin Curved', + }), + onClick: () => valueBulder.getConnectionStyleModel().setValue(LineType.THIN_CURVED), + selected: () => valueBulder.getConnectionStyleModel().getValue() === LineType.THIN_CURVED, + }, { icon: , tooltip: intl.formatMessage({ @@ -196,30 +191,30 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo selected: () => valueBulder.getConnectionStyleModel().getValue() === LineType.POLYLINE_CURVED, }, - // null, - // { - // icon: () => , - // tooltip: intl.formatMessage({ - // id: 'editor-panel.tooltip-connection-style-color', - // defaultMessage: 'Color', - // }), - // options: [ - // { - // render: (closeModal) => { - // return ( - // - // ); - // }, - // }, - // ], - // }, + null, + { + icon: () => , + tooltip: intl.formatMessage({ + id: 'editor-panel.tooltip-connection-color', + defaultMessage: 'Color', + }), + options: [ + { + render: (closeModal) => { + return ( + + ); + }, + }, + ], + }, ], disabled: () => { const selected = model.getDesignerModel().filterSelectedTopics(); - return selected.length === 0 || (selected.length === 1 && selected[0].isCentralTopic()); + return selected.length === 0; }, }; @@ -278,6 +273,7 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo onClick: valueBulder.getFontStyleModel().switchValue, selected: () => valueBulder.getFontStyleModel().getValue() === 'italic', }, + null, { icon: () => , tooltip: intl.formatMessage({ @@ -347,7 +343,7 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo { tooltip: 'Node note', render: (closeModal) => ( - + ), }, ], @@ -403,10 +399,10 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo deleteNodeToolbarConfiguration, colorAndShapeToolbarConfiguration, fontFormatToolbarConfiguration, + connectionStyleConfiguration, editIconConfiguration, editNoteConfiguration, editLinkUrlConfiguration, - connectionStyleConfiguration, addRelationConfiguration, ]; } diff --git a/packages/mindplot/src/components/ConnectionLine.ts b/packages/mindplot/src/components/ConnectionLine.ts index ff599501..5f38b850 100644 --- a/packages/mindplot/src/components/ConnectionLine.ts +++ b/packages/mindplot/src/components/ConnectionLine.ts @@ -37,23 +37,20 @@ class ConnectionLine { protected _lineType: LineType; - protected _line2d: Line; + protected _line: Line; private _type: LineType; + private _color: string; + constructor(sourceNode: Topic, targetNode: Topic, type: LineType = LineType.THIN_CURVED) { - $assert(targetNode, 'parentNode node can not be null'); - $assert(sourceNode, 'childNode node can not be null'); $assert(sourceNode !== targetNode, 'Circular connection'); this._targetTopic = targetNode; this._sourceTopic = sourceNode; this._type = type; - - const line = this._createLine(type); - - // Set line styles ... - this._line2d = line; + this._line = this.createLine(type); + this.updateColor(); } private _getCtrlPoints(sourceNode: Topic, targetNode: Topic) { @@ -66,53 +63,68 @@ class ConnectionLine { ]; } - protected _createLine(lineType: LineType): ConnectionLine { + protected createLine(lineType: LineType): ConnectionLine { this._lineType = lineType; let line: ConnectionLine; - const strokeColor = ConnectionLine.getStrokeColor(); switch (lineType) { case LineType.POLYLINE_MIDDLE: line = new PolyLine(); (line as PolyLine).setStyle('MiddleStraight'); - (line as PolyLine).setStroke(1, 'solid', strokeColor, 1); break; case LineType.POLYLINE_CURVED: line = new PolyLine(); (line as PolyLine).setStyle('Curved'); - (line as PolyLine).setStroke(1, 'solid', strokeColor, 1); break; case LineType.THIN_CURVED: line = new CurvedLine(); - (line as CurvedLine).setStroke(1, 'solid', strokeColor, 1); - (line as CurvedLine).setFill(strokeColor, 1); break; case LineType.THICK_CURVED: line = new CurvedLine(); - (line as CurvedLine).setStroke(1, 'solid', strokeColor, 1); - (line as CurvedLine).setFill(strokeColor, 1); (line as CurvedLine).setWidth(this._targetTopic.isCentralTopic() ? 15 : 3); break; default: throw new Error(`Unexpected line type. ${lineType}`); } - return line; } + private updateColor(): void { + const color = this._targetTopic.getConnectionColor(); + this._color = color; + switch (this._lineType) { + case LineType.POLYLINE_MIDDLE: + this._line.setStroke(1, 'solid', color, 1); + break; + case LineType.POLYLINE_CURVED: + this._line.setStroke(1, 'solid', color, 1); + break; + case LineType.THIN_CURVED: + this._line.setStroke(1, 'solid', color, 1); + this._line.setFill(color, 1); + break; + case LineType.THICK_CURVED: + this._line.setStroke(1, 'solid', color, 1); + this._line.setFill(color, 1); + break; + default: + throw new Error(`Unexpected line type. ${this._lineType}`); + } + } + setVisibility(value: boolean, fade = 0): void { - this._line2d.setVisibility(value, fade); + this._line.setVisibility(value, fade); } isVisible(): boolean { - return this._line2d.isVisible(); + return this._line.isVisible(); } setOpacity(opacity: number): void { - this._line2d.setOpacity(opacity); + this._line.setOpacity(opacity); } redraw(): void { - const line2d = this._line2d; + const line2d = this._line; const sourceTopic = this._sourceTopic; const sourcePosition = sourceTopic.getPosition(); @@ -132,10 +144,13 @@ class ConnectionLine { } // Add connector ... - this._positionateConnector(targetTopic); + this._positionLine(targetTopic); + + // Update color ... + this.updateColor(); } - protected _positionateConnector(targetTopic: Topic): void { + protected _positionLine(targetTopic: Topic): void { const targetPosition = targetTopic.getPosition(); const offset = TopicConfig.CONNECTOR_WIDTH / 2; const targetTopicSize = targetTopic.getSize(); @@ -161,16 +176,21 @@ class ConnectionLine { } setStroke(color: string, style: string, opacity: number) { - this._line2d.setStroke(null, null, color, opacity); + this._line.setStroke(null, null, color, opacity); + this._color = color; + } + + getStrokeColor(): string { + return this._color; } addToWorkspace(workspace: Workspace) { - workspace.append(this._line2d); - this._line2d.moveToBack(); + workspace.append(this._line); + this._line.moveToBack(); } removeFromWorkspace(workspace: Workspace) { - workspace.removeChild(this._line2d); + workspace.removeChild(this._line); } getTargetTopic(): Topic { @@ -186,7 +206,7 @@ class ConnectionLine { } getLine(): Line { - return this._line2d; + return this._line; } getType(): string { @@ -194,14 +214,12 @@ class ConnectionLine { } moveToBack(): void { - this._line2d.moveToBack(); + this._line.moveToBack(); } moveToFront() { - this._line2d.moveToFront(); + this._line.moveToFront(); } - - static getStrokeColor = () => '#495879'; } export default ConnectionLine; diff --git a/packages/mindplot/src/components/Designer.ts b/packages/mindplot/src/components/Designer.ts index 50d554fe..a6070e4b 100644 --- a/packages/mindplot/src/components/Designer.ts +++ b/packages/mindplot/src/components/Designer.ts @@ -439,56 +439,6 @@ class Designer extends Events { this._actionDispatcher.addTopics([childModel], [parentTopicId]); } - private _copyNodeProps(sourceModel: NodeModel, targetModel: NodeModel) { - // I don't copy the font size if the target is the source is the central topic. - if (sourceModel.getType() !== 'CentralTopic') { - const fontSize = sourceModel.getFontSize(); - if (fontSize) { - targetModel.setFontSize(fontSize); - } - } - - const fontFamily = sourceModel.getFontFamily(); - if (fontFamily) { - targetModel.setFontFamily(fontFamily); - } - - const fontColor = sourceModel.getFontColor(); - if (fontColor) { - targetModel.setFontColor(fontColor); - } - - const fontWeight = sourceModel.getFontWeight(); - if (fontWeight) { - targetModel.setFontWeight(fontWeight); - } - - const fontStyle = sourceModel.getFontStyle(); - if (fontStyle) { - targetModel.setFontStyle(fontStyle); - } - - const shape = sourceModel.getShapeType(); - if (shape) { - targetModel.setShapeType(shape); - } - - const borderColor = sourceModel.getBorderColor(); - if (borderColor) { - targetModel.setBorderColor(borderColor); - } - - const backgroundColor = sourceModel.getBackgroundColor(); - if (backgroundColor) { - targetModel.setBackgroundColor(backgroundColor); - } - - const connectType = sourceModel.getConnectionStyle(); - if ($defined(connectType)) { - targetModel.setConnectionStyle(connectType!); - } - } - private _createChildModel(topic: Topic, mousePos: Point = null): NodeModel { // Create a new node ... const parentModel = topic.getModel(); @@ -503,7 +453,7 @@ class Designer extends Events { const { position } = result; childModel.setPosition(position.x, position.y); - this._copyNodeProps(parentModel, childModel); + childModel.copy(parentModel); return childModel; } @@ -558,8 +508,7 @@ class Designer extends Events { const order = topic.getOrder() + 1; result.setOrder(order); result.setPosition(10, 10); // Set a dummy position ... - - this._copyNodeProps(model, result); + result.copy(model); } return result; @@ -884,6 +833,15 @@ class Designer extends Events { } } + changeConnectionColor(value: string): void { + const topicsIds = this.getModel() + .filterSelectedTopics() + .map((t) => t.getId()); + if (topicsIds.length > 0) { + this._actionDispatcher.changeConnectionColorToTopic(topicsIds, value); + } + } + changeFontWeight(): void { const topicsIds = this.getModel().filterTopicsIds(); if (topicsIds.length > 0) { diff --git a/packages/mindplot/src/components/Relationship.ts b/packages/mindplot/src/components/Relationship.ts index ea290880..1dc5ddb6 100644 --- a/packages/mindplot/src/components/Relationship.ts +++ b/packages/mindplot/src/components/Relationship.ts @@ -56,15 +56,15 @@ class Relationship extends ConnectionLine { const strokeColor = Relationship.getStrokeColor(); // Build line .. - this._line2d.setIsSrcControlPointCustom(false); - this._line2d.setIsDestControlPointCustom(false); - this._line2d.setCursor('pointer'); - this._line2d.setStroke(1, 'solid', strokeColor); - this._line2d.setDashed(4, 2); - this._line2d.setTestId(`${model.getFromNode()}-${model.getToNode()}-relationship`); + this._line.setIsSrcControlPointCustom(false); + this._line.setIsDestControlPointCustom(false); + this._line.setCursor('pointer'); + this._line.setStroke(1, 'solid', strokeColor); + this._line.setDashed(4, 2); + this._line.setTestId(`${model.getFromNode()}-${model.getToNode()}-relationship`); // Build focus shape ... - this._focusShape = this._createLine(LineType.THIN_CURVED); + this._focusShape = this.createLine(LineType.THIN_CURVED); this._focusShape.setStroke(8, 'solid', '#3f96ff'); this._focusShape.setIsSrcControlPointCustom(false); this._focusShape.setIsDestControlPointCustom(false); @@ -122,7 +122,7 @@ class Relationship extends ConnectionLine { } private updatePositions() { - const line2d = this._line2d; + const line2d = this._line; const sourceTopic = this._sourceTopic; const sPos = sourceTopic.getPosition(); @@ -132,7 +132,7 @@ class Relationship extends ConnectionLine { tPos = Shape.workoutIncomingConnectionPoint(targetTopic, sPos); } - this._line2d.setStroke(2); + this._line.setStroke(2); let ctrlPoints: [Point, Point]; // Position line ... @@ -164,7 +164,7 @@ class Relationship extends ConnectionLine { this.positionArrows(); // Add connector ... - this._positionateConnector(targetTopic); + this._positionLine(targetTopic); // Poisition refresh shape ... this.positionRefreshShape(); @@ -173,7 +173,7 @@ class Relationship extends ConnectionLine { redraw(): void { this.updatePositions(); - this._line2d.moveToFront(); + this._line.moveToFront(); this._startArrow.moveToBack(); if (this._endArrow) { this._endArrow.moveToBack(); @@ -189,24 +189,24 @@ class Relationship extends ConnectionLine { } private positionArrows(): void { - const tpos = this._line2d.getTo(); - const spos = this._line2d.getFrom(); + const tpos = this._line.getTo(); + const spos = this._line.getFrom(); this._startArrow.setFrom(spos.x, spos.y); if (this._endArrow) { this._endArrow.setFrom(tpos.x, tpos.y); } - if (this._line2d.getType() === 'CurvedLine') { - const controlPoints = this._line2d.getControlPoints(); + if (this._line.getType() === 'CurvedLine') { + const controlPoints = this._line.getControlPoints(); this._startArrow.setControlPoint(controlPoints[0]); if (this._endArrow) { this._endArrow.setControlPoint(controlPoints[1]); } } else { - this._startArrow.setControlPoint(this._line2d.getTo()); + this._startArrow.setControlPoint(this._line.getTo()); if (this._endArrow) { - this._endArrow.setControlPoint(this._line2d.getFrom()); + this._endArrow.setControlPoint(this._line.getFrom()); } } } @@ -218,9 +218,9 @@ class Relationship extends ConnectionLine { workspace.append(this._controlPointsController); if (workspace.isReadOnly()) { - this._line2d.setCursor('default'); + this._line.setCursor('default'); } else { - this._line2d.addEvent('click', this._onFocusHandler); + this._line.addEvent('click', this._onFocusHandler); this._focusShape.addEvent('click', this._onFocusHandler); } this._isInWorkspace = true; @@ -237,7 +237,7 @@ class Relationship extends ConnectionLine { workspace.removeChild(this._focusShape); workspace.removeChild(this._controlPointsController); - this._line2d.removeEvent('click', this._onFocusHandler); + this._line.removeEvent('click', this._onFocusHandler); this._isInWorkspace = false; workspace.removeChild(this._startArrow); if (this._endArrow) { @@ -268,10 +268,10 @@ class Relationship extends ConnectionLine { } private positionRefreshShape(): void { - const sPos = this._line2d.getFrom(); - const tPos = this._line2d.getTo(); + const sPos = this._line.getFrom(); + const tPos = this._line.getTo(); - const ctrlPoints = this._line2d.getControlPoints(); + const ctrlPoints = this._line.getControlPoints(); this._focusShape.setFrom(sPos.x, sPos.y); this._focusShape.setTo(tPos.x, tPos.y); @@ -289,7 +289,7 @@ class Relationship extends ConnectionLine { type = 'mousedown'; } - const line = this._line2d; + const line = this._line; line.addEvent(type, listener); } @@ -342,7 +342,7 @@ class Relationship extends ConnectionLine { $assert($defined(x), 'x must be defined'); $assert($defined(y), 'y must be defined'); - this._line2d.setFrom(x, y); + this._line.setFrom(x, y); this._startArrow.setFrom(x, y); } @@ -350,18 +350,18 @@ class Relationship extends ConnectionLine { $assert($defined(x), 'x must be defined'); $assert($defined(y), 'y must be defined'); - this._line2d.setTo(x, y); + this._line.setTo(x, y); if (this._endArrow) this._endArrow.setFrom(x, y); } setSrcControlPoint(control: PositionType): void { - this._line2d.setSrcControlPoint(control); + this._line.setSrcControlPoint(control); this._focusShape.setSrcControlPoint(control); this._startArrow.setControlPoint(control); } setDestControlPoint(control: PositionType) { - this._line2d.setDestControlPoint(control); + this._line.setDestControlPoint(control); this._focusShape.setSrcControlPoint(control); if (this._showEndArrow) { this._endArrow.setControlPoint(control); @@ -369,23 +369,23 @@ class Relationship extends ConnectionLine { } getControlPoints(): PositionType { - return this._line2d.getControlPoints(); + return this._line.getControlPoints(); } isSrcControlPointCustom(): boolean { - return this._line2d.isSrcControlPointCustom(); + return this._line.isSrcControlPointCustom(); } isDestControlPointCustom(): boolean { - return this._line2d.isDestControlPointCustom(); + return this._line.isDestControlPointCustom(); } setIsSrcControlPointCustom(isCustom: boolean) { - this._line2d.setIsSrcControlPointCustom(isCustom); + this._line.setIsSrcControlPointCustom(isCustom); } setIsDestControlPointCustom(isCustom: boolean) { - this._line2d.setIsDestControlPointCustom(isCustom); + this._line.setIsDestControlPointCustom(isCustom); } getId(): number { @@ -393,7 +393,7 @@ class Relationship extends ConnectionLine { } fireEvent(type: string, event): void { - const elem = this._line2d; + const elem = this._line; elem.trigger(type, event); } diff --git a/packages/mindplot/src/components/ShrinkConnector.ts b/packages/mindplot/src/components/ShrinkConnector.ts index 5acd03aa..7d3139e5 100644 --- a/packages/mindplot/src/components/ShrinkConnector.ts +++ b/packages/mindplot/src/components/ShrinkConnector.ts @@ -31,7 +31,8 @@ class ShirinkConnector { const ellipse = new Elipse(TopicConfig.INNER_RECT_ATTRIBUTES); this._ellipse = ellipse; - ellipse.setFill('rgb(62,118,179)'); + const fillColor = topic.getConnectionColor(); + ellipse.setFill(fillColor); ellipse.setSize(TopicConfig.CONNECTOR_WIDTH, TopicConfig.CONNECTOR_WIDTH); ellipse.addEvent('click', (event: Event) => { diff --git a/packages/mindplot/src/components/StandaloneActionDispatcher.ts b/packages/mindplot/src/components/StandaloneActionDispatcher.ts index dad30a6b..cf974a21 100644 --- a/packages/mindplot/src/components/StandaloneActionDispatcher.ts +++ b/packages/mindplot/src/components/StandaloneActionDispatcher.ts @@ -110,7 +110,7 @@ class StandaloneActionDispatcher extends ActionDispatcher { topic.setFontStyle(style, true); return result; }; - const command = new GenericFunctionCommand(commandFunc, topicsIds, null); + const command = new GenericFunctionCommand(commandFunc, topicsIds, undefined); this.execute(command); } @@ -225,9 +225,9 @@ class StandaloneActionDispatcher extends ActionDispatcher { } changeConnectionStyleToTopic(topicsIds: number[], lineType: LineType) { - const commandFunc = (topic: Topic, commandShapeType: LineType) => { + const commandFunc = (topic: Topic, type: LineType) => { const result = topic.getConnectionStyle(); - topic.setConnectionStyle(commandShapeType); + topic.setConnectionStyle(type); return result; }; @@ -235,6 +235,17 @@ class StandaloneActionDispatcher extends ActionDispatcher { this.execute(command); } + changeConnectionColorToTopic(topicsIds: number[], value: string) { + const commandFunc = (topic: Topic, color: string) => { + const result: string = topic.getConnectionColor(); + topic.setConnectionColor(color); + return result; + }; + + const command = new GenericFunctionCommand(commandFunc, topicsIds, value); + this.execute(command); + } + changeFontWeightToTopic(topicsIds: number[]) { $assert(topicsIds, 'topicsIds can not be null'); @@ -246,7 +257,7 @@ class StandaloneActionDispatcher extends ActionDispatcher { return result; }; - const command = new GenericFunctionCommand(commandFunc, topicsIds, null); + const command = new GenericFunctionCommand(commandFunc, topicsIds, undefined); this.execute(command); } diff --git a/packages/mindplot/src/components/Topic.ts b/packages/mindplot/src/components/Topic.ts index fbc9e428..9a616f27 100644 --- a/packages/mindplot/src/components/Topic.ts +++ b/packages/mindplot/src/components/Topic.ts @@ -114,11 +114,6 @@ abstract class Topic extends NodeGraph { if ($defined(updateModel) && updateModel) { model.setShapeType(type); } - // If shape is line, reset background color to default. - if (type === 'line') { - const color = TopicStyle.defaultBackgroundColor(this); - this.setBackgroundColor(color); - } const oldInnerShape = this.getInnerShape(); if (oldInnerShape != null) { @@ -163,11 +158,34 @@ abstract class Topic extends NodeGraph { getConnectionStyle(): LineType { const model = this.getModel(); - let result = model.getConnectionStyle(); - if (!result) { - result = TopicStyle.defaultConnectionType(this); + 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; + return result!; + } + + 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) { + result = parent.getConnectionColor(); + } else { + result = TopicStyle.defaultConnectionColor(this); + } + } + return result!; } private _removeInnerShape(): ElementClass { @@ -205,7 +223,7 @@ abstract class Topic extends NodeGraph { $assert(attributes, 'attributes can not be null'); $assert(shapeType, 'shapeType can not be null'); - let result; + let result: ElementClass; if (shapeType === 'rectangle') { result = new Rect(0, attributes); } else if (shapeType === 'image') { @@ -229,22 +247,21 @@ abstract class Topic extends NodeGraph { } else if (shapeType === 'rounded rectangle') { result = new Rect(0.3, attributes); } else if (shapeType === 'line') { + const stokeColor = this.getConnectionColor(); result = new Line({ - strokeColor: '#495879', + strokeColor: stokeColor, strokeWidth: 1, }); + const me = this; result.setSize = function setSize(width: number, height: number) { - this.size = { - width, - height, - }; + this.size = { width, height }; result.setFrom(0, height); result.setTo(width, height); - // Lines will have the same color of the default connection lines... - const stokeColor = ConnectionLine.getStrokeColor(); - result.setStroke(1, 'solid', stokeColor); + // // Lines will have the same color of the default connection lines... + const color = me.getConnectionColor(); + result.setStroke(1, 'solid', color); }; result.getSize = function getSize() { @@ -522,7 +539,7 @@ abstract class Topic extends NodeGraph { if ($defined(updateModel) && updateModel) { const model = this.getModel(); - model.setText(text); + model.setText(text || undefined); } } @@ -552,13 +569,29 @@ abstract class Topic extends NodeGraph { model.setConnectionStyle(type); // Needs to change change all the lines types. Outgoing are part of the children. - this.getChildren().map((topic: Topic) => topic.redraw()); + this.getChildren().forEach((topic: Topic) => topic.redraw()); - // If chidren nodes does not children, set the connection style too. We don't want to have cascade changes on all the branches. - model - .getChildren() - .filter((c) => c.getChildren().length === 0) - .forEach((c) => c.setConnectionStyle(type)); + // If connection of the childen matches, just reset the style in the model. + this.getChildren().forEach((topic: Topic) => { + if (topic.getModel().getConnectionStyle() === type) { + topic.getModel().setConnectionStyle(undefined); + } + }); + } + + setConnectionColor(value: string): void { + const model = this.getModel(); + model.setConnectionColor(value); + + // Needs to change change all the lines color. Outgoing are part of the children. + this.getChildren().forEach((topic: Topic) => topic.redraw()); + + // If connection of the childen matches, just reset the style in the model. + this.getChildren().forEach((topic: Topic) => { + if (topic.getModel().getConnectionColor() === value) { + topic.getModel().setConnectionColor(undefined); + } + }); } private _setBackgroundColor(color: string, updateModel: boolean) { @@ -1247,19 +1280,33 @@ abstract class Topic extends NodeGraph { let result = false; if (this._isInWorkspace) { // Adjust connection line if there is a change in the parent... - const connStyleChanged = - this._outgoingLine?.getLineType() !== this.getParent()?.getConnectionStyle(); - if (this._outgoingLine && connStyleChanged) { - // Todo: Review static reference ... - const workspace = designer.getWorkSpace(); - this._outgoingLine.removeFromWorkspace(workspace); + if (this._outgoingLine) { + // Has the style change ? + const connStyleChanged = + this._outgoingLine.getLineType() !== this.getParent()!.getConnectionStyle(); + if (connStyleChanged) { + // Todo: Review static reference ... + const workspace = designer.getWorkSpace(); + this._outgoingLine.removeFromWorkspace(workspace); - const targetTopic = this.getOutgoingConnectedTopic()!; - this._outgoingLine = this.createConnectionLine(targetTopic); - this._outgoingLine.setVisibility(this.isVisible()); - workspace.append(this._outgoingLine); + const targetTopic = this.getOutgoingConnectedTopic()!; + this._outgoingLine = this.createConnectionLine(targetTopic); + this._outgoingLine.setVisibility(this.isVisible()); + workspace.append(this._outgoingLine); - result = true; + // Update all the children... + this.getChildren().forEach((t) => t.redraw()); + result = true; + } + + // Has the color changed ? + const color = this._outgoingLine.getStrokeColor(); + const connColorChanged = color !== this.getParent()!.getConnectionColor(); + if (connColorChanged) { + this._outgoingLine.redraw(); + this._connector.setFill(color); + this.getChildren().forEach((t) => t.redraw()); + } } } return result; diff --git a/packages/mindplot/src/components/TopicStyle.ts b/packages/mindplot/src/components/TopicStyle.ts index 6c3f571c..ff5816b2 100644 --- a/packages/mindplot/src/components/TopicStyle.ts +++ b/packages/mindplot/src/components/TopicStyle.ts @@ -28,10 +28,12 @@ type FontStlye = { weight: string; color: string; }; + type TopicStyleType = { borderColor: string; backgroundColor: string; connectionStyle: LineType; + connectionColor: string; fontStyle: FontStlye; msgKey: string; shapeType: TopicShapeType; @@ -49,6 +51,7 @@ const TopicDefaultStyles = { color: '#ffffff', }, connectionStyle: LineType.THICK_CURVED, + connectionColor: '#495879', msgKey: 'CENTRAL_TOPIC', shapeType: 'rounded rectangle' as TopicShapeType, }, @@ -63,6 +66,7 @@ const TopicDefaultStyles = { color: 'rgb(82,92,97)', }, connectionStyle: LineType.THICK_CURVED, + connectionColor: '#495879', msgKey: 'MAIN_TOPIC', shapeType: 'line' as TopicShapeType, }, @@ -76,7 +80,8 @@ const TopicDefaultStyles = { weight: 'normal', color: 'rgb(82,92,97)', }, - connectionStyle: LineType.THIN_CURVED, + connectionStyle: LineType.THICK_CURVED, + connectionColor: '#495879', msgKey: 'SUB_TOPIC', shapeType: 'line' as TopicShapeType, }, @@ -93,6 +98,7 @@ const TopicDefaultStyles = { }, msgKey: 'ISOLATED_TOPIC', connectionStyle: LineType.THIN_CURVED, + connectionColor: '#495879', shapeType: 'line' as TopicShapeType, }, }; @@ -147,6 +153,10 @@ class TopicStyle { static defaultConnectionType(topic: Topic): LineType { return this._getStyles(topic).connectionStyle; } + + static defaultConnectionColor(topic: Topic): string { + return this._getStyles(topic).connectionColor; + } } export default TopicStyle; diff --git a/packages/mindplot/src/components/commands/GenericFunctionCommand.ts b/packages/mindplot/src/components/commands/GenericFunctionCommand.ts index 1c0c5592..d61aa4e3 100644 --- a/packages/mindplot/src/components/commands/GenericFunctionCommand.ts +++ b/packages/mindplot/src/components/commands/GenericFunctionCommand.ts @@ -20,7 +20,7 @@ import Command from '../Command'; import CommandContext from '../CommandContext'; import Topic from '../Topic'; -type CommandTypes = string | object | boolean | number | null; +type CommandTypes = string | object | boolean | number | undefined; class GenericFunctionCommand extends Command { private _value: CommandTypes; diff --git a/packages/mindplot/src/components/export/FreemindExporter.ts b/packages/mindplot/src/components/export/FreemindExporter.ts index 3ffc33c6..14e75c78 100644 --- a/packages/mindplot/src/components/export/FreemindExporter.ts +++ b/packages/mindplot/src/components/export/FreemindExporter.ts @@ -251,7 +251,7 @@ class FreemindExporter extends Exporter { const fontFamily: string = mindmapTopic.getFontFamily(); const fontSize: number = mindmapTopic.getFontSize(); const fontColor: string = mindmapTopic.getFontColor(); - const fontWeigth: string | number | boolean = mindmapTopic.getFontWeight(); + const fontWeigth: string | number | boolean | undefined = mindmapTopic.getFontWeight(); const fontStyle: string = mindmapTopic.getFontStyle(); if (fontFamily || fontSize || fontColor || fontWeigth || fontStyle) { diff --git a/packages/mindplot/src/components/model/INodeModel.ts b/packages/mindplot/src/components/model/INodeModel.ts index b1af01c2..d8156852 100644 --- a/packages/mindplot/src/components/model/INodeModel.ts +++ b/packages/mindplot/src/components/model/INodeModel.ts @@ -70,7 +70,7 @@ abstract class INodeModel { this.putProperty('type', type); } - setText(text: string | null): void { + setText(text: string | undefined): void { this.putProperty('text', text); } @@ -221,14 +221,22 @@ abstract class INodeModel { this.putProperty('shrunken', value); } - setConnectionStyle(type: LineType): void { + setConnectionStyle(type: LineType | undefined): void { this.putProperty('connectionStyle', type); } - getConnectionStyle(): LineType | null { + getConnectionStyle(): LineType | undefined { return this.getProperty('connectionStyle') as LineType; } + setConnectionColor(value: string | undefined): void { + this.putProperty('connectionColor', value); + } + + getConnectionColor(): string | undefined { + return this.getProperty('connectionColor') as string; + } + isNodeModel(): boolean { return true; } @@ -297,9 +305,9 @@ abstract class INodeModel { abstract getPropertiesKeys(): string[]; - abstract getProperty(key: string): number | string | boolean; + abstract getProperty(key: string): number | string | boolean | undefined; - abstract putProperty(key: string, value: number | string | boolean | null): void; + abstract putProperty(key: string, value: number | string | boolean | undefined): void; abstract setParent(parent: INodeModel): void; diff --git a/packages/mindplot/src/components/model/NodeModel.ts b/packages/mindplot/src/components/model/NodeModel.ts index 32cd2957..8f58f616 100644 --- a/packages/mindplot/src/components/model/NodeModel.ts +++ b/packages/mindplot/src/components/model/NodeModel.ts @@ -108,7 +108,7 @@ class NodeModel extends INodeModel { * @param value * @throws will throw an error if key is null or undefined */ - putProperty(key: string, value: string | number | boolean) { + putProperty(key: string, value: string | number | boolean): void { $defined(key, 'key can not be null'); this._properties[key] = value; } @@ -117,7 +117,7 @@ class NodeModel extends INodeModel { return this._properties; } - getProperty(key: string): number | string | boolean { + getProperty(key: string): number | string | boolean | undefined { $defined(key, 'key can not be null'); return this._properties[key]; } @@ -135,6 +135,61 @@ 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 borderColor = value.getBorderColor(); + if (borderColor) { + this.setBorderColor(borderColor); + } + + const backgroundColor = value.getBackgroundColor(); + if (backgroundColor) { + this.setBackgroundColor(backgroundColor); + } + + const connectType = value.getConnectionStyle(); + if ($defined(connectType)) { + this.setConnectionStyle(connectType!); + } + + const connectColor = value.getConnectionColor(); + if ($defined(connectColor)) { + this.setConnectionColor(connectColor!); + } + } + deepCopy(): NodeModel { const result = new NodeModel(this.getType(), this._mindmap); result._children = this._children.map((node) => { diff --git a/packages/mindplot/src/components/persistence/XMLSerializerTango.ts b/packages/mindplot/src/components/persistence/XMLSerializerTango.ts index 6d31a643..3215ee32 100644 --- a/packages/mindplot/src/components/persistence/XMLSerializerTango.ts +++ b/packages/mindplot/src/components/persistence/XMLSerializerTango.ts @@ -148,7 +148,7 @@ class XMLSerializerTango implements XMLMindmapSerializer { } const brColor = topic.getBorderColor(); - if ($defined(brColor)) { + if (brColor) { parentTopic.setAttribute('brColor', brColor); } @@ -157,6 +157,11 @@ class XMLSerializerTango implements XMLMindmapSerializer { parentTopic.setAttribute('connStyle', `${connectionStyle}`); } + const connectionColor = topic.getConnectionColor(); + if (connectionColor) { + parentTopic.setAttribute('connColor', connectionColor); + } + const metadata = topic.getMetadata(); if ($defined(metadata)) { parentTopic.setAttribute('metadata', metadata); @@ -351,6 +356,11 @@ class XMLSerializerTango implements XMLMindmapSerializer { topic.setConnectionStyle(lineType); } + const connColor = domElem.getAttribute('connColor'); + if ($defined(connColor) && connColor) { + topic.setConnectionColor(connColor); + } + const borderColor = domElem.getAttribute('brColor'); if (borderColor) { topic.setBorderColor(borderColor); diff --git a/packages/web2d/src/components/peer/svg/CurvedLinePeer.js b/packages/web2d/src/components/peer/svg/CurvedLinePeer.js index 0d5b7dfb..bd27586f 100644 --- a/packages/web2d/src/components/peer/svg/CurvedLinePeer.js +++ b/packages/web2d/src/components/peer/svg/CurvedLinePeer.js @@ -24,12 +24,11 @@ class CurvedLinePeer extends ElementPeer { const svgElement = window.document.createElementNS(ElementPeer.svgNamespace, 'path'); super(svgElement); this._style = { fill: '#495879' }; - this._updateStyle(); this._customControlPoint_1 = false; this._customControlPoint_2 = false; this._control1 = new Point(0, 0); this._control2 = new Point(0, 0); - this._width = 1; + this.setWidth(1); } setSrcControlPoint(control) {