Link activity selection with journal

This commit is contained in:
Sipke Schoorstra 2021-04-22 11:18:38 +02:00
parent ab72de9a4a
commit e1a9a50bb9
5 changed files with 81 additions and 20 deletions

View file

@ -41,7 +41,7 @@ export namespace Components {
interface ElsaDesignerTreeActivity {
"displayContext": ActivityDesignDisplayContext;
"icon": string;
"selected": boolean;
"isSelected": boolean;
}
interface ElsaDropdownButton {
"icon"?: any;
@ -177,6 +177,7 @@ export namespace Components {
interface ElsaWorkflowInstanceJournal {
"activityDescriptors": Array<ActivityDescriptor>;
"hide": () => Promise<void>;
"selectActivityRecord": (activityId?: string) => Promise<void>;
"serverUrl": string;
"show": () => Promise<void>;
"workflowBlueprint": WorkflowBlueprint;
@ -483,15 +484,19 @@ declare namespace LocalJSX {
}
interface ElsaDesignerTree {
"model"?: WorkflowModel;
"onActivityDeselected"?: (event: CustomEvent<ActivityModel>) => void;
"onActivitySelected"?: (event: CustomEvent<ActivityModel>) => void;
"onWorkflow-changed"?: (event: CustomEvent<WorkflowModel>) => void;
"selectedActivityId"?: string;
}
interface ElsaDesignerTreeActivity {
"displayContext"?: ActivityDesignDisplayContext;
"icon"?: string;
"isSelected"?: boolean;
"onDeselected"?: (event: CustomEvent<ActivityModel>) => void;
"onEdit-activity"?: (event: CustomEvent<ActivityModel>) => void;
"onRemove-activity"?: (event: CustomEvent<ActivityModel>) => void;
"selected"?: boolean;
"onSelected"?: (event: CustomEvent<ActivityModel>) => void;
}
interface ElsaDropdownButton {
"icon"?: any;

View file

@ -14,9 +14,11 @@ export class ElsaDesignerTreeActivity {
@Prop() displayContext: ActivityDesignDisplayContext
@Prop() icon: string
@Prop() selected: boolean;
@Prop({attribute:"selected"}) isSelected: boolean;
@Event({eventName: 'remove-activity', bubbles: true}) removeActivityEmitter: EventEmitter<ActivityModel>;
@Event({eventName: 'edit-activity', bubbles: true}) editActivityEmitter: EventEmitter<ActivityModel>;
@Event() selected: EventEmitter<ActivityModel>;
@Event() deselected: EventEmitter<ActivityModel>;
@State() showMenu: boolean
contextMenu: HTMLElement;
el: HTMLElement;
@ -41,16 +43,26 @@ export class ElsaDesignerTreeActivity {
this.removeActivityEmitter.emit(this.displayContext.activityModel);
}
onSelectActivity(e: Event) {
this.isSelected = !this.isSelected;
if(this.selected)
this.selected.emit(this.displayContext.activityModel);
else
this.deselected.emit(this.displayContext.activityModel);
}
render() {
const displayContext = this.displayContext;
const activity = displayContext.activityModel;
const activityId = activity.activityId;
const displayName = activity.displayName && activity.displayName.length > 0 ? activity.displayName : activity.name && activity.name.length > 0 ? activity.name : activity.type
const activityClass = this.selected ? 'border-blue-600' : 'border-white hover:border-blue-600';
const activityClass = this.isSelected ? 'border-blue-600' : 'border-white hover:border-blue-600';
return (
<Host id={`activity-${activityId}`}
onDblClick={e => this.onEditActivityClick(e)}
onClick={e => this.onSelectActivity(e)}
class={`${activityClass} activity border-2 border-solid rounded bg-white text-left text-black text-lg select-none max-w-md shadow-sm relative`}>
<div class="p-5">
<div class="flex justify-between space-x-8">

View file

@ -16,9 +16,11 @@ import state from "../../../../utils/store";
})
export class ElsaWorkflowDesigner {
@Prop() model: WorkflowModel = { activities: [], connections: [], persistenceBehavior: WorkflowPersistenceBehavior.WorkflowBurst };
@Prop() model: WorkflowModel = {activities: [], connections: [], persistenceBehavior: WorkflowPersistenceBehavior.WorkflowBurst};
@Prop() selectedActivityId?: string;
@Event({eventName: 'workflow-changed', bubbles: true, composed: true, cancelable: true}) workflowChanged: EventEmitter<WorkflowModel>
@Event() activitySelected: EventEmitter<ActivityModel>;
@Event() activityDeselected: EventEmitter<ActivityModel>;
@State() workflowModel: WorkflowModel
el: HTMLElement
canvasElement: HTMLElement;
@ -32,7 +34,7 @@ export class ElsaWorkflowDesigner {
}
@Method()
async destroyJsPlumb(){
async destroyJsPlumb() {
destroy();
}
@ -60,8 +62,8 @@ export class ElsaWorkflowDesigner {
componentWillLoad() {
this.workflowModel = this.model;
}
onActivityPicked = async (args) =>{
onActivityPicked = async (args) => {
const activityDescriptor = (args as ActivityDescriptor);
const connectFromRoot = !this.parentActivityOutcome || this.parentActivityOutcome == "";
const sourceId = connectFromRoot ? null : this.parentActivityId;
@ -70,7 +72,7 @@ export class ElsaWorkflowDesigner {
this.showActivityEditor(activityModel, false);
};
onUpdateActivity = args => {
const activityModel = (args as ActivityModel);
this.updateActivity(activityModel);
@ -101,7 +103,7 @@ export class ElsaWorkflowDesigner {
eventBus.emit(EventTypes.ActivityDesignDisplaying, this, displayContext);
displayContexts[model.activityId] = displayContext;
}
this.activityDisplayContexts = displayContexts;
}
@ -232,7 +234,7 @@ export class ElsaWorkflowDesigner {
this.workflowChanged.emit(model);
}
removeInvalidConnections(invalidConnections: Array<ConnectionModel>){
removeInvalidConnections(invalidConnections: Array<ConnectionModel>) {
if (invalidConnections.length > 0) {
const isValid = (connection: ConnectionModel): boolean => invalidConnections.findIndex(x => x.targetId == connection.targetId && x.sourceId == connection.sourceId && x.outcome == connection.outcome) < 0;
@ -391,7 +393,7 @@ export class ElsaWorkflowDesigner {
workflowModel.connections = [...connections, newConnection];
} else {
// Create new connection.
const newConnection: ConnectionModel = {
sourceId: sourceActivityId,
@ -405,6 +407,14 @@ export class ElsaWorkflowDesigner {
this.updateWorkflowModel(workflowModel);
}
private onActivitySelected(e: CustomEvent<ActivityModel>) {
this.activitySelected.emit(e.detail);
}
private onActivityDeselected(e: CustomEvent<ActivityModel>) {
this.activityDeselected.emit(e.detail);
}
render() {
const renderedActivities = new Set<string>();
@ -442,7 +452,7 @@ export class ElsaWorkflowDesigner {
if (list.length === 0)
return list;
const sourceActivityConnection: ConnectionDefinitionMapped = connections.find(x => x.targetActivityId === list[0].activityId);
if (!sourceActivityConnection)
@ -456,7 +466,7 @@ export class ElsaWorkflowDesigner {
const sourceActivityConnections: Array<ConnectionDefinitionMapped> = connections.filter(x => x.sourceActivityId === sourceActivityId);
const sourceActivityDisplayContext = this.activityDisplayContexts[sourceActivity.activityId];
if (sourceActivityDisplayContext.outcomes) {
sourceActivity.outcomes = sourceActivityDisplayContext.outcomes;
}
@ -491,7 +501,7 @@ export class ElsaWorkflowDesigner {
const children = getChildActivities(this.workflowModel, activityId);
const displayContext = this.activityDisplayContexts[activityId];
if(renderedActivities.has(displayContext.activityModel.activityId))
if (renderedActivities.has(displayContext.activityModel.activityId))
return;
renderedActivities.add(displayContext.activityModel.activityId);
@ -538,10 +548,12 @@ export class ElsaWorkflowDesigner {
renderActivity(displayContext: ActivityDesignDisplayContext) {
const selected = this.selectedActivityId == displayContext.activityModel.activityId;
return <elsa-designer-tree-activity displayContext={displayContext}
return <elsa-designer-tree-activity displayContext={displayContext}
draggable={true}
selected={selected}
isSelected={selected}
onDragStart={e => this.onDragStartActivity(e, displayContext)}
onSelected={e => this.onActivitySelected(e)}
onDeselected={e => this.onActivityDeselected(e)}
/>;
}
}

View file

@ -49,6 +49,9 @@ export class ElsaWorkflowInstanceJournal {
@Method()
async show() {
if(this.isVisible)
return;
this.isVisible = true;
enter(this.el);
@ -56,9 +59,19 @@ export class ElsaWorkflowInstanceJournal {
@Method()
async hide() {
if(!this.isVisible)
return;
leave(this.el).then(() => this.isVisible = false);
}
@Method()
async selectActivityRecord(activityId?: string) {
const record = !!activityId ? this.records.items.find(x => x.activityId == activityId) : null;
this.selectedRecordId = !!record ? record.id : null;
await this.show();
}
@Watch('workflowInstanceId')
async workflowInstanceIdChangedHandler(newValue: string) {
const workflowInstanceId = newValue;
@ -68,7 +81,7 @@ export class ElsaWorkflowInstanceJournal {
try {
this.records = await client.workflowExecutionLogApi.get(workflowInstanceId);
} catch {
console.warn(`The specified workflow definition does not exist. Creating a new one.`);
console.warn('The specified workflow instance does not exist.');
}
}
}

View file

@ -35,6 +35,7 @@ export class ElsaWorkflowInstanceViewerScreen {
@State() selectedActivityId?: string;
el: HTMLElement;
designer: HTMLElsaDesignerTreeElement;
journal: HTMLElsaWorkflowInstanceJournalElement;
@Method()
async getServerUrl(): Promise<string> {
@ -162,12 +163,25 @@ export class ElsaWorkflowInstanceViewerScreen {
this.selectedActivityId = e.detail.activityId;
}
onActivitySelected(e: CustomEvent<ActivityModel>) {
this.selectedActivityId = e.detail.activityId;
this.journal.selectActivityRecord(this.selectedActivityId);
}
onActivityDeselected(e: CustomEvent<ActivityModel>) {
if(this.selectedActivityId == e.detail.activityId)
this.selectedActivityId = null;
this.journal.selectActivityRecord(this.selectedActivityId);
}
render() {
const descriptors: Array<ActivityDescriptor> = state.activityDescriptors;
return (
<Host class="flex flex-col w-full relative" ref={el => this.el = el}>
{this.renderCanvas()}
<elsa-workflow-instance-journal workflowInstanceId={this.workflowInstanceId}
<elsa-workflow-instance-journal ref={el => this.journal = el}
workflowInstanceId={this.workflowInstanceId}
serverUrl={this.serverUrl}
activityDescriptors={descriptors}
workflowBlueprint={this.workflowBlueprint}
@ -179,7 +193,12 @@ export class ElsaWorkflowInstanceViewerScreen {
renderCanvas() {
return (
<div class="flex-1 flex">
<elsa-designer-tree model={this.workflowModel} class="flex-1" ref={el => this.designer = el} selectedActivityId={this.selectedActivityId}/>
<elsa-designer-tree model={this.workflowModel}
class="flex-1" ref={el => this.designer = el}
selectedActivityId={this.selectedActivityId}
onActivitySelected={e => this.onActivitySelected(e)}
onActivityDeselected={e => this.onActivityDeselected(e)}
/>
</div>
);
}