Skip to content

Commit

Permalink
Add stereotype visibility toggle button (#367)
Browse files Browse the repository at this point in the history
  • Loading branch information
farisd16 authored Aug 9, 2024
1 parent 38209a3 commit a717547
Show file tree
Hide file tree
Showing 29 changed files with 444 additions and 43 deletions.
16 changes: 16 additions & 0 deletions src/main/components/controls/icon/stereotype-off.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React, { SVGAttributes } from 'react';
import { Icon } from './icon';

type Props = SVGAttributes<SVGSVGElement>;

export const StereotypeOffIcon = (props: Props): React.JSX.Element => (
<Icon viewBox="0 0 128 128" {...props}>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M82.1654 32.6199C82.0806 32.6106 81.9949 32.6059 81.9085 32.6059H74.1387C73.2767 32.6059 72.4831 33.0754 72.068 33.8309C71.653 34.5864 71.6824 35.508 72.1447 36.2356L74.6334 40.1518L82.1654 32.6199ZM42.8297 71.9556L37.7981 64.0579L55.5222 36.2378C55.9856 35.5104 56.0158 34.5883 55.601 33.8321C55.1861 33.0759 54.3922 32.6059 53.5297 32.6059H45.7599C44.9738 32.6059 44.2392 32.9969 43.8002 33.6489L24.6599 62.0751C24.3973 62.465 24.257 62.9245 24.257 63.3946V64.7212C24.257 65.1913 24.3973 65.6507 24.6599 66.0407L34.3513 80.4339L42.8297 71.9556ZM47.3686 95.5099L53.7626 89.116L55.5222 91.8779C55.9856 92.6054 56.0158 93.5275 55.601 94.2837C55.1861 95.0399 54.3922 95.5099 53.5297 95.5099H47.3686ZM28.0126 86.7726L19.9011 94.8841C19.7674 94.7608 19.647 94.6212 19.5431 94.4669L0.402837 66.0407C0.140261 65.6507 0 65.1913 0 64.7212V63.3946C0 62.9245 0.140261 62.465 0.402837 62.0751L19.5431 33.6489C19.9822 32.9969 20.7168 32.6059 21.5028 32.6059H29.2727C30.1352 32.6059 30.9291 33.0759 31.344 33.8321C31.7588 34.5883 31.7286 35.5104 31.2652 36.2378L13.5411 64.0579L28.0126 86.7726ZM85.5494 57.3292L94.0734 48.8051L103.009 62.0751C103.271 62.465 103.411 62.9245 103.411 63.3946V64.7212C103.411 65.1913 103.271 65.6507 103.009 66.0407L83.8682 94.4669C83.4292 95.1189 82.6946 95.5099 81.9085 95.5099H74.1387C73.2767 95.5099 72.4831 95.0404 72.068 94.2849C71.653 93.5294 71.6824 92.6078 72.1447 91.8802L89.8253 64.0579L85.5494 57.3292ZM100.584 42.2947L114.414 64.0579L96.7334 91.8802C96.271 92.6078 96.2416 93.5294 96.6567 94.2849C97.0717 95.0404 97.8653 95.5099 98.7273 95.5099H106.497C107.283 95.5099 108.018 95.1189 108.457 94.4669L127.597 66.0407C127.86 65.6507 128 65.1913 128 64.7212V63.3946C128 62.9245 127.86 62.465 127.597 62.0751L108.768 34.1107L100.584 42.2947Z"
fill="black"
/>
<rect x="110.479" y="10" width="11.8127" height="141.697" rx="2" transform="rotate(45 110.479 10)" fill="black" />
</Icon>
);
29 changes: 29 additions & 0 deletions src/main/components/controls/icon/stereotype-on.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { SVGAttributes } from 'react';
import { Icon } from './icon';

type Props = SVGAttributes<SVGSVGElement>;

export const StereotypeOnIcon = (props: Props): React.JSX.Element => (
<Icon viewBox="0 0 128 128" {...props}>
<path
d="M74.1387 93.5415L92.8052 64.1677H101.049V65.1153L81.9085 93.5415H74.1387ZM74.1387 35.3625H81.9085L101.049 63.7887V64.7363H92.8052L74.1387 35.3625ZM98.7273 93.5415L117.394 64.1677H125.637V65.1153L106.497 93.5415H98.7273ZM98.7273 35.3625H106.497L125.637 63.7887V64.7363H117.394L98.7273 35.3625Z"
fill="black"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M72.068 34.225C72.4831 33.4695 73.2767 33 74.1387 33H81.9085C82.6946 33 83.4292 33.391 83.8682 34.043L103.009 62.4692C103.271 62.8592 103.411 63.3186 103.411 63.7887V65.1153C103.411 65.5854 103.271 66.0448 103.009 66.4348L83.8682 94.861C83.4292 95.513 82.6946 95.904 81.9085 95.904H74.1387C73.2767 95.904 72.4831 95.4345 72.068 94.679C71.653 93.9235 71.6824 93.0019 72.1447 92.2743L89.8254 64.452L72.1447 36.6297C71.6824 35.9021 71.653 34.9805 72.068 34.225ZM93.7418 67.0988L78.4393 91.1789H80.6511L96.8651 67.0988H93.7418ZM96.8651 61.8052H93.7418L78.4393 37.7251H80.6511L96.8651 61.8052ZM96.6567 34.225C97.0717 33.4695 97.8653 33 98.7273 33H106.497C107.283 33 108.018 33.391 108.457 34.043L127.597 62.4692C127.86 62.8592 128 63.3186 128 63.7887V65.1153C128 65.5854 127.86 66.0448 127.597 66.4348L108.457 94.861C108.018 95.513 107.283 95.904 106.497 95.904H98.7273C97.8653 95.904 97.0717 95.4345 96.6567 94.679C96.2416 93.9235 96.271 93.0019 96.7334 92.2743L114.414 64.452L96.7334 36.6297C96.271 35.9021 96.2416 34.9805 96.6567 34.225ZM118.33 67.0988L103.028 91.1789H105.24L121.454 67.0988H118.33ZM121.454 61.8052H118.33L103.028 37.7251H105.24L121.454 61.8052Z"
fill="black"
/>
<path
d="M29.2727 93.5415H21.5028L2.36253 65.1153V64.1677H10.5588L29.2727 93.5415ZM29.2727 35.3625L10.5588 64.7363H2.36253V63.7887L21.5028 35.3625H29.2727ZM53.5297 93.5415H45.7599L26.6196 65.1153V64.1677H34.8158L53.5297 93.5415ZM53.5297 35.3625L34.8158 64.7363H26.6196V63.7887L45.7599 35.3625H53.5297Z"
fill="black"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M19.5431 34.043C19.9822 33.391 20.7168 33 21.5028 33H29.2727C30.1352 33 30.9291 33.47 31.344 34.2262C31.7588 34.9824 31.7286 35.9045 31.2652 36.632L13.5411 64.452L31.2652 92.2721C31.7286 92.9995 31.7588 93.9216 31.344 94.6778C30.9291 95.434 30.1352 95.904 29.2727 95.904H21.5028C20.7168 95.904 19.9822 95.513 19.5431 94.861L0.402837 66.4348C0.140261 66.0448 0 65.5854 0 65.1153V63.7887C0 63.3186 0.140261 62.8592 0.402837 62.4692L19.5431 34.043ZM6.54628 67.0988L22.7602 91.1789H24.9662L9.62486 67.0988H6.54628ZM9.62486 61.8052H6.54628L22.7602 37.7251H24.9662L9.62486 61.8052ZM43.8002 34.043C44.2392 33.391 44.9738 33 45.7599 33H53.5297C54.3922 33 55.1861 33.47 55.601 34.2262C56.0158 34.9824 55.9856 35.9045 55.5222 36.632L37.7981 64.452L55.5222 92.2721C55.9856 92.9995 56.0158 93.9216 55.601 94.6778C55.1861 95.434 54.3922 95.904 53.5297 95.904H45.7599C44.9738 95.904 44.2392 95.513 43.8002 94.861L24.6599 66.4348C24.3973 66.0448 24.257 65.5854 24.257 65.1153V63.7887C24.257 63.3186 24.3973 62.8592 24.6599 62.4692L43.8002 34.043ZM30.8033 67.0988L47.0173 91.1789H49.2233L33.8819 67.0988H30.8033ZM33.8819 61.8052H30.8033L47.0173 37.7251H49.2233L33.8819 61.8052Z"
fill="black"
/>
</Icon>
);
Original file line number Diff line number Diff line change
@@ -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<Props> {
render(): React.JSX.Element {
const { value, onChange } = this.props;

return (
<Button color="link" tabIndex={-1} onClick={onChange}>
{value ? <StereotypeOnIcon /> : <StereotypeOffIcon />}
</Button>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ export const UMLComponentComponent: FunctionComponent<Props> = ({ element, child
/>
</g>
<Text fill={element.textColor}>
{element.stereotype && (
{element.stereotype && element.displayStereotype && (
<tspan x="50%" dy={-8} textAnchor="middle" fontSize="85%">
{${element.stereotype}»`}
</tspan>
)}
<tspan x="50%" dy={element.stereotype ? 18 : 10} textAnchor="middle">
<tspan x="50%" dy={element.stereotype && element.displayStereotype ? 18 : 0} textAnchor="middle">
{element.name}
</tspan>
</Text>
Expand Down
89 changes: 89 additions & 0 deletions src/main/packages/common/uml-component/uml-component-update.tsx
Original file line number Diff line number Diff line change
@@ -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<Props, State> {
state = { colorOpen: false };

private toggleColor = () => {
this.setState((state) => ({
colorOpen: !state.colorOpen,
}));
};

render() {
const { element } = this.props;

return (
<div>
<section>
<Flex>
<Textfield value={element.name} onChange={this.onRename} autoFocus />
<ColorButton onClick={this.toggleColor} />
<StereotypeToggle value={element.displayStereotype} onChange={this.onStereotypeVisibilityToggle} />
<Button color="link" tabIndex={-1} onClick={() => this.props.delete(element.id)}>
<TrashIcon />
</Button>
</Flex>
</section>
<StylePane
open={this.state.colorOpen}
element={element}
onColorChange={this.props.update}
lineColor
textColor
fillColor
/>
</div>
);
}

private onRename = (value: string) => {
const { element, update } = this.props;
update<IUMLComponent>(element.id, { name: value });
};

private onStereotypeVisibilityToggle = () => {
const { element, update } = this.props;
const newVisibilityValue = !element.displayStereotype;
update<IUMLComponent>(element.id, { displayStereotype: newVisibilityValue });
};
}

type OwnProps = {
element: UMLComponent;
};

type StateProps = {};

type DispatchProps = {
update: typeof UMLElementRepository.update;
delete: AsyncDispatch<typeof UMLElementRepository.delete>;
};

type Props = OwnProps & StateProps & DispatchProps;

const enhance = connect<StateProps, DispatchProps, OwnProps, ModelState>(null, {
update: UMLElementRepository.update,
delete: UMLElementRepository.delete,
});

export const UMLComponentUpdate: ConnectedComponent<ComponentType<Props>, OwnProps> = enhance(ComponentUpdate);
5 changes: 4 additions & 1 deletion src/main/packages/common/uml-component/uml-component.ts
Original file line number Diff line number Diff line change
@@ -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;
}
8 changes: 5 additions & 3 deletions src/main/packages/popups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ export const UMLComponentSubsystem: FunctionComponent<Props> = ({ element, child
/>
</g>
<Text fill={element.textColor} y={`${25}px`}>
{element.stereotype && (
{element.stereotype && element.displayStereotype && (
<tspan x="50%" dy={-8} textAnchor="middle" fontSize="85%">
{${element.stereotype}»`}
</tspan>
)}
<tspan x="50%" dy={element.stereotype ? 18 : 10} textAnchor="middle">
<tspan x="50%" dy={element.stereotype && element.displayStereotype ? 18 : 0} textAnchor="middle">
{element.name}
</tspan>
</Text>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Props, State> {
state = { colorOpen: false };

private toggleColor = () => {
this.setState((state) => ({
colorOpen: !state.colorOpen,
}));
};

render() {
const { element } = this.props;

return (
<div>
<section>
<Flex>
<Textfield value={element.name} onChange={this.onRename} autoFocus />
<ColorButton onClick={this.toggleColor} />
<StereotypeToggle value={element.displayStereotype} onChange={this.onStereotypeVisibilityToggle} />
<Button color="link" tabIndex={-1} onClick={() => this.props.delete(element.id)}>
<TrashIcon />
</Button>
</Flex>
</section>
<StylePane
open={this.state.colorOpen}
element={element}
onColorChange={this.props.update}
lineColor
textColor
fillColor
/>
</div>
);
}

private onRename = (value: string) => {
const { element, update } = this.props;
update<IUMLSubsystem>(element.id, { name: value });
};

private onStereotypeVisibilityToggle = () => {
const { element, update } = this.props;
const newVisibilityValue = !element.displayStereotype;
update<IUMLSubsystem>(element.id, { displayStereotype: newVisibilityValue });
};
}

type OwnProps = {
element: UMLSubsystem;
};

type StateProps = {};

type DispatchProps = {
update: typeof UMLElementRepository.update;
delete: AsyncDispatch<typeof UMLElementRepository.delete>;
};

type Props = OwnProps & StateProps & DispatchProps;

const enhance = connect<StateProps, DispatchProps, OwnProps, ModelState>(null, {
update: UMLElementRepository.update,
delete: UMLElementRepository.delete,
});

export const UMLComponentSubsystemUpdate: ConnectedComponent<ComponentType<Props>, OwnProps> = enhance(
ComponentSubsystemUpdate,
);
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -12,5 +17,32 @@ export class UMLSubsystem extends UMLPackage implements IUMLSubsystem {
ComponentRelationshipType.ComponentInterfaceRequired,
];
stereotype = 'subsystem';
displayStereotype = true;
type = ComponentElementType.Subsystem;

constructor(values?: DeepPartial<IUMLSubsystem>) {
super();
assign<IUMLSubsystem>(this, values);
}

serialize(): Apollon.UMLComponentSubsystem {
return {
...super.serialize(),
type: this.type as keyof typeof ComponentElementType,
stereotype: this.stereotype,
displayStereotype: this.displayStereotype,
};
}

deserialize<T extends Apollon.UMLModelElement>(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;
}
}
Loading

0 comments on commit a717547

Please sign in to comment.