diff --git a/src/designer/elsa-workflows-studio/src/components.d.ts b/src/designer/elsa-workflows-studio/src/components.d.ts index c054d38a7..6f3599f38 100644 --- a/src/designer/elsa-workflows-studio/src/components.d.ts +++ b/src/designer/elsa-workflows-studio/src/components.d.ts @@ -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; "hide": () => Promise; + "selectActivityRecord": (activityId?: string) => Promise; "serverUrl": string; "show": () => Promise; "workflowBlueprint": WorkflowBlueprint; @@ -483,15 +484,19 @@ declare namespace LocalJSX { } interface ElsaDesignerTree { "model"?: WorkflowModel; + "onActivityDeselected"?: (event: CustomEvent) => void; + "onActivitySelected"?: (event: CustomEvent) => void; "onWorkflow-changed"?: (event: CustomEvent) => void; "selectedActivityId"?: string; } interface ElsaDesignerTreeActivity { "displayContext"?: ActivityDesignDisplayContext; "icon"?: string; + "isSelected"?: boolean; + "onDeselected"?: (event: CustomEvent) => void; "onEdit-activity"?: (event: CustomEvent) => void; "onRemove-activity"?: (event: CustomEvent) => void; - "selected"?: boolean; + "onSelected"?: (event: CustomEvent) => void; } interface ElsaDropdownButton { "icon"?: any; diff --git a/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree-activity/elsa-designer-tree-activity.tsx b/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree-activity/elsa-designer-tree-activity.tsx index 7e5e5f566..fc7299abe 100644 --- a/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree-activity/elsa-designer-tree-activity.tsx +++ b/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree-activity/elsa-designer-tree-activity.tsx @@ -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; @Event({eventName: 'edit-activity', bubbles: true}) editActivityEmitter: EventEmitter; + @Event() selected: EventEmitter; + @Event() deselected: EventEmitter; @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 ( 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`}>
diff --git a/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx b/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx index b9633177b..1c9a6bcc0 100644 --- a/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx +++ b/src/designer/elsa-workflows-studio/src/components/designers/tree/elsa-designer-tree/elsa-designer-tree.tsx @@ -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 + @Event() activitySelected: EventEmitter; + @Event() activityDeselected: EventEmitter; @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){ + removeInvalidConnections(invalidConnections: Array) { 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) { + this.activitySelected.emit(e.detail); + } + + private onActivityDeselected(e: CustomEvent) { + this.activityDeselected.emit(e.detail); + } + render() { const renderedActivities = new Set(); @@ -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 = 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 this.onDragStartActivity(e, displayContext)} + onSelected={e => this.onActivitySelected(e)} + onDeselected={e => this.onActivityDeselected(e)} />; } } diff --git a/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-journal.tsx b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-journal.tsx index 668bde77f..14d0f9d1e 100644 --- a/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-journal.tsx +++ b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-journal.tsx @@ -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.'); } } } diff --git a/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx index 2875d4937..389fd7af8 100644 --- a/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx +++ b/src/designer/elsa-workflows-studio/src/components/screens/workflow-instance-viewer/elsa-workflow-instance-viewer-screen/elsa-workflow-instance-viewer-screen.tsx @@ -35,6 +35,7 @@ export class ElsaWorkflowInstanceViewerScreen { @State() selectedActivityId?: string; el: HTMLElement; designer: HTMLElsaDesignerTreeElement; + journal: HTMLElsaWorkflowInstanceJournalElement; @Method() async getServerUrl(): Promise { @@ -162,12 +163,25 @@ export class ElsaWorkflowInstanceViewerScreen { this.selectedActivityId = e.detail.activityId; } + onActivitySelected(e: CustomEvent) { + this.selectedActivityId = e.detail.activityId; + this.journal.selectActivityRecord(this.selectedActivityId); + } + + onActivityDeselected(e: CustomEvent) { + if(this.selectedActivityId == e.detail.activityId) + this.selectedActivityId = null; + + this.journal.selectActivityRecord(this.selectedActivityId); + } + render() { const descriptors: Array = state.activityDescriptors; return ( this.el = el}> {this.renderCanvas()} - this.journal = el} + workflowInstanceId={this.workflowInstanceId} serverUrl={this.serverUrl} activityDescriptors={descriptors} workflowBlueprint={this.workflowBlueprint} @@ -179,7 +193,12 @@ export class ElsaWorkflowInstanceViewerScreen { renderCanvas() { return (
- this.designer = el} selectedActivityId={this.selectedActivityId}/> + this.designer = el} + selectedActivityId={this.selectedActivityId} + onActivitySelected={e => this.onActivitySelected(e)} + onActivityDeselected={e => this.onActivityDeselected(e)} + />
); }