From a717547efba11f4f3b60a64a8645de239ef68945 Mon Sep 17 00:00:00 2001 From: Faris Demirovic <63470556+farisd16@users.noreply.github.com> Date: Fri, 9 Aug 2024 09:32:02 +0200 Subject: [PATCH] Add stereotype visibility toggle button (#367) --- .../controls/icon/stereotype-off.tsx | 16 ++++ .../controls/icon/stereotype-on.tsx | 29 ++++++ .../stereotype-toggle/stereotype-toggle.tsx | 19 ++++ .../uml-component/uml-component-component.tsx | 4 +- .../uml-component/uml-component-update.tsx | 89 ++++++++++++++++++ .../common/uml-component/uml-component.ts | 5 +- src/main/packages/popups.ts | 8 +- .../uml-component-subsystem-component.tsx | 4 +- .../uml-component-subsystem-update.tsx | 91 +++++++++++++++++++ .../uml-component-subsystem.ts | 34 ++++++- .../uml-component/uml-component-component.ts | 29 +++++- .../uml-deployment-component/uml-component.ts | 28 ++++++ .../uml-deployment-node-component.tsx | 9 +- .../uml-deployment-node-update.tsx | 12 ++- .../uml-deployment-node.ts | 6 +- src/main/typings.ts | 14 +++ .../flowchart-decision-update-test.tsx.snap | 2 +- .../flowchart-flowline-update-test.tsx.snap | 2 +- ...owchart-function-call-update-test.tsx.snap | 2 +- ...lowchart-input-output-update-test.tsx.snap | 2 +- .../flowchart-process-update-test.tsx.snap | 2 +- .../flowchart-terminal-update-test.tsx.snap | 2 +- .../class-association-popup-test.tsx.snap | 14 +-- ...uml-communication-link-popup-test.tsx.snap | 14 +-- .../deployment-node-popup-test.tsx.snap | 38 +++++++- .../uml-petri-net-arc-update-test.tsx.snap | 2 +- .../uml-petri-net-place-update-test.tsx.snap | 6 +- ...eachability-graph-arc-update-test.tsx.snap | 2 +- ...ability-graph-marking-update-test.tsx.snap | 2 +- 29 files changed, 444 insertions(+), 43 deletions(-) create mode 100644 src/main/components/controls/icon/stereotype-off.tsx create mode 100644 src/main/components/controls/icon/stereotype-on.tsx create mode 100644 src/main/components/controls/stereotype-toggle/stereotype-toggle.tsx create mode 100644 src/main/packages/common/uml-component/uml-component-update.tsx create mode 100644 src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem-update.tsx diff --git a/src/main/components/controls/icon/stereotype-off.tsx b/src/main/components/controls/icon/stereotype-off.tsx new file mode 100644 index 000000000..1ae0a4d8f --- /dev/null +++ b/src/main/components/controls/icon/stereotype-off.tsx @@ -0,0 +1,16 @@ +import React, { SVGAttributes } from 'react'; +import { Icon } from './icon'; + +type Props = SVGAttributes; + +export const StereotypeOffIcon = (props: Props): React.JSX.Element => ( + + + + +); diff --git a/src/main/components/controls/icon/stereotype-on.tsx b/src/main/components/controls/icon/stereotype-on.tsx new file mode 100644 index 000000000..f1e32e177 --- /dev/null +++ b/src/main/components/controls/icon/stereotype-on.tsx @@ -0,0 +1,29 @@ +import React, { SVGAttributes } from 'react'; +import { Icon } from './icon'; + +type Props = SVGAttributes; + +export const StereotypeOnIcon = (props: Props): React.JSX.Element => ( + + + + + + +); diff --git a/src/main/components/controls/stereotype-toggle/stereotype-toggle.tsx b/src/main/components/controls/stereotype-toggle/stereotype-toggle.tsx new file mode 100644 index 000000000..19fec39ec --- /dev/null +++ b/src/main/components/controls/stereotype-toggle/stereotype-toggle.tsx @@ -0,0 +1,19 @@ +import React, { Component } from 'react'; + +import { StereotypeOnIcon } from '../icon/stereotype-on'; +import { StereotypeOffIcon } from '../icon/stereotype-off'; +import { Button } from '../button/button'; + +type Props = { onChange: () => void; value: boolean }; + +export class StereotypeToggle extends Component { + render(): React.JSX.Element { + const { value, onChange } = this.props; + + return ( + + ); + } +} diff --git a/src/main/packages/common/uml-component/uml-component-component.tsx b/src/main/packages/common/uml-component/uml-component-component.tsx index 475ef2121..c66f16211 100644 --- a/src/main/packages/common/uml-component/uml-component-component.tsx +++ b/src/main/packages/common/uml-component/uml-component-component.tsx @@ -28,12 +28,12 @@ export const UMLComponentComponent: FunctionComponent = ({ element, child /> - {element.stereotype && ( + {element.stereotype && element.displayStereotype && ( {`«${element.stereotype}»`} )} - + {element.name} diff --git a/src/main/packages/common/uml-component/uml-component-update.tsx b/src/main/packages/common/uml-component/uml-component-update.tsx new file mode 100644 index 000000000..42e750319 --- /dev/null +++ b/src/main/packages/common/uml-component/uml-component-update.tsx @@ -0,0 +1,89 @@ +import React, { Component, ComponentType } from 'react'; +import { connect, ConnectedComponent } from 'react-redux'; +import { Button } from '../../../components/controls/button/button'; +import { ColorButton } from '../../../components/controls/color-button/color-button'; +import { TrashIcon } from '../../../components/controls/icon/trash'; +import { Textfield } from '../../../components/controls/textfield/textfield'; +import { ModelState } from '../../../components/store/model-state'; +import { StylePane } from '../../../components/style-pane/style-pane'; +import { styled } from '../../../components/theme/styles'; +import { UMLElementRepository } from '../../../services/uml-element/uml-element-repository'; +import { AsyncDispatch } from '../../../utils/actions/actions'; +import { IUMLComponent, UMLComponent } from './uml-component'; +import { StereotypeToggle } from '../../../components/controls/stereotype-toggle/stereotype-toggle'; + +const Flex = styled.div` + display: flex; + align-items: baseline; + justify-content: space-between; +`; + +type State = { colorOpen: boolean }; + +class ComponentUpdate extends Component { + state = { colorOpen: false }; + + private toggleColor = () => { + this.setState((state) => ({ + colorOpen: !state.colorOpen, + })); + }; + + render() { + const { element } = this.props; + + return ( +
+
+ + + + + + +
+ +
+ ); + } + + private onRename = (value: string) => { + const { element, update } = this.props; + update(element.id, { name: value }); + }; + + private onStereotypeVisibilityToggle = () => { + const { element, update } = this.props; + const newVisibilityValue = !element.displayStereotype; + update(element.id, { displayStereotype: newVisibilityValue }); + }; +} + +type OwnProps = { + element: UMLComponent; +}; + +type StateProps = {}; + +type DispatchProps = { + update: typeof UMLElementRepository.update; + delete: AsyncDispatch; +}; + +type Props = OwnProps & StateProps & DispatchProps; + +const enhance = connect(null, { + update: UMLElementRepository.update, + delete: UMLElementRepository.delete, +}); + +export const UMLComponentUpdate: ConnectedComponent, OwnProps> = enhance(ComponentUpdate); diff --git a/src/main/packages/common/uml-component/uml-component.ts b/src/main/packages/common/uml-component/uml-component.ts index bd1cbbbea..cb7934d47 100644 --- a/src/main/packages/common/uml-component/uml-component.ts +++ b/src/main/packages/common/uml-component/uml-component.ts @@ -1,9 +1,12 @@ +import { IUMLContainer } from '../../../services/uml-container/uml-container'; import { UMLPackage } from '../uml-package/uml-package'; -export interface IUMLComponent { +export interface IUMLComponent extends IUMLContainer { stereotype: string; + displayStereotype: boolean; } export abstract class UMLComponent extends UMLPackage implements IUMLComponent { stereotype = 'component'; + displayStereotype = true; } diff --git a/src/main/packages/popups.ts b/src/main/packages/popups.ts index 6be2b02d9..30dcd5292 100644 --- a/src/main/packages/popups.ts +++ b/src/main/packages/popups.ts @@ -4,6 +4,8 @@ import { DefaultRelationshipPopup } from './common/default-relationship-popup'; import { UMLClassifierUpdate } from './common/uml-classifier/uml-classifier-update'; import { UMLActivityControlFlowUpdate } from './uml-activity-diagram/uml-activity-control-flow/uml-activity-control-flow-update'; import { UMLActivityMergeNodeUpdate } from './uml-activity-diagram/uml-activity-merge-node/uml-activity-merge-node-update'; +import { UMLComponentSubsystemUpdate } from './uml-component-diagram/uml-component-subsystem/uml-component-subsystem-update'; +import { UMLComponentUpdate } from './common/uml-component/uml-component-update'; import { UMLClassAssociationUpdate } from './uml-class-diagram/uml-class-association/uml-class-association-update'; import { UMLCommunicationLinkUpdate } from './uml-communication-diagram/uml-communication-link/uml-communication-link-update'; import { UMLComponentAssociationUpdate } from './uml-component-diagram/uml-component-association-update'; @@ -58,11 +60,11 @@ export const Popups: { [key in UMLElementType | UMLRelationshipType]: ComponentT [UMLElementType.UseCase]: DefaultPopup, [UMLElementType.UseCaseActor]: DefaultPopup, [UMLElementType.UseCaseSystem]: DefaultPopup, - [UMLElementType.Component]: DefaultPopup, - [UMLElementType.Subsystem]: DefaultPopup, + [UMLElementType.Component]: UMLComponentUpdate, + [UMLElementType.Subsystem]: UMLComponentSubsystemUpdate, [UMLElementType.ComponentInterface]: DefaultPopup, [UMLElementType.DeploymentNode]: UMLDeploymentNodeUpdate, - [UMLElementType.DeploymentComponent]: DefaultPopup, + [UMLElementType.DeploymentComponent]: UMLComponentUpdate, [UMLElementType.DeploymentArtifact]: DefaultPopup, [UMLElementType.DeploymentInterface]: DefaultPopup, [UMLElementType.PetriNetPlace]: UMLPetriNetPlaceUpdate, diff --git a/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem-component.tsx b/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem-component.tsx index d717de65d..71e3809ab 100644 --- a/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem-component.tsx +++ b/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem-component.tsx @@ -28,12 +28,12 @@ export const UMLComponentSubsystem: FunctionComponent = ({ element, child /> - {element.stereotype && ( + {element.stereotype && element.displayStereotype && ( {`«${element.stereotype}»`} )} - + {element.name} diff --git a/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem-update.tsx b/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem-update.tsx new file mode 100644 index 000000000..a621ce492 --- /dev/null +++ b/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem-update.tsx @@ -0,0 +1,91 @@ +import React, { Component, ComponentType } from 'react'; +import { connect, ConnectedComponent } from 'react-redux'; +import { Button } from '../../../components/controls/button/button'; +import { ColorButton } from '../../../components/controls/color-button/color-button'; +import { TrashIcon } from '../../../components/controls/icon/trash'; +import { Textfield } from '../../../components/controls/textfield/textfield'; +import { ModelState } from '../../../components/store/model-state'; +import { StylePane } from '../../../components/style-pane/style-pane'; +import { styled } from '../../../components/theme/styles'; +import { UMLElementRepository } from '../../../services/uml-element/uml-element-repository'; +import { AsyncDispatch } from '../../../utils/actions/actions'; +import { IUMLSubsystem, UMLSubsystem } from './uml-component-subsystem'; +import { StereotypeToggle } from '../../../components/controls/stereotype-toggle/stereotype-toggle'; + +const Flex = styled.div` + display: flex; + align-items: baseline; + justify-content: space-between; +`; + +type State = { colorOpen: boolean }; + +class ComponentSubsystemUpdate extends Component { + state = { colorOpen: false }; + + private toggleColor = () => { + this.setState((state) => ({ + colorOpen: !state.colorOpen, + })); + }; + + render() { + const { element } = this.props; + + return ( +
+
+ + + + + + +
+ +
+ ); + } + + private onRename = (value: string) => { + const { element, update } = this.props; + update(element.id, { name: value }); + }; + + private onStereotypeVisibilityToggle = () => { + const { element, update } = this.props; + const newVisibilityValue = !element.displayStereotype; + update(element.id, { displayStereotype: newVisibilityValue }); + }; +} + +type OwnProps = { + element: UMLSubsystem; +}; + +type StateProps = {}; + +type DispatchProps = { + update: typeof UMLElementRepository.update; + delete: AsyncDispatch; +}; + +type Props = OwnProps & StateProps & DispatchProps; + +const enhance = connect(null, { + update: UMLElementRepository.update, + delete: UMLElementRepository.delete, +}); + +export const UMLComponentSubsystemUpdate: ConnectedComponent, OwnProps> = enhance( + ComponentSubsystemUpdate, +); diff --git a/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem.ts b/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem.ts index 0abb1b793..963132267 100644 --- a/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem.ts +++ b/src/main/packages/uml-component-diagram/uml-component-subsystem/uml-component-subsystem.ts @@ -1,8 +1,13 @@ +import { DeepPartial } from 'redux'; import { UMLPackage } from '../../common/uml-package/uml-package'; import { ComponentElementType, ComponentRelationshipType } from '..'; +import { IUMLContainer } from '../../../services/uml-container/uml-container'; +import * as Apollon from '../../../typings'; +import { assign } from '../../../utils/fx/assign'; -export interface IUMLSubsystem { +export interface IUMLSubsystem extends IUMLContainer { stereotype: string; + displayStereotype: boolean; } export class UMLSubsystem extends UMLPackage implements IUMLSubsystem { @@ -12,5 +17,32 @@ export class UMLSubsystem extends UMLPackage implements IUMLSubsystem { ComponentRelationshipType.ComponentInterfaceRequired, ]; stereotype = 'subsystem'; + displayStereotype = true; type = ComponentElementType.Subsystem; + + constructor(values?: DeepPartial) { + super(); + assign(this, values); + } + + serialize(): Apollon.UMLComponentSubsystem { + return { + ...super.serialize(), + type: this.type as keyof typeof ComponentElementType, + stereotype: this.stereotype, + displayStereotype: this.displayStereotype, + }; + } + + deserialize(values: T, children?: Apollon.UMLModelElement[]): void { + const assert = (v: Apollon.UMLModelElement): v is Apollon.UMLComponentSubsystem => + v.type === ComponentElementType.Subsystem; + if (!assert(values)) { + return; + } + + super.deserialize(values, children); + this.stereotype = values.stereotype; + this.displayStereotype = values.displayStereotype; + } } diff --git a/src/main/packages/uml-component-diagram/uml-component/uml-component-component.ts b/src/main/packages/uml-component-diagram/uml-component/uml-component-component.ts index f371f190d..efb243fea 100644 --- a/src/main/packages/uml-component-diagram/uml-component/uml-component-component.ts +++ b/src/main/packages/uml-component-diagram/uml-component/uml-component-component.ts @@ -1,5 +1,8 @@ import { ComponentElementType, ComponentRelationshipType } from '..'; -import { UMLComponent } from '../../common/uml-component/uml-component'; +import { IUMLComponent, UMLComponent } from '../../common/uml-component/uml-component'; +import { DeepPartial } from 'redux'; +import * as Apollon from '../../../typings'; +import { assign } from '../../../utils/fx/assign'; export class UMLComponentComponent extends UMLComponent { static supportedRelationships = [ @@ -8,4 +11,28 @@ export class UMLComponentComponent extends UMLComponent { ComponentRelationshipType.ComponentInterfaceRequired, ]; type = ComponentElementType.Component; + + constructor(values?: DeepPartial) { + super(); + assign(this, values); + } + + serialize(): Apollon.UMLComponentComponent { + return { + ...super.serialize(), + type: this.type as keyof typeof ComponentElementType, + displayStereotype: this.displayStereotype, + }; + } + + deserialize(values: T, children?: Apollon.UMLModelElement[]): void { + const assert = (v: Apollon.UMLModelElement): v is Apollon.UMLComponentComponent => + v.type === ComponentElementType.Component; + if (!assert(values)) { + return; + } + + super.deserialize(values, children); + this.displayStereotype = values.displayStereotype; + } } diff --git a/src/main/packages/uml-deployment-diagram/uml-deployment-component/uml-component.ts b/src/main/packages/uml-deployment-diagram/uml-deployment-component/uml-component.ts index 68d9ac952..61b9e4a4c 100644 --- a/src/main/packages/uml-deployment-diagram/uml-deployment-component/uml-component.ts +++ b/src/main/packages/uml-deployment-diagram/uml-deployment-component/uml-component.ts @@ -1,4 +1,8 @@ +import { DeepPartial } from 'redux'; import { DeploymentElementType, DeploymentRelationshipType } from '..'; +import { IUMLComponent } from '../../common/uml-component/uml-component'; +import * as Apollon from '../../../typings'; +import { assign } from '../../../utils/fx/assign'; import { UMLComponent } from '../../common/uml-component/uml-component'; export class UMLDeploymentComponent extends UMLComponent { @@ -9,4 +13,28 @@ export class UMLDeploymentComponent extends UMLComponent { DeploymentRelationshipType.DeploymentInterfaceRequired, ]; type = DeploymentElementType.DeploymentComponent; + + constructor(values?: DeepPartial) { + super(); + assign(this, values); + } + + serialize(): Apollon.UMLDeploymentComponent { + return { + ...super.serialize(), + type: this.type as keyof typeof DeploymentElementType, + displayStereotype: this.displayStereotype, + }; + } + + deserialize(values: T, children?: Apollon.UMLModelElement[]): void { + const assert = (v: Apollon.UMLModelElement): v is Apollon.UMLDeploymentComponent => + v.type === DeploymentElementType.DeploymentComponent; + if (!assert(values)) { + return; + } + + super.deserialize(values, children); + this.displayStereotype = values.displayStereotype; + } } diff --git a/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node-component.tsx b/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node-component.tsx index 7a22395c5..3ed1d1b58 100644 --- a/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node-component.tsx +++ b/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node-component.tsx @@ -26,12 +26,17 @@ export const UMLDeploymentNodeComponent: FunctionComponent = ({ element, /> - {element.stereotype && ( + {element.stereotype && element.displayStereotype && ( {`«${element.stereotype}»`} )} - + {element.name} diff --git a/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node-update.tsx b/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node-update.tsx index 73b5dabc7..64c3b04c6 100644 --- a/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node-update.tsx +++ b/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node-update.tsx @@ -11,6 +11,7 @@ import { styled } from '../../../components/theme/styles'; import { UMLElementRepository } from '../../../services/uml-element/uml-element-repository'; import { AsyncDispatch } from '../../../utils/actions/actions'; import { IUMLDeploymentNode, UMLDeploymentNode } from './uml-deployment-node'; +import { StereotypeToggle } from '../../../components/controls/stereotype-toggle/stereotype-toggle'; const Flex = styled.div` display: flex; @@ -38,6 +39,7 @@ class DeploymentNodeUpdate extends Component { + @@ -54,7 +56,7 @@ class DeploymentNodeUpdate extends Component {
- +
@@ -66,7 +68,13 @@ class DeploymentNodeUpdate extends Component { update(element.id, { name: value }); }; - private onUpdate = (value: string) => { + private onStereotypeVisibilityToggle = () => { + const { element, update } = this.props; + const newVisibilityValue = !element.displayStereotype; + update(element.id, { displayStereotype: newVisibilityValue }); + }; + + private onStereotypeRename = (value: string) => { const { element, update } = this.props; update(element.id, { stereotype: value }); }; diff --git a/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node.ts b/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node.ts index 4d63e18e7..9c298f449 100644 --- a/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node.ts +++ b/src/main/packages/uml-deployment-diagram/uml-deployment-node/uml-deployment-node.ts @@ -8,6 +8,7 @@ import { UMLElementType } from '../../uml-element-type'; export interface IUMLDeploymentNode extends IUMLContainer { stereotype: string; + displayStereotype: boolean; } export class UMLDeploymentNode extends UMLPackage implements IUMLDeploymentNode { @@ -19,6 +20,7 @@ export class UMLDeploymentNode extends UMLPackage implements IUMLDeploymentNode ]; type: UMLElementType = DeploymentElementType.DeploymentNode; stereotype: string = 'node'; + displayStereotype = true; constructor(values?: DeepPartial) { super(); @@ -30,10 +32,11 @@ export class UMLDeploymentNode extends UMLPackage implements IUMLDeploymentNode ...super.serialize(), type: this.type as keyof typeof DeploymentElementType, stereotype: this.stereotype, + displayStereotype: this.displayStereotype, }; } - deserialize(values: T, children?: Apollon.UMLModelElement[]) { + deserialize(values: T, children?: Apollon.UMLModelElement[]): void { const assert = (v: Apollon.UMLModelElement): v is Apollon.UMLDeploymentNode => v.type === DeploymentElementType.DeploymentNode; if (!assert(values)) { @@ -42,5 +45,6 @@ export class UMLDeploymentNode extends UMLPackage implements IUMLDeploymentNode super.deserialize(values, children); this.stereotype = values.stereotype; + this.displayStereotype = values.displayStereotype; } } diff --git a/src/main/typings.ts b/src/main/typings.ts index 16d57b3fb..122b3dc67 100644 --- a/src/main/typings.ts +++ b/src/main/typings.ts @@ -86,6 +86,20 @@ export type UMLClassifier = UMLElement & { export type UMLDeploymentNode = UMLElement & { stereotype: string; + displayStereotype: boolean; +}; + +export type UMLDeploymentComponent = UMLElement & { + displayStereotype: boolean; +}; + +export type UMLComponentSubsystem = UMLElement & { + stereotype: string; + displayStereotype: boolean; +}; + +export type UMLComponentComponent = UMLElement & { + displayStereotype: boolean; }; export type UMLPetriNetPlace = UMLElement & { diff --git a/src/tests/unit/packages/flowchart/flowchart-decision/__snapshots__/flowchart-decision-update-test.tsx.snap b/src/tests/unit/packages/flowchart/flowchart-decision/__snapshots__/flowchart-decision-update-test.tsx.snap index ef5dfd57e..841f71125 100644 --- a/src/tests/unit/packages/flowchart/flowchart-decision/__snapshots__/flowchart-decision-update-test.tsx.snap +++ b/src/tests/unit/packages/flowchart/flowchart-decision/__snapshots__/flowchart-decision-update-test.tsx.snap @@ -6,7 +6,7 @@ exports[`test flowchart decision update render 1`] = `