Add connection color

This commit is contained in:
Paulo Gustavo Veiga 2022-12-31 01:50:41 -08:00
parent f2c9762446
commit f6b7d19cb1
15 changed files with 338 additions and 219 deletions

View File

@ -22,6 +22,7 @@ class NodePropertyBuilder {
private topicShapeModel: NodeProperty<TopicShapeType>; private topicShapeModel: NodeProperty<TopicShapeType>;
private topicIconModel: NodeProperty<string>; private topicIconModel: NodeProperty<string>;
private connetionStyleModel: NodeProperty<LineType>; private connetionStyleModel: NodeProperty<LineType>;
private connectionColoreModel: NodeProperty<string>;
private noteModel: NodeProperty<string>; private noteModel: NodeProperty<string>;
private linkModel: NodeProperty<string>; private linkModel: NodeProperty<string>;
@ -137,10 +138,6 @@ class NodePropertyBuilder {
return this.fontColorModel; return this.fontColorModel;
} }
/**
*
* @returns model to get and set topic icon
*/
getTopicIconModel(): NodeProperty<string> { getTopicIconModel(): NodeProperty<string> {
if (!this.topicIconModel) if (!this.topicIconModel)
this.topicIconModel = { this.topicIconModel = {
@ -204,6 +201,15 @@ class NodePropertyBuilder {
return this.connetionStyleModel; return this.connetionStyleModel;
} }
getConnectionColorModel(): NodeProperty<string> {
if (!this.connectionColoreModel)
this.connectionColoreModel = {
getValue: () => this.selectedTopic()?.getConnectionColor(),
setValue: (value: string) => this.designer.changeConnectionColor(value),
};
return this.connectionColoreModel;
}
getTopicShapeModel(): NodeProperty<TopicShapeType> { getTopicShapeModel(): NodeProperty<TopicShapeType> {
if (!this.topicShapeModel) if (!this.topicShapeModel)
this.topicShapeModel = { this.topicShapeModel = {

View File

@ -36,6 +36,7 @@ import PolylineOutlined from '@mui/icons-material/PolylineOutlined';
import GestureOutlined from '@mui/icons-material/GestureOutlined'; import GestureOutlined from '@mui/icons-material/GestureOutlined';
import TimelineOutined from '@mui/icons-material/TimelineOutlined'; import TimelineOutined from '@mui/icons-material/TimelineOutlined';
import ShareOutlined from '@mui/icons-material/ShareOutlined'; import ShareOutlined from '@mui/icons-material/ShareOutlined';
import SwapCallsOutlined from '@mui/icons-material/SwapCallsOutlined';
import Palette from '@mui/icons-material/Square'; import Palette from '@mui/icons-material/Square';
import SquareOutlined from '@mui/icons-material/SquareOutlined'; import SquareOutlined from '@mui/icons-material/SquareOutlined';
@ -104,11 +105,7 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
}, },
null, null,
{ {
icon: () => ( icon: () => <Palette htmlColor={valueBulder.getSelectedTopicColorModel().getValue()} />,
<Palette
htmlColor={valueBulder.getSelectedTopicColorModel().getValue() as string}
></Palette>
),
tooltip: intl.formatMessage({ tooltip: intl.formatMessage({
id: 'editor-panel.tooltip-topic-fill-color', id: 'editor-panel.tooltip-topic-fill-color',
defaultMessage: 'Fill color', defaultMessage: 'Fill color',
@ -120,16 +117,14 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
<ColorPicker <ColorPicker
closeModal={closeModal} closeModal={closeModal}
colorModel={valueBulder.getSelectedTopicColorModel()} colorModel={valueBulder.getSelectedTopicColorModel()}
></ColorPicker> />
); );
}, },
}, },
], ],
}, },
{ {
icon: () => ( icon: () => <SquareOutlined htmlColor={valueBulder.getColorBorderModel().getValue()} />,
<SquareOutlined htmlColor={valueBulder.getColorBorderModel().getValue() as string} />
),
tooltip: intl.formatMessage({ tooltip: intl.formatMessage({
id: 'editor-panel.tooltip-topic-border-color', id: 'editor-panel.tooltip-topic-border-color',
defaultMessage: 'Border color', defaultMessage: 'Border color',
@ -141,7 +136,7 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
<ColorPicker <ColorPicker
closeModal={closeModal} closeModal={closeModal}
colorModel={valueBulder.getColorBorderModel()} colorModel={valueBulder.getColorBorderModel()}
></ColorPicker> />
); );
}, },
}, },
@ -158,15 +153,6 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
defaultMessage: 'Connection Style', defaultMessage: 'Connection Style',
}), }),
options: [ options: [
{
icon: <GestureOutlined />,
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: <GestureOutlined />, icon: <GestureOutlined />,
tooltip: intl.formatMessage({ tooltip: intl.formatMessage({
@ -176,6 +162,15 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
onClick: () => valueBulder.getConnectionStyleModel().setValue(LineType.THICK_CURVED), onClick: () => valueBulder.getConnectionStyleModel().setValue(LineType.THICK_CURVED),
selected: () => valueBulder.getConnectionStyleModel().getValue() === LineType.THICK_CURVED, selected: () => valueBulder.getConnectionStyleModel().getValue() === LineType.THICK_CURVED,
}, },
{
icon: <SwapCallsOutlined />,
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: <PolylineOutlined />, icon: <PolylineOutlined />,
tooltip: intl.formatMessage({ tooltip: intl.formatMessage({
@ -196,30 +191,30 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
selected: () => selected: () =>
valueBulder.getConnectionStyleModel().getValue() === LineType.POLYLINE_CURVED, valueBulder.getConnectionStyleModel().getValue() === LineType.POLYLINE_CURVED,
}, },
// null, null,
// { {
// icon: () => <Palette htmlColor={valueBulder.getFontColorModel().getValue() as string} />, icon: () => <Palette htmlColor={valueBulder.getConnectionColorModel().getValue()} />,
// tooltip: intl.formatMessage({ tooltip: intl.formatMessage({
// id: 'editor-panel.tooltip-connection-style-color', id: 'editor-panel.tooltip-connection-color',
// defaultMessage: 'Color', defaultMessage: 'Color',
// }), }),
// options: [ options: [
// { {
// render: (closeModal) => { render: (closeModal) => {
// return ( return (
// <ColorPicker <ColorPicker
// closeModal={closeModal} closeModal={closeModal}
// colorModel={valueBulder.getFontColorModel()} colorModel={valueBulder.getConnectionColorModel()}
// /> />
// ); );
// }, },
// }, },
// ], ],
// }, },
], ],
disabled: () => { disabled: () => {
const selected = model.getDesignerModel().filterSelectedTopics(); 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, onClick: valueBulder.getFontStyleModel().switchValue,
selected: () => valueBulder.getFontStyleModel().getValue() === 'italic', selected: () => valueBulder.getFontStyleModel().getValue() === 'italic',
}, },
null,
{ {
icon: () => <Palette htmlColor={valueBulder.getFontColorModel().getValue() as string} />, icon: () => <Palette htmlColor={valueBulder.getFontColorModel().getValue() as string} />,
tooltip: intl.formatMessage({ tooltip: intl.formatMessage({
@ -347,7 +343,7 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
{ {
tooltip: 'Node note', tooltip: 'Node note',
render: (closeModal) => ( render: (closeModal) => (
<TopicNote closeModal={closeModal} noteModel={valueBulder.getNoteModel()}></TopicNote> <TopicNote closeModal={closeModal} noteModel={valueBulder.getNoteModel()} />
), ),
}, },
], ],
@ -403,10 +399,10 @@ export function buildEditorPanelConfig(model: Editor, intl: IntlShape): ActionCo
deleteNodeToolbarConfiguration, deleteNodeToolbarConfiguration,
colorAndShapeToolbarConfiguration, colorAndShapeToolbarConfiguration,
fontFormatToolbarConfiguration, fontFormatToolbarConfiguration,
connectionStyleConfiguration,
editIconConfiguration, editIconConfiguration,
editNoteConfiguration, editNoteConfiguration,
editLinkUrlConfiguration, editLinkUrlConfiguration,
connectionStyleConfiguration,
addRelationConfiguration, addRelationConfiguration,
]; ];
} }

View File

@ -37,23 +37,20 @@ class ConnectionLine {
protected _lineType: LineType; protected _lineType: LineType;
protected _line2d: Line; protected _line: Line;
private _type: LineType; private _type: LineType;
private _color: string;
constructor(sourceNode: Topic, targetNode: Topic, type: LineType = LineType.THIN_CURVED) { 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'); $assert(sourceNode !== targetNode, 'Circular connection');
this._targetTopic = targetNode; this._targetTopic = targetNode;
this._sourceTopic = sourceNode; this._sourceTopic = sourceNode;
this._type = type; this._type = type;
this._line = this.createLine(type);
const line = this._createLine(type); this.updateColor();
// Set line styles ...
this._line2d = line;
} }
private _getCtrlPoints(sourceNode: Topic, targetNode: Topic) { 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; this._lineType = lineType;
let line: ConnectionLine; let line: ConnectionLine;
const strokeColor = ConnectionLine.getStrokeColor();
switch (lineType) { switch (lineType) {
case LineType.POLYLINE_MIDDLE: case LineType.POLYLINE_MIDDLE:
line = new PolyLine(); line = new PolyLine();
(line as PolyLine).setStyle('MiddleStraight'); (line as PolyLine).setStyle('MiddleStraight');
(line as PolyLine).setStroke(1, 'solid', strokeColor, 1);
break; break;
case LineType.POLYLINE_CURVED: case LineType.POLYLINE_CURVED:
line = new PolyLine(); line = new PolyLine();
(line as PolyLine).setStyle('Curved'); (line as PolyLine).setStyle('Curved');
(line as PolyLine).setStroke(1, 'solid', strokeColor, 1);
break; break;
case LineType.THIN_CURVED: case LineType.THIN_CURVED:
line = new CurvedLine(); line = new CurvedLine();
(line as CurvedLine).setStroke(1, 'solid', strokeColor, 1);
(line as CurvedLine).setFill(strokeColor, 1);
break; break;
case LineType.THICK_CURVED: case LineType.THICK_CURVED:
line = new CurvedLine(); 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); (line as CurvedLine).setWidth(this._targetTopic.isCentralTopic() ? 15 : 3);
break; break;
default: default:
throw new Error(`Unexpected line type. ${lineType}`); throw new Error(`Unexpected line type. ${lineType}`);
} }
return line; 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 { setVisibility(value: boolean, fade = 0): void {
this._line2d.setVisibility(value, fade); this._line.setVisibility(value, fade);
} }
isVisible(): boolean { isVisible(): boolean {
return this._line2d.isVisible(); return this._line.isVisible();
} }
setOpacity(opacity: number): void { setOpacity(opacity: number): void {
this._line2d.setOpacity(opacity); this._line.setOpacity(opacity);
} }
redraw(): void { redraw(): void {
const line2d = this._line2d; const line2d = this._line;
const sourceTopic = this._sourceTopic; const sourceTopic = this._sourceTopic;
const sourcePosition = sourceTopic.getPosition(); const sourcePosition = sourceTopic.getPosition();
@ -132,10 +144,13 @@ class ConnectionLine {
} }
// Add connector ... // 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 targetPosition = targetTopic.getPosition();
const offset = TopicConfig.CONNECTOR_WIDTH / 2; const offset = TopicConfig.CONNECTOR_WIDTH / 2;
const targetTopicSize = targetTopic.getSize(); const targetTopicSize = targetTopic.getSize();
@ -161,16 +176,21 @@ class ConnectionLine {
} }
setStroke(color: string, style: string, opacity: number) { 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) { addToWorkspace(workspace: Workspace) {
workspace.append(this._line2d); workspace.append(this._line);
this._line2d.moveToBack(); this._line.moveToBack();
} }
removeFromWorkspace(workspace: Workspace) { removeFromWorkspace(workspace: Workspace) {
workspace.removeChild(this._line2d); workspace.removeChild(this._line);
} }
getTargetTopic(): Topic { getTargetTopic(): Topic {
@ -186,7 +206,7 @@ class ConnectionLine {
} }
getLine(): Line { getLine(): Line {
return this._line2d; return this._line;
} }
getType(): string { getType(): string {
@ -194,14 +214,12 @@ class ConnectionLine {
} }
moveToBack(): void { moveToBack(): void {
this._line2d.moveToBack(); this._line.moveToBack();
} }
moveToFront() { moveToFront() {
this._line2d.moveToFront(); this._line.moveToFront();
} }
static getStrokeColor = () => '#495879';
} }
export default ConnectionLine; export default ConnectionLine;

View File

@ -439,56 +439,6 @@ class Designer extends Events {
this._actionDispatcher.addTopics([childModel], [parentTopicId]); 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 { private _createChildModel(topic: Topic, mousePos: Point = null): NodeModel {
// Create a new node ... // Create a new node ...
const parentModel = topic.getModel(); const parentModel = topic.getModel();
@ -503,7 +453,7 @@ class Designer extends Events {
const { position } = result; const { position } = result;
childModel.setPosition(position.x, position.y); childModel.setPosition(position.x, position.y);
this._copyNodeProps(parentModel, childModel); childModel.copy(parentModel);
return childModel; return childModel;
} }
@ -558,8 +508,7 @@ class Designer extends Events {
const order = topic.getOrder() + 1; const order = topic.getOrder() + 1;
result.setOrder(order); result.setOrder(order);
result.setPosition(10, 10); // Set a dummy position ... result.setPosition(10, 10); // Set a dummy position ...
result.copy(model);
this._copyNodeProps(model, result);
} }
return result; 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 { changeFontWeight(): void {
const topicsIds = this.getModel().filterTopicsIds(); const topicsIds = this.getModel().filterTopicsIds();
if (topicsIds.length > 0) { if (topicsIds.length > 0) {

View File

@ -56,15 +56,15 @@ class Relationship extends ConnectionLine {
const strokeColor = Relationship.getStrokeColor(); const strokeColor = Relationship.getStrokeColor();
// Build line .. // Build line ..
this._line2d.setIsSrcControlPointCustom(false); this._line.setIsSrcControlPointCustom(false);
this._line2d.setIsDestControlPointCustom(false); this._line.setIsDestControlPointCustom(false);
this._line2d.setCursor('pointer'); this._line.setCursor('pointer');
this._line2d.setStroke(1, 'solid', strokeColor); this._line.setStroke(1, 'solid', strokeColor);
this._line2d.setDashed(4, 2); this._line.setDashed(4, 2);
this._line2d.setTestId(`${model.getFromNode()}-${model.getToNode()}-relationship`); this._line.setTestId(`${model.getFromNode()}-${model.getToNode()}-relationship`);
// Build focus shape ... // 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.setStroke(8, 'solid', '#3f96ff');
this._focusShape.setIsSrcControlPointCustom(false); this._focusShape.setIsSrcControlPointCustom(false);
this._focusShape.setIsDestControlPointCustom(false); this._focusShape.setIsDestControlPointCustom(false);
@ -122,7 +122,7 @@ class Relationship extends ConnectionLine {
} }
private updatePositions() { private updatePositions() {
const line2d = this._line2d; const line2d = this._line;
const sourceTopic = this._sourceTopic; const sourceTopic = this._sourceTopic;
const sPos = sourceTopic.getPosition(); const sPos = sourceTopic.getPosition();
@ -132,7 +132,7 @@ class Relationship extends ConnectionLine {
tPos = Shape.workoutIncomingConnectionPoint(targetTopic, sPos); tPos = Shape.workoutIncomingConnectionPoint(targetTopic, sPos);
} }
this._line2d.setStroke(2); this._line.setStroke(2);
let ctrlPoints: [Point, Point]; let ctrlPoints: [Point, Point];
// Position line ... // Position line ...
@ -164,7 +164,7 @@ class Relationship extends ConnectionLine {
this.positionArrows(); this.positionArrows();
// Add connector ... // Add connector ...
this._positionateConnector(targetTopic); this._positionLine(targetTopic);
// Poisition refresh shape ... // Poisition refresh shape ...
this.positionRefreshShape(); this.positionRefreshShape();
@ -173,7 +173,7 @@ class Relationship extends ConnectionLine {
redraw(): void { redraw(): void {
this.updatePositions(); this.updatePositions();
this._line2d.moveToFront(); this._line.moveToFront();
this._startArrow.moveToBack(); this._startArrow.moveToBack();
if (this._endArrow) { if (this._endArrow) {
this._endArrow.moveToBack(); this._endArrow.moveToBack();
@ -189,24 +189,24 @@ class Relationship extends ConnectionLine {
} }
private positionArrows(): void { private positionArrows(): void {
const tpos = this._line2d.getTo(); const tpos = this._line.getTo();
const spos = this._line2d.getFrom(); const spos = this._line.getFrom();
this._startArrow.setFrom(spos.x, spos.y); this._startArrow.setFrom(spos.x, spos.y);
if (this._endArrow) { if (this._endArrow) {
this._endArrow.setFrom(tpos.x, tpos.y); this._endArrow.setFrom(tpos.x, tpos.y);
} }
if (this._line2d.getType() === 'CurvedLine') { if (this._line.getType() === 'CurvedLine') {
const controlPoints = this._line2d.getControlPoints(); const controlPoints = this._line.getControlPoints();
this._startArrow.setControlPoint(controlPoints[0]); this._startArrow.setControlPoint(controlPoints[0]);
if (this._endArrow) { if (this._endArrow) {
this._endArrow.setControlPoint(controlPoints[1]); this._endArrow.setControlPoint(controlPoints[1]);
} }
} else { } else {
this._startArrow.setControlPoint(this._line2d.getTo()); this._startArrow.setControlPoint(this._line.getTo());
if (this._endArrow) { 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); workspace.append(this._controlPointsController);
if (workspace.isReadOnly()) { if (workspace.isReadOnly()) {
this._line2d.setCursor('default'); this._line.setCursor('default');
} else { } else {
this._line2d.addEvent('click', this._onFocusHandler); this._line.addEvent('click', this._onFocusHandler);
this._focusShape.addEvent('click', this._onFocusHandler); this._focusShape.addEvent('click', this._onFocusHandler);
} }
this._isInWorkspace = true; this._isInWorkspace = true;
@ -237,7 +237,7 @@ class Relationship extends ConnectionLine {
workspace.removeChild(this._focusShape); workspace.removeChild(this._focusShape);
workspace.removeChild(this._controlPointsController); workspace.removeChild(this._controlPointsController);
this._line2d.removeEvent('click', this._onFocusHandler); this._line.removeEvent('click', this._onFocusHandler);
this._isInWorkspace = false; this._isInWorkspace = false;
workspace.removeChild(this._startArrow); workspace.removeChild(this._startArrow);
if (this._endArrow) { if (this._endArrow) {
@ -268,10 +268,10 @@ class Relationship extends ConnectionLine {
} }
private positionRefreshShape(): void { private positionRefreshShape(): void {
const sPos = this._line2d.getFrom(); const sPos = this._line.getFrom();
const tPos = this._line2d.getTo(); const tPos = this._line.getTo();
const ctrlPoints = this._line2d.getControlPoints(); const ctrlPoints = this._line.getControlPoints();
this._focusShape.setFrom(sPos.x, sPos.y); this._focusShape.setFrom(sPos.x, sPos.y);
this._focusShape.setTo(tPos.x, tPos.y); this._focusShape.setTo(tPos.x, tPos.y);
@ -289,7 +289,7 @@ class Relationship extends ConnectionLine {
type = 'mousedown'; type = 'mousedown';
} }
const line = this._line2d; const line = this._line;
line.addEvent(type, listener); line.addEvent(type, listener);
} }
@ -342,7 +342,7 @@ class Relationship extends ConnectionLine {
$assert($defined(x), 'x must be defined'); $assert($defined(x), 'x must be defined');
$assert($defined(y), 'y 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); this._startArrow.setFrom(x, y);
} }
@ -350,18 +350,18 @@ class Relationship extends ConnectionLine {
$assert($defined(x), 'x must be defined'); $assert($defined(x), 'x must be defined');
$assert($defined(y), 'y 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); if (this._endArrow) this._endArrow.setFrom(x, y);
} }
setSrcControlPoint(control: PositionType): void { setSrcControlPoint(control: PositionType): void {
this._line2d.setSrcControlPoint(control); this._line.setSrcControlPoint(control);
this._focusShape.setSrcControlPoint(control); this._focusShape.setSrcControlPoint(control);
this._startArrow.setControlPoint(control); this._startArrow.setControlPoint(control);
} }
setDestControlPoint(control: PositionType) { setDestControlPoint(control: PositionType) {
this._line2d.setDestControlPoint(control); this._line.setDestControlPoint(control);
this._focusShape.setSrcControlPoint(control); this._focusShape.setSrcControlPoint(control);
if (this._showEndArrow) { if (this._showEndArrow) {
this._endArrow.setControlPoint(control); this._endArrow.setControlPoint(control);
@ -369,23 +369,23 @@ class Relationship extends ConnectionLine {
} }
getControlPoints(): PositionType { getControlPoints(): PositionType {
return this._line2d.getControlPoints(); return this._line.getControlPoints();
} }
isSrcControlPointCustom(): boolean { isSrcControlPointCustom(): boolean {
return this._line2d.isSrcControlPointCustom(); return this._line.isSrcControlPointCustom();
} }
isDestControlPointCustom(): boolean { isDestControlPointCustom(): boolean {
return this._line2d.isDestControlPointCustom(); return this._line.isDestControlPointCustom();
} }
setIsSrcControlPointCustom(isCustom: boolean) { setIsSrcControlPointCustom(isCustom: boolean) {
this._line2d.setIsSrcControlPointCustom(isCustom); this._line.setIsSrcControlPointCustom(isCustom);
} }
setIsDestControlPointCustom(isCustom: boolean) { setIsDestControlPointCustom(isCustom: boolean) {
this._line2d.setIsDestControlPointCustom(isCustom); this._line.setIsDestControlPointCustom(isCustom);
} }
getId(): number { getId(): number {
@ -393,7 +393,7 @@ class Relationship extends ConnectionLine {
} }
fireEvent(type: string, event): void { fireEvent(type: string, event): void {
const elem = this._line2d; const elem = this._line;
elem.trigger(type, event); elem.trigger(type, event);
} }

View File

@ -31,7 +31,8 @@ class ShirinkConnector {
const ellipse = new Elipse(TopicConfig.INNER_RECT_ATTRIBUTES); const ellipse = new Elipse(TopicConfig.INNER_RECT_ATTRIBUTES);
this._ellipse = ellipse; 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.setSize(TopicConfig.CONNECTOR_WIDTH, TopicConfig.CONNECTOR_WIDTH);
ellipse.addEvent('click', (event: Event) => { ellipse.addEvent('click', (event: Event) => {

View File

@ -110,7 +110,7 @@ class StandaloneActionDispatcher extends ActionDispatcher {
topic.setFontStyle(style, true); topic.setFontStyle(style, true);
return result; return result;
}; };
const command = new GenericFunctionCommand(commandFunc, topicsIds, null); const command = new GenericFunctionCommand(commandFunc, topicsIds, undefined);
this.execute(command); this.execute(command);
} }
@ -225,9 +225,9 @@ class StandaloneActionDispatcher extends ActionDispatcher {
} }
changeConnectionStyleToTopic(topicsIds: number[], lineType: LineType) { changeConnectionStyleToTopic(topicsIds: number[], lineType: LineType) {
const commandFunc = (topic: Topic, commandShapeType: LineType) => { const commandFunc = (topic: Topic, type: LineType) => {
const result = topic.getConnectionStyle(); const result = topic.getConnectionStyle();
topic.setConnectionStyle(commandShapeType); topic.setConnectionStyle(type);
return result; return result;
}; };
@ -235,6 +235,17 @@ class StandaloneActionDispatcher extends ActionDispatcher {
this.execute(command); 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[]) { changeFontWeightToTopic(topicsIds: number[]) {
$assert(topicsIds, 'topicsIds can not be null'); $assert(topicsIds, 'topicsIds can not be null');
@ -246,7 +257,7 @@ class StandaloneActionDispatcher extends ActionDispatcher {
return result; return result;
}; };
const command = new GenericFunctionCommand(commandFunc, topicsIds, null); const command = new GenericFunctionCommand(commandFunc, topicsIds, undefined);
this.execute(command); this.execute(command);
} }

View File

@ -114,11 +114,6 @@ abstract class Topic extends NodeGraph {
if ($defined(updateModel) && updateModel) { if ($defined(updateModel) && updateModel) {
model.setShapeType(type); 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(); const oldInnerShape = this.getInnerShape();
if (oldInnerShape != null) { if (oldInnerShape != null) {
@ -163,11 +158,34 @@ abstract class Topic extends NodeGraph {
getConnectionStyle(): LineType { getConnectionStyle(): LineType {
const model = this.getModel(); const model = this.getModel();
let result = model.getConnectionStyle(); let result: LineType | undefined = model.getConnectionStyle();
if (!result) {
result = TopicStyle.defaultConnectionType(this); // 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 { private _removeInnerShape(): ElementClass {
@ -205,7 +223,7 @@ abstract class Topic extends NodeGraph {
$assert(attributes, 'attributes can not be null'); $assert(attributes, 'attributes can not be null');
$assert(shapeType, 'shapeType can not be null'); $assert(shapeType, 'shapeType can not be null');
let result; let result: ElementClass;
if (shapeType === 'rectangle') { if (shapeType === 'rectangle') {
result = new Rect(0, attributes); result = new Rect(0, attributes);
} else if (shapeType === 'image') { } else if (shapeType === 'image') {
@ -229,22 +247,21 @@ abstract class Topic extends NodeGraph {
} else if (shapeType === 'rounded rectangle') { } else if (shapeType === 'rounded rectangle') {
result = new Rect(0.3, attributes); result = new Rect(0.3, attributes);
} else if (shapeType === 'line') { } else if (shapeType === 'line') {
const stokeColor = this.getConnectionColor();
result = new Line({ result = new Line({
strokeColor: '#495879', strokeColor: stokeColor,
strokeWidth: 1, strokeWidth: 1,
}); });
const me = this;
result.setSize = function setSize(width: number, height: number) { result.setSize = function setSize(width: number, height: number) {
this.size = { this.size = { width, height };
width,
height,
};
result.setFrom(0, height); result.setFrom(0, height);
result.setTo(width, height); result.setTo(width, height);
// Lines will have the same color of the default connection lines... // // Lines will have the same color of the default connection lines...
const stokeColor = ConnectionLine.getStrokeColor(); const color = me.getConnectionColor();
result.setStroke(1, 'solid', stokeColor); result.setStroke(1, 'solid', color);
}; };
result.getSize = function getSize() { result.getSize = function getSize() {
@ -522,7 +539,7 @@ abstract class Topic extends NodeGraph {
if ($defined(updateModel) && updateModel) { if ($defined(updateModel) && updateModel) {
const model = this.getModel(); const model = this.getModel();
model.setText(text); model.setText(text || undefined);
} }
} }
@ -552,13 +569,29 @@ abstract class Topic extends NodeGraph {
model.setConnectionStyle(type); model.setConnectionStyle(type);
// Needs to change change all the lines types. Outgoing are part of the children. // 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. // If connection of the childen matches, just reset the style in the model.
model this.getChildren().forEach((topic: Topic) => {
.getChildren() if (topic.getModel().getConnectionStyle() === type) {
.filter((c) => c.getChildren().length === 0) topic.getModel().setConnectionStyle(undefined);
.forEach((c) => c.setConnectionStyle(type)); }
});
}
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) { private _setBackgroundColor(color: string, updateModel: boolean) {
@ -1247,19 +1280,33 @@ abstract class Topic extends NodeGraph {
let result = false; let result = false;
if (this._isInWorkspace) { if (this._isInWorkspace) {
// Adjust connection line if there is a change in the parent... // Adjust connection line if there is a change in the parent...
const connStyleChanged = if (this._outgoingLine) {
this._outgoingLine?.getLineType() !== this.getParent()?.getConnectionStyle(); // Has the style change ?
if (this._outgoingLine && connStyleChanged) { const connStyleChanged =
// Todo: Review static reference ... this._outgoingLine.getLineType() !== this.getParent()!.getConnectionStyle();
const workspace = designer.getWorkSpace(); if (connStyleChanged) {
this._outgoingLine.removeFromWorkspace(workspace); // Todo: Review static reference ...
const workspace = designer.getWorkSpace();
this._outgoingLine.removeFromWorkspace(workspace);
const targetTopic = this.getOutgoingConnectedTopic()!; const targetTopic = this.getOutgoingConnectedTopic()!;
this._outgoingLine = this.createConnectionLine(targetTopic); this._outgoingLine = this.createConnectionLine(targetTopic);
this._outgoingLine.setVisibility(this.isVisible()); this._outgoingLine.setVisibility(this.isVisible());
workspace.append(this._outgoingLine); 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; return result;

View File

@ -28,10 +28,12 @@ type FontStlye = {
weight: string; weight: string;
color: string; color: string;
}; };
type TopicStyleType = { type TopicStyleType = {
borderColor: string; borderColor: string;
backgroundColor: string; backgroundColor: string;
connectionStyle: LineType; connectionStyle: LineType;
connectionColor: string;
fontStyle: FontStlye; fontStyle: FontStlye;
msgKey: string; msgKey: string;
shapeType: TopicShapeType; shapeType: TopicShapeType;
@ -49,6 +51,7 @@ const TopicDefaultStyles = {
color: '#ffffff', color: '#ffffff',
}, },
connectionStyle: LineType.THICK_CURVED, connectionStyle: LineType.THICK_CURVED,
connectionColor: '#495879',
msgKey: 'CENTRAL_TOPIC', msgKey: 'CENTRAL_TOPIC',
shapeType: 'rounded rectangle' as TopicShapeType, shapeType: 'rounded rectangle' as TopicShapeType,
}, },
@ -63,6 +66,7 @@ const TopicDefaultStyles = {
color: 'rgb(82,92,97)', color: 'rgb(82,92,97)',
}, },
connectionStyle: LineType.THICK_CURVED, connectionStyle: LineType.THICK_CURVED,
connectionColor: '#495879',
msgKey: 'MAIN_TOPIC', msgKey: 'MAIN_TOPIC',
shapeType: 'line' as TopicShapeType, shapeType: 'line' as TopicShapeType,
}, },
@ -76,7 +80,8 @@ const TopicDefaultStyles = {
weight: 'normal', weight: 'normal',
color: 'rgb(82,92,97)', color: 'rgb(82,92,97)',
}, },
connectionStyle: LineType.THIN_CURVED, connectionStyle: LineType.THICK_CURVED,
connectionColor: '#495879',
msgKey: 'SUB_TOPIC', msgKey: 'SUB_TOPIC',
shapeType: 'line' as TopicShapeType, shapeType: 'line' as TopicShapeType,
}, },
@ -93,6 +98,7 @@ const TopicDefaultStyles = {
}, },
msgKey: 'ISOLATED_TOPIC', msgKey: 'ISOLATED_TOPIC',
connectionStyle: LineType.THIN_CURVED, connectionStyle: LineType.THIN_CURVED,
connectionColor: '#495879',
shapeType: 'line' as TopicShapeType, shapeType: 'line' as TopicShapeType,
}, },
}; };
@ -147,6 +153,10 @@ class TopicStyle {
static defaultConnectionType(topic: Topic): LineType { static defaultConnectionType(topic: Topic): LineType {
return this._getStyles(topic).connectionStyle; return this._getStyles(topic).connectionStyle;
} }
static defaultConnectionColor(topic: Topic): string {
return this._getStyles(topic).connectionColor;
}
} }
export default TopicStyle; export default TopicStyle;

View File

@ -20,7 +20,7 @@ import Command from '../Command';
import CommandContext from '../CommandContext'; import CommandContext from '../CommandContext';
import Topic from '../Topic'; import Topic from '../Topic';
type CommandTypes = string | object | boolean | number | null; type CommandTypes = string | object | boolean | number | undefined;
class GenericFunctionCommand extends Command { class GenericFunctionCommand extends Command {
private _value: CommandTypes; private _value: CommandTypes;

View File

@ -251,7 +251,7 @@ class FreemindExporter extends Exporter {
const fontFamily: string = mindmapTopic.getFontFamily(); const fontFamily: string = mindmapTopic.getFontFamily();
const fontSize: number = mindmapTopic.getFontSize(); const fontSize: number = mindmapTopic.getFontSize();
const fontColor: string = mindmapTopic.getFontColor(); 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(); const fontStyle: string = mindmapTopic.getFontStyle();
if (fontFamily || fontSize || fontColor || fontWeigth || fontStyle) { if (fontFamily || fontSize || fontColor || fontWeigth || fontStyle) {

View File

@ -70,7 +70,7 @@ abstract class INodeModel {
this.putProperty('type', type); this.putProperty('type', type);
} }
setText(text: string | null): void { setText(text: string | undefined): void {
this.putProperty('text', text); this.putProperty('text', text);
} }
@ -221,14 +221,22 @@ abstract class INodeModel {
this.putProperty('shrunken', value); this.putProperty('shrunken', value);
} }
setConnectionStyle(type: LineType): void { setConnectionStyle(type: LineType | undefined): void {
this.putProperty('connectionStyle', type); this.putProperty('connectionStyle', type);
} }
getConnectionStyle(): LineType | null { getConnectionStyle(): LineType | undefined {
return this.getProperty('connectionStyle') as LineType; 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 { isNodeModel(): boolean {
return true; return true;
} }
@ -297,9 +305,9 @@ abstract class INodeModel {
abstract getPropertiesKeys(): string[]; 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; abstract setParent(parent: INodeModel): void;

View File

@ -108,7 +108,7 @@ class NodeModel extends INodeModel {
* @param value * @param value
* @throws will throw an error if key is null or undefined * @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'); $defined(key, 'key can not be null');
this._properties[key] = value; this._properties[key] = value;
} }
@ -117,7 +117,7 @@ class NodeModel extends INodeModel {
return this._properties; return this._properties;
} }
getProperty(key: string): number | string | boolean { getProperty(key: string): number | string | boolean | undefined {
$defined(key, 'key can not be null'); $defined(key, 'key can not be null');
return this._properties[key]; return this._properties[key];
} }
@ -135,6 +135,61 @@ class NodeModel extends INodeModel {
return result; 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 { deepCopy(): NodeModel {
const result = new NodeModel(this.getType(), this._mindmap); const result = new NodeModel(this.getType(), this._mindmap);
result._children = this._children.map((node) => { result._children = this._children.map((node) => {

View File

@ -148,7 +148,7 @@ class XMLSerializerTango implements XMLMindmapSerializer {
} }
const brColor = topic.getBorderColor(); const brColor = topic.getBorderColor();
if ($defined(brColor)) { if (brColor) {
parentTopic.setAttribute('brColor', brColor); parentTopic.setAttribute('brColor', brColor);
} }
@ -157,6 +157,11 @@ class XMLSerializerTango implements XMLMindmapSerializer {
parentTopic.setAttribute('connStyle', `${connectionStyle}`); parentTopic.setAttribute('connStyle', `${connectionStyle}`);
} }
const connectionColor = topic.getConnectionColor();
if (connectionColor) {
parentTopic.setAttribute('connColor', connectionColor);
}
const metadata = topic.getMetadata(); const metadata = topic.getMetadata();
if ($defined(metadata)) { if ($defined(metadata)) {
parentTopic.setAttribute('metadata', metadata); parentTopic.setAttribute('metadata', metadata);
@ -351,6 +356,11 @@ class XMLSerializerTango implements XMLMindmapSerializer {
topic.setConnectionStyle(lineType); topic.setConnectionStyle(lineType);
} }
const connColor = domElem.getAttribute('connColor');
if ($defined(connColor) && connColor) {
topic.setConnectionColor(connColor);
}
const borderColor = domElem.getAttribute('brColor'); const borderColor = domElem.getAttribute('brColor');
if (borderColor) { if (borderColor) {
topic.setBorderColor(borderColor); topic.setBorderColor(borderColor);

View File

@ -24,12 +24,11 @@ class CurvedLinePeer extends ElementPeer {
const svgElement = window.document.createElementNS(ElementPeer.svgNamespace, 'path'); const svgElement = window.document.createElementNS(ElementPeer.svgNamespace, 'path');
super(svgElement); super(svgElement);
this._style = { fill: '#495879' }; this._style = { fill: '#495879' };
this._updateStyle();
this._customControlPoint_1 = false; this._customControlPoint_1 = false;
this._customControlPoint_2 = false; this._customControlPoint_2 = false;
this._control1 = new Point(0, 0); this._control1 = new Point(0, 0);
this._control2 = new Point(0, 0); this._control2 = new Point(0, 0);
this._width = 1; this.setWidth(1);
} }
setSrcControlPoint(control) { setSrcControlPoint(control) {